プログラム講座実践編

粗筋

プログラム講座第2弾です。 前回初心者用の準備が完了したので、 今回は実践的なの行ってみます。
何をやるか、というと、 XNScrの移植(爆)

とりあえずリンクとか張るのが面倒なので、 NScripterって何? とか、月姫って何? とか歌月十夜頂戴、 って何?とかは置いときます(^^;
で、この企画、 かなり入門編、という趣では無いのですが、 大人になるっていうのはそういう事なんだよ。
目標は、テキストのみ表示、が出来たら終了、 とします。 DOSモバで絵が見れる事に意味があるかどうかは微妙なので。

入手

入手は、 この手の偉い人、 ごうさんの所から取ってきます。
Current(最新版をこう呼ぶ)は大抵このように、 時代はCVSだ、 とかいう展開になります。
あぁ、CSVね、Excellから他のに持って行く時によく使うよ、 などと勘違いしてはいけません。 あれはCSV、カンマでセパレートした単なるテキストファイルです。 CVSはリビジョン管理、 というか、 まあanoncvs、というのにしててくれるなら、 誰でも持って来る事が出来るので、 みんな幸せ、という物です(偉く違うけどまあいいです)
チェックイン出来ません、 とか思う所まで行ったら、 偉い人と交渉してアカウントもらうなり、 アーカイブどっかに置いてそのURLをメールで送るなり、 いろいろする訳です。

では実際にやってみましょう。 cvsをインストールして、なんかsshとかいろいろ入れたりとかしますが、 ここらへんは割愛。 よくおぼえてません。ごめんなさい。
で、コマンドラインから、
cvs -d :pserver:anoncvs@denpa.org:/cvs/ login
とか入力すると、
(Logging in to anoncvs@denpa.org)
CVS password: 
とか表示されるので、 何も考えずに
anoncvs
と打ち込みます。 すると 何事もなかったかのようにコマンドプロンプトに戻るので、 また何も考えずに
cvs -d :pserver:anoncvs@denpa.org:/cvs co xnscr
と打ち込むと、カレントディレクトリにわらわらと展開されます。 で、こいつで最新版ゲッチュー出来た訳です。 アップデートの方法とかいろいろありますが、 割愛。

あたりをつける

とりあえず最初にやる事はあたりをつける事です。 この手のゲーム、 とかを解析したり移植したりしてる人々は、 元々クロスプラットフォームを意識してる人が多く、 グラフィックス回りとか、プラットフォームに依存しそうな所は、 切り分けしててくれる事が多く、 そこの切り分けられた部分だけをMGL化すると、 内部を一切理解してなくても、 結構簡単に出来たりします。
つまりスキルなんて大していりません。 特に、今回のように文字だけ表示、 の場合はすごく簡単な事が多いです。
閑話休題、で、 そのプラットフォーム依存な所を探します。 %mobiledevで、ピアーはどこですか? とか聞く、 とかいう大人の選択肢もありますが、 入門編、 という事で教えて君モードはやめます。 なお、peerというのは、 プラットフォームにどうしても依存してしまうような、 非常に原始的な部分を集めた物です。 peer、というのがあるプログラムは、 原理的にはそこだけ移植すると動くはずです(世の中そんなに甘くないですが)
で、展開すると、

CVS/ X11/ win32/
の3つのフォルダがあります。 おぉ、XNscrなのにwin32があるよ! これにこっそりメッセージスキップでも実装しちゃおうかな、 とか良くない考えが一瞬頭をよぎりますが、 そういう事する人嫌いです、 という事でやめます。
で、あたりを付けるなら予測が必要です。 まず、おそらく仮想スクリーン的メモリ配列を用意して、 そいつにいろいろ描画したりする、 というあたりまではプラットフォーム非依存でやってる、 と仮定します。 どうせ彼らはザウとかポケピとかに移植して、 歩きながらシエル先輩ハァハァ、 とかやるに決まってます(決まってません)
という事は最初からそんな感じで作ってる可能性が極めて高い訳です(希望)
理想としては、 画像の配列、等は使わないので切り離したいのですが、 まあそこらへんは内部がわかってから考えます。
という事で、 その配列を画面に表示する所、 とか、文字列を画面に表示する所、 とかを探して、 画像関係は空の関数、 文字列描画は真面目に実装、 という路線で行ってみます。 変数保存、 等はXのコードがそのまま流用出来るでしょうから、 X11、という方をベースにします。
で、X11をまずはlsしてみます。
CVS/		   FullScreenShellP.h  NscripterCore.h	 config.tmpl
FullScreenShell.c  Imakefile	       NscripterCoreP.h  sximage.c
FullScreenShell.h  NscripterCore.c     XtMain.c		 sximage.h
CVSっていうのはいつでも無視していいです。 で、なんかFullScreenShell、とかいうのは、 画面の大きさ一杯にウィンドウ開いてフルスクリーン化、 とかいう処理な予感がするので、DOSモバにはいりません。 XtMain.cとsximage.cあたりがそれっぽい。
XtMain.cというのはいわゆるmainファイルだと思われます。 ここでは、ウィンドウシステムの初期化して、 イベントループ回す、という感じだと予測。 まあここでこの予測を確かめてもいいけど、 せっかくwin32用もあるのだから、 こいつも利用しよう、という事で、 win32の方も見る。 lsすると、
CVS/	  button.c  graphic.c  scenario.c  system.h	   window.c
Makefile  button.h  graphic.h  scenario.h  system_win32.c  window.h
だって。 あれ?共通のファイルが無いよ? うーむ。 よくわからんので、 とりあえずチャットで作者に聞く(ぉ
教えて君モードはやめるんじゃなかったんですかぁ〜、 とか言う人、嫌いです。
で、返事が来るまでの間でちらちらと覗いてみる。 こういう時は、ヘッダファイルを覗いて予測して、 それっぽい関数の実装を.cの方で見る、 というのが正しいあたりのつけかたです。 という訳でsximage.hを見るとlvns、とかなんか書いてある(笑)
ふむ、ごうさんがかいた、とか言ってたので、 lvnsのソースベースでいじった、 という感じなのだろうな。
で、見るとここにあるのは、 データ構造としては画像構造体、カラーマップ、という感じで、 関数としては、画像を表示、画像を破棄、 画像をクリア、 エフェクト用のpeer部分とおもわれる物、 copy_area、 とかそういうのみたいだ。 つまり画像表示回りのPeerらしい。
とここまで来て、 あのKenjoさんが、 いろんなファイルをwin32の方のフォルダからlnしてるんだよ、 とヒントをくれる。
おぉ、本当だ。 IMakefile読むのをさぼってるのバレバレ。
という事で、 情報を整理。
Xの方には、Xに必要なファイルしか入っていない、 という事は、 ここにあるファイルは全てpeerである、 という事がわかる。 で、そのうち、FullScreen回りはいらない、 と思われ、 sximageも画像つかわないならいらない気がする。 ひょっとしたらバックログとかスクロールとかで使うのかもしれないから、 あくまで予測だけど。
で、NscripterCore.cと、XtMain.c あたりをちらちら読む、が正しい、という事かな。 NscripterCore.hはまだよんでないけど。
ここまでで大切な事は、 ヘッダファイルを読む、という事。 これには公開されてる関数が含まれているので、 これを読むだけで、その.cが何をやるかは、 普通は予測がつくように出来てる。
ここにダーっとグローバル変数があるようなのは、 ちょっとつらいコードです。 そういう時は覚悟を決めてがんばって読むしか無い事が多いですが、 今回はそういう事もなさそうです。
で、実際にこの手のソフトを作るとどうなるだろうか、 という事を私は知っているので、 sximage.hを読むとそのプログラム内での位置付け等がわかったりするのですが、 これは知らない類のコードを読む場合は自分で調べないといけません。
これを調べるには、 mainからコードを追う、 が楽だと私は思います。
で、どういう関数が呼ばれているか、 というのをなんとなく把握したら、 ヘッダファイルを覗く、 という感じ。 ここらへんは勉強として面白いのですが、 今回は私が知ってるのでスルー。
余談ですが、 この手の、いろんなジャンルのアプリがどう作られているか、 を知っている、 というのはプログラマーとしては重要な気がします。 これらをたくさん知っていれば知っているほど、 知らないジャンルを学ぶのも楽になるし、 知ってるジャンルの物は簡単に組めます。 綺麗なコード書く、 という目標を達成するにも、 やはり最初から大筋を理解してて、 インターフェース考える時点でそれなりに練る、 というのが重要だと思う訳で。
で、話を戻して、今度はNscripterCore.hを見ると、、、 公開関数が全然無いじゃん!ん?なんでだ? どっかで定義されてるのの実装だけをNscripterCore.cが持ってる、 と考えるのが自然だな。 なんかいろいろincludeしてるし。
という訳でNscripterCore.cをほげほげする、 が作業の大部分になりそうな予感ではある。 というか、ほとんどを空の関数にするだけだろうなぁ。
という事でだいたいイメージがまとまる。 路線、XtMain.cとNscripterCore.cをいじる。 で、Makefileで外す、 という感じ。 Imakeなんてしったこっちゃ無い、 という方向で直にMakefileつけとく。 という感じ。 ここまでで、予想作業量を見る。
cat NscripterCore.c XtMain.c | wc
と実行すると、
    683    1666   15831
だそうで。 683行か。 余裕だな。 とかとらぬ狸の皮算用してみる訳です。

二日目

まあ基本的に、 やる事はたくさんあるのでちまちまやる。 ある程度作業が進んだらモバの方にうつして、 電車の中等で作業するが、 今はモバは伽藍の洞、 という月姫プラスディスクに入ってた小説読んでるので、 他の物入れる気無し。
今日もまあ、 あたりつける続き、 って感じではあります、はい。 とりあえずXtMainをちらちら見ると、 main関数はなんか初期化した後に、 どうもNscripterMain、というのを呼び出してるっぽい気がする。 本当に初期化程度しかやってないのかはよくわからんけど、 とりあえずMain、などという名前がついてる位だから、 これがメインなんだろう、と信じてみる。
で、grepかけます。 emacs使いなので、

M-! grep -e "NscripterMain" *.c *.h
と反射的にキーを叩くと、 NscripterCore.cがひっかかった。
という事でこのファイルを開く。 etagsはいるかなぁ、とも思ったけど、 まずはなしで行く。 この程度の規模ならたぶんいらない。
で、NscripterMainを読むと、 バリバリのイベントループっぽい。 基本はscn_run()とwnd_print1()で、 PRINTING、 というのは、なんかstateとかフラグ立てたりしてるので、 たぶん基本的にはウェイトかけて出力だけど、 マウス入力があったら飛ばす、 という処理である、 とあたりをつける。もちろん大嘘かもしれないけど、 予測があたれば読む量は減るので。
という事で、まずはwnd_print1()、 というのが出力処理であるかどうかを追ってみる、、、、 前に、state、 ってのがどっから降ってくるのかを追ってみる。 何故なら、 こいつはイベントキューから取り出される物っぽいので、 そこを追うと核心にあっという間に到達、 の予感だから。
で、C-rで検索していくと、、、、 グローバル変数じゃん! 予想バリバリはずれてるじゃん!
で、辿っていく途中でも似たようなイベントループっぽいのがあったので、 なんとなく、 これは適当に組んだ状態遷移、 とあたりをつける。 まあこれまたよく外れるけど、 こうやって目的を持って読むのが重要です(たぶん)
で、wnd_print1をgrepすると、 あれ?どこにも無い。 って事はwin32側かな? という事でwin32の方をgrepするとたらたら出る。 ふむ、これはプラットフォーム非依存なのか。
じゃあrunの方もそうかな? という事でgrepかけると、やはりこっちもwin32の方にありました。 って事は依存部分はどこにあるんじゃろうか? とりあえず残りのFlipっていう重要そうじゃない関数を見てみると、 どうもここはウェイト処理っぽい。 あれ?さっきの予測はちと外れたっぽい予感だな。 まあいい。
で、どこで実際の出力をやってるのかは相変わらず謎だ。 wnd_print1がなんかをexternで呼んでるのか? うーむ、という事でwnd_print1を読んでみると、 なんかmsg_bufが0x80のビットたってたら一文字進める、 とかやってるので、 ここは普通に文字列出力とみた。 で、grp_draw_charってのがピアーっぽい。 指定座標に指定文字を出力、 みたいな。 あれ?これ移植して任務完了なのか? ってボタンとかが無いか。 でもまあ一歩前進。
で、そこ見ると、 今度は、sys_が最初についてる関数を呼び出してる。 これはおそらくプラットフォーム依存だろうな、 とあたりをつけてgrepすると、 system_win32.cというファイルがひっかかる。 これでビンゴっぽい。 試しにX11のディレクトリの方でもgrepかけると、 確かにNscripterCore.cがひっかかる。 ふむ。 という事はまずはここらへんだな。
なんかgrp_draw_char関数の最初の方で、 sys_get_font_bitmap_bufとかいう怪しい名前の関数呼んだり、 X11でifdefしたりしてるけど、 まあ見なかった事にする。
ふーむ、とりあえずsystem回りを実装する、 何も考えずにrunとか呼び出す、 で、当然動かないのでその後適当に考える、 でいいのかな? それとも一旦デバッガで走らせて、 どこで何やってるのか追う方が早いか? と悩むが、 まあ入門編なので、 地道にソース追う事にする(ってすでにちっとも入門編じゃない気も)。
ふむ、system回りを何も考えず追う、 という方向で決定でいいかな、たぶん。 で、sys_draw_scrをちょろっと見たら、 なんかNscripterCoreうんたらかんたら、 とかいう関数のラッパーだった。 つまりNscripterCore.cだけ移植、 でおおむねいける予感。 という訳で、 そのうち隙を見つけてNscripterCore.cをモバにうつして、 移動中にちまちまいじろうかな。

3日目

ほとんど日記だな(^^;
とりあえずNscripterCore.cを移植、という方向で動くが、 sys_get_font_bitmap_buf、というのが何やってるかがあいかわらずわからない。
なんか、フォントを描画させて、そのピクセルデータを取るように見えるが、ほんとかな?自信無し。
という事でチャットで作者に聞く。 なお、このすぐにチャットで聞く人種の事を教えて君、と言い非常に駄目な傾向である事がわかっているので注意(<ーわかってるならやめれ)
という事で聞くと、 文字のデータを32*32*1bppの配列として得るらしい。 うぐぅ、mglでそんな事やるにはどうしたらよいのだろう? 読書、とかの反転のコード読めば何かが見えるかもしれないが面倒。
直観的には,mglならば、create_memscreenでもして、 そこに描画して、その配列の形式を適当に翻訳してやれば良いはず。 という事で、その32*32*1bppというのをどういう形式にでっちあげればいいか、 という事を考えてみる。 ただ、問題は所詮は白黒4階調、という事だ。 って1bpp、って1byte per pixelじゃなくて、 1bitか。そりゃそうだ。 じゃあ存在があるかどうか、というのだけでいいんだな。
うーむ、grp_draw_charに手を入れると、 すごく楽におわりそうな予感もしたが、 まあ流儀に従ってみる。
という事で、特定の形式、というのを読むと、 どうもデータがあるかどうか、というのをchar配列をビット列、とみなして処理しているようだ。 この順番はどういうエンディアンなんだ? とか思うが、 まあいい。適当に並べてみて、変だったら逆にすればいいのさ(ぉ
うーん、スクリーンの配列って、結構nativeだ、 とかあった気も、、、、ってこれはMGL2じゃなくてMGL1の話なので問題無いか。 とりあえず、実装に依存しまくりなコードでいってみようっと。
という事で、sys_get_font_bitmap_bufはだいたい理解した。 実装の時はいろいろ手間取るが、まずは全部0の32*32ビット(PocketBSDではcharの4*4)の配列でも返せばよさげ。 という事で、他のも覗くべくNscripterCore.cを読む。
最初の方のたくさんのデータ構造はとりあえず無視、という方向で行く。 これで読めなかったら、真面目に考える、という事で。 で、getCurrentTimeはそのまんまでよさげ。 どこで使われてるかは知らんが。
で、Initializeは、なんかどこで呼ばれてるのかなぁ、 と思ったら、grepかけると、なんかデータ構造の所にひっかかる。 関数定義がstatic voidである事から考えて、 これ以外で使う可能性は無いので、 関数ポインタとして登録してるみたいだね。
で、Xtなんて私はよく知らないので、 なるべくこれは見ないで済ますように、 深追いはしない。 とりあえず、 btn_init、とか、こういうのを呼び出す必要がある、というのはわかった。 また、NCW、というのにいろいろ代入している所で、 なんかmoji_imageとかいう見覚えのあるのを初期化している。
これって確か、sys_get_font_bitmap_bufとか追ってる時に見た気がするなぁ、 と思いC-sで検索すると、 たしかにここで、return NCW.moji_image->data;なんてのがある。
つまり、この手のグローバルに取っておきたい、 Nscripter的システム変数のような物が入るのかな? とか推測を立てておくが、 結局この関数はMGLならmainで直に呼びだす事になるので、 気にしなくてよさげ。
という事で先に進み、Destoryを見ると、 これはいろいろ解放してるだけっぽいね。 これも関数ポインタとして登録するのかな? まあここらへんはコード書いてる途中で、確保したのを解放したくなったら付けたす、 といういい加減な方法でやっていけばいい、 と思いあまり真面目に読まずに先に進む。
Realize、という所で、 なんかフォント変えたり、カーソル変えたりしてる所のようだ。 これも関数のポインタに登録してる。
うーん、この関数のポインタは誰が呼びだすのかな? という事でまずはNCWにgrepかけると、なんかNscripterCore.cでしか使ってなさそうに見える。 ただしこれって、ncw->nscrcoreをdefineしてる物なので、 こっちを追う必要があるかもしれないけど。 まあいい。まずはNscripterCore.cでしかおおむね使われない物、 と仮定する。
で、NCWはおいといて、関数のポインタが登録されてるのは、 nscripterCoreClassRecなので、 こいつをgrepかけると、なんかWidgetClassという構造体変数、 nscripterCoreWidgetClassとうのに代入してる。
ふむ、よくわからんが、WidgetClass、というのはXt用語っぽいので、 システムが呼ぶ物だ、 と仮定しておく。
システムが違うのだから、こちらが適切なタイミングでこれらを呼ぶようにしてやらなきゃいけない訳だ。 まあいい、後回し。
で、 他にもResizeとかRedisplayとかあるけど、 ここらへんはMGLでは発生しない状況なので問題なさそう。
で、やってきましたClickLeft。 こいつはどうしましょ? とりあえずやってる事は、 クリックされた座標を計算して、 btn_btnup(int x,int y)って関数を呼びだしてるだけだ。 これを実行したら、 その選択肢、というかボタンが押された、 とみなす訳だが、 もちろんこれはマウスやタッチペンの話。 DOSモバ使いには当然そんな物は無い。
うーむ、どうしようかなぁ、と悩むもいい解決策を思い付かないので、 今日はここらへんまで。

三日目

間にちと日々が空いてしまったが、まあ三日目。 ここまでで、ClickLeftをどうすっか、 と思っていたが、 まあここは、呼ばれ方だけ決める、という事にする。 使う引数はX座標とY座標のみなので、こいつを引数に呼びだすような関数に変える。 という訳でこの関数もほとんどいじらないですみそう。
で、今度はFlip、というのを見ていくと、 この関数は、

Flip(){
  while(){
    Xのイベント処理;
  }
  ウェイトのスリープ
}
という事をやってるようだ。 ここで、ncw、という引数に待ち時間等が入ってるっぽい。
どうも、ここのncw、という引数はある方が便利そうだな。 という事でNscripterCoreWidget、というのは定義する事にする。 という訳でこいつの素性を探る。
grepすると、なんか
typedef struct _NscripterCoreRec *NscripterCoreWidget;
なんてのが見つかる。 うぐぅ、実体はCoreRecか。という訳でまたgrep。 すると、NscripterCoreP.h、というのにひっかかる。 うーん、この中のぞくと、なんかいろいろあるなぁ。 _ImageCorePart、というのの、wait_clock,wait_count,moji,moji_image等が使われてた気がするな。 まあ実際同じのを使う必要は無いんだが。
bitmap_buf、とかいうのの時に、なんらかのstruct screenみたいなのが必要ではあるが、 まあそこらへんはアドホックに考える。 で、このstruct _ImageCorePartというのはNscripterCorePartにtypedefされてて、 NscripterCoreRec、というのは
typedef struct _NscripterCoreRec {
    CorePart             core;
    NscripterCorePart    nscrcore;
} NscripterCoreRec;
となってるので、CorePart、というのを今度はgrepすると、、、なんかひっかかんない。 あや?
という事は、win32側かな? と思いそっちも見るが、そっちもひっかからず。
Xtが提供してるのかなぁ、 とあたりをつけて、無視する事にする。 少なくとも、まだ必要になってはいない気がするので。
で、Flip、はこのNscripterCoreRecのwait_clock等を使うようにする。 という訳で問題は、Xのイベント処理、の所。 これをmglのイベントにするのだが、 実際にイベントがいるか、 というのは悩む所ではある。 結局キー入力と画像描画しか無いので、 そこはkeyがキューにつまってなければ一回refleshしてイベント終了、 でいい気がする。 適当なキーをマウスの変わりにしてやんなきゃいけないけど、 まずはいいだろう。
で、次はNscripterCoreDrawScr。 うーむ、ここは、buf、という仮想VRAMを実画面に描画しているっぽい。 仮想VRAMの仕様は、 640*480*3のu_char配列っぽい、 つまりunsigned char配列っぽい。 うーむ、これはちと無駄が多いな。
640*480がピクセル数で、*3、 という事は、unsinged charが8bitなので(環境依存)、 24bitカラーを扱ってる、 という事になる。
ふーむ、本来なら画像出さなきゃおっけーか、 と思っていたが、 仮想VRAMを描画しているだけなら、 仮想VRAMのレベルで4色に落としてもらわないと、 無駄が多すぎ。 別にフルカラーで扱う、というのも可能性としてなくはないが、 640*480*3バイト、は、921k。 ちとメモリ食いだな。 ちなみに4色なら、 必要なのは1ピクセルにつき、0,1,2,3の2bitなので、 640*240*2/8バイトで、38。 まあ実際は640*480、で処理するので、76kで、 やっぱりメモリ的には大分得する。
うーむ、でもこのbuf回りを全て直す、 は結構骨が折れるのも事実。どうしたもんか。
まずはフルカラーで処理したのを白黒で表示、 という路線でいってみる。 次にbufを使ってどんあん処理をしているか、 を見ると、 どうも1ピクセルずつ描画している、 という物らしい。 うーむ、これはまた、結構きつい処理だろうな。 DOSモバじゃ動かないかも。
単純に計算して、 for文を2回まわしているが、 基本的には最初のループで高さを、 次のループで幅を網羅しているので、 基本的には、640*480回のループ。 その一回一回でif文が実行されるようなものだ。 30万回位。 これはDOSモバじゃきついレベルだろう、やってみないとわからないが。
ふむ、これは問題だ。 buf回りの処理は、まるまる変えなくてはならないっぽいが、 それって結構オリジナルに手を入れる事になり、 開発がマージ出来ない。
うーむ、ここは難しい所だ。 選択肢としては、
  1. 開発終わるまで待ってからモバに移植
  2. buf使う所を全部ifdefでくくってモバ用コードも入れる
  3. すでにある関数を使用はするが、システム全体は使わない
  4. とりあえずモバの処理速度を信じてやってみる
  5. その他
あたりかな。 5は特には思いつかないが、一応書いておいた。 ふむ、ここは悩む所だな。 1、として、1の開発、をふるかわさんと一緒になってやってしまう、 が正義の考え方だが、私は文字列回りとセーブ回りが出来れば十分なんだよな、 という自分の使い方、というのも考えなくてはならない。 いらない事はやらない、これは基本だ。 楽しくやらなきゃね。
で、2は一番避けたいパターンだな。 出来たらやめたい。 ただ、これならすぐにゴリゴリコーディングにうつれる。 まあ3あたりが個人的には妥協点かな、 と思わないでも無い。 インターフェースだけ揃えておいて、 内部をいろいろ変える、 という事で、buf回りだけを変えていけばいいし。 まあいい、今日はここまで。 チャットで相談かな(笑)

4日目

とりあえずいろいろ相談したり考えたり、 関係無いお絵描きしたりした結果、 まずはエミュでしか動かないコードを書いてみる、 という路線で行く。
で、まずはMakefileをでっちあげる。 適当に、win32から.cをlnするようにして、 includeは直接win32もインクルードのパスに含める、 という感じで。
で、 NscripterCore.cをいじる。 とりあえず、NCW、というマクロを、グローバル変数にする。 同じ名前のグローバル変数はちとださい、 とも思うが、 まあいいだろう。
ウィンドウハンドル、 という概念はMGLには無いので、 そこらへんはダミーで十分なはずだし。 引数渡ししてもいいけど、 とりあえずエミュでしか動かないコード書く予定なので、 汚なくても動く物、という路線で行くつもり。
まずはボタンのクリックには、qキーが押されたら終了、 ってだけのコード入れて、 画像表示の所では、RGBのうちRの値を64で割ったのを色、 として使う。 まあ試験なので。
で、mglの実装に依存しまくりのコードを突っ込んで、 一通りの処理は終わり。 で動かそうとするが、 ここで始めて、実はこのXNscrの使い方を知らない事に気づく(爆)
適当にfopenしてる所をgrepで覗くも、 0.txt、というのを開いてる所しかみつからず。 ん?よくわからんな。
という訳で、チャットで聞く(ぉ
すると、Nscripterのマニュアルに暗号化前のシナリオファイルを0.txtと呼ぶ、 とか書いてあるらしいです。 ふむ、つまり、nscript.datをデコードして0.txtにすればいいのかな? どうやってデコードするんだっけか。 xor取るんだっけ。 あれ?アーカイブはsarじゃない方もされてるのか?
知って驚く以外な事実、私はnscripterの事を全然知りません(ぉ
よくわからんので、ごうさんに聞いたら、 結局全部デコードしておいとかなきゃ駄目っぽい。 うぐぅ。デコードってどうやってやるんだったかなぁ。 一覧を表示、という感じのコードを、 昔ごうさんからもらった気がするので、 自分で組むのもそう大変ではなさそうだが、 もしあるなら、もらいたい所。
ふむ。

5日目

とりあえずkenjoさんから、展開するコードをもらって、 データ展開してためしてみる。
segmentation fault (core dump)
ふむ、まあ予想通り。 という訳で、とりあえずデバッガで落ちてる部分のスタックトレースだけ表示させてみる。

% gdb ./mgscript
run
back
と入力する。実際は間に文字がいろいろ出るけど。
で、どうもrefreshで落ちてるらしい。 という事は画面外に描画してる、 とかそこらへんだろう、 という事で座標をチェックするも、なんか正しそう。
あれ?んじゃあ、ここのスクリーンの大きが悪いのかな? と思い、こいつを出力しようとしたら落ちた。 ふむ、つまりスクリーンを作っていない、 という事か。 という訳で、Initializeのコードを見ると、 ちゃんと作ってるように見える、、、、 ってこの関数呼んでないじゃん!
という訳で、mainの中で呼ぶ。 すると、なんか画面は表示されて、そこでフリーズ。
ふむ、イベントループが回ってるかな? という事で、Flipの入り口で、fprintfしてみる。 ふむ、なんかたくさんflipと出力されてるので、 どうもここは呼ばれているっぽい。でもget_keyはされない。
なんかよくわからんな。という事で、get_key(0)をget_key(-1)にしてみると、 なんか一定回数くりかえすと落ちる、という感じになった。 get_key(0)だと落ちないで、get_key(-1)にすると落ちる、 というのはなんか妙だが、もともとget_key(0)の方も落ちないだけで、 キー入力はうけつけてないので、 きっと変な事してるんなろう、という事でここは見ない。
怪しさ120%の、sys_get_font_bitmap_bufと、 NscripterCoreDrawScrの2つが怪しい、 という事で、まずはここを追う。
あ、なんか480行全部をdraw_pixelしてるや。 240行しか画面は無いんだから、こりゃまずいわな、 という事で行の%2が0じゃなければdraw_pixel、とかやってみる。
お、なんか表示された。 で、キーが押されているのもfprintfで確認は出来る。 ただし、最初にTYPE-MOON、とすごい速度で表示された後は何もおきない。
ふーむ、という事で、今度はsys_get_font_bitmap_bufが呼ばれているかをチェック。 ふむ、fprintfしたら、ちゃんと呼ばれている。
TYPE-MOON、というのは画像データだ。 つまり画像は表示されている、という事で、 NscripterCoreDrawScrはこれでちゃんと動いている、 という事だ。
だから、文字を描画している所がおかしい、 というのが予想。 以前読んだ理解では、 sys_get_font_bitmap_bufで文字の情報をもらって、 そいつを仮想VRAMに描画していた気がする。
という事で、sys_get_font_bitmap_bufを重点的にチェック。
適当に作った、 struct screenからcharの1bit per pixelのピクセル配列へのマップが怪しいな。 そもそも、画面の左方向を、バイトの上にもってくるのか、下にもってくるのかがわからんし。
という訳で順番入れかえたのを作ったり、 とかいろいろやったけど、 何も表示されず。
とりあえず、まずはピクセルデータがちゃんとstruct screen(メモリスクリーン)から取りだせているかを試すのが先決だな、 という事で、 ピクセルデータをチェックして、 そこに存在していたらその座標を出力、 とかいうコードを書いて、gnuplotで確認(ぉ
ふーむ、 なんかこの時点で何も表示されてないな。 という事は、struct screenから取りだす時点で間違えてる事がわかる。
となると、こんどは、取り出す所のミスなのか、 その前のミスなのか、 という事で実スクリーンの適当な座標に描画してrefresh()した後に、 get_key(-1);で一旦止める、 という事をやってみると、、、 確かに何も表示されない。 あ、背景色で描画してた。
という事で色を黒にして、 また実験。
、、、ふむ、描画されたが、 なんか予定と違う字だな。 という事で、 今度はfprintfしてみる。 、、、あれ?やっぱり違う字だ。 どうもEUCで出力、 が失敗してるようだな。 この関数に渡されるのはちゃんとsjisになってる。
よくわからないので、 eucへの変換をとりあえず自分で書いてみる。
ふむ、EUCに出力されてる。
という事で画面に出力、
うむ、正しく出力された。
X11版でもほとんど同じ処理をしているように見えるのだが、、、 まあいい。
で、またgnuplotで確認、まで進むと、 まあまあ実際の形に近づいたが、なんか横棒が入ってるな。 なんでだ?
ん?その前の文字がーなのか。 つまり、クリアーしてないから、前のが残ってる、 という事だな。 という訳でpush_screenした後にクリアーする事にする。
で、またgnuplotで確認。 ふむ、ちゃんと文字に見える様子。 うむ。
で、普通に実行してみると、 今度は何かが表示されてる。 より文字に近くはなったが、やっぱり全然文字では無い。
という事は代入のされ方が悪い、 という事っぽいので、 代入の所を、ちゃんとロジックを追うと、 確かにおかしい気がする。
という訳でちゃんと直してやると、 さらに文字には近づいた気がするが、 どうも変。 文字を1/4に分けて、それの並び順を逆にした感じだ。
ふむ、という事は1byteにつめる順番が逆さ、という事だな、 という事で逆さにすると、 汚い文字だけど、動いたっ!
ふむ、で、なんで汚ない文字なのか、 を考えると、どうも何行か、出力されてないで透明な線になってる事、 上下が欠けてる事、が原因に見える。
まずは上下の欠けを見る。 よくわからんが、grp_draw_charでこの取りだしたピクセルデータを使って、 仮想VRAMに描画していた、 という事なので、 そこらへんを追う。
まず代入がちゃんとされてるか、 というのを見るために、for文の範囲をprintfしてみる。
あや?hが24だって。 これじゃあ、32までは回らないでは無いか。
、、、、つまり、だ。 これは、ピクセルデータのうち、24行目までしか見ない、 というコードになってる。 つまり、そこまでで、文字は終わってなくてはならない。 32*32のピクセル配列でありながら、 24行目までで、字が終わっている、 というのが、sys_get_font_bitmap_bufで返される結果に期待される物だ。
ふむ、つまり、32ピクセル一杯に文字を書くのは間違い、 という事だな。 24ドットフォントなら24ドットまでしか見ない、という事だ。
つまり、32*32な画面に、24ドットフォントで文字を描画して欲しい、 という事なので、 今までのように16ドットで描画した物を32*32に拡大する、 というのは無駄な事だ。
ふむ、理解。 という事で、ちゃんと正しくコードを書き直す。 うむ、なんか小さい字だけど、上下が切れる事はなくなった。
あれ?24ドット、と向こうは判断してるけど、 こっちはそんなにおおきくは見えないな。
という事でset_fontしてる所を見てみると、 sys_set_fontでやってるね。 ここに来るw、の値を出力してみると、 25だって。
で、この幅だけ見て、それをset_fontに渡していたので、 どうも存在しない大きさの25、という値のせいで、 適当な大きさのフォントになってしまってるようだ。 という訳で24以上なら24に、間の値はそれを越えない最大の存在するフォント、 という感じにする。
で、見ると、おお、大きく表示された。
で、次に表示されてない行、という方を考える。 ピクセルデータ的にはちゃんと表示されている事をgnuplotで確認した。 うむ、表示されている。 怪しいのは、480から240にするのに、 2回に一回しか描画しない、とかいう怪しい方法を取ってる所だ。
ふむ、つまり、描画されて欲しい横棒、とかが飛ばす所にあると、 そこが描画されない、という事だな。
という訳で、2行の平均を出力、 にコードを変えたら、おぉ、美しい!
という事でこれでよし、 という感じらしい。
という訳で、RETURNキーが押されたら0,0を左クリックされた、 とみなすコードを書くと、 オープニング画面までそのまま行けた。 ただ、当然、最初のメニューを選ぶ方法は無い。
という事で、mouse_x、mouse_yというグローバル変数用意して、 右や左が押されたら、+10、-10されるようにコードを書いて、 リターンが押されたら、ClickLeft(mouse_x,mouse_y)を呼ぶようにする。 これで、心の目でメニューが選べるはず、 という事で、やると、おぉ、メニューが選べた。 ただ、なんか押した回数とあってない気がする。
という事でちとソースを読むと、 ああ、そうか。画面は240行なんだけど、ClickLeftの座標系は480行だから、 半分になってるのか。
という事で*2してやる。うむ、うまくいってるぽい。
という訳で心の目で選択していくのはちとつらいので、 一応mouse_x、mouse_yの位置にカーソルを描画させる。 ただし、消去はしないので、 動かすと、だらだらっとカーソルが連続で表示される。
まあテスト用としては十分だし、 ゲームやる事も不可能では無いので、 まずはこのまま。
で、続きをやると、どうも文字が変に変換されてる。
「事」が「待」になったり、とか。 sjis2eucが変なのかな、 という事で、ごうさんのコードをちゃんと読むと、 どうもごうさんのは最後に上位ビットを立ててないように見える。 という訳で立ててfprintfしてみると、 ちゃんとEUCが出てる、という事なので、 このコードを使うと、ちゃんと全部変換された。
で、そのままゲームを続けていくと、 必ずオープニングが終わった所で落ちる。 うむ、よくわからんが、ここまで表示されてる以上、 mgscriptのせいでは無い気がする。
先生が居なくなった瞬間に落ちる、 という事は、先生の魔力か?(ぉ
という訳でログと0.txtの中をつきあわせると、 ログの最後はimage/word/park_01a.jpg、と出力される事から、 ここまではうまくいってるっぽい。 その次のgosub *right_phase、があやしいな。
何も考えずにコメントアウトすると、 おぉ、ちゃんと学校まで行けた。
という事は、可能性としては、
  1. gosubが実装されてない
  2. right_phase、の中で使う処理が実装されてない
  3. 上記ふたつの要素の中で、実はmgl側でやってやんなきゃいけない処理がある
あたりが高い、と思う。 ただ、どのみち全部はまだ実装されてないので、 ここを真面目に追うのは労力の無駄、と見た。
今後は、 XNscrの本体の部分の開発を共同で進めるか、 2bpp化を推し進めるか、 悩む所ではある。
とりあえずは、今日はここまで。

6日目

とりあえず、本体の方を開発する、 という方向で、行く。
あと、XNscrが名前変更してGnscripterになった。 cvsのチェックアウトも

cvs -d :pserver:anoncvs@denpa.org:/cvs co gnscripter
に変更されてた。 確かにXNscrじゃX11用、と勘違いしちゃうからね(ベースは実はWin32用)
で、まずは毎回オープニング見るの嫌だから、 セーブとか、ロードとかを実装したいな。 これさえあれば、 もう一通り最後までは出来る、 と思うし。
右クリックのメニューとかを実装するのは面倒そうなので、 sでセーブ、lでロードのセーブデータ一つ、 みたいな感じで良いだろう。
保存するのは、現在のファイルポインタの位置と、 _numalias、位かな。 保存形式は:でセパレートして、 :自体はエスケープ、でいいかな。 それとも非印字文字の方がいいか? うむ、なんか非印字文字の方が楽そうなので、 手抜きって事でそれでいこう。
という訳で保存をちゃっちゃと実装して、 アルクシナリオでもやろうかな。
クリアしたら保存されるクリアデータってのも、 どうせならやってしまいたい予感もするが。 まあいい。まずは保存だ。