
本棚から、パロディー版ASCIIを掘り出したら、2001年のパロディー版BSDマガジンも出てきました。
パロディー版BSDマガジンには、南新宿アドベンチャーが掲載されています。
いうまでもなく、これは、表参道アドベンチャー、南青山アドベンチャーと続く、パロディー版ASCIIの定番ゲームの流れです。
購入当時、実は、自宅にあったFreeBSD環境(PC-9821)が調子悪くなっていて、Linux上でビルドしようとしたら、コンパイルエラーが出まくって、そのまま放置していたんですよ。
掘り出したら、どうしてもやってみたくなって、コンパイルエラーを直してみることに。
コンパイルエラーは、gccの文字列の扱いが、BSDのそれと多分違っていることに起因するものでした。オリジナルのソースは、文字列リテラル……即ちダブルクォーテーションマークで囲まれている範囲に改行を含んでもいい、ということを期待しているのですが、gccはこれをエラーとしてはじきます。文字列は一行に収まっている必要があるようで、行ごとに、ダブルクォーテーションマークを閉じて、行末に\nを挿入してやって、エラーを消すことには成功しました。
が、時の流れは、他の部分にも問題をもたらしていました。2001年当時は、UNIX系の日本語環境は、EUC-JPを文字コードとして使うのが主流でした。なので、南新宿アドベンチャーもご多分に漏れず、メッセージの類いは全てEUC-JPで既述されています。ネタバレしないようにと、ROT13/47でエンコードされたメッセージも、デコードして出てくるのはEUC-JP。
しかし、今時のLinux環境は、文字コードはUTF-8なので、化け化けです。エンコードされていない文字列は、とりあえず、nkfでUTF-8にしてしまい、エンコードされている文字列は iconvで出力時にEUC-JP→UTF-8変換してやることにします。
これでゲームになる……と、思ったのですが、出てくるメッセージがまだ壊れ気味です。原因を探っていくと、どうやら、文字列を表示するrawmsg()という関数が問題を起こしているらしいことに気づきました。
メッセージに埋め込まれた '$M'とか'$P'とかのマクロ文字を適当な文字列や処理に展開しながら表示する関数なのですが、文字列の処理がEUC-JPにべったりと依存しているのに気づきました。
else if((*s & 0x80)== 0){
fputc(*s, stderr);
}
else{
fputc(*s, stderr);
++s;
fputc(*s, stderr);
if(!status.f_nowait){
if(s[-1] == "。"[0] && s[0] == "。"[1]
&& s[1] == "。"[0] && s[2] == "。"[1] )
usleep(200000);
else if(s[-1] == "…"[0] && s[0] == "…"[1]
&& s[1] == "…"[0] && s[2] == "…"[1] )
usleep(200000);
}
}
この部分、MSBが立ってたら、2byte表示した後に、表示したモノが、「。」か「…」なら、ウェイト処理をするというものですが、MSBが立っていたら日本語とか、日本語は2byteとか、EUCにべったりな実装です。ここにUTF-8のような可変長文字コードを喰わせたらまともに動くわけがありません。
なので、UTF-8べったりな実装で置き換えました。
else if((*s & 0x80)== 0){
fputc(*s, stderr);
}
else{
char *tmp = s;
fputc(*s, stderr);
++s;
while((*s & 0xC0) == 0x80)
{
fputc(*s++, stderr);
}
--s;
if (!status.f_nowait)
{
if (strncmp(tmp, "。", strlen("。")) == 0 ||
strncmp(tmp, "…", strlen("…")) == 0)
{
usleep(200000);
}
}
UTF-8の文字列は可変ですが、2byte目以降の上位2bitは、必ず"10"になるように定められています。なので、1byte目を表示したあと、次の文字の先頭の手前まで表示して、さらに表示した文字が、「。」か「…」かを、strncmp()で比較しています。本当は、wchar_tを処理する関数でやるべきでしょうが、まあ、コレでも動くのでよしとしておきます。
とりあえず、この変更を加えたところで、それらしく動くようになりました。が、秋葉原駅で早くも行き詰まりました。どうにもなりません。さて、苦労して始めたゲームの明日はどっちだ?
コメント