Mini-Pkg: 簡易パッケージシステムの設計
iOSやAndroidのようなシステム側のファイルシステムがそもそもパッケージをサポートしている場合は、そのパッケージシステム(AAsset
等)を直接使うことになる。PCやWebではパッケージシステムが存在しないため自前のものを設計する必要がある。
Prior art
手元のプロジェクトでは伝統的にスクストの事例 https://www.jp.square-enix.com/conference/2014/technical_seminar/img/pdf/SQEX_DevCon_sugimoto.pdf を手本にしたidベースのパッケージシステムを使ってきた。が、YuniframeはUnityや他の既存のPOSIXアプリをWasm経由で動かすというrequirementが有るため名前ベースのファイルシステムを採用せざるを得ない。
よくあるライブラリとしてはSDLと同作者のPhysicsFS https://icculus.org/physfs/ がある。これはパッケージとして.zipのような既存のアーカイブフォーマットを使用する。
Requirements
今回のターゲットはアセットエディタ(いわゆるお絵描きソフト)なので大量のスクリプト(4KiB以下)および多くのアセット類(256KiB以上のBMP類)を抱えることになる。これは要求としてはゲーム自体と同様のものと言える。
- 64KiB程度のブロックサイズでデータを圧縮できる。Zopfli( https://github.com/google/zopfli )...だとやりすぎかもしれないので、Zstd( https://facebook.github.io/zstd/ )のLZ4相当モードあたりだろうか。。Deflateが考察に入っているのは、DirectStorageがGDeflate( https://github.com/microsoft/DirectStorage/blob/main/GDeflate/GDeflate/README.md )として専用のエンコードを持っているため。
- マイコン向けだとZX0 https://github.com/einar-saukas/ZX0 だろうか。。
- 非圧縮も選択できる。
- データ化けを検出できる。xxHashのXXH32で良いかな。(LZ4がオプションとして採用している) ハッシュ関数をチェックサムとして使うことはxxHashのissueにも議論( https://github.com/Cyan4973/xxHash/issues/229 )があるが、まぁ大きな問題ではない。
- オーバーレイによるパッチやDLCをサポートする
アーカイバとかを用意する前に、 AAsset
のような他のアセットシステムと共通に使えるwrapperを用意した方が良いかな。
パスをハッシュ化するかは悩みどころ。。ハッシュ化するならFNV1aかな。巨大領域だと遅い https://aras-p.info/blog/2016/08/09/More-Hash-Function-Tests/ けど、まぁシンプルなので。
ファイル領域の抽象化
通常のファイルシステムとかなり違うのでどうやってドキュメントしたもんか。。
path
はC文字列であり、通常のスラッシュ区切りのファイルパスやWindowsにおけるバックスラッシュ(円記号)区切りのパスの 両方 を取り扱う。プログラマはファイルパスがMini-Pkgで扱われる内部のパスなのか、OSネイティブのパスなのかを意識する必要がある。
プロバイダ
は path
を resolve
(後述) できる主体であり、OSのファイルシステムであったり、パッケージであったりする。Mini-Pkgのランタイムは、いくつかデフォルトのプロバイダを提供し、バックエンドに依存しないプログラミングを可能にする。プロバイダは使用前に activate
されている必要があり、必要に応じて deactivate
できる。プロバイダによっては、複数の activate
可能な領域を持っていることがある。
全ての path
は resolve
される必要がある。一度Resolveされたパスはidが発行され以降の処理はidを通して行う。 idは、使用後解放する必要がある 。 resolve
はファイルのopenではなく、メモリの許す限り行える。(普通のシステムはファイルを開ける数は4096とかで打ち止めになるので、ライブラリ側で自動的に閉じたりといった配慮が必要になる。)
I/Oキューはとりあえず "default" の一本だけとする。たぶんプロバイダに紐付けられた高優先度かつ低レイテンシキューと、共通の低プライオリティキューは追加で必要だろう。
プロバイダの遅延
プロバイダはいくつかのプラットフォーム要因の遅延を起こすことがある。
- On Demand Resourceの取得
- クラウドストレージとの同期や衝突
- 前回異常終了時からの回復処理
API的にどう吸収すべきかは何とも言えない。アプリケーションでハンドルさせない方が良いのかもしれない。。