/*
 *	clipsnrs.c
 *
 *	シナリオシーケンサ
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2017 Naoyuki Sawa
 *
 *	* Sat Oct 14 22:22:29 JST 2017 Naoyuki Sawa
 *	- 1st リリース。
 *	  今回はまだ、dSnrSeqC.exe,及び,dSnrSeqD.exeを作成するため定数を定義しただけで、当モジュールの関数は未実装です。
 *	* Sun Oct 22 23:53:05 JST 2017 Naoyuki Sawa
 *	- 当モジュールの関数を実装しました。
 *	  基本的には、動作実績のある既存のclipdirs.cをベースにして作成したので、ロジックの大幅な間違いは無いと思います。
 *	  ただしclipdirs.cと比べて、命令の違い以外にも、以下の点を変更しました。
 *	- 演出番号バッファ(ActBuf)を廃止しました。
 *	  clipdirs.cでは、コールバック関数の中からシーケンサに対する処理が行われた場合に、処理をシリアライズするために、演出番号バッファを用いていました。
 *	  しかし、実際にいくつかのアプリケーションで使用したところ、コールバック関数の中からシーケンサに対する処理を行う必要は、ほとんど有りませんでした。
 *	  逆に、'act'に対するコールバックは演出番号バッファによって遅延するが、'if'に対するコールバックは演出番号バッファを介さないので遅延しないという、却って判り辛い問題が生じていました。
 *	  そこで今回、clipsnrs.cにおいては、演出番号バッファを廃止して、全てのコールバックは単純に即実行するという仕様にしました。
 *	  変更による利点としては、単純で判り易くなりましたし、メモリ消費量も低減しました。(clipdirs.cでは、演出番号バッファをスタック上に取っていたので、スタック溢れの危険が有った。)
 *	  変更による欠点としては、コールバック関数の中からシーケンサに対する処理が呼び出せなくなりました(もし呼び出した場合の動作は未定義です)が、前述の通り、実際のアプリケーションではまず不要だと思います。	⇒{{2017/10/24コメント追記:もし呼び出した場合はエラー停止するようにしました。}}
 *	- clipdirs.cでは、停止時にも実行する命令を'act flush'という名前にしていましたが、clipsnrs.cでは'act ensure'という名前に変更しました。
 *	  'flush'では、目的と名前が合っていなくて、直観的に判り辛いと思ったからです。
 *	  'ensure'という名前は、Ruby言語の例外処理のensure句に倣いました。
 *	  この変更は名前だけの変更であり、機能的にはclipdirs.cの'act flush'とclipsnrs.cの'act ensure'に違いは有りません。
 *	* Tue Oct 24 23:47:20 JST 2017 Naoyuki Sawa
 *	- ST_SnrSeqCh構造体に再入不可フラグ(bNonReentrant)を追加しました。
 *	  コールバック関数の中から、同一チャネルに対するシーケンサの処理を呼び出した場合、エラー停止します。
 *	  コールバック関数の中から、別のチャネルに対するシーケンサの処理を呼び出す事は可能ですが、その先で元のチャネルに対するシーケンサの処理を呼び出した場合も、エラー停止します。
 *	- 上記の検査を追加した理由は、以下の通りです。
 *	  2017/10/22のコメントに書いた通り、実際のアプリケーションで、コールバック関数の中からシーケンサに対する処理を呼び出す事はまず無いのですが、万一おこなった場合に発見し辛いバグとなってしまうため、明示的に検査してエラー停止する事にしました。
 *	  また、偶然、ST_SnrSeqCh構造体にパディング領域が1バイト余っていたので、今回、ST_SnrSeqCh構造体のサイズを増やさずに、再入不可フラグ(bNonReentrant)を追加する事が出来ました。
 *	* Sun Oct 29 23:41:29 JST 2017 Naoyuki Sawa
 *	- ロックバッファのロジックにバグが見つかり、修正しました。
 *	  clipdirs.cモジュール由来のバグであり、clipdirs.cモジュールの昨日を使用していた既存のアプリケーションにも、このバグが内包されていた事になります。
 *	  具体的には、以下の通りです。
 *	- ロックバッファが空の時に、ロック時間を減らす処理が行われていませんでした。
 *	  正しくは、ロックバッファが空の時も、ロック時間を減らす処理は行うべきです。
 *	  このバグが顕在化する状況としては、以下の通りです。
 *	  │Test
 *	  │        Trig1   .Trig1  5
 *	  │        Trig2   .Trig2
 *	  │end
 *	  │.Trig1
 *	  │        Puts    "Trig1"
 *	  │end
 *	  │.Trig2
 *	  │        Puts    "Trig2"
 *	  │end
 *	  T=0でTrig1を送信し、T=3でTrig2を送信したとします。
 *	  正しくは、T=5でTrig2が開始すべきですが、上記のバグのために、T=8でTrig2が開始していました。
 *	  Trig2がロックバッファに入るまで、ロック時間が減らず、5のままになってしまっていたからです。
 *	  今回、上記のバグを修正し、正しく、T=5でTrig2が開始するようになりました。
 *	- 今回、上記の修正に加えて、以下の仕様変更も行いました。
 *	- これまでは、_SnrSeqCh_Start()にて、シナリオシーケンスの開始と同時に、シナリオシーケンスを1フレーム実行する仕様としていました。					//┬{{2017/11/21コメント追記:やっぱり、シナリオシーケンスの開始と同時にシナリオシーケンスを1フレーム実行したいケースも有るので、'on exec',及び,'on unlock exec'命令を追加して、アプリケーションが明示的に指定出来るようにしました。}}
 *	  しかし、実際に使って来た所、意図せず1フレーム進み過ぎて、却って直観に反して判り辛いと思い、この仕様は廃止する事にしました。						//│
 *	  この仕様を廃止しても、開始即停止でも実行したいが有る場合は、明示的に'act ensure'命令を使えば実現出来ます。								//│
 *	  アプリケーションの観点からは、SnrSeq_SendTrig()の時点では1フレーム目が実行されず、SnrSeqCh_Exec()の時点で1フレーム目が実行されるようになった事に注意して下さい。	//┘
 *	- これまでは、SnrSeqCh_Unlock()にて、ロックバッファが空になった時点で実行されている、最後のシーケンスのロック時間はクリアしませんでした。
 *	  そうしていた理由は、既にロックバッファに複数のシーケンスが入っている状態でアンロックした場合に、最後のシーケンスのロック時間を残すのが自然と思っていたからです。
 *	  しかし、実際に使って来た所、SnrSeqCh_Unlock()を読んだ直後にSnrSeq_SendTrig()を行った場合にそのトリガがロックされる可能性が有るため、却って判り辛いと感じました。
 *	  そこで今回、SnrSeqCh_Unlock()は、最後のシーケンスのロック時間もクリアするように変更しました。
 *	  SnrSeqCh_Unlock()を読んだ直後は、ロック時間がクリアされ,且つ,ロックバッファが空になっている事が保証されるようになりました。
 *	- SnrSeqCh_GetStat()にて、ロック時間が残っているか,又は,ロックバッファが空でなければ、このチャネルは動作中であると見なように変更しました。
 *	  これまでは、上記の判定を行っていなかったため、ロック中の状態であってもSnrSeqCh_GetStat()が0を返す可能性が有りました。
 *	  時間経過によって自動的に再びSnrSeqCh_GetStat()が1に戻る可能性があり、これまでの動作は適切ではありませんでした。
 *	  今回の変更により、SnrSeqCh_GetStat()は、ロック中の状態も動作中と見なして、1を返すようになりました。
 *	- 今回の変更点は、以上です。
 *	  変更箇所が多かったので、変更前のコードは、コメントアウトして残していません。
 *	  変更前のコードを参照する必要が有る場合は、前回のアーカイブを参照して下さい。
 *	  また、今回修正した点は、既存のclipdirs.cモジュール由来のバグだった点も多く、本来はclipdirs.cモジュールにもバックポートすべきなのですが、それは行わない事にしました。
 *	  今回の修正による挙動の変更が大きく、既存のclipdirs.cモジュールの(意図しない)動作に依存して動作していたアプリケーションが動作しなくなる恐れが有るからです。
 *	  今後ゆくゆくはclipdirs.cモジュールを廃止し、clipsnrs.cモジュールへ移行して行く予定なので、clipdirs.cモジュールは修正せずにそのままにしておく事にしました。
 *	* Sun Oct 29 23:57:57 JST 2017 Naoyuki Sawa
 *	- 'wait for'命令と'wait for not'命令を追加しました。
 *	  既存の命令でも'while cond; wait; end',及び,'until cond; wait; end'と同じですが、使用頻度が高いと思うので、利便性のために一命令で実行出来るようにしました。
 *	* Sat Nov 04 23:56:59 JST 2017 Naoyuki Sawa
 *	- ラベルテーブルを検索するユーティリティ関数を追加しました。
 *	  詳細は、/clip/tool/dSnrSeqC/winapp.h の同日のコメントを参照して下さい。
 *	* Sat Nov 11 23:59:59 JST 2017 Naoyuki Sawa
 *	- 'escape'命令を追加しました。
 *	  停止時の処理の中でescape命令を見つけたら、exit命令と同じと見なします。
 *	  動作中の処理の中でescape命令を見つけたら、NOPと見なします。
 *	- escape命令を追加した理由は、以下の通りです。
 *	  当モジュールの仕様は、前身であるclipdirs.cモジュールの、さらに前身のclipprds.cモジュールに由来しています。
 *	  元々、clipprds.cモジュールは、パチスロゲームのトリガ単位の演出を再生し易いように作成したものでした。
 *	  パチスロゲームの演出の特性上、プレイヤーが演出を最後まで見ずに早打ちしたとしても、演出を最後まで見て停止した場合と同じ状態になって停止するのが望ましいと考えました。
 *	  そうしないと、演出の後半で表示(or消去)して次のトリガへ引き継がれるレイヤが表示(or消去)されなかったりして、次のトリガの処理で考慮する点が増えてしまい煩雑だからです。
 *	  上記の煩雑さを避けるために、各演出自身が(たとえ途中で停止されても)常に同じ状態で終了出来る事が目標でした。
 *	  その目標は達成されたのですが、その後、clipdirs.c,clipsnrs.cへと機能を発展させて行く中で、元々想定していた用途以外でも使用したいと思うようになりました。
 *	  そうなると、シーケンスが停止時にも常に最後まで走査されるという仕様が、支障となるケースも生じました。
 *	  シーケンス自身でループを回して処理を実行し、停止時には即終了したい事も有るからです。(演出に特化せず、汎用性を考慮すると、むしろこちらの方が一般的です。)
 *	  対策としては、停止時にシーケンスの残りを走査しないように仕様変更する事も可能ではあるのですが、元々の目的は捨てたくないので、それは行わない事にしました。
 *	  その代わりに、escape命令を追加して、停止時の走査終了可能ポイントを明示的に示す事にしました。
 *	  一般的に、処理の原子性を保障したいケースでは、「位置A～位置Bの途中で停止要求が発行されたら、位置Bまでは処理を行う」という要求になる事が多いと思います。
 *	  その場合、位置Aの直前と位置Bの直後にescape命令を置く事で実現出来ます。
 *	  │MySeq1
 *	  │    wait    100
 *	  │    escape
 *	  │    Play    1,Bgm1		#//位置A
 *	  │    wait    100
 *	  │    Stop    1       ensure	#//位置B	停止時にも実行したい処理には'act ensure'命令(この例では簡略記法)を使う必要が有る事に注意して下さい。この点についてはこれまでと変わっていません。
 *	  │    escape
 *	  │    wait    100
 *	  │end
 *	  この例では、escape命令を使わずに最後まで走査してもさほど違いませんが、もっと長いシーケンスや、goto命令で無限ループが組まれているようなシーケンスの場合は、escape命令が有効に使えると思います。
 *	  尚、escape命令の仕組みはスタックを考慮しないので、call命令等の呼び出し先のシーケンスボディ内でescape命令を見つけた場合に、呼び出し元へ戻って走査するのでは無く、即終了する事に注意して下さい。
 *	  使用方法のガイドラインとしては、escape命令を使用する箇所は、シナリオテーブルから直接呼び出されたシナリオシーケンス内だけに限定しておく方が、混乱しなくて良いと思います。(必須ではありません。)
 *	* Tue Nov 21 23:59:59 JST 2017 Naoyuki Sawa
 *	- 'on unlock','on exec','on unlock exec'命令を追加しました。
 *	- 'on unlock'命令を追加した理由は、以下の通りです。
 *	  │・シナリオシーケンスを開始せずに、アンロックのみを行いたいケースが、時々有りました。
 *	  │　たとえば、前ゲームの3on演出がロックを掛けて、3off演出がロックされていても、次betトリガで強制的にアンロックして、3off演出を即再生したい場合等です。
 *	  │　しかし、これまでは、シナリオシーケンスを開始せずにアンロックのみを行いたい場合は、SnrSeqCh_SendTrig()は使用出来ず、明示的にSnrSeqCh_Unlock()を呼び出す必要が有りました。
 *	  │　なぜなら、既存の'on unlock goto'と'on unlock exec'命令は、シナリオシーケンスを開始せずにアンロックのみをおこなう機能は無く、トリガの処理として記述出来なかったからです。
 *	  │　そのため、トリガを契機として上記の処理を行いたい場合でも、SnrSeqCh_SendTrig()は使用出来ず、SnrSeqCh_SendTrig()/SnrSeqCh_Unlock()を使い分ける必要が有り、アプリケーションが処理内容を意識しなければいけない問題が有りました。
 *	  │・そこで今回、新たに'on unlock'命令を追加して、シナリオシーケンスを開始せずにアンロックのみを行う処理を、トリガの処理として記述出来るようにしました。
 *	  │　アプリケーションは、トリガの処理内容を意識せずに、常にSnrSeqCh_SendTrig()を使用出来るようになりました。
 *	  │　尚、SnrSeqCh_SendTrig()を使って'on unlock'命令が使用されているトリガを起動する事と、明示的にSnrSeqCh_Unlock()を呼び出す事の違いは、呼び出し方の違いだけで結果は同じです。
 *	  │　明示的にSnrSeqCh_Unlock()を呼び出した場合と同じく、ロックされているシナリオシーケンスが有ればそれが開始され、ロックされているシナリオシーケンスが無ければ何も行いません。
 *	  │・尚、これまでと同様に、アプリケーションが明示的にSnrSeqCh_Unlock()を呼び出してアンロックする事も可能ですので、用途によって使い分けて下さい。
 *	  │　ガイドラインとしては、アンロックする目的がシナリオの一部と見なせるならばSnrSeqCh_SendTrig()を使い、アンロックする目的がシナリオと無関係でアプリケーションの処理と見なせるならばSnrSeqCh_Unlock()を使うのが適切だと思います。
 *	- 'on exec','on unlock exec'命令を追加した理由は、以下の通りです。
 *	  │・clipdirs.cでは、シナリオシーケンスの開始と同時にシナリオシーケンスを1フレーム実行していましたが、当モジュール(clipsnrs.c)では、その仕様は廃止していました。(clipsnrs.cの'Sun Oct 29 23:41:29 JST 2017 Naoyuki Sawa'のコメント参照)
 *	  │　ほとんどのケースでは仕様を廃止して良かったのですが、やっぱり、シナリオシーケンスの開始と同時にシナリオシーケンスを1フレーム実行したいケースも有る事に気付きました。
 *	  │　例えば、setトリガやclrトリガから起動されるシナリオシーケンスに1フレームで完了する処理を記述しておき、setトリガやclrトリガを契機としてそれらの処理を即実行したいケース等です。(左記はあくまでも例であり、必ずしもsetトリガやclrトリガに限りません。)
 *	  │　上記のようなケースにおいては、clipdirs.cの頃のようなシナリオシーケンスの開始と同時にシナリオシーケンスを1フレーム実行する仕様の方が望ましいのですが、'Sun Oct 29 23:41:29 JST 2017 Naoyuki Sawa'以降、その仕様は廃止していました。
 *	  │　別の方法として、'act ensure'命令を使えば、直後にSnrSeqCh_Exec()が呼ばれた時に処理が実行される事は保証出来ますが、プログラムの他の部分の処理とのタイミングの兼ね合いのために、トリガを起動した瞬間に処理が実行されて欲しいケースはやはり有り得ます。
 *	  │　結局の所、シナリオシーケンスの開始と同時にシナリオシーケンスを1フレーム実行するのが適切か、実行しないのが適切かは、処理内容に依存するため、モジュールの機能としては両方に対応しておき、アプリケーションが明示的に指定出来る必要が有ると思いました。
 *	  │・そこで今回、新たに'on exec','on unlock exec'命令を追加しました。
 *	  │　既存の'on goto','on unlock goto'命令との違いは、シナリオシーケンスの開始と同時にシナリオシーケンスを1フレーム実行する事のみです。
 *	  │　尚、シナリオシーケンスの開始と同時にシナリオシーケンスを1フレーム実行するのはSnrSeqCh_SendTrig()から直接起動された場合だけであり、ロックされた場合は'on goto'命令と同じ扱いとなります。
 *	  │　具体的には、以下の通りです。
 *	  │　│'on goto'		ロック時間が残っていなければ、シナリンシーケンスを開始し、1フレーム目を即実行する事はおこなわない。	ロック時間が残っていたら、ロックバッファに入る。ロックが(自然に,又は,明示的に)解除された時に、1フレーム目を即実行する事はおこなわない。
 *	  │　│'on unlock goto'	強制的にアンロックして、確実にシナリンシーケンスを開始し、1フレーム目を即実行する事はおこなわない。
 *	  │　│'on exec'		ロック時間が残っていなければ、シナリンシーケンスを開始し、1フレーム目を即実行する。			ロック時間が残っていたら、ロックバッファに入る。ロックが(自然に,又は,明示的に)解除された時に、1フレーム目を即実行する事はおこなわない。
 *	  │　│'on unlock exec'	強制的にアンロックして、確実にシナリンシーケンスを開始し、1フレーム目を即実行する。
 *	  │　尚、対象性のために'on exec'命令も用意しておきましたが、上記の'on exec'命令の(ロックされるかロックされないかによって挙動が変わるという)動作が望ましいケースはあまり無いと思うので、実際に使用するのは'on goto','on unlock goto','on unlock exec'の三種類だけだと思います。
 */
#include "clip.h"
//*****************************************************************************
//	ローカル関数宣言
//*****************************************************************************
static ST_SnrSeqCh* _SnrSeq_GetSnrSeqCh(ST_SnrSeq* pSnrSeq, int iCh);
static ST_SnrSeqCtx* _SnrSeq_GetSnrSeqCtx(ST_SnrSeq* pSnrSeq, int iCtx);
static int _SnrSeqCtx_GetByte(ST_SnrSeq* pSnrSeq, int iCtx);
static int _SnrSeqCtx_GetHalf(ST_SnrSeq* pSnrSeq, int iCtx);
static int _SnrSeqCtx_GetDist(ST_SnrSeq* pSnrSeq, int iCtx);
static int _SnrSeq_CallFunc(ST_SnrSeq* pSnrSeq, int iCh, int iSnrFnc, const uint8_t* pArgs);
static int _SnrSeq_CallCond(ST_SnrSeq* pSnrSeq, int iCh, int iSnrFnc, const uint8_t* pArgs, int bNot);
static void _SnrSeqCtx_PushStack(ST_SnrSeq* pSnrSeq, int iCtx, int nRep, const uint8_t* pRet);
static void _SnrSeqCtx_PopStack(ST_SnrSeq* pSnrSeq, int iCtx, int* pnRep, const uint8_t** ppRet);
static int _SnrSeq_AllocSnrSeqCtx(ST_SnrSeq* pSnrSeq, int iCh);
static void _SnrSeq_FreeSnrSeqCtx(ST_SnrSeq* pSnrSeq, int iCh, int iCtx);
static void _SnrSeqCh_Start(ST_SnrSeq* pSnrSeq, int iCh, const uint8_t* p, int bExec);
static void _SnrSeqCh_Exec1(ST_SnrSeq* pSnrSeq, int iCh, int bUnlock);
static void _SnrSeqCh_Exec2(ST_SnrSeq* pSnrSeq, int iCh, int bStop);
static void _SnrSeqCtx_Exec(ST_SnrSeq* pSnrSeq, int iCh, int iCtx, int bStop);
//*****************************************************************************
//	グローバル関数
//*****************************************************************************
//シナリオシーケンサを動的に作成する。
//[in]
//	pFunc		コールバック関数。
//	nCh		シナリオシーケンサチャネル数。		0～255
//	nCtx		コンテキストバッファサイズ。		0～255
//[out]
//	戻り値		シナリオシーケンサ。
ST_SnrSeq* SnrSeq_New(int (*pFunc)(struct _ST_SnrSeq* pSnrSeq, int iCh, int iSnrFnc, struct blob_reader* pArgs), int nCh, int nCtx) {
	ST_SnrSeq* pSnrSeq;
#if 0	//この制約は廃止しました。「レイヤ管理のためにチャネル数を多く設定するが、全てのレイヤが同時にアクティブになる事は無い」ような(=TAPのような)ケースでは、チャネル数をコンテキスト数よりも多く設定する事も理に適っているからです。
	if((unsigned)nCh > (unsigned)nCtx) { DIE(); }	//チャネル数がコンテキスト数よりも多ければエラー停止します。全てのチャネルを同時実行するには少なくともチャネル数以上のコンテキスト数が必要であり、チャネル数がコンテキスト数よりも多いのは初期化パラメータエラーと思われるからです。ただし厳密には、管理のためだけにチャネル数を多めに用意しておき実際には全てのチャネルを同時実行しないような使い方(TAPのような使い方)をする場合はコンテキストバッファのメモリを節約するためにチャネル数がコンテキスト数よりも多くても構わないのですが、一般的にはチャネル数がコンテキスト数よりも多いのは初期化パラメータエラーと見なす方が安全だと思うのでエラー停止する事にしました。もし本当に左記のような使い方をするケースが生じた場合は、この行の検査を削除して下さい。
#endif
	//構造体のメモリを確保する。
	pSnrSeq = malloc(sizeof(ST_SnrSeq)			//ST_SnrSeq
	              + (sizeof(ST_SnrSeqCh)  * nCh)		//ST_SnrSeq.TBL_SnrSeqCh[nCh]
	              + (sizeof(ST_SnrSeqCtx) * nCtx));		//ST_SnrSeq.TBL_SnrSeqCtx[nCtx]
	if(!pSnrSeq) { DIE(); }	//メモリ不足
	//構造体を初期化する。
	SnrSeq_Init(pSnrSeq, pFunc, nCh, nCtx);
	return pSnrSeq;
}
//-----------------------------------------------------------------------------
//シナリオシーケンサを静的に初期化する。
//[in]
//	pSnrSeq		構造体のメモリ。
//	pFunc		コールバック関数。
//	nCh		シナリオシーケンサチャネル数。		0～255
//	nCtx		コンテキストバッファサイズ。		0～255
void SnrSeq_Init(ST_SnrSeq* pSnrSeq, int (*pFunc)(struct _ST_SnrSeq* pSnrSeq, int iCh, int iSnrFnc, struct blob_reader* pArgs), int nCh, int nCtx) {
	int iCh, iCtx;
#if 0	//この制約は廃止しました。「レイヤ管理のためにチャネル数を多く設定するが、全てのレイヤが同時にアクティブになる事は無い」ような(=TAPのような)ケースでは、チャネル数をコンテキスト数よりも多く設定する事も理に適っているからです。
	if((unsigned)nCh > (unsigned)nCtx) { DIE(); }	//チャネル数がコンテキスト数よりも多ければエラー停止します。全てのチャネルを同時実行するには少なくともチャネル数以上のコンテキスト数が必要であり、チャネル数がコンテキスト数よりも多いのは初期化パラメータエラーと思われるからです。ただし厳密には、管理のためだけにチャネル数を多めに用意しておき実際には全てのチャネルを同時実行しないような使い方(TAPのような使い方)をする場合はコンテキストバッファのメモリを節約するためにチャネル数がコンテキスト数よりも多くても構わないのですが、一般的にはチャネル数がコンテキスト数よりも多いのは初期化パラメータエラーと見なす方が安全だと思うのでエラー停止する事にしました。もし本当に左記のような使い方をするケースが生じた場合は、この行の検査を削除して下さい。
#endif
	//構造体をクリアする。
	memset(pSnrSeq, 0, sizeof(ST_SnrSeq)			//ST_SnrSeq
	                + (sizeof(ST_SnrSeqCh)  * nCh)		//ST_SnrSeq.TBL_SnrSeqCh[nCh]
	                + (sizeof(ST_SnrSeqCtx) * nCtx));	//ST_SnrSeq.TBL_SnrSeqCtx[nCtx]
	//コールバック関数を格納する。
	pSnrSeq->pFunc = pFunc;
	//シナリオシーケンサチャネル数を格納する。
	pSnrSeq->nCh = nCh;
	//コンテキストバッファサイズを格納する。
	pSnrSeq->nCtx = nCtx;
	//シナリオシーケンサチャネルを初期化する。
	for(iCh = 0; iCh < pSnrSeq->nCh; iCh++) {
		ST_SnrSeqCh* pSnrSeqCh = _SnrSeq_GetSnrSeqCh(pSnrSeq, iCh);
		pSnrSeqCh->iCtxHead = 255/*終端*/;
	}
	//コンテキストバッファを初期化する。
	for(iCtx = 0; iCtx < pSnrSeq->nCtx; iCtx++) {
		ST_SnrSeqCtx* pSnrSeqCtx = _SnrSeq_GetSnrSeqCtx(pSnrSeq, iCtx);
		pSnrSeqCtx->iCtxNext = 255/*終端*/;
	}
}
//-----------------------------------------------------------------------------
//動的に作成したシナリオシーケンサを解放する。
//[in]
//	pSnrSeq		シナリオシーケンサ。
void SnrSeq_Free(ST_SnrSeq* pSnrSeq) {
	//構造体のメモリを開放する。
	free(pSnrSeq);
}
//-----------------------------------------------------------------------------
//全てのチャネルのシナリオシーケンサを実行する。
//[in]
//	pSnrSeq		シナリオシーケンサ。
void SnrSeq_Exec(ST_SnrSeq* pSnrSeq) {
	int iCh;
	for(iCh = 0; iCh < pSnrSeq->nCh; iCh++) {
		SnrSeqCh_Exec(pSnrSeq, iCh);
	}
}
//-----------------------------------------------------------------------------
//指定されたチャネルのシナリオシーケンサを実行する。
//[in]
//	pSnrSeq		シナリオシーケンサ。
//	iCh		チャネル番号。						0～nCh-1
void SnrSeqCh_Exec(ST_SnrSeq* pSnrSeq, int iCh) {
	ST_SnrSeqCh* pSnrSeqCh = _SnrSeq_GetSnrSeqCh(pSnrSeq, iCh);
	//再入不可フラグがセットされていない事を確認する。
	if(pSnrSeqCh->bNonReentrant) { DIE(); }
	//ロックバッファ処理を行う。			//┐
	_SnrSeqCh_Exec1(pSnrSeq, iCh, 0/*bUnlock*/);	//│この処理順序は必須です。
	//シナリオシーケンス処理を行う。		//│ロックバッファ処理によって開始したシナリオシーケンスを、その後、即、シナリオシーケンス処理の対象とするためです。
	_SnrSeqCh_Exec2(pSnrSeq, iCh, 0/*bStop*/);	//┘
}
//-----------------------------------------------------------------------------
//全てのチャネルのシナリオテーブルをクリアする。
//[in]
//	pSnrSeq		シナリオシーケンサ。
void SnrSeq_ClrTbl(ST_SnrSeq* pSnrSeq) {
	int iCh;
	for(iCh = 0; iCh < pSnrSeq->nCh; iCh++) {
		SnrSeqCh_SetTbl(pSnrSeq, iCh, NULL);
	}
}
//-----------------------------------------------------------------------------
//指定されたチャネルのシナリオテーブルを設定,又は,クリアする。
//[in]
//	pSnrSeq		シナリオシーケンサ。
//	iCh		チャネル番号。						0～nCh-1
//	pSnrTbl		シナリオテーブル。					NULL=クリア
void SnrSeqCh_SetTbl(ST_SnrSeq* pSnrSeq, int iCh, const uint8_t* pSnrTbl/*NULL=クリア*/) {
	ST_SnrSeqCh* pSnrSeqCh = _SnrSeq_GetSnrSeqCh(pSnrSeq, iCh);
	//再入不可フラグがセットされていない事を確認する。
	if(pSnrSeqCh->bNonReentrant) { DIE(); }
	//'clr'トリガを送信する。
	SnrSeqCh_SendTrig(pSnrSeq, iCh, SnrTrg_clr);
	//シナリオテーブルを設定する。
	pSnrSeqCh->pSnrTbl = pSnrTbl;
	//'set'トリガを送信する。
	SnrSeqCh_SendTrig(pSnrSeq, iCh, SnrTrg_set);
}
//-----------------------------------------------------------------------------
//全てのチャネルにトリガを送信する。
//[in]
//	pSnrSeq		シナリオシーケンサ。
//	iCh		チャネル番号。						0～nCh-1
//	iSnrTrg		トリガ番号。						0～255
void SnrSeq_SendTrig(ST_SnrSeq* pSnrSeq, int iSnrTrg) {
	int iCh;
	for(iCh = 0; iCh < pSnrSeq->nCh; iCh++) {
		SnrSeqCh_SendTrig(pSnrSeq, iCh, iSnrTrg);
	}
}
//-----------------------------------------------------------------------------
//指定されたチャネルにトリガを送信する。
//[in]
//	pSnrSeq		シナリオシーケンサ。
//	iSnrTrg		トリガ番号。						0～255
void SnrSeqCh_SendTrig(ST_SnrSeq* pSnrSeq, int iCh, int iSnrTrg) {
	ST_SnrSeqCh* pSnrSeqCh = _SnrSeq_GetSnrSeqCh(pSnrSeq, iCh);
	//再入不可フラグがセットされていない事を確認する。
	if(pSnrSeqCh->bNonReentrant) { DIE(); }
	//シナリオテーブルが設定されていれば…
	if(pSnrSeqCh->pSnrTbl) {
		const uint8_t* p = pSnrSeqCh->pSnrTbl;
		if(*p == SnrTbl_end) { DIE(); }	//dSnrSeqC.exeが空のシナリオテーブルを生成する事は無いはずです。もしここで停止したらdSnrSeqC.exeのバグ,又は,当モジュールのバグです。
		//シナリオテーブルを走査して…
		for(;;) {
			//命令を取得する。
			int c = *p++;
			switch(c) {
			default:DIE();
			case SnrTbl_end:
				{
					return;	//ここまで
				}
				break;
			case SnrTbl_on_unlock:
			case SnrTbl_on_goto:
			case SnrTbl_on_unlock_goto:
			case SnrTbl_on_exec:
			case SnrTbl_on_unlock_exec:
				{
					int i, t;
					//トリガ番号を取得する。
					t = *p++;
					//トリガ番号が一致したら…
					if(t == iSnrTrg) {
						//ロック解除する命令(on unlock,又は,on unlock goto,又は,on unlock exec)ならば…
						if((c == SnrTbl_on_unlock) || (c == SnrTbl_on_unlock_goto) || (c == SnrTbl_on_unlock_exec)) {
							//ロック解除する。
							SnrSeqCh_Unlock(pSnrSeq, iCh);
							//ロック時間がクリアされ,且つ,ロックバッファが空になった事を確認する。
							if(pSnrSeqCh->LockCnt || pSnrSeqCh->TBL_LockBuf[0]) { DIE(); }	//バグ
						}
						//シナリオシーケンスを開始する命令(on goto,又は,on unlock goto,又は,on exec,又は,on unlock exec)ならば…
						if((c == SnrTbl_on_goto) || (c == SnrTbl_on_unlock_goto) || (c == SnrTbl_on_exec) || (c == SnrTbl_on_unlock_exec)) {
							//ロック時間が残っているか、ロックバッファが空でなければ…
							if(pSnrSeqCh->LockCnt || pSnrSeqCh->TBL_LockBuf[0]) {
								//ロックバッファの空き要素を検索する。
								for(i = 0; i < SnrSeqCh_LockBufSize; i++) {
									if(!pSnrSeqCh->TBL_LockBuf[i]) { break; }
								}
								//ロックバッファに空きが無ければ…
								if(i >= SnrSeqCh_LockBufSize) {
									//現在のロック時間を強制的にクリアする。
									pSnrSeqCh->LockCnt = 0;
									//最低一個以上のシナリオシーケンスを開始し、空きを確保する。
									_SnrSeqCh_Exec1(pSnrSeq, iCh, 0/*bUnlock*/);
									//ロック時間が残っているか,又は,ロックバッファが空でなければ…
									if(pSnrSeqCh->LockCnt || pSnrSeqCh->TBL_LockBuf[0]) {
										//ロックバッファの空き要素を検索する。
										for(i = 0; i < SnrSeqCh_LockBufSize; i++) {
											if(!pSnrSeqCh->TBL_LockBuf[i]) { break; }
										}
										//ロックバッファに空きが確保出来ている事を確認する。
										if(i >= SnrSeqCh_LockBufSize) { DIE(); }
										//ロックバッファの空き要素にシナリオシーケンスを格納する。
										pSnrSeqCh->TBL_LockBuf[i] = p;
									//ロック時間が残っておらず,且つ,ロックバッファが空ならば…
									} else {
										//シナリオシーケンスを開始する。
										_SnrSeqCh_Start(pSnrSeq, iCh, p, ((c == SnrTbl_on_exec) || (c == SnrTbl_on_unlock_exec))/*bExec*/);	//'on exec'命令,又は,'on unlock exec'命令ならば、シナリオシーケンスの開始と同時に、シナリオシーケンスを1フレーム実行する。
									}
								//ロックバッファに空きが有れば…
								} else {
									//ロックバッファの空き要素にシナリオシーケンスを格納する。
									pSnrSeqCh->TBL_LockBuf[i] = p;
								}
							//ロック時間が残っておらず,且つ,ロックバッファが空ならば…
							} else {
								//シナリオシーケンスを開始する。
								_SnrSeqCh_Start(pSnrSeq, iCh, p, ((c == SnrTbl_on_exec) || (c == SnrTbl_on_unlock_exec))/*bExec*/);	//'on exec'命令,又は,'on unlock exec'命令ならば、シナリオシーケンスの開始と同時に、シナリオシーケンスを1フレーム実行する。
							}
						}
						return;	//ここまで
					}
					//シナリオシーケンスを開始する命令(on goto,又は,on unlock goto,又は,on exec,又は,on unlock exec)ならば…
					if((c == SnrTbl_on_goto) || (c == SnrTbl_on_unlock_goto) || (c == SnrTbl_on_exec) || (c == SnrTbl_on_unlock_exec)) {
						//シナリオシーケンスボディへの相対距離とロック時間を読み飛ばす。
						p += (3/*シナリオシーケンスボディへの相対距離*/ + 2/*ロック時間*/);
					}
				}
				break;
			}
		}
	}
}
//-----------------------------------------------------------------------------
//全てのチャネルのロックを解除する。
//[in]
//	pSnrSeq		シナリオシーケンサ。
void SnrSeq_Unlock(ST_SnrSeq* pSnrSeq) {
	int iCh;
	for(iCh = 0; iCh < pSnrSeq->nCh; iCh++) {
		SnrSeqCh_Unlock(pSnrSeq, iCh);
	}
}
//-----------------------------------------------------------------------------
//指定されたチャネルのロックを解除する。
//[in]
//	pSnrSeq		シナリオシーケンサ。
//	iCh		チャネル番号。						0～nCh-1
void SnrSeqCh_Unlock(ST_SnrSeq* pSnrSeq, int iCh) {
	ST_SnrSeqCh* pSnrSeqCh = _SnrSeq_GetSnrSeqCh(pSnrSeq, iCh);
	//再入不可フラグがセットされていない事を確認する。
	if(pSnrSeqCh->bNonReentrant) { DIE(); }
	//ロックバッファ処理を行う。
	_SnrSeqCh_Exec1(pSnrSeq, iCh, 1/*bUnlock*/);
}
//-----------------------------------------------------------------------------
//全てのチャネルのシナリオシーケンスを停止する。
//[in]
//	pSnrSeq		シナリオシーケンサ。
void SnrSeq_Stop(ST_SnrSeq* pSnrSeq) {
	int iCh;
	for(iCh = 0; iCh < pSnrSeq->nCh; iCh++) {
		SnrSeqCh_Stop(pSnrSeq, iCh);
	}
}
//-----------------------------------------------------------------------------
//指定されたチャネルのシナリオシーケンスを停止する。
//[in]
//	pSnrSeq		シナリオシーケンサ。
//	iCh		チャネル番号。						0～nCh-1
void SnrSeqCh_Stop(ST_SnrSeq* pSnrSeq, int iCh) {
	ST_SnrSeqCh* pSnrSeqCh = _SnrSeq_GetSnrSeqCh(pSnrSeq, iCh);
	//再入不可フラグがセットされていない事を確認する。
	if(pSnrSeqCh->bNonReentrant) { DIE(); }
	//シナリオシーケンス処理を行う。
	_SnrSeqCh_Exec2(pSnrSeq, iCh, 1/*bStop*/);
}
//-----------------------------------------------------------------------------
//シナリオシーケンスが動作中であるチャネル数を返す。
//[in]
//	pSnrSeq		シナリオシーケンサ。
//[out]
//	戻り値		シナリオシーケンスが動作中であるチャネル数。		0～nCh
int SnrSeq_GetStat(ST_SnrSeq* pSnrSeq) {
	int iCh, nActive = 0;
	for(iCh = 0; iCh < pSnrSeq->nCh; iCh++) {
		nActive += SnrSeqCh_GetStat(pSnrSeq, iCh);
	}
	return nActive;
}
//-----------------------------------------------------------------------------
//このチャネルが動作中ならば1、停止中ならば0を返す。
//[in]
//	pSnrSeq		シナリオシーケンサ。
//	iCh		チャネル番号。						0～nCh-1
//[out]
//	戻り値		このチャネルが動作中ならば1、停止中ならば0。		0～1
//[note]
//	- 当関数は、コールバック関数の中から呼び出しても安全です。
//	  従って、当関数内で再入不可フラグの検査は行っていません。
//	- とは言え、コールバック関数を呼び出したチャネルが動作中である事は自明ですから、コールバック関数の中から当関数を呼び出す必要はほとんど無いと思います。
//	  唯一、コンテキストチェインが空でないが全て隠れコンテキストである場合を判別するために、コールバック関数の中から当関数を呼び出す事には意味が有ります。
int SnrSeqCh_GetStat(ST_SnrSeq* pSnrSeq, int iCh) {
	ST_SnrSeqCh* pSnrSeqCh = _SnrSeq_GetSnrSeqCh(pSnrSeq, iCh);
	int iCtx = pSnrSeqCh->iCtxHead;
	//ロック時間が残っているか,又は,ロックバッファが空でなければ、このチャネルは動作中であると見なす。
	if(pSnrSeqCh->LockCnt || pSnrSeqCh->TBL_LockBuf[0]) { return 1; }
	//このチャネルのコンテキストチェインの、各コンテキストについて…
	while(iCtx != 255/*終端*/) {
		ST_SnrSeqCtx* pSnrSeqCtx = _SnrSeq_GetSnrSeqCtx(pSnrSeq, iCtx);
		//通常のコンテキストが一つでも有れば、このチャネルは動作中であると見なす。
		if(!pSnrSeqCtx->bHide) { return 1; }
		iCtx = pSnrSeqCtx->iCtxNext;
	}
	//コンテキストチェインが空か,又は,全て隠れコンテキストならば、このチャネルは停止中であると見なす。
	return 0;
}
//*****************************************************************************
//	ローカル関数
//*****************************************************************************
//シナリオシーケンサチャネルを取得する。
//[in]
//	pSnrSeq		シナリオシーケンサ。
//	iCh		チャネル番号。						0～nCh-1
//[out]
//	戻り値		シナリオシーケンサチャネル。
static ST_SnrSeqCh* _SnrSeq_GetSnrSeqCh(ST_SnrSeq* pSnrSeq, int iCh) {
	if((unsigned)iCh >= (unsigned)pSnrSeq->nCh) { DIE(); }	//バグ
	return (ST_SnrSeqCh*)(pSnrSeq + 1) + iCh;
}
//-----------------------------------------------------------------------------
//シナリオシーケンサコンテキストを取得する。
//[in]
//	pSnrSeq		シナリオシーケンサ。
//	iCtx		コンテキスト番号。					0～nCtx-1
//[out]
//	戻り値		シナリオシーケンサコンテキスト。
static ST_SnrSeqCtx* _SnrSeq_GetSnrSeqCtx(ST_SnrSeq* pSnrSeq, int iCtx) {
	if((unsigned)iCtx >= (unsigned)pSnrSeq->nCtx) { DIE(); }	//バグ
	return (ST_SnrSeqCtx*)((ST_SnrSeqCh*)(pSnrSeq + 1) + pSnrSeq->nCh) + iCtx;
}
//-----------------------------------------------------------------------------
//現在の命令位置から、符号無し1バイト値を読み出す。
//[in]
//	pSnrSeq		シナリオシーケンサ。
//	iCtx		コンテキスト番号。					0～nCtx-1
//[out]
//	戻り値		読み出した値。						0～UINT8_MAX
static int _SnrSeqCtx_GetByte(ST_SnrSeq* pSnrSeq, int iCtx) {
	ST_SnrSeqCtx* pSnrSeqCtx = _SnrSeq_GetSnrSeqCtx(pSnrSeq, iCtx);
	return             *pSnrSeqCtx->pSnrSeq++;
}
//-----------------------------------------------------------------------------
//現在の命令位置から、符号無し2バイト値を読み出す。
//[in]
//	pSnrSeq		シナリオシーケンサ。
//	iCtx		コンテキスト番号。					0～nCtx-1
//[out]
//	戻り値		読み出した値。						0～UINT16_MAX
static int _SnrSeqCtx_GetHalf(ST_SnrSeq* pSnrSeq, int iCtx) {
	ST_SnrSeqCtx* pSnrSeqCtx = _SnrSeq_GetSnrSeqCtx(pSnrSeq, iCtx);
	int x =            *pSnrSeqCtx->pSnrSeq++;
	return  (x << 8) | *pSnrSeqCtx->pSnrSeq++;
}
//-----------------------------------------------------------------------------
//現在の命令位置から、符号付き3バイト値を読み出す。
//[in]
//	pSnrSeq		シナリオシーケンサ。
//	iCtx		コンテキスト番号。					0～nCtx-1
//[out]
//	戻り値		読み出した値。						INT24_MIN～INT24_MAX
static int _SnrSeqCtx_GetDist(ST_SnrSeq* pSnrSeq, int iCtx) {
	ST_SnrSeqCtx* pSnrSeqCtx = _SnrSeq_GetSnrSeqCtx(pSnrSeq, iCtx);
	int x =    (int8_t)*pSnrSeqCtx->pSnrSeq++;	//符号拡張、忘れずに!!
	    x = (x << 8) | *pSnrSeqCtx->pSnrSeq++;
	return  (x << 8) | *pSnrSeqCtx->pSnrSeq++;
}
//-----------------------------------------------------------------------------
//コールバック関数を呼び出す。
//[in]
//	pSnrSeq		シナリオシーケンサ。
//	iCh		チャネル番号。						0～nCh-1
//	iSnrFnc		関数番号。						0～255
//	pArgs		引数リスト。
//[out]
//	戻り値		コールバック関数の戻り値。
static int _SnrSeq_CallFunc(ST_SnrSeq* pSnrSeq, int iCh, int iSnrFnc, const uint8_t* pArgs) {
	ST_SnrSeqCh* pSnrSeqCh = _SnrSeq_GetSnrSeqCh(pSnrSeq, iCh);
	struct blob_reader reader;
	int size, retval;
	//BLOBサイズを取得する。
	size = *pArgs++;
	//BLOBリーダーを初期化する。
	blob_reader_init(&reader, (const int*)pArgs, size);
	//再入不可フラグをセットする。
	if(pSnrSeqCh->bNonReentrant) { DIE(); }		//もしここで停止したら、当モジュールのバグです。
	pSnrSeqCh->bNonReentrant = 1;
	//コールバック関数を呼び出す。
	retval = (*pSnrSeq->pFunc)(pSnrSeq, iCh, iSnrFnc, &reader);
	//再入不可フラグをクリアする。
	if(!pSnrSeqCh->bNonReentrant) { DIE(); }	//もしここで停止したら、当モジュールのバグです。
	pSnrSeqCh->bNonReentrant = 0;
	//コールバック関数の戻り値を返す。
	return retval;
}
//-----------------------------------------------------------------------------
//コールバック関数を呼び出し、条件判定を行う。
//[in]
//	pSnrSeq		シナリオシーケンサ。
//	iCh		チャネル番号。						0～nCh-1
//	iSnrFnc		関数番号。						0～255
//	pArgs		引数リスト。
//	bNot		条件を反転するならば1、反転しないならば0。		0～1
//[out]
//	戻り値		┌───┬───┬───┐
//			│　①　│　②　│戻り値│①=コールバック関数の戻り値、②=引数bNot
//			├───┼───┼───┤
//			│０　　│　０　│　０　│
//			│０　　│　１　│　１　│
//			│０以外│　０　│　１　│
//			│０以外│　１　│　０　│
//			└───┴───┴───┘
static int _SnrSeq_CallCond(ST_SnrSeq* pSnrSeq, int iCh, int iSnrFnc, const uint8_t* pArgs, int bNot) {
	return !_SnrSeq_CallFunc(pSnrSeq, iCh, iSnrFnc, pArgs) ^ !bNot;
}
//-----------------------------------------------------------------------------
//繰り返し回数と戻り先の命令位置を、スタックにプッシュする。
//[in]
//	pSnrSeq		シナリオシーケンサ。
//	iCtx		コンテキスト番号。					0～nCtx-1
//	nRep		繰り返し回数。						0=Callスタック,1～65534=Repeatスタックの残り繰返し回数,65535=Repeatスタック(無限)
//	pRet		戻り先の命令位置。					NULL不可
static void _SnrSeqCtx_PushStack(ST_SnrSeq* pSnrSeq, int iCtx, int nRep, const uint8_t* pRet) {
	ST_SnrSeqCtx* pSnrSeqCtx = _SnrSeq_GetSnrSeqCtx(pSnrSeq, iCtx);
	if(((unsigned)nRep > 65535/*無限*/) || !pRet) { DIE(); }
	//スタックに空きが有る事を確認する。
	if(pSnrSeqCtx->TBL_StackRet[SnrSeqCtx_StackSize - 1]) { DIE(); }	//スタック溢れ
	//スタック全体を後ろへずらす。
	memmove(&pSnrSeqCtx->TBL_StackRep[1],
	        &pSnrSeqCtx->TBL_StackRep[0],
	 (sizeof pSnrSeqCtx->TBL_StackRep[0]) * (SnrSeqCtx_StackSize - 1));
//{{このmemmove()の使い方は全く正しいにもかかわらずVC++6.0では警告出るので一時的に警告を抑制して回避する。
#ifdef  _MSC_VER
#pragma warning(push)
#pragma warning(disable:4090)	//'function' : 'const' 修飾子は異なります。
#pragma warning(disable:4022)	//'memmove' : 1 番目の実引数のﾎﾟｲﾝﾀの型が、仮引数のﾎﾟｲﾝﾀの型と異なります。
#endif//_MSC_VER
	memmove(&pSnrSeqCtx->TBL_StackRet[1],
	        &pSnrSeqCtx->TBL_StackRet[0],
	 (sizeof pSnrSeqCtx->TBL_StackRet[0]) * (SnrSeqCtx_StackSize - 1));
#ifdef  _MSC_VER
#pragma warning(pop)
#endif//_MSC_VER
//}}このmemmove()の使い方は全く正しいにもかかわらずVC++6.0では警告出るので一時的に警告を抑制して回避する。
	//スタックの先頭に繰り返し回数と戻り先の命令位置を格納する。
	pSnrSeqCtx->TBL_StackRep[0] = nRep;
	pSnrSeqCtx->TBL_StackRet[0] = pRet;
}
//-----------------------------------------------------------------------------
//繰り返し回数と戻り先の命令位置を、スタックからポップする。
//[in]
//	pSnrSeq		シナリオシーケンサ。
//	iCtx		コンテキスト番号。					0～nCtx-1
//	pnRep		繰り返し回数を受け取る変数へのポインタ。
//	ppRet		命令位置を受け取る変数へのポインタ。
//[out]
//	*pnRep		繰り返し回数。						0=Callスタック,1～65534=Repeatスタックの残り繰返し回数,65535=Repeatスタック(無限)
//	*ppRet		戻り先の命令位置。					NULL不可
static void _SnrSeqCtx_PopStack(ST_SnrSeq* pSnrSeq, int iCtx, int* pnRep, const uint8_t** ppRet) {
	ST_SnrSeqCtx* pSnrSeqCtx = _SnrSeq_GetSnrSeqCtx(pSnrSeq, iCtx);
	//スタックが空でない事を確認する。
	if(!pSnrSeqCtx->TBL_StackRet[0]) { DIE(); }
	//スタックの先頭の繰り返し回数と戻り先の命令位置を取り出す。
	*pnRep = pSnrSeqCtx->TBL_StackRep[0];
	*ppRet = pSnrSeqCtx->TBL_StackRet[0];
	//スタック全体を手前へずらす。
	memmove(&pSnrSeqCtx->TBL_StackRep[0],
	        &pSnrSeqCtx->TBL_StackRep[1],
	 (sizeof pSnrSeqCtx->TBL_StackRep[0]) * (SnrSeqCtx_StackSize - 1));
//{{このmemmove()の使い方は全く正しいにもかかわらずVC++6.0では警告出るので一時的に警告を抑制して回避する。
#ifdef  _MSC_VER
#pragma warning(push)
#pragma warning(disable:4090)	//'function' : 'const' 修飾子は異なります。
#pragma warning(disable:4022)	//'memmove' : 1 番目の実引数のﾎﾟｲﾝﾀの型が、仮引数のﾎﾟｲﾝﾀの型と異なります。
#endif//_MSC_VER
	memmove(&pSnrSeqCtx->TBL_StackRet[0],
	        &pSnrSeqCtx->TBL_StackRet[1],
	 (sizeof pSnrSeqCtx->TBL_StackRet[0]) * (SnrSeqCtx_StackSize - 1));
#ifdef  _MSC_VER
#pragma warning(pop)
#endif//_MSC_VER
//}}このmemmove()の使い方は全く正しいにもかかわらずVC++6.0では警告出るので一時的に警告を抑制して回避する。
	//スタックの末尾の要素を確実にクリアする。
//不要	pSnrSeqCtx->TBL_StackRep[SnrSeqCtx_StackSize - 1] = 0;
	pSnrSeqCtx->TBL_StackRet[SnrSeqCtx_StackSize - 1] = NULL;
}
//-----------------------------------------------------------------------------
//シナリオシーケンサコンテキストを確保する。
//[in]
//	pSnrSeq		シナリオシーケンサ。
//	iCh		チャネル番号。						0～nCh-1
//[out]
//	戻り値		コンテキスト番号。					0～nCtx-1
static int _SnrSeq_AllocSnrSeqCtx(ST_SnrSeq* pSnrSeq, int iCh) {
	int iCtx;
	//空きコンテキストを検索する。
	for(iCtx = 0; iCtx < pSnrSeq->nCtx; iCtx++) {
		ST_SnrSeqCtx* pSnrSeqCtx = _SnrSeq_GetSnrSeqCtx(pSnrSeq, iCtx);
		//空きコンテキストが見つかったら…
		if(!pSnrSeqCtx->pSnrSeq) {
			ST_SnrSeqCh* pSnrSeqCh = _SnrSeq_GetSnrSeqCh(pSnrSeq, iCh);
			//このチャネルのコンテキストチェインの先頭に、確保したコンテキストを追加する。
			if(pSnrSeqCtx->WaitCnt         ||			//_SnrSeqCtx_Exec()が_SnrSeq_FreeSnrSeqCtx()を呼び出す時に、既に(WaitCnt==0)になっているはずです。もしここでエラー停止した場合、コンテキストバッファが不正に破壊された可能性が有ります。
			   pSnrSeqCtx->TBL_StackRet[0] ||			//_SnrSeqCtx_Exec()が_SnrSeq_FreeSnrSeqCtx()を呼び出す時に、既にスタックが空になっているはずです。もしここでエラー停止した場合、コンテキストバッファが不正に破壊された可能性が有ります。
			   pSnrSeqCtx->bHide           ||			//_SnrSeq_FreeSnrSeqCtx()がコンテキストを解放する処理の中で、(bHide=0)を格納しているはずです。もしここでエラー停止した場合、コンテキストバッファが不正に破壊された可能性が有ります。
			  (pSnrSeqCtx->iCtxNext != 255/*終端*/)) { DIE(); }	//_SnrSeq_FreeSnrSeqCtx()がコンテキストを解放する処理の中で、(iCtxNext=255)を格納しているはずです。もしここでエラー停止した場合、コンテキストバッファが不正に破壊された可能性が有ります。
			   pSnrSeqCtx->iCtxNext = pSnrSeqCh->iCtxHead;
			                          pSnrSeqCh->iCtxHead = iCtx;
			return iCtx;	//ここまで
		}
	}
	DIE();	//空きコンテキストが見つかりませんでした。コンテキストバッファサイズを増やして下さい。
}
//-----------------------------------------------------------------------------
//シナリオシーケンサコンテキストを開放する。
//[in]
//	pSnrSeq		シナリオシーケンサ。
//	iCh		チャネル番号。						0～nCh-1
//	iCtx		コンテキスト番号。					0～nCtx-1
static void _SnrSeq_FreeSnrSeqCtx(ST_SnrSeq* pSnrSeq, int iCh, int iCtx) {
	ST_SnrSeqCh* pSnrSeqCh = _SnrSeq_GetSnrSeqCh(pSnrSeq, iCh);
	ST_SnrSeqCtx* pSnrSeqCtx = _SnrSeq_GetSnrSeqCtx(pSnrSeq, iCtx);
	//このチャネルのコンテキストチェインから、このコンテキストを取り外す。
	if(pSnrSeqCtx->WaitCnt         ||	//_SnrSeqCtx_Exec()が_SnrSeq_FreeSnrSeqCtx()を呼び出す時に、既に(WaitCnt==0)になっているはずです。もしここでエラー停止した場合、コンテキストバッファが不正に破壊された可能性が有ります。
	   pSnrSeqCtx->TBL_StackRet[0] ||	//_SnrSeqCtx_Exec()が_SnrSeq_FreeSnrSeqCtx()を呼び出す時に、既にスタックが空になっているはずです。もしここでエラー停止した場合、コンテキストバッファが不正に破壊された可能性が有ります。
	  !pSnrSeqCtx->pSnrSeq) { DIE(); }	//_SnrSeqCtx_Exec()が_SnrSeq_FreeSnrSeqCtx()を呼び出す時は、まだ(pSnrSeq!=NULL)のままであるはずです。もしここでエラー停止した場合、_SnrSeqCtx_Exec()の処理にバグが有る可能性があります。
	//取り外すコンテキストが、コンテキストチェインの先頭ならば…
	if(pSnrSeqCh->iCtxHead == iCtx) {
	   pSnrSeqCh->iCtxHead = pSnrSeqCtx->iCtxNext;
	//取り外すコンテキストが、コンテキストチェインの先頭でなければ…
	} else {
		int iCtxPrev = pSnrSeqCh->iCtxHead;
		for(;;) {
			ST_SnrSeqCtx* pCtxPrev = _SnrSeq_GetSnrSeqCtx(pSnrSeq, iCtxPrev);	//(iCtxPrev==255/*終端*/)になったら、_SnrSeq_GetSnrSeqCtx()の中でエラー停止します。指定されたコンテキストが、このチャネルのコンテキストチェインの中に見つからないバグです。
			if(pCtxPrev->iCtxNext == iCtx) {
			   pCtxPrev->iCtxNext = pSnrSeqCtx->iCtxNext;
			   break;
			}
			iCtxPrev = pCtxPrev->iCtxNext;
		}
	}
	//取り外したコンテキストを、空きコンテキストとする。
	pSnrSeqCtx->pSnrSeq = NULL/*空き*/;
	pSnrSeqCtx->iCtxNext = 255/*終端*/;	//(pSnrSeq==NULL)ならば、(iCtxNext==255)とします。	処理上必須ではありませんが、メモリ破壊やバグ検出のためです。
	pSnrSeqCtx->bHide = 0;			//(pSnrSeq==NULL)ならば、(bHide==0)とします。		処理上必須ではありませんが、メモリ破壊やバグ検出のためです。
}
//-----------------------------------------------------------------------------
//シナリオシーケンスを開始する。
//[in]
//	pSnrSeq		シナリオシーケンサ。
//	iCh		チャネル番号。						0～nCh-1
//	p		'on goto'命令,又は,'on unlock goto'命令,又は,'on exec'命令,又は,'on unlock exec'命令の、3バイト目。
//	bExec		シナリオシーケンスの開始と同時に、シナリオシーケンスを1フレーム実行するならば1、実行しないならば0。	0～1
static void _SnrSeqCh_Start(ST_SnrSeq* pSnrSeq, int iCh, const uint8_t* p, int bExec) {
	ST_SnrSeqCh* pSnrSeqCh = _SnrSeq_GetSnrSeqCh(pSnrSeq, iCh);
	ST_SnrSeqCtx* pSnrSeqCtx;
	int iCtx, s, l;
	//実行中のシナリオシーケンスが有れば停止する。
	// - 確保⇒停止の順でなく、停止⇒確保の順とする事に注意して下さい。
	//   停止によって解放したコンテキストも、確保の対象とするためです。
	SnrSeqCh_Stop(pSnrSeq, iCh);
	//コンテキストを確保する。
	iCtx = _SnrSeq_AllocSnrSeqCtx(pSnrSeq, iCh);
	pSnrSeqCtx = _SnrSeq_GetSnrSeqCtx(pSnrSeq, iCtx);
	pSnrSeqCtx->pSnrSeq = p;
	//シナリオシーケンスボディへの相対距離を取得する。
	s = _SnrSeqCtx_GetDist(pSnrSeq, iCtx);
	//ロック時間を取得する。
	l = _SnrSeqCtx_GetHalf(pSnrSeq, iCtx);
	//命令位置とロック時間を設定する。
	pSnrSeqCtx->pSnrSeq += s;
	pSnrSeqCh->LockCnt   = l;
	//bExec引数に0以外の値が指定されていたら、シナリオシーケンスの開始と同時に、シナリオシーケンスを1フレーム実行する。
	if(bExec) { SnrSeqCh_Exec(pSnrSeq, iCh); }
}
//-----------------------------------------------------------------------------
//ロックバッファ処理を行う。
//[in]
//	pSnrSeq		シナリオシーケンサ。
//	iCh		チャネル番号。						0～nCh-1
//	bUnlock		ロック解除時の処理ならば1、それ以外ならば0。		0～1
static void _SnrSeqCh_Exec1(ST_SnrSeq* pSnrSeq, int iCh, int bUnlock) {
	ST_SnrSeqCh* pSnrSeqCh = _SnrSeq_GetSnrSeqCh(pSnrSeq, iCh);
	const uint8_t* p;
	for(;;) {
		//ロック時間が残っていたら…
		if(pSnrSeqCh->LockCnt) {
			//ロック解除時の処理でなければ…
			if(!bUnlock) { 
				//次回のためにロック時間を減らす。
				pSnrSeqCh->LockCnt--;
				return;	//ここまで
			//ロック解除時の処理ならば…
			} else { 
				//ロック時間をクリアする。
				pSnrSeqCh->LockCnt = 0;
			}
		}
		//ロックバッファが空ならば…
		if(!(p = pSnrSeqCh->TBL_LockBuf[0])) {
			return;	//ここまで
		}
		//ロックバッファの先頭要素を削除する。
//{{このmemmove()の使い方は全く正しいにもかかわらずVC++6.0では警告出るので一時的に警告を抑制して回避する。
#ifdef  _MSC_VER
#pragma warning(push)
#pragma warning(disable:4090)	//'function' : 'const' 修飾子は異なります。
#pragma warning(disable:4022)	//'memmove' : 1 番目の実引数のﾎﾟｲﾝﾀの型が、仮引数のﾎﾟｲﾝﾀの型と異なります。
#endif//_MSC_VER
		memmove(&pSnrSeqCh->TBL_LockBuf[0],
		        &pSnrSeqCh->TBL_LockBuf[1],
		 (sizeof pSnrSeqCh->TBL_LockBuf[0]) * (SnrSeqCh_LockBufSize - 1));
#ifdef  _MSC_VER
#pragma warning(pop)
#endif//_MSC_VER
//}}このmemmove()の使い方は全く正しいにもかかわらずVC++6.0では警告出るので一時的に警告を抑制して回避する。
		         pSnrSeqCh->TBL_LockBuf[      (SnrSeqCh_LockBufSize - 1)] = NULL;
		//シナリオシーケンスを開始する。
		_SnrSeqCh_Start(pSnrSeq, iCh, p, 0/*bExec*/);
	}
}
//-----------------------------------------------------------------------------
//シナリオシーケンス処理を行う。
//[in]
//	pSnrSeq		シナリオシーケンサ。
//	iCh		チャネル番号。						0～nCh-1
//	bStop		停止時の処理ならば1、それ以外ならば0。			0～1
static void _SnrSeqCh_Exec2(ST_SnrSeq* pSnrSeq, int iCh, int bStop) {
	ST_SnrSeqCh* pSnrSeqCh = _SnrSeq_GetSnrSeqCh(pSnrSeq, iCh);
	int iCtx = pSnrSeqCh->iCtxHead;
	//このチャネルのコンテキストチェインの、各コンテキストについて…
	while(iCtx != 255/*終端*/) {
		ST_SnrSeqCtx* pSnrSeqCtx = _SnrSeq_GetSnrSeqCtx(pSnrSeq, iCtx);
		//コンテキストを実行する。
		int iCtxNext = pSnrSeqCtx->iCtxNext;	//────┐
		_SnrSeqCtx_Exec(pSnrSeq, iCh, iCtx, bStop);	//├_SnrSeqCtx_Exec()の中で(iCtxNext==255)になる可能性が有るので、_SnrSeqCtx_Exec()を呼び出す前にiCtxNextを取り出しておく。
		iCtx = iCtxNext;	//←───────────┘
	}
	if(bStop && (pSnrSeqCh->iCtxHead != 255/*終端*/)) { DIE(); }	//停止時の処理ならば、コンテキストチェインが空になったはずです。もしここでエラー停止したら、当モジュールのバグです。
}
//-----------------------------------------------------------------------------
//コンテキストを実行する。
//[in]
//	pSnrSeq		シナリオシーケンサ。
//	iCh		チャネル番号。						0～nCh-1
//	iCtx		コンテキスト番号。					0～nCtx-1
//	bStop		停止時の処理ならば1、それ以外ならば0。			0～1
static void _SnrSeqCtx_Exec(ST_SnrSeq* pSnrSeq, int iCh, int iCtx, int bStop) {
	ST_SnrSeqCh* pSnrSeqCh = _SnrSeq_GetSnrSeqCh(pSnrSeq, iCh);
	ST_SnrSeqCtx* pSnrSeqCtx = _SnrSeq_GetSnrSeqCtx(pSnrSeq, iCtx);
	if(!pSnrSeqCtx->pSnrSeq) { DIE(); }	//既に(pSnrSeq==NULL)であるコンテキストに対して、当関数が呼び出される事は無いはずです。もしここでエラー停止したら、当モジュールのバグです。
	for(;;) {
		int c;
		//待ち時間が残っていたら…
		if(pSnrSeqCtx->WaitCnt) {
			//停止時の処理でなければ…
			if(!bStop) {
				//次回のために待ち時間を減らす。
				pSnrSeqCtx->WaitCnt--;
				return;	//ここまで
			//停止時の処理ならば…
			} else {
				//待ち時間をクリアする。
				pSnrSeqCtx->WaitCnt = 0;
			}
		}
		//命令を取得する。
		c = _SnrSeqCtx_GetByte(pSnrSeq, iCtx);
		switch(c) {
		default:DIE();
		case SnrSeq_end:
			{
				int r1, r2, s;
				const uint8_t* p;
				//スタックが空でなければ…
				if(pSnrSeqCtx->TBL_StackRet[0]) {
					//繰り返し回数と戻り先の命令位置を、スタックからポップする。
					_SnrSeqCtx_PopStack(pSnrSeq, iCtx, &r1, &p);
					//命令位置を設定する。
					pSnrSeqCtx->pSnrSeq = p;
					//停止時の処理でなく,且つ,Repeatスタックだったならば…
					if(!bStop &&  r1) {
						//繰り返し回数が無限(65535)か,又は,繰り返し回数を1減らしてまだ残っていたら…
						if((  r1 == 65535/*無限*/) ||
						   (--r1 != 0)) {
							//繰り返し回数と戻り先の命令位置を、スタックにプッシュする。
							_SnrSeqCtx_PushStack(pSnrSeq, iCtx, r1, p);
							//命令位置を設定する。
							pSnrSeqCtx->pSnrSeq -= (1/*命令*/ + 2/*繰り返し回数*/ + 3/*シナリオシーケンスボディへの相対距離*/);
							c  = _SnrSeqCtx_GetByte(pSnrSeq, iCtx);
							r2 = _SnrSeqCtx_GetHalf(pSnrSeq, iCtx);
							s  = _SnrSeqCtx_GetDist(pSnrSeq, iCtx);
							if((c != SnrSeq_repeat) || (r2 < r1)) { DIE(); }	//無限ならば(r1==r2==65535)のまま変化していないはず，無限以外ならば(r1<r2<65535)になっているはずなので、いずれにしてもここでは(r2<r1)になっているはずです。
							pSnrSeqCtx->pSnrSeq += s;
						}
					}
				//スタックが空ならば…
				} else {
					//コンテキストを解放する。
					_SnrSeq_FreeSnrSeqCtx(pSnrSeq, iCh, iCtx);	//この中で、(pSnrSeq==NULL)になります。
					return;	//ここまで
				}
			}
			break;
		case SnrSeq_exit:
		case SnrSeq_escape:
			{
				//停止時の処理か,又は,無条件に停止する命令(exit)ならば…
				if(bStop || (c == SnrSeq_exit)) {
					//スタックを空にする。
					// - 先頭や末尾の要素だけでなく、全体をクリアする必要が有る事に注意して下さい。
					//   もし途中の要素をクリアせずに残していると、_SnrSeqCtx_PushStack()によって残った要素が末尾へ移動した時に、誤ってスタックがフルと判断してしまう問題が生じます。
//{{このmemset()の使い方は全く正しいにもかかわらずVC++6.0では警告出るので一時的に警告を抑制して回避する。
#ifdef  _MSC_VER
#pragma warning(push)
#pragma warning(disable:4090)	//'function' : 'const' 修飾子は異なります。
#pragma warning(disable:4022)	//'memset' : 1 番目の実引数のﾎﾟｲﾝﾀの型が、仮引数のﾎﾟｲﾝﾀの型と異なります。
#endif//_MSC_VER
				//不要	memset(pSnrSeqCtx->TBL_StackRep, 0, sizeof pSnrSeqCtx->TBL_StackRep);
					memset(pSnrSeqCtx->TBL_StackRet, 0, sizeof pSnrSeqCtx->TBL_StackRet);
#ifdef  _MSC_VER
#pragma warning(pop)
#endif//_MSC_VER
//}}このmemset()の使い方は全く正しいにもかかわらずVC++6.0では警告出るので一時的に警告を抑制して回避する。
					//コンテキストを解放する。
					_SnrSeq_FreeSnrSeqCtx(pSnrSeq, iCh, iCtx);	//この中で、(pSnrSeq==NULL)になります。
					return;	//ここまで
				}
			}
			break;
		case SnrSeq_wait:
			{
				int w;
				//待ち時間を取得する。
				w = _SnrSeqCtx_GetHalf(pSnrSeq, iCtx);
				if(!w) { DIE(); }	//データバグ
				//待ち時間を格納する。
				pSnrSeqCtx->WaitCnt = w;
			}
			break;
		case SnrSeq_wait_for:
		case SnrSeq_wait_for_not:
			{
				int f, a;
				//関数番号を取得する。
				f = _SnrSeqCtx_GetByte(pSnrSeq, iCtx);
				//引数リストへの相対距離を取得する。
				a = _SnrSeqCtx_GetDist(pSnrSeq, iCtx);
				//条件が成立しなければ…
				if(_SnrSeq_CallCond(pSnrSeq, iCh, f, pSnrSeqCtx->pSnrSeq + a, c != SnrSeq_wait_for_not)) {
					//停止時の処理でなければ…
					if(!bStop) {
						//命令位置を設定する。
						pSnrSeqCtx->pSnrSeq -= (1/*命令*/ + 1/*関数番号*/ + 3/*引数リストへの相対距離*/);
						return;	//ここまで
					}
				}
			}
			break;
		case SnrSeq_fork:
			{
				int s;
				//シナリオシーケンスボディへの相対距離を取得する。
				s = _SnrSeqCtx_GetDist(pSnrSeq, iCtx);
				{
					//コンテキストを確保する。
					int           iCtxFork = _SnrSeq_AllocSnrSeqCtx(pSnrSeq, iCh);
					ST_SnrSeqCtx* pCtxFork = _SnrSeq_GetSnrSeqCtx(  pSnrSeq, iCtxFork);
					//命令位置を設定する。
					pCtxFork->pSnrSeq = pSnrSeqCtx->pSnrSeq + s;
					//元コンテキストの隠れコンテキストフラグを継承する。
					// - 通常のコンテキストがfork命令を実行して新たなコンテキストを開始すると、そのコンテキストは初期状態で通常のコンテキストとなります。
					//   隠れコンテキストがfork命令を実行して新たなコンテキストを開始すると、そのコンテキストは初期状態で既に隠れコンテキストとなります。
					//   そうした理由については、clipdirs.cの'Wed May 24 21:26:30 JST 2017 Naoyuki Sawa'のコメントを参照して下さい。
					pCtxFork->bHide = pSnrSeqCtx->bHide;
					//フォークと同時に、シナリオシーケンスを1フレーム,又は,最後まで実行する。
					// - 以下の理由により、この処理は必須です。
					// - フォークしたコンテキストは、このチャネルのコンテキストチェインの先頭に追加されます。
					//   当関数は_SnrSeqCh_Exec2()から呼び出されますが、今回の_SnrSeqCh_Exec2()では走査位置を過ぎているので、追加したコンテキストが実行される事は有りません。
					// - 停止時の処理でない(bStop==0)場合、フォークしてブロックの1フレーム目の命令(有れば)は、フォークを行ったフレームで実行されるのが自然だと思います。
					//   従って、ここで明示的に、シナリオシーケンスを1フレーム実行する事にしました。
					//   行わなくても動作はしますが、もしそうするとフォークしたブロックの実行が1フレーム遅れる動作になってしまい、良くないと思います。
					// - 停止時の処理である(bStop!=0)場合、フォークしたコンテキストが終了するまで実行する必要が有ります。
					//   しかし、前述の通り、今回の_SnrSeqCh_Exec2()では走査位置を過ぎているので、追加したコンテキストが実行されません。
					//   従って、ここで明示的に、シナリオシーケンスを最後まで実行する必要が有ります。
					//   もしここで実行しないと、停止したチャネルのコンテキストが残ってしまうバグになってしまい、_SnrSeqCh_Exec2()にてアサートします。
					_SnrSeqCtx_Exec(pSnrSeq, iCh, iCtxFork, bStop);
				}
			}
			break;
		case SnrSeq_hide:
			{
				//隠れコンテキストフラグをセットする。
				// - 既に隠れコンテキストフラグがセットされていても、エラーにはしない事にしました。
				//   一つのコンテキストの中でhideを複数回実行しても、二回目以降のhideがダミー処理となるだけで、実行時エラーにはなりません。
				//   実際の所一つのコンテキストの中で意図的にhideを複数回実行する事はほとんど無いと思いますが、稀なケースとしてcall命令の呼び出し先でhideを実行するような場合に、呼び出し元が既に隠れコンテキストだとエラーになると不便だと思うので、一つのコンテキストの中でhideを複数回実行してもエラーにはしない事にしました。
				pSnrSeqCtx->bHide = 1;
			}
			break;
		case SnrSeq_join:
			{
				//停止時の処理でなければ…
				if(!bStop) {
					//自分以外の全てのコンテキストが終了するまで待つ。
					// - joinは、隠れコンテキストフラグ(bHide)を考慮しません。
					//   通常コンテキスト(bHide==0)も隠れコンテキスト(bHide==1)も含めて、自分以外の全てのコンテキストが終了するまで待ちます。
					// - 別の案として、自分以外の通常コンテキスト(bHide==0)だけを待って、自分以外の隠れコンテキスト(bHide==1)が残っていても待たないという仕様も検討したのですが、その案は採用しない事にしました。
					//   別の案を採用しなかった理由は、join命令が自分以外の隠れコンテキストを待つのが適切かどうかについて、必然的な理由が見出だせなかったからです。
					//   また、もし、join命令が隠れコンテキストを考慮するようにすると、join命令を実行しているコンテキスト自身が隠れコンテキストだった場合に、特例とするかどうかも考慮が必要になるかも知れません。
					//   上記のように、join命令が隠れコンテキストを考慮すると、直観的でない問題が生じるので、隠れコンテキストフラグを考慮せずに自分意外の全てのコンテキストを待つ方が、単純で良いと判断しました。
					if((pSnrSeqCh->iCtxHead != iCtx) ||		//自分が、このチャネルのコンテキストチェインの、最初のコンテキストでないか、又は、
					   (pSnrSeqCtx->iCtxNext != 255/*終端*/)) {	//自分が、このチャネルのコンテキストチェインの、最後のコンテキストでなければ、自分以外のコンテキストが残っていると見なせる。
						pSnrSeqCtx->pSnrSeq--;			//命令位置を1バイト戻して、次回もこの'join'を処理するようにする。
						return;	//ここまで
					}
				}
			}
			break;
		case SnrSeq_goto:
		case SnrSeq_call:
			{
				int s;
				//シナリオシーケンスボディへの相対距離を取得する。
				s = _SnrSeqCtx_GetDist(pSnrSeq, iCtx);
				//'goto'命令ならば…
				if(c == SnrSeq_goto) {
					//スタックを空にする。
					// - 先頭や末尾の要素だけでなく、全体をクリアする必要が有る事に注意して下さい。
					//   もし途中の要素をクリアせずに残していると、_SnrSeqCtx_PushStack()によって残った要素が末尾へ移動した時に、誤ってスタックがフルと判断してしまう問題が生じます。
//{{このmemset()の使い方は全く正しいにもかかわらずVC++6.0では警告出るので一時的に警告を抑制して回避する。
#ifdef  _MSC_VER
#pragma warning(push)
#pragma warning(disable:4090)	//'function' : 'const' 修飾子は異なります。
#pragma warning(disable:4022)	//'memset' : 1 番目の実引数のﾎﾟｲﾝﾀの型が、仮引数のﾎﾟｲﾝﾀの型と異なります。
#endif//_MSC_VER
				//不要	memset(pSnrSeqCtx->TBL_StackRep, 0, sizeof pSnrSeqCtx->TBL_StackRep);
					memset(pSnrSeqCtx->TBL_StackRet, 0, sizeof pSnrSeqCtx->TBL_StackRet);
#ifdef  _MSC_VER
#pragma warning(pop)
#endif//_MSC_VER
//}}このmemset()の使い方は全く正しいにもかかわらずVC++6.0では警告出るので一時的に警告を抑制して回避する。
				//'call'命令ならば…
				} else {
					//繰り返し回数と戻り先の命令位置を、スタックにプッシュする。
					_SnrSeqCtx_PushStack(pSnrSeq, iCtx, 0/*Callスタック*/, pSnrSeqCtx->pSnrSeq);
				}
				//命令位置を設定する。
				pSnrSeqCtx->pSnrSeq += s;
			}
			break;
		case SnrSeq_if:
		case SnrSeq_if_not:
			{
				const uint8_t* p = NULL;	//条件が成立したら、呼び出し先の命令位置を格納する。
				int f, a, s;
				//関数番号を取得する。
				f = _SnrSeqCtx_GetByte(pSnrSeq, iCtx);
				//引数リストへの相対距離を取得する。
				a = _SnrSeqCtx_GetDist(pSnrSeq, iCtx);
				//シナリオシーケンスボディへの相対距離を取得する。
				s = _SnrSeqCtx_GetDist(pSnrSeq, iCtx);
				//条件が成立したら…
				if(_SnrSeq_CallCond(pSnrSeq, iCh, f, pSnrSeqCtx->pSnrSeq + a, c == SnrSeq_if_not)) {
					//呼び出し先の命令位置を格納する。
					p = pSnrSeqCtx->pSnrSeq + s;
				}
				//'elif'命令,及び,'elif not'命令を処理する。
				while((*pSnrSeqCtx->pSnrSeq == SnrSeq_elif) ||
				      (*pSnrSeqCtx->pSnrSeq == SnrSeq_elif_not)) {
					//命令を取得する。
					c = _SnrSeqCtx_GetByte(pSnrSeq, iCtx);
					//関数番号を取得する。
					f = _SnrSeqCtx_GetByte(pSnrSeq, iCtx);
					//引数リストへの相対距離を取得する。
					a = _SnrSeqCtx_GetDist(pSnrSeq, iCtx);
					//シナリオシーケンスボディへの相対距離を取得する。
					s = _SnrSeqCtx_GetDist(pSnrSeq, iCtx);
					//まだ条件が成立していなければ…
					if(!p) {
						//条件が成立したら…
						if(_SnrSeq_CallCond(pSnrSeq, iCh, f, pSnrSeqCtx->pSnrSeq + a, c == SnrSeq_elif_not)) {
							//呼び出し先の命令位置を格納する。
							p = pSnrSeqCtx->pSnrSeq + s;
						}
					}
				}
				//'else'命令を処理する。
				if(*pSnrSeqCtx->pSnrSeq == SnrSeq_else) {
					//命令を取得する。
					c = _SnrSeqCtx_GetByte(pSnrSeq, iCtx);
					//シナリオシーケンスボディへの相対距離を取得する。
					s = _SnrSeqCtx_GetDist(pSnrSeq, iCtx);
					//まだ条件が成立していなければ…
					if(!p) {
						//呼び出し先の命令位置を格納する。
						p = pSnrSeqCtx->pSnrSeq + s;
					}
				}
				//条件が成立していたら…
				if(p) {
					//繰り返し回数と戻り先の命令位置を、スタックにプッシュする。
					_SnrSeqCtx_PushStack(pSnrSeq, iCtx, 0/*Callスタック*/, pSnrSeqCtx->pSnrSeq);
					//命令位置を設定する。
					pSnrSeqCtx->pSnrSeq = p;
				}
			}
			break;
		case SnrSeq_case:
			{
				const uint8_t* p = NULL;	//条件が成立したら、呼び出し先の命令位置を格納する。
				int f, a, s, v, retval;
				//関数番号を取得する。
				f = _SnrSeqCtx_GetByte(pSnrSeq, iCtx);
				//引数リストへの相対距離を取得する。
				a = _SnrSeqCtx_GetDist(pSnrSeq, iCtx);
				//コールバック関数を呼び出す。
				retval = _SnrSeq_CallFunc(pSnrSeq, iCh, f, pSnrSeqCtx->pSnrSeq + a);
				//'of'命令を処理する。
				if(   *pSnrSeqCtx->pSnrSeq != SnrSeq_of) { DIE(); }	//dSnrSeqC.exeが'of'命令の無い'case'命令を生成する事は無いはずです。もしここで停止したらdSnrSeqC.exeのバグ,又は,当モジュールのバグです。
				while(*pSnrSeqCtx->pSnrSeq == SnrSeq_of) {
					//命令を取得する。
					c = _SnrSeqCtx_GetByte(pSnrSeq, iCtx);
					//値を取得する。
					v = _SnrSeqCtx_GetByte(pSnrSeq, iCtx);
					//シナリオシーケンスボディへの相対距離を取得する。
					s = _SnrSeqCtx_GetDist(pSnrSeq, iCtx);
					//まだ条件が成立していなければ…
					if(!p) {
						//値が一致したら…
						if(v == retval) {
							//呼び出し先の命令位置を格納する。
							p = pSnrSeqCtx->pSnrSeq + s;
						}
					}
				}
				//'else'命令を処理する。
				if(*pSnrSeqCtx->pSnrSeq == SnrSeq_else) {
					//命令を取得する。
					c = _SnrSeqCtx_GetByte(pSnrSeq, iCtx);
					//シナリオシーケンスボディへの相対距離を取得する。
					s = _SnrSeqCtx_GetDist(pSnrSeq, iCtx);
					//まだ条件が成立していなければ…
					if(!p) {
						//呼び出し先の命令位置を格納する。
						p = pSnrSeqCtx->pSnrSeq + s;
					}
				}
				//条件が成立していたら…
				if(p) {
					//繰り返し回数と戻り先の命令位置を、スタックにプッシュする。
					_SnrSeqCtx_PushStack(pSnrSeq, iCtx, 0/*Callスタック*/, pSnrSeqCtx->pSnrSeq);
					//命令位置を設定する。
					pSnrSeqCtx->pSnrSeq = p;
				}
			}
			break;
		case SnrSeq_repeat:
			{
				int r, s;
				//繰り返し回数を取得する。
				r = _SnrSeqCtx_GetHalf(pSnrSeq, iCtx);
				if(!r) { DIE(); }	//データバグ
				//シナリオシーケンスボディへの相対距離を取得する。
				s = _SnrSeqCtx_GetDist(pSnrSeq, iCtx);
				//繰り返し回数と戻り先の命令位置を、スタックにプッシュする。
				_SnrSeqCtx_PushStack(pSnrSeq, iCtx, r, pSnrSeqCtx->pSnrSeq);
				//命令位置を設定する。
				pSnrSeqCtx->pSnrSeq += s;
			}
			break;
		case SnrSeq_break:
			{
				int n, r;
				const uint8_t* p;
				//ネストしたrepeatを抜ける数を取得する。	PHPのbreakを参考にしました。
				n = _SnrSeqCtx_GetByte(pSnrSeq, iCtx);
				if(!n) { DIE(); }	//データバグ
				//ネストしたrepeatを抜ける数が0になるまで…
				do {
					if(!pSnrSeqCtx->TBL_StackRet[0]) { DIE(); }	//ネストしたrepeatを抜ける数が、repeatのネスト数よりも大きかった。
					//繰り返し回数と戻り先の命令位置を、スタックからポップする。
					_SnrSeqCtx_PopStack(pSnrSeq, iCtx, &r, &p);
					//Repeatスタックならば、ネストしたrepeatを抜ける数を減らす。
					// - 当スクリプトのbreak命令はrepeatループだけが対象であり、while,及び,untilループは考慮しない事に注意して下さい。
					//   スクリプトの制約ではなくドライバ(=clipsnrs.c)の制約で、while,及び,untilとCallスタックを区別出来ないからです。
					if(r) { n--; }
				} while(n);
				//命令位置を設定する。
				pSnrSeqCtx->pSnrSeq = p;
			}
			break;
		case SnrSeq_while:
		case SnrSeq_while_not:
			{
				int f, a, s;
				//関数番号を取得する。
				f = _SnrSeqCtx_GetByte(pSnrSeq, iCtx);
				//引数リストへの相対距離を取得する。
				a = _SnrSeqCtx_GetDist(pSnrSeq, iCtx);
				//シナリオシーケンスボディへの相対距離を取得する。
				s = _SnrSeqCtx_GetDist(pSnrSeq, iCtx);
				//条件が成立したら…
				if(_SnrSeq_CallCond(pSnrSeq, iCh, f, pSnrSeqCtx->pSnrSeq + a, c == SnrSeq_while_not)) {
					//繰り返し回数と戻り先の命令位置を、スタックにプッシュする。
					_SnrSeqCtx_PushStack(pSnrSeq, iCtx, 0/*Callスタック*/,
					                     !bStop ? pSnrSeqCtx->pSnrSeq - (1/*命令*/ + 1/*関数番号*/ + 3/*引数リストへの相対距離*/ + 3/*シナリオシーケンスボディへの相対距離*/)
					                            : pSnrSeqCtx->pSnrSeq);	//停止時の処理ならば、無限ループにならないよう、'while'の次の命令へ戻る事にした。
					//命令位置を設定する。
					pSnrSeqCtx->pSnrSeq += s;
				}
			}
			break;
		case SnrSeq_act:
		case SnrSeq_act_ensure:
			{
				int f, a, retval;
				//関数番号を取得する。
				f = _SnrSeqCtx_GetByte(pSnrSeq, iCtx);
				//引数リストへの相対距離を取得する。
				a = _SnrSeqCtx_GetDist(pSnrSeq, iCtx);
				//停止時の処理でないか,又は,停止時にも実行する命令(act ensure)ならば…
				if(!bStop || (c == SnrSeq_act_ensure)) {
					//コールバック関数を呼び出す。
					retval = _SnrSeq_CallFunc(pSnrSeq, iCh, f, pSnrSeqCtx->pSnrSeq + a);
					if((unsigned)retval > UINT16_MAX) { DIE(); }
					//待ち時間を格納する。
					pSnrSeqCtx->WaitCnt = retval;
				}
			}
			break;
		}
	}
}
//*****************************************************************************
//	dSnrSeqC.exeが出力したバイナリ形式を扱うためのユーティリティ関数
//*****************************************************************************
#ifdef  USE_SNRSEQ_BIN
void SnrSeqCh_SetTblNo(ST_SnrSeq* pSnrSeq, int iCh, const void* pSnrSeqBin, int iSnrTblNo) {
	const int* const TBL_SnrTblNo = pSnrSeqBin;						//バイナリの先頭にアドレステーブルが有る。アドレスはデータ本体(BYTE配列)のインデクスである。
	const int nSnrTblNo = TBL_SnrTblNo[0];							//アドレステーブルの先頭[0]に要素数が入っている。要素数はそれ自身([0])も含む。
	const unsigned char* const TBL_SnrSeq = (const unsigned char*)&TBL_SnrTblNo[nSnrTblNo];	//アドレステーブルの直後からデータ本体(BYTE配列)が開始する。
	if((iSnrTblNo <= 0) || (iSnrTblNo >= nSnrTblNo)) { DIE(); }
	SnrSeqCh_SetTbl(pSnrSeq, iCh, &TBL_SnrSeq[TBL_SnrTblNo[iSnrTblNo]]);
}
//-----------------------------------------------------------------------------
//{{2017/11/04追加:ラベルテーブルを検索するユーティリティ関数を追加しました。
//ラベルテーブルを取得する。
const char* SnrSeq_GetLabelTable(const void* pSnrSeqBin) {
	const int* const TBL_SnrTblNo = pSnrSeqBin;						//バイナリの先頭にアドレステーブルが有る。アドレスはデータ本体(BYTE配列)のインデクスである。
	const int nSnrTblNo = TBL_SnrTblNo[0];							//アドレステーブルの先頭[0]に要素数が入っている。要素数はそれ自身([0])も含む。
	return (const char*)&TBL_SnrTblNo[nSnrTblNo];						//アドレステーブルの直後からデータ本体(BYTE配列)が開始する。ラベルテーブルはデータ本体の先頭部分に有る。(/clip/tool/dSnrSeqC/winapp.hの'Sat Nov 04 23:56:59 JST 2017 Naoyuki Sawa'のコメントを参照して下さい。)
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//シナリオテーブル番号に対応する、シナリオテーブル名を取得する。
//見付からなければ、エラー停止する。
const char* SnrSeq_GetTableName(const void* pSnrSeqBin, int iSnrTblNo) {
	const char* pTableName = SnrSeq_TryGetTableName(pSnrSeqBin, iSnrTblNo);
	if(!pTableName) { DIE(); }
	return pTableName;
}
//トリガ番号に対応する、トリガ名を取得する。
//見付からなければ、エラー停止する。
const char* SnrSeq_GetTrigName(const void* pSnrSeqBin, int iSnrTrg) {
	const char* pTrigName = SnrSeq_TryGetTrigName(pSnrSeqBin, iSnrTrg);
	if(!pTrigName) { DIE(); }
	return pTrigName;
}
//関数番号に対応する、関数名を取得する。
//見付からなければ、エラー停止する。
const char* SnrSeq_GetFuncName(const void* pSnrSeqBin, int iSnrFnc) {
	const char* pFuncName = SnrSeq_TryGetFuncName(pSnrSeqBin, iSnrFnc);
	if(!pFuncName) { DIE(); }
	return pFuncName;
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//シナリオテーブル名に対応する、シナリオテーブル番号を取得する。
//見付からなければ、エラー停止する。
int SnrSeq_GetTableNo(const void* pSnrSeqBin, const char* pTableName) {
	int iSnrTblNo = SnrSeq_TryGetTableNo(pSnrSeqBin, pTableName);
	if(iSnrTblNo == -1) { DIE(); }
	return iSnrTblNo;
}
//トリガ名に対応する、トリガ番号を取得する。
//見付からなければ、エラー停止する。
int SnrSeq_GetTrigNo(const void* pSnrSeqBin, const char* pTrigName) {
	int iSnrTrg = SnrSeq_TryGetTrigNo(pSnrSeqBin, pTrigName);
	if(iSnrTrg == -1) { DIE(); }
	return iSnrTrg;
}
//関数名に対応する、関数番号を取得する。
//見付からなければ、エラー停止する。
int SnrSeq_GetFuncNo(const void* pSnrSeqBin, const char* pFuncName) {
	int iSnrFnc = SnrSeq_TryGetFuncNo(pSnrSeqBin, pFuncName);
	if(iSnrFnc == -1) { DIE(); }
	return iSnrFnc;
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//シナリオテーブル番号に対応する、シナリオテーブル名を取得する。
//見付からなければ、NULLを返す。
const char* SnrSeq_TryGetTableName(const void* pSnrSeqBin, int iSnrTblNo) {
	const char* p = SnrSeq_GetLabelTable(pSnrSeqBin);
	while(*p) {							//シナリオテーブルの名前のリスト内を検索する。
		if(--iSnrTblNo == 0) { return p; }
		p = strchr(p, '\0') + 1/*'\0'*/;
	}
	return NULL;
}
//トリガ番号に対応する、トリガ名を取得する。
//見付からなければ、NULLを返す。
const char* SnrSeq_TryGetTrigName(const void* pSnrSeqBin, int iSnrTrg) {
	const char* p = SnrSeq_GetLabelTable(pSnrSeqBin);
	if(iSnrTrg == SnrTrg_set) { return "set"; }			//┬トリガ#0("set")とトリガ#255("clr")は格納されない事に注意して下さい。
	if(iSnrTrg == SnrTrg_clr) { return "clr"; }			//┘
	while(*p++) { p = strchr(p, '\0') + 1/*'\0'*/; }		//シナリオテーブルの名前のリストを読み飛ばす。	少しトリッキーですが"\0\0"で抜けます。
	while(*p) {							//トリガの名前のリスト内を検索する。
		if(--iSnrTrg == 0) { return p; }
		p = strchr(p, '\0') + 1/*'\0'*/;
	}
	return NULL;
}
//関数番号に対応する、関数名を取得する。
//見付からなければ、NULLを返す。
const char* SnrSeq_TryGetFuncName(const void* pSnrSeqBin, int iSnrFnc) {
	const char* p = SnrSeq_GetLabelTable(pSnrSeqBin);
	while(*p++) { p = strchr(p, '\0') + 1/*'\0'*/; }		//シナリオテーブルの名前のリストを読み飛ばす。	少しトリッキーですが"\0\0"で抜けます。
	while(*p++) { p = strchr(p, '\0') + 1/*'\0'*/; }		//トリガの名前のリストを読み飛ばす。		少しトリッキーですが"\0\0"で抜けます。
	while(*p) {							//関数の名前のリスト内を検索する。
		if(--iSnrFnc == 0) { return p; }
		p = strchr(p, '\0') + 1/*'\0'*/;
	}
	return NULL;
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
//シナリオテーブル名に対応する、シナリオテーブル番号を取得する。
//見付からなければ、-1を返す。
int SnrSeq_TryGetTableNo(const void* pSnrSeqBin, const char* pTableName) {
	const char* p = SnrSeq_GetLabelTable(pSnrSeqBin);
	int iSnrTblNo = 1;
	while(*p) {							//シナリオテーブルの名前のリスト内を検索する。
		if(!strcmp(p, pTableName)) { return iSnrTblNo; }
		p = strchr(p, '\0') + 1/*'\0'*/;
		iSnrTblNo++;
	}
	return -1;
}
//トリガ名に対応する、トリガ番号を取得する。
//見付からなければ、-1を返す。
int SnrSeq_TryGetTrigNo(const void* pSnrSeqBin, const char* pTrigName) {
	const char* p = SnrSeq_GetLabelTable(pSnrSeqBin);
	int iSnrTrg = 1;
	if(!strcmp(pTrigName, "set")) { return SnrTrg_set; }		//┬トリガ#0("set")とトリガ#255("clr")は格納されない事に注意して下さい。
	if(!strcmp(pTrigName, "clr")) { return SnrTrg_clr; }		//┘
	while(*p++) { p = strchr(p, '\0') + 1/*'\0'*/; }		//シナリオテーブルの名前のリストを読み飛ばす。	少しトリッキーですが"\0\0"で抜けます。
	while(*p) {							//トリガの名前のリスト内を検索する。
		if(!strcmp(p, pTrigName)) { return iSnrTrg; }
		p = strchr(p, '\0') + 1/*'\0'*/;
		iSnrTrg++;
	}
	return -1;
}
//関数名に対応する、関数番号を取得する。
//見付からなければ、-1を返す。
int SnrSeq_TryGetFuncNo(const void* pSnrSeqBin, const char* pFuncName) {
	const char* p = SnrSeq_GetLabelTable(pSnrSeqBin);
	int iSnrFnc = 1;
	while(*p++) { p = strchr(p, '\0') + 1/*'\0'*/; }		//シナリオテーブルの名前のリストを読み飛ばす。	少しトリッキーですが"\0\0"で抜けます。
	while(*p++) { p = strchr(p, '\0') + 1/*'\0'*/; }		//トリガの名前のリストを読み飛ばす。		少しトリッキーですが"\0\0"で抜けます。
	while(*p) {							//関数の名前のリスト内を検索する。
		if(!strcmp(p, pFuncName)) { return iSnrFnc; }
		p = strchr(p, '\0') + 1/*'\0'*/;
		iSnrFnc++;
	}
	return -1;
}
//}}2017/11/04追加:ラベルテーブルを検索するユーティリティ関数を追加しました。
//-----------------------------------------------------------------------------
#endif//USE_SNRSEQ_BIN
