HALCA 2002 !?

2002-09-14

HALCAの移植を試してみた

ふと思い立ち、ウチの HALCA を PWS2002 に移植できないか挑戦してみた。 プ社によれば、PWS2002 が過去の PWS用バイナリを実行できる他、綾織言語も過去の綾織との互換性を保っているそうだし。 真偽の程は分からないにしても、ともかくやってみようと。

ドキュメントが整備されていない他、PDK も暫定版だし、PWS2002 も改修の真っ最中。 ちと時期尚早な気がする。 しかも知識は「綾織のプログラミング書式」に目を通した程度。 そんな状態で移植に挑戦だ。 ちょっと無謀か? (^^;

以下、具体的に HALCA を移植するのに必要な作業を紹介してみる。
結果については、下の方にありマス。

プロトタイプ宣言

自分で作った関数はプロトタイプ宣言して外部参照を可能にするのだが、実は 標準関数(LoadBitmap() など)も同じ扱いだ。 標準関数を使うためには、プロトタイプ宣言する必要がある。 PDK に添付されているので、これを import すればよい。 場所は、各モジュール(*.out)単位の先頭。

#import  "Prototype150.aya"
#import  "prototype200.aya"

action@( 0 )

アクションの起動条件は、PWS 0.93 では「比較文」となっていた。
これが PWS 1.5 では「boolean型の式」となっている。 boolean型ってのは「真(しん)」か「偽(ぎ)」の何れかを取るもので、関係演算子の評価値が典型的。 早い話が「比較文」と同じ意味である。

ただし、条件を「常に偽」とする場合の書き方が異なる。 PWS 0.93 では 0 が偽、1 が真1 を意味していたから、起動条件は「0」と書いていた。 これが PWS 1.5 では定数 false が偽、true が真 となっているので、起動条件は「false」としなければならない。

action@( 0 )        // PWS 0.93風
action@( false )    // PWS 1.5風

つまり、上のように書き換えて回る必要がある。
HALCA では 364箇所であった。

while( 1 )

本質的に上の action@( 0 ) と同じなのだが、制御構造の条件判断も boolean型の式に変わった。 通常 if()while() の中には「比較文」を書くモノなので、深刻な影響はない。

ただし、永久ループに影響があった。 永久ループを作って、条件を満たした時にループを抜けるような処理は、次のように書いたりする。

while( 1 ) {        // PWS 0.93風
    処理;
    if( 条件式 ) { break; }
}

上で触れたように PWS 0.93 では 1 が真を意味していたからこの様になる。

何れにせよ、次のように書き直す必要がある。
HALCA では 49箇所であった。

while( true ) {     // PWS 1.5風

配列をextern

PWS 0.93 では、別モジュール(*.out)で宣言している変数を参照する場合、extern 宣言していた。 これは PWS 1.5 でも変わらない。 関数の場合にはプロトタイプ宣言を使うくらいの違いである。

どうにも不明瞭なのだが、どうやら配列を extern する場合の扱いが変わったようだ。 例えば、aya00.out で宣言されている num と dim[128] を aya01.out から参照するとしよう。 aya00.out のソースは、こんな風である:

// aya00.aya
int      num;
string   dim[128];

そして aya01.out のソースはこんな感じ:

// aya01.aya
extern int      num;        // PWS 0.93風
extern string   dim;

これで dim[n] (ただし 0≦n<128) を読み書きできる。 PWS 0.93 では、シンボル名 dim だけを extern していれば当座の問題はなかったと言う事だ。

だが、PWS2002 用の PDK では、dim[1] に代入しようとする所で蹴られる。 明確にどうしろと書いていない(書いてある場所を見つけられない)ので、カンで dim[] として(要素数は問わないが)配列である旨を明記してみた。

// aya01.aya
extern int      num;        // PWS 1.5風
extern string   dim[];

これでコンパイルは通るようになった。
ただし動作上の問題については未確認なので注意。

グローバル変数

PWS 0.93 では、宣言された変数は全てグローバル変数であった。 さらに宣言はアクションの中で行うよう、暗に示されてる(サンプルである"綾夏"は守っていないが)。

綾織の変数は,すべてグローバル変数(あるアクションで宣言した変数が, ほかのアクショ ンから自由に読み書きできる)です.

PWS 1.5 では、アクションの中で宣言した変数はローカル変数となる。 より正確には、宣言された場所が { } の中である変数は、その { } の中でだけ有効。 アクションや関数も { } で囲うから、アクションの中で宣言した変数は、そのアクションの中でだけ有効になる。

グローバル変数は、アクションの外で宣言する必要がある。 よって、全ての変数宣言を { } の外に出す作業が必要となる。 HALCA では変数宣言を1箇所にまとめてあったので苦ではないが、バラバラに宣言しているペルソナでは大変であろう。

重複するグローバル変数

もう一つ、グローバル変数絡みの話を。

別のモジュール(*.out)で宣言された変数を参照するには extern 宣言を行う必要があるのは、上記のとおり。 逆に言うと、extern しなければモジュール間で重複する変数が存在しても構わない。 少なくとも、ayac や実行時に問題は検出されない。

ループカウンタの int i; なんかは、aya00.out にも aya01.out にも aya03.out にも存在していた。 そしたら、実行時に i が重複宣言されていると警告が出やんの。 グローバル変数は、extern する/しないに関わらず重複してはいけないみたいだ。

これは簡単、1つを除いてローカル変数にしてしまえばいい。
あるいは、どれか1つに絞って、他は extern して使うようにするか。

GetToken()

GetToken() は、文字列をデリミタ(区切文字)で分割する標準関数である。 PWS 0.93で文字列をバラす手段はコレだけなので、多用する事になる。

これが、ayac を通らない。
標準関数のプロトタイプ宣言(PDKに含まれる Prototype150.aya 内)に GetToken() が無いのが直接原因であろう。 ドキュメントでは GetTokenは互換性のために残されています。 となっているが、これは PWS 1.5 の話。 PWS2002 では廃止され、StrToken() を使うようになったようだ。

下の方で書いてあるが、"" と NULL は別物であって何らかの対処が必要。 そこで、こんな関数を作って GetToken()StrToken() の橋渡しをする事にした。

string GetToken( string str, string dlm, int num )
{
    string  sResult;
    
    sResult = StrToken( str, dlm, num );
    if( sResult == NULL ) {
        sResult = "";
    }
    return sResult;
}

HALCA では GetToken() を 498箇所で使っているが、これで 498箇所に手を入れる必要は無くなった。

DDE関連

HALCA では一部 ZMemo の機能を使うため、DDEextension.aya を import している。 だが、PWS2002 用という事で import するのを止めた。 コード中で EnableDDEReservedWord なんかを参照するのだが、これらは当然のようにエラーとなる。

また、ZMemo を想定した処理もあちこちに存在する。 単にメッセージが違うだけの物から、*.euu の関数を呼んでいる物まで色々。 この辺りから、単純な整形では対処できなくなって来る。 ZMemoを意識しないように、ロジックを変更しなくてはならないのだ。

ユーザ情報関連

ユーザ情報の設定ダイアログを開く ShowUserInfoDialog() は、PWS 1.5 では廃止されているようだ。 各ペルソナが個別に訊ねるんじゃなくて、ペルソナウェアインストール時に設定した情報を勝手に流用する流儀に変わったんだな。 これに伴って、標準アクション UserInfoUpdated も無くなった。

この辺り、HALCA では結構面倒な処理をやっている。 ユーザの呼称関係だけなんだが、導入時に訪ねるだけではなく、途中で変更された時の対応が入っていたりする。 更にはキャンセルした呼称を覚えていたりするので、ちーと処理が長い。 どこまで消して大丈夫か?を判断するだけでゲンナリするし、更に、PWS2002風の呼称を取得しなくてはいけない。 非常に面倒である。

EditBox()

ユーザ情報ダイアログは、任意の文字列を入力するために使われたりもする。 HALCA では、アラームの注釈を付けるのに使用している。 が、上記のとおり ShowUserInfoDialog() は廃止された。

対応策は、EditBox() を使う事である。
幸い DDE対応で EditBox() を使っていたので、PWS 0.93 用のコードをごっそり削れば対応可能であった。 引数の数と意味も同じなので、手間がなくて良かった。

"" と NULL

ここまでの修正を何とかすれば、とりあえず ayac は通る。 あとは PWS2002 上での動作を確認するだけ。
…と思いきや、これが一筋縄では行かない事が判明。

非常に困るのが、string 型標準関数の返値が変更された事である。
例えば次のコードがあるとしよう。 前提として、プロパティ "hogehoge" は存在しない。

str1 = GetProperty( "hogehoge" );
str2 = GetToken( str1, ",", 0 );

PWS 0.93 でこれを実行すると、ともに長さ 0 の文字列が得られる。

str1 = ""                  // PWS 0.93の処理結果
str2 = ""

これが PWS 2002 ではどうか。

str1 = NLLL                 // PWS 2002の処理結果
str2 = (実行エラー)

存在しないプロパティを GetProperty() すると、"" ではなく string型の NULL が返るのである。 ただし、この NULL を string型の関数の引数として指定する事はできない。 指定すると、実行エラーとなりペルソナは動かない。 ベンダで対処せよといった旨のダイアログが出るだけである。

一番簡単な対処療法は、もし NULL だったら "" に置き換える事だろう。 ただし、str2 も NULL が返ることを忘れてはいけない。 モグラ叩きよろしく、全部の string型関数について潰す必要がある。

str1 = GetProperty( "hogehoge" );
if( str1 == NULL ) {
    str1 = "";
}
str2 = GetToken( str1, ",", 0 );

永久ループ

上記の問題は、エラーダイアログが出るだけ"良性"の障害である。 厄介なのは、こんな処理をしている部分だ。

string  src = "1,2,3,4,5";
string  elm;
int     i = 0;

elm = StrToken( src, ",", i );
while( elm != "" ) {
    
    elm に対する処理;
    
    i = i + 1;                          // 次のelementへ
    elm = StrToken( src, ",", i );
}

5 までの処理は、問題なく進む。 その次を処理しようとした時、StrToken() の返値は NULL である。 両者は別物("" != NULL)だから、while( elm != "" ) の判定は常に true となる。 永久ループに陥ってしまうのである。 現象としては、突然ペルソナが応答しなくなる。 ペルソナウェアも応答しなくなるようで、他のキャラクターの操作も出来なくなる。 persona.exe の CPU使用率が上がり、50%程度で安定する。2

これは…見つけにくい。 実際に走らせてハングするか否かを見るのも良いが、基本的に全ての string型関数について処理をチェックしなくてはならない。 かなりの手間と言えよう。 しかも基本的に対処療法だから、ホントに大丈夫かどうかは怪しいものである。

実行エラー

それでも騙し騙し動かしてみると、意外な実行時エラーが頻発する。

例えば、トップメニューから [タイマ] を選び、そのまま [キャンセル] で戻ると「Stack Underflow」エラーとなる。 サブルーチンのアクションを呼んでるだけなのに。 悪い事に、[設定]→[設定終了] では問題ない。 ほとんど同じ処理が走ってるんだけどな〜。

もしやと思い actionを関数に書き換えてみたが、現象は変わらない (「タイマ」がOKで 「設定」がNGになった!?)。 なんかヘンな状態になってるっぽい事は分かるんだが、それ以上は分からないなぁ…。 こうなるとお手上げだ。

あと、終了操作をするとハングするとか。 上記の GetToken(), GetProperty() に関する NULL の件は潰した(はず)なので、別の要因で永久ループに陥っているのか…。

無理っぽいです

「現在動いている奴はどうやって動かしているのだ?」と言うのが正直な感想だ。

移植は、0から作り上げるのに比べれば楽なのは確かだろう。 しかしロジックを見る必要があるとなると、気楽に移植できるレベルではない。 むしろ関数化できる部分を整理しながら、ソースを流用しつつ新しいモノを作る方がスッキリして良い気がする。 また、座標をスクリプト側で管理するようにすれば、画像の数もグッと減らせられる。

PWS 1.0 が出たときに、移行を考えたベンダも多い事だろう。 PWS 1.0 がシェアウェアだと言う点に反発した人も居るだろうが、余りの手間に移行をあきらめてしまった人も多いのではないか。 そう思えるくらいに、面倒なのだ。 現実には、「カネ払って苦労して移行して、その結果得られる物が『カネ払ってないユーザは使えない』ペルソナになる事」ってんじゃ、移行の妨げになった可能性が高いな。 サイアクな状況である。

PWS2002 は、プラットフォームが無償で利用可能3になった。 苦労しても、移行させれば誰もが使える状況ではある。 そこに価値を見いだせるか否かで、対応は変わるだろう。

HALCA の場合は、かなりあきらめムードである。
この記事を読んで PWS2002版 HALCA に期待した人、ごめんなさい。

  1. 1が真
    もしかすると、「非0が真」かも知れず。
    少なくとも、Talk( itoa( 1 == 1 ) ); などとやると、"1" が返っていた。

  2. 50%程度で安定
    開発に使ったPCの OS が Windows2000 だからかも知れない。
    確認していないが、WIndows95系OSの場合は OSごとハングアップしてしまうのではないだろうか。

  3. 無償で利用可能
    文字通り無償で提供されていて、現状のまま利用が可能という意味。 「フリーソフト」ではない。

Copyright© 1998-2006 Hira