UEFIでうごくゲームを作ってみた〜2017年夏休みの自由研究〜

58ab8900254150820e685da7cd0d4d9f?s=47 Narazaka
September 13, 2017

 UEFIでうごくゲームを作ってみた〜2017年夏休みの自由研究〜

UEFIで動くノベルゲーを作ってみたので、その時の苦労とか知見とかまとめた自由研究発表です。
発表時のコードとかはこちら https://github.com/Narazaka/efigame

58ab8900254150820e685da7cd0d4d9f?s=128

Narazaka

September 13, 2017
Tweet

Transcript

  1. None
  2. 奈良阪 奈良阪 ドリコム2015新卒(3年目) T yp e S cr ipt と

    R u by 書いてます T yp e S cr ipt / C # / P er l / R u by あたりが好き その他: 伺か / 漫画読み描き / OP アニメ愛好家 / 鉄 T w itt er: @nar az aka G it hu b: @N ar az aka np m: @nar az aka CPAN : NARA Z AKA R u byG ems: N ar az aka P ix iv: 奈良阪 M edia M ar ker: nar az aka
  3. ※ 注意: いわゆる「◦◦ やってみた」 系の発表です。 UEFI ガチ勢、C ++ガチ勢の方々 は生暖かく聞いて下さ い。

  4. 夏休みの自由研究の目標 ( そういうテー マでなんかやってくれって言われたので) UEFI で動くなんらかのゲー ムエンジンを作りたい

  5. UEFI とは コンピュー ター で OS を立ち上げる前に動く BIOS 関係の仕様 W

    indows 立ち上げる前に HP とか T hink P ad とか Z OT AC とかロゴ が出てるあそこ 2010年以降の PC (M ac込み)で旧来の BIOS から徐々 に置き換わって いる OS なし状態にもかかわらず結構高水準な API が存在する← ここ重要 例: キー ボー ド, マウス, ファイルシステム, 画面表示, ネットワー ク, et c.
  6. モチベー ション ‑ 個人的に「 どこでも動く」 プログラムが至高という想い ‑ 近年の PC にはあまねく

    UEFI が載っている ‑ UEFI で実装すれば OS という枷を超えてどこでも動く ‑ JVM なんかメじゃないぜ( 無理 ‑ 最近 C ++触ってなかったのでリハビリしておきたい
  7. 参考資料 C 92(コミケ)新刊: フルスクラッチで作る!UEFI ベアメタルプログラミング (大神 祐真) http://yu ma.ohgami.jp/ ※

    ベアメタルプログラミング: OS を使わない、 ハー ドウェア上の低レイヤー のプログラミング ※ この資料上では仕様からヘッダファイルを起こすところまで自作していますが、 さす がにきついので EDK II 由来のヘッダファイル群 kiz nit/u efi‑headers を利用しました。 リファレンスとしては P hoenixW iki が割と見やすいと思います。
  8. UEFI ベアメタルプログラミング OS なし libc/libc++ではない(POSI X標準ではない)独自の API ベー ス C

    ++ (参考資料では C ) ファイルシステムは FA T (USB フラッシュメモリに入れる) 動作確認は QEMU 最近の PC (M ac系含む)には載ってるので一応ある程度動作するはず
  9. 実装したもの とりあえず UEFI の API をラップ C ons ole(wr it

    e L ineとか), F ile S yst em(r ead, r eaddir 等) ゲー ムに要りそうなやつ 描画系(1dot、 矩形、 円形、 画像描画) 入力系( get c相当他、 on K eyP r ess、 on M ous e*) 簡易イベントルー プ ゲー ムに要りそうなやつで付随して必要になったもの p ng等一般画像形式読み込みライブラリの統合 st dlib.hとstr ing.hの一部(mallocとか) フォント ゲー ム本体 簡易シー ンマネー ジャー( 諸事情によりマクロ) ノベルゲー ムシナリオインター プリター マウスカー ソル描画
  10. 大体こんなん こういうシナリオファイルと画像を入れておけばマウスキー ボー ドポチ ポチで読み進められるノベルゲー # b g _ s

    c h o o l . p n g ( 背景ファイル) : R 0 = s u r f a c e 0 . p n g ( 人物0 のファイルを右に描画) : L 1 = s u r f a c e 1 0 . p n g ( 人物1 のファイル左に描画) @ ねぷ ( 名前欄) > はろー ( セリフ) ‐ ( ここまでが1 セリフ) : R 0 = s u r f a c e 1 . p n g > にゅー わー るど ‐
  11. アレのパクリスプラッシュ

  12. マウスクリックでも動きます

  13. ポチポチすすめる ADV

  14. ポチポチすすめる ADV

  15. ポチポチすすめる ADV

  16. C onsole(write L ineとか), F ile S ystem(read, readdir等) st

    dout とst derr の別はないが文字は出せるので普通にラッパを実装 op en, r ead, r eaddir, clos eの流れは大体一緒 op endir とop enの区別がなくて一緒くたに処理する なお文字列は w c h a r _ t * 相当の C H A R 1 6 * = E F I _ S T R I N G (UTF 16LE ) パス区切りは FA T っぽく \ \ ( E F I _ S T R I N G ) L " f o n t s \ \ 3 3 . p n g "
  17. 描画系 1ドットごと色変更と、 ビットマップをバルク転送する B lt() API 矩形、 円形描画、 画像描画( 透過色対応)

    など実装 参考に読んだ記事では EDK II では透過対応が完全ではないとのこ と。 ( まあ見るからに重い処理だし……) 入力系 待ちなしでキー 入力とマウスが受けられる イベントルー プ中差分把握を実装してon M ous e L eftC lick等も実装 QEMU だとマウスが効かなかったので実機確認のみになった
  18. イベントルー プ 複数のイベントを待ち受けできる API が存在するので、w hile(tru e) ルー プ内でイベント番号でsw it

    chして処理できる 今回は30fps で発生するイベントとキー ボー ド入力イベントをとっ てます マウスカー ソル 1フレー ムごとに、 描画する領域色を保存して描画して前にいた場 所の色を戻すみたいなことをしている オフスクリー ンレンダリングやレイヤー 利用していないので、 タイ ミングによっては色が乱れる 画像、 フォント、 libc、 シー ンマネー ジャー このあと述べます
  19. 直面した問題・ ハマりどころ (UEFI だから、 というより制約下のフルスクラッチ風味 C /C ++開発故と いうものが多いです)

  20. libc/libc++相当が使えない クロスコンパイラ氏、 そういうものを用意してくれてはいない。 最終的に m a l l o c

    , f r e e , r e a l l o c , a b s , i t o a ( s t d l i b . h ) m e m s e t , m e m c p y , s t r n c p y , s t r c p y , s t r c a t , s t r l e n ( s t r i n g . h ) を自前で書くことに……。 / / 例: m a l l o c v o i d * m a l l o c ( U I N T N s i z e ) { v o i d * p t r ; E F I _ S T A T U S S t a t u s = l i b c : : B o o t S e r v i c e s ‐ > A l l o c a t e P o o l ( E f i L o a d e r D a t a , s i z e , & p t r ) ; r e t u r n S t a t u s = = E F I _ S U C C E S S ? p t r : n u l l p t r ; } フルスクラッチ風味とかこだわらず単に UEFI アプリケー ションを作りたいだけなら、 EDK II という SDK を使うとこの辺には困らないはず。
  21. 画像処理系等のライブラリ stb の導入 やっぱ画像なら PNG を読みたい。 フォントも使いたい。 しかし前提としてlibcがないのでlibp ngとかは無謀そう。 stb

    ‑ single‑file public domain libraries for C /C ++ (nothings/stb) 画像、 音声、 タイルマップ、 フォント周りとかが揃ったシングルファイ ルヘッダー 構成かつ他への依存がないライブラリがあった。 神か。
  22. 画像処理系等のライブラリ stb の導入(続き) #inclu de <st dlib.h> // 自前libc #define

    STB _IMAGE _IMPLEMENT A TION #define STBI _ASSERT (x) // assert.h がないので無視 #define STBI _NO _STDIO // stdio.h がないのではずす #define STBI _NO _LINEAR // math.h がないのではずす #define STBI _NO _HDR // math.h がないのではずす #define STBI _ONL Y_PNG #define STBI _MALLOC (sz) malloc(sz) // 自前malloc, free, realloc #define STBI _FREE (p) fr ee(p) #define STBI _REALLOC _SI Z ED (p,oldsz,newsz) r ealloc_s iz ed(p,oldsz,newsz) #inclu de <st b_image.h> // stbの画像処理系本体 制約の強い環境も考えてくれてる良いライブラリだ……
  23. 画像処理系等のライブラリ stb の導入(続き) 画像読み込み系のst b_image.hは導入できた 画像リサイズ系のst b_image_r es iz e.hはmath.hが必要

    フォント処理系のst b_tru etyp e.hもmath.hががっつり必要 → mat h.h実装がないと画像リサイズ不可&フォントは別で用意する必要
  24. reallocをまともに実装すると実は大変 st b導入に際し r e a l l o c

    が必要になったが…… ネイティブ API は m a l l o c 相当の A l l o c a t e P o o l と f r e e 相当 の F r e e P o o l しかない この制約下での r e a l l o c は f r e e → m a l l o c か、 最初にメモリ位置 指定までできる A l l o c a t e P a g e s を使うしかなさそう。 どちらにしても、 現在どのポインタがどのサイズを持っているかとい う情報を保持せねばならない。 ↑ 大変 (EDK II では頑張って実装していた。) (st bにはサイズ情報を追加で渡すr ealloc_s iz edという拡張呼び出しが用 意されていたのでなんとか事なきを得た。)
  25. 失われた math.h を求めて 1. フォントやリサイズを使うためにはmat h.hが必要 2. ヘッダー ファイルとしてのmat h.hは使えるのだが、UEFI

    クロスコ ンパイル環境だと実装がなさそう? 3. 「 mat h.h 実装」[検索] 4. どうやらfdlibmというライブラリがもとになっているらしい? 5. fdlibmは古く、W ebでファイルを配ってるだけなので導入しにくさ 6. ただし M akefileがめっちゃシンプルで良い 7. G it hu bにあるのだと J ulia L ang/openlibm が使いやすそう 8. 結局時間切れにより導入には至らず
  26. フォントの用意 前提: BIOS に日本語フォントはなく、ttf、 otfは使えない → フォントを PNG にして都度読み込み 1.

    フォントを用意 JF ドット K app a20‑0213 2. t ar lety/tt f2p ngがフォントが対応している文字ごとに1つずつテキス トファイルに出してくれるのでその部分だけ使う( 他部分は古くて 動かなかった) 3. テキストファイル全部をさらって I mage M agickでp ngにする c o n v e r t ‐ b a c k g r o u n d n o n e ‐ f i l l b l a c k + a n t i a l i a s ‐ f o n t J F ‐ D o t ‐ K a p p a 2 0 ‐ 0 2 1 3 . t t f ‐ p o i n t s i z e 2 0 l a b e l : @ # { t e x t _ f i l e } f s / f o n t s / 文字コー ド数値. p n g " 4. UEFI では FA T ファイルシステム上においたファイルが普通に読める ので頑張って1文字ごと表示時に画像読みまくって描画
  27. フォントの用意( ボツ案) UEFI では FA T ファイルシステム上においたファイルが普通に読める ので頑張って1文字ごと表示時に読みまくって描画 1. 控えめに言って酷い

    2. 昔のコンピュー タの日本語フォント ROM よろしくデー タを全部 st at icに持ってしまうのはどうか? 3. p ngからむりやりデー タのcpp ファイルを生成↓ c o n s t p n g = P N G . s y n c . r e a d ( i m a g e . r e a d F i l e S y n c ( ) ) ; . . . f o n t s S t r + = ` s t a t i c U I N T 8 c f o n t _ $ { c h a r C o d e } _ a l p h a [ ] { ` ; f o r ( l e t i = 0 ; i < l e n g t h ; + + i ) { f o n t s S t r + = ` $ { p n g . d a t a [ i * 4 + 3 ] } , ` ; } f o n t s S t r + = ` } ; \ n ` ; バイナリが8MB くらいになって、 起動失敗したので断念
  28. C ++はlib(std)c++がないとクラス関連機能が 色々 使えない シー ンをしようとクラスを作ったところで判明。 まずnew 演算子の参照がないと怒られるのでnew できない →

    v o i d * o p e r a t o r n e w ( U I N T N s i z e ) { r e t u r n m a l l o c ( s i z e ) ; } など定義することで一応解決はできる v irtu al関係はlibc++が型解決を受け持っているらしく使えない → cxx abi.hで定義されててる__class_typ e_infoまたは __v mi_class_typ e_infoと言うクラスがないと実現できないっぽい。 C ++は言語組み込みでクラスの全挙動をサポー トしているわけではなく、 インター フェー スだけ用意して実装は本質的に自前でやる必要がある…… だと……ッ!?
  29. なおvirtualが使えないと…… シー ンマネー ジャー を作ろうとしたんですが…… / / 親クラス = n

    e w 子クラス( ) S c e n e * s c e n e = n e w S t a r t S c e n e ( ) ; s c e n e ‐ > u p d a t e ( ) ; この u p d a t e ( ) が子クラス S t artS ceneのものを指せないので、 無理矢理 ダウンキャストするなどしかない……? 今回はマクロで定数定義してsw it chで分岐するなど無理矢理対応し た……。 # d e f i n e s c e n e I d ( C l a s s ) C l a s s # # I d # d e f i n e d e l r a r e S c e n e ( C l a s s ) s t a t i c C l a s s * C l a s s # # _ o b j # d e f i n e i n i t S c e n e ( C l a s s ) C l a s s # # _ o b j = n e w C l a s s ( ) # d e f i n e s c e n e C a s e ( C l a s s ) c a s e C l a s s # # I d : \ C l a s s # # _ o b j ‐ > u p d a t e ( ) ; + + C l a s s # # _ o b j ‐ > t i c k ; b r e a k ; # d e f i n e c h a n g e S c e n e ( C l a s s ) c u r r e n t S c e n e I d = C l a s s # # I d
  30. 60fpsが出ない 多くのゲー ムにおいて最大の障害 単純な w h i l e (

    t r u e ) ; ではなくタイマー で描画スピー ドを調整でき はするが……? C or e i7が載ってる PC の QEMU では元気に60fps 出してくれる Z BO X CI 320だと30fpsもでない → 早期に認識していたので、 その辺関係ないノベルゲー に絞った やり方が間違っていたりするだろうか……?
  31. M acで立ち上げると FA T が読めなさそう? 会社の M acでも立ち上げてみたけれど、 画像とかが USB

    メモリから 読めてなさそうだった UEFI 、FA T 読めるはずだけど、 仕様上実装必須ではないのか? 「 どこでも動く」 の障害になりそう
  32. 解像度変更すると画面上の各ピクセルを表す ポインタ位置がバグる 解像度変更の API があるのだけれど、 画面上の色を表すポインタが 画面上シー ケンシャルにならない UEFI 起動すると解像度変更しなくてもディスプレイ最大に合わせて

    くれるのであまり意識しなくて良い事項ではある
  33. 描画の微妙な問題? 描画系(GRAPHICS _OUTPUT _PROTOCOL ) の API コー ル、 関数を

    またいで呼ばれると描画処理が行われない場合がある? とりあえず同じトコにまとめちゃったりしてうやむや解決したので 定かではないです。
  34. 課題( 積み残し) UEFI で動くなんらかのゲー ムエンジンを作りたい mat h.hを入れてフォントとリサイズしたい レイヤー 管理等がなくベタ描画なのでチラつくし面倒 イベントルー

    プまではなんとかしたが、 シー ン管理とかは大分微妙 最後突貫工事でなんとかノベルゲー にしたのでライブラリの形には 整っていない ……思ったより意外と積み残しが少ない? ライブラリ化だけなんとかしてこのまんまギッハブに投げてもいい……?
  35. まとめ UEFI で動くなんらかのゲー ムエンジンを作りたい ゲー ムエンジンではなく単体のゲー ムができました 主に強い制限状況下での C ++コー

    ディングで、C ++の基盤を垣間見 ることができました ゲー ムエンジンっぽい部分は一応分けてあるのでなんとかして公開 したいですね