WebGL-Native: 脱 require("fs");
prev: https://zenn.dev/okuoku/scraps/1d00a240007452
next: https://zenn.dev/okuoku/scraps/9b315e2f5a6639
ファイルシステムを自前の関数でアクセスする
...これは面倒くさいぞ。。
現状OS抽象化層としてはSDLを採用しているが、このSDLは基本的にゲーム用で、 ファイルの列挙や更新日付の取得などの機能がない 。このため、これらの機能は自分でSDLに頼らず実装する必要がある。
今回は、ファイルの実際のアクセスとメタデータへのアクセスの実装を分離し、メタデータへのアクセスはC++17の std::filesystem
を使うことにする。
SDLのファイル読み書きルーチン
よくあるopen/close/read/write/seekをサポートしているが、 パスはUTF-8を常に使用する必要がある 。Androidにおける.apkの内部など、通常のファイルシステムが使えないケースでもこれらの抽象化を活用できるため、可能な限りこちらに寄せておいた方が移植性の面では便利と言える。
SDLはディレクトリ内のファイルを列挙するといった機能性は提供しない。常識的なシチュエーションでは、ゲームは自分自身が開くべきファイルは事前にわかっているので問い合わせる必要がない。
C++標準ライブラリのファイルシステムライブラリ
C++17はかなり強力なファイルシステムライブラリを備えていて、UTF-16とUTF-8の両方をサポートしている。これらはWindowsでも使用でき、ネイティブコードで記述する場合は最もポータブルな手段と言える。
ちなみにモバイルの場合、iOS 13以降(2019年)、Android NDK r22 以降(2020年)のサポートとなっている。
Clang now supports the C++17 <filesystem> library for iOS 13, macOS 10.15, watchOS 6, and tvOS 13.
実装が必要なAPIの列挙
$ git grep fs\\. storage.js
storage.js: const stats = fs.statSync(path);
storage.js: const stats = fs.statSync(pathexpand(node));
storage.js: fs.mkdirSync(path, node.mode);
storage.js: fs.writeFileSync(path, "");
storage.js: fs.renameSync(from, to);
storage.js: fs.unlinkSync(target);
storage.js: fs.rmdirSync(target);
storage.js: return fs.readdirSync(path);
storage.js: const fd = fs.openSync(path);
storage.js: fs.closeSync(stream.NativeFD);
storage.js: const stats = fs.fstatSync(stream.NativeFD);
storage.js: return fs.readSync(stream.NativeFD, buffer, offset, length, pos);
storage.js: return fs.writeSync(stream.NativeFD, buffer, offset, length, pos);
意外と少いな。。
open/close
openSync
closeSync
メタデータAPI
statSync
fstatSync
mkdirSync
rmdirSync
renameSync
unlinkSync
readdirSync
ちなみにstatの結果は is_directoryとサイズしか使っていない。
アクセスAPI
readSync
writeSync
wrilteFileSync
DOSboxだけ動かない問題
一応実装できたが、なぜかDOSboxだけクラッシュしてしまう。適当にprintを入れて確認したところ、何故か.EXEに発行されるReadが1つ少いようだ。
(← Node.js標準の fs
実装 : 今回手作りした実装 →)
... これは単にEOFを越えてreadしようとするとエラービットが立つのが要因なようだ。 clear()
を入れることで正常になった。