//
//	clipdirs.cs
//
//	演出シーケンサ
//
//	CLiP - Common Library for P/ECE
//	Copyright (C) 2017 Naoyuki Sawa
//
//	* Fri Mar 03 22:34:24 JST 2017 Naoyuki Sawa
//	- 1st リリース。
//	* Thu May 11 22:16:11 JST 2017 Naoyuki Sawa
//	- _DirSeq_GetActBuf()にて、演出番号のbit23が誤って符号拡張されていたバグを修正しました。
//	  このバグは、C#特有の明示的な(int)キャストを追加した際にエンバグしたものであり、元のC言語版では問題有りません。
//	* Sun May 14 23:23:22 JST 2017 Naoyuki Sawa
//	- fork命令を追加しました。
//	  2017/05/13にclipdirs.cに対して行った変更を、C#版にも適用したものです。
//	* Tue May 23 23:35:30 JST 2017 Naoyuki Sawa
//	- hide命令を追加しました。
//	  2017/05/23にclipdirs.cに対して行った変更を、C#版にも適用したものです。
//	* Wed May 24 21:26:30 JST 2017 Naoyuki Sawa
//	- fork命令で開始したコンテキストは、元コンテキストの隠れコンテキストフラグを継承するようにしました。
//	  2017/05/24にclipdirs.cに対して行った変更を、C#版にも適用したものです。
//	* Tue Jun 20 21:14:16 JST 2017 Naoyuki Sawa
//	- join命令を追加しました。
//	  2017/06/20にclipdirs.cに対して行った変更を、C#版にも適用したものです。
//
using System;
using System.Collections.Generic;
namespace org.piece_me {
	public static partial class libclip {
		//*****************************************************************************
		//	定数
		//*****************************************************************************
		//□命令コード定義用マクロ
		//--- 共通命令 ---
		public const int DirSeq_Inst_end			= 0x00;
		//--- 演出ｼｰｹﾝｽ命令 ---
		public const int DirSeq_Inst_join			= 0x10;
		public const int DirSeq_Inst_hide			= 0x11;
		public const int DirSeq_Inst_repeat			= 0x12;
		public const int DirSeq_Inst_wait			= 0x13;
		public const int DirSeq_Inst_call			= 0x14;
		public const int DirSeq_Inst_fork			= 0x15;
		public const int DirSeq_Inst_act			= 0x16;
		public const int DirSeq_Inst_act_flush			= 0x17;
		//--- 演出ﾃｰﾌﾞﾙ命令 ---
		public const int DirSeq_Inst_on_goto_lock		= 0x20;
		public const int DirSeq_Inst_on_unlock_goto_lock	= 0x21;
		//-----------------------------------------------------------------------------
		public const int DirSeq_ActBufSize			= 256;					//演出番号バッファサイズ	1～65535で調整可	調整可ではあるがアプリケーション毎に調整する事は想定していない。変更する場合はライブラリをリビルドせよ。
		//-----------------------------------------------------------------------------
		public const int DirTrg_set				=   0;					//'set'トリガ番号		┐0='set'と255='clr'は、_DirSeqCh_SetTbl()の動作仕様のため固定です。
		public const int DirTrg_clr				= 255;					//'clr'トリガ番号		┘1～254はアプリケーション定義で、dDirSeqC.exeによって定義されます。
		//*****************************************************************************
		//	構造体
		//*****************************************************************************
		//演出シーケンサコンテキスト
		public class ST_DirSeqCtx {
			public BytePtr				pDirSeq;					//演出シーケンスの次に実行する命令位置		null=空き
			public ushort				WaitCnt;					//残り待ち時間																(pDirSeq==null)ならば、(WaitCnt==0)とします。		処理上必須ではありませんが、メモリ破壊やバグ検出のためです。詳細は、_DirSeq_AllocDirSeqCtx(),及び,_DirSeq_RemoveDirSeqCtx()のコメントを参照して下さい。
			public byte				iCtxNext;					//コンテキストチェイン				0～254=このチャネルのコンテキストチェインの次のコンテキストのインデクス,255=終端	(pDirSeq==null)ならば、(iCtxNext==255)とします。	処理上必須ではありませんが、メモリ破壊やバグ検出のためです。詳細は、_DirSeq_AllocDirSeqCtx(),及び,_DirSeq_RemoveDirSeqCtx()のコメントを参照して下さい。
			public bool				bHide;						//隠れコンテキストフラグ			0=通常のコンテキスト,1=隠れコンテキスト							(pDirSeq==null)ならば、(bHide==0)とします。		処理上必須ではありませんが、メモリ破壊やバグ検出のためです。詳細は、_DirSeq_AllocDirSeqCtx(),及び,_DirSeq_RemoveDirSeqCtx()のコメントを参照して下さい。
			//--- スタック ---
			public struct ST_RepRet {
				public byte			Rep;						//0=Callスタック,1～254=Repeatスタックの残り繰返し回数,255=Repeatスタック(無限)
				public BytePtr			Ret;						//戻り先の命令位置				call,又は,repeatの次の命令を指す
			}
			public Stack<ST_RepRet>			TBL_Stack	= new Stack<ST_RepRet>();	//スタック
		}
		//-----------------------------------------------------------------------------
		//演出シーケンサチャネル
		public class ST_DirSeqCh {
			public BytePtr				pDirTbl;					//現在設定されている演出テーブル		null=無し
			public byte				iCtxHead;					//コンテキストチェイン				0～254=このチャネルのコンテキストチェインの最初のコンテキストのインデクス,255=終端
			//--- ロック ---
			public ushort				LockCnt;					//残りロック時間
			public Queue<BytePtr>			TBL_LockBuf	= new Queue<BytePtr>();		//ロック終了を待っている演出シーケンス		'on'命令の3バイト目を指す
		}
		//-----------------------------------------------------------------------------
		//演出シーケンサ
		public class ST_DirSeq {
			public Action<ST_DirSeq/*pDirSeq*/,int/*iCh*/,int/*ActNo*/,bool/*bOverFlow*/>	fnAct;	//演出実行関数へのポインタ
			public uint[/*DirSeq_ActBufSize*/]	pActBuf;					//演出番号バッファへのポインタ			_DirSeq_SetActBuf()にてスタック上に確保したバッファのポインタをバッファが有効な期間中のみST_DirSeq.pActBufに設定する。充分なサイズ,かつ,スタックが溢れないようにDirSeq_ActBufSizeを調整せよ。
			public ushort				ActBufCnt;					//演出番号バッファの格納数
			public ushort				ActBufPos;					//演出番号バッファの読出し位置
			public int				nCh { get { return TBL_DirSeqCh.Length; } }	//演出シーケンサチャネル数
			public ST_DirSeqCh[/*nCh*/]		TBL_DirSeqCh;					//演出シーケンサチャネル配列
			public int				nCtx { get { return TBL_DirSeqCtx.Length; } }	//演出シーケンサコンテキスト数
			public ST_DirSeqCtx[/*nCtx*/]		TBL_DirSeqCtx;					//演出シーケンサコンテキスト配列
		}
		//*****************************************************************************
		//	
		//*****************************************************************************
		public static ST_DirSeq DirSeq_New(Action<ST_DirSeq/*pDirSeq*/,int/*iCh*/,int/*ActNo*/,bool/*bOverFlow*/> fnAct, int nCh, int nCtx) {
			ST_DirSeq pDirSeq;
			if((uint)nCh > (uint)nCtx) { throw new ApplicationException(); }	//チャネル数がコンテキスト数よりも多ければエラー停止します。全てのチャネルを同時実行するには少なくともチャネル数以上のコンテキスト数が必要であり、チャネル数がコンテキスト数よりも多いのは初期化パラメータエラーと思われるからです。ただし厳密には、管理のためだけにチャネル数を多めに用意しておき実際には全てのチャネルを同時実行しないような使い方(TAPのような使い方)をする場合はコンテキストバッファのメモリを節約するためにチャネル数がコンテキスト数よりも多くても構わないのですが、一般的にはチャネル数がコンテキスト数よりも多いのは初期化パラメータエラーと見なす方が安全だと思うのでエラー停止する事にしました。もし本当に左記のような使い方をするケースが生じた場合は、この行の検査を削除して下さい。
			//構造体のメモリを確保する。
			pDirSeq = new ST_DirSeq();
			//構造体を初期化する。
			DirSeq_Init(pDirSeq, fnAct, nCh, nCtx);
			return pDirSeq;
		}
		//-----------------------------------------------------------------------------
		public static void DirSeq_Init(ST_DirSeq pDirSeq, Action<ST_DirSeq/*pDirSeq*/,int/*iCh*/,int/*ActNo*/,bool/*bOverFlow*/> fnAct, int nCh, int nCtx) {
			int iCh, iCtx;
			if((uint)nCh > (uint)nCtx) { throw new ApplicationException(); }	//チャネル数がコンテキスト数よりも多ければエラー停止します。全てのチャネルを同時実行するには少なくともチャネル数以上のコンテキスト数が必要であり、チャネル数がコンテキスト数よりも多いのは初期化パラメータエラーと思われるからです。ただし厳密には、管理のためだけにチャネル数を多めに用意しておき実際には全てのチャネルを同時実行しないような使い方(TAPのような使い方)をする場合はコンテキストバッファのメモリを節約するためにチャネル数がコンテキスト数よりも多くても構わないのですが、一般的にはチャネル数がコンテキスト数よりも多いのは初期化パラメータエラーと見なす方が安全だと思うのでエラー停止する事にしました。もし本当に左記のような使い方をするケースが生じた場合は、この行の検査を削除して下さい。
			//構造体をクリアする。
		//不要	pDirSeq.fnAct     = null;	//演出実行関数へのポインタ
			pDirSeq.pActBuf   = null;	//演出番号バッファへのポインタ
			pDirSeq.ActBufCnt = 0;		//演出番号バッファの格納数
			pDirSeq.ActBufPos = 0;		//演出番号バッファの読出し位置
		//不要	TBL_DirSeqCh      = null;	//演出シーケンサチャネル配列
		//不要	TBL_DirSeqCtx     = null;	//演出シーケンサコンテキスト配列
			//演出実行関数を格納する。
			pDirSeq.fnAct = fnAct;
			//演出シーケンサチャネル配列を確保する。
			pDirSeq.TBL_DirSeqCh = new ST_DirSeqCh[nCh];
			//演出シーケンサコンテキスト配列を確保する。
			pDirSeq.TBL_DirSeqCtx = new ST_DirSeqCtx[nCtx];
			//演出シーケンサチャネルを初期化する。
			for(iCh = 0; iCh < nCh; iCh++) {
				ST_DirSeqCh pDirSeqCh = new ST_DirSeqCh();
				pDirSeqCh.iCtxHead = 255/*終端*/;
				pDirSeq.TBL_DirSeqCh[iCh] = pDirSeqCh;
			}
			//演出シーケンサコンテキストを初期化する。
			for(iCtx = 0; iCtx < nCtx; iCtx++) {
				ST_DirSeqCtx pDirSeqCtx = new ST_DirSeqCtx();
				pDirSeqCtx.iCtxNext = 255/*終端*/;
				pDirSeq.TBL_DirSeqCtx[iCtx] = pDirSeqCtx;
			}
		}
		//*****************************************************************************
		//	
		//*****************************************************************************
		private static ST_DirSeqCh _DirSeq_GetDirSeqCh(ST_DirSeq pDirSeq, int iCh) {
			//演出シーケンサチャネルを取得する。
			if((uint)iCh >= (uint)pDirSeq.nCh) { throw new ApplicationException(); }	//バグ
			return pDirSeq.TBL_DirSeqCh[iCh];
		}
		//*****************************************************************************
		//	
		//*****************************************************************************
		private static ST_DirSeqCtx _DirSeq_GetDirSeqCtx(ST_DirSeq pDirSeq, int iCtx) {
			//演出シーケンサコンテキストを取得する。
			if((uint)iCtx >= (uint)pDirSeq.nCtx) { throw new ApplicationException(); }	//バグ
			return pDirSeq.TBL_DirSeqCtx[iCtx];
		}
		//*****************************************************************************
		//	
		//*****************************************************************************
		private static void _DirSeq_AllocDirSeqCtx(ST_DirSeq pDirSeq, int iCh) {
			int iCtx;
			//空きコンテキストを検索する。
			for(iCtx = 0; iCtx < pDirSeq.nCtx; iCtx++) {
				ST_DirSeqCtx pDirSeqCtx = _DirSeq_GetDirSeqCtx(pDirSeq, iCtx);
				//空きコンテキストが見つかったら…
				if(!pDirSeqCtx.pDirSeq) {
					ST_DirSeqCh pDirSeqCh = _DirSeq_GetDirSeqCh(pDirSeq, iCh);
					//このチャネルのコンテキストチェインの先頭に、確保したコンテキストを追加する。
					if((pDirSeqCtx.WaitCnt  != 0) ||						//_DirSeqCtx_Exec1()が_DirSeq_FreeDirSeqCtx()を呼び出す時に、既に(WaitCnt==0)になっているはずです。もしここでエラー停止した場合、コンテキストバッファが不正に破壊された可能性が有ります。
					   (pDirSeqCtx.TBL_Stack.Count != 0) ||						//_DirSeqCtx_Exec1()が_DirSeq_FreeDirSeqCtx()を呼び出す時に、既に(TBL_Stack.Count==0)になっているはずです。もしここでエラー停止した場合、コンテキストバッファが不正に破壊された可能性が有ります。
					    pDirSeqCtx.bHide ||								//_DirSeq_FreeDirSeqCtx()がコンテキストを解放する処理の中で、(bHide=0)を格納しているはずです。もしここでエラー停止した場合、コンテキストバッファが不正に破壊された可能性が有ります。
					   (pDirSeqCtx.iCtxNext != 255/*終端*/)) { throw new ApplicationException(); }	//_DirSeq_FreeDirSeqCtx()がコンテキストを解放する処理の中で、(iCtxNext=255)を格納しているはずです。もしここでエラー停止した場合、コンテキストバッファが不正に破壊された可能性が有ります。
					   pDirSeqCtx.iCtxNext = pDirSeqCh.iCtxHead;
					                         pDirSeqCh.iCtxHead = (byte)iCtx;
					return;	//ここまで
				}
			}
			throw new ApplicationException();	//空きコンテキストが見つかりませんでした。コンテキストバッファサイズを増やして下さい。
		}
		//*****************************************************************************
		//	
		//*****************************************************************************
		private static void _DirSeq_FreeDirSeqCtx(ST_DirSeq pDirSeq, int iCh, int iCtx) {
			ST_DirSeqCh pDirSeqCh = _DirSeq_GetDirSeqCh(pDirSeq, iCh);
			ST_DirSeqCtx pDirSeqCtx = _DirSeq_GetDirSeqCtx(pDirSeq, iCtx);
			//このチャネルのコンテキストチェインから、このコンテキストを取り外す。
			if((pDirSeqCtx.WaitCnt  != 0) ||				//_DirSeqCtx_Exec1()が_DirSeq_FreeDirSeqCtx()を呼び出す時に、既に(WaitCnt==0)になっているはずです。もしここでエラー停止した場合、コンテキストバッファが不正に破壊された可能性が有ります。
			   (pDirSeqCtx.TBL_Stack.Count != 0) ||				//_DirSeqCtx_Exec1()が_DirSeq_FreeDirSeqCtx()を呼び出す時に、既に(TBL_Stack.Count==0)になっているはずです。もしここでエラー停止した場合、コンテキストバッファが不正に破壊された可能性が有ります。
			   !pDirSeqCtx.pDirSeq) { throw new ApplicationException(); }	//_DirSeqCtx_Exec1()が_DirSeq_FreeDirSeqCtx()を呼び出す時は、まだ(pDirSeq!=null)のままであるはずです。もしここでエラー停止した場合、_DirSeqCtx_Exec1()の処理にバグが有る可能性があります。
			//取り外すコンテキストが、コンテキストチェインの先頭ならば…
			if(pDirSeqCh.iCtxHead == iCtx) {
			   pDirSeqCh.iCtxHead = pDirSeqCtx.iCtxNext;
			//取り外すコンテキストが、コンテキストチェインの先頭でなければ…
			} else {
				int iCtxPrev = pDirSeqCh.iCtxHead;
				for(;;) {
					ST_DirSeqCtx pCtxPrev = _DirSeq_GetDirSeqCtx(pDirSeq, iCtxPrev);	//(iCtxPrev==255/*終端*/)になったら、_DirSeq_GetDirSeqCtx()の中でエラー停止します。指定されたコンテキストが、このチャネルのコンテキストチェインの中に見つからないバグです。
					if(pCtxPrev.iCtxNext == iCtx) {
					   pCtxPrev.iCtxNext = pDirSeqCtx.iCtxNext;
					   break;
					}
					iCtxPrev = pCtxPrev.iCtxNext;
				}
			}
			//取り外したコンテキストを、空きコンテキストとする。
			pDirSeqCtx.pDirSeq = null/*空き*/;
			pDirSeqCtx.iCtxNext = 255/*終端*/;	//(pDirSeq==null)ならば、(iCtxNext==255)とします。	処理上必須ではありませんが、メモリ破壊やバグ検出のためです。
			pDirSeqCtx.bHide = false;		//(pDirSeq==null)ならば、(bHide==0)とします。		処理上必須ではありませんが、メモリ破壊やバグ検出のためです。
		}
		//*****************************************************************************
		//	
		//*****************************************************************************
		private static void _DirSeq_SetActBuf(ST_DirSeq pDirSeq, Action<ST_DirSeq/*pDirSeq*/,object[]/*args*/> fn, params object[] args) {
			uint[] ActBuf = new uint[DirSeq_ActBufSize];	//_DirSeq_SetActBuf()にてスタック上に確保したバッファのポインタをバッファが有効な期間中のみST_DirSeq.pActBufに設定する。充分なサイズ,かつ,スタックが溢れないようにDirSeq_ActBufSizeを調整せよ。
			if(pDirSeq.pActBuf != null) { throw new ApplicationException(); }	//バグ
			//演出番号バッファを設定する。
			pDirSeq.pActBuf = ActBuf;
			//引数で指定された関数を実行する。
			fn.Invoke(pDirSeq, args);	//この場合は「(*fn)(pDirSeq, arg1, arg2)」でも同じ結果になるはずですが、第二引数が(intptr_t)型である事を強調するために、関数型を明示的にキャストする事にしました。
			//演出番号を全て送出する。
			while(pDirSeq.ActBufCnt != 0) {
				_DirSeq_GetActBuf(pDirSeq, false/*bOverFlow*/);	//通常の送出であることを示す。
			}
			//演出番号バッファをクリアする。
			pDirSeq.pActBuf = null;
		}
		//*****************************************************************************
		//	
		//*****************************************************************************
		private static void _DirSeq_AddActBuf(ST_DirSeq pDirSeq, int iCh, int ActNo) {
			int i;
			if(pDirSeq.pActBuf == null) { throw new ApplicationException(); }	//バグ
			//演出番号バッファに空きが無ければ…
			if(pDirSeq.ActBufCnt >= DirSeq_ActBufSize) {
				//演出番号を一個送出して空きを確保する。
				_DirSeq_GetActBuf(pDirSeq, true/*bOverFlow*/);	//オーバーフロー時の送出であることを示す。
				if(pDirSeq.ActBufCnt >= DirSeq_ActBufSize) { throw new ApplicationException(); }	//バグ
			}
			//演出番号を演出番号バッファに追加する。
			i = pDirSeq.ActBufCnt + pDirSeq.ActBufPos;
			if(i >= DirSeq_ActBufSize) { i -= DirSeq_ActBufSize; }
			pDirSeq.pActBuf[i] = (uint)(ActNo | (iCh << 24));	//[31:24]=演出シーケンサチャネル番号,[23:0]=演出番号
			pDirSeq.ActBufCnt++;
		}
		//*****************************************************************************
		//	
		//*****************************************************************************
		private static void _DirSeq_GetActBuf(ST_DirSeq pDirSeq, bool bOverFlow) {
			int iCh, ActNo;
			if((pDirSeq.pActBuf == null) ||
			   (pDirSeq.ActBufCnt == 0)) { throw new ApplicationException(); }	//バグ
			//演出番号を一個送出する。
			ActNo = (int)pDirSeq.pActBuf[pDirSeq.ActBufPos];
			iCh   =      (byte)(ActNo >> 24);	//[31:24]=演出シーケンサチャネル番号
			ActNo = (int)((uint)(ActNo << 8) >> 8);	//[23: 0]=演出番号
			pDirSeq.ActBufCnt--;
			if(++pDirSeq.ActBufPos >= DirSeq_ActBufSize) { pDirSeq.ActBufPos = 0; }
			pDirSeq.fnAct.Invoke(pDirSeq, iCh, ActNo, bOverFlow);
		}
		//*****************************************************************************
		//	
		//*****************************************************************************
		private static void _DirSeqCh_Start(ST_DirSeq pDirSeq, int iCh, BytePtr p) {
			ST_DirSeqCh pDirSeqCh = _DirSeq_GetDirSeqCh(pDirSeq, iCh);
			ST_DirSeqCtx pDirSeqCtx;
			int c1, c2;
			//実行中の演出シーケンスが有れば停止する。
			_DirSeqCh_Stop(pDirSeq, iCh);
			//相対位置とロック時間を取得する。
			c1  =        p.Read();
			c1 |=        p.Read() <<  8;
			c1 |= (sbyte)p.Read() << 16;	//符号拡張、忘れずに!!
			c2  =        p.Read();
			c2 |=        p.Read() <<  8;
			//コンテキストを確保する。
			_DirSeq_AllocDirSeqCtx(pDirSeq, iCh);
			pDirSeqCtx = _DirSeq_GetDirSeqCtx(pDirSeq, pDirSeqCh.iCtxHead);
			//命令位置とロック時間を設定する。
			pDirSeqCtx.pDirSeq =     p + c1;
			pDirSeqCh.LockCnt  = (ushort)c2;
			//演出シーケンスの開始と同時に、演出シーケンスを1フレーム実行する。
			_DirSeqCh_Exec1(pDirSeq, iCh, false/*bStop*/);
		}
		//*****************************************************************************
		//	
		//*****************************************************************************
		public static void DirSeq_Exec(ST_DirSeq pDirSeq) {
			if(pDirSeq.pActBuf != null) {
				_DirSeq_Exec(pDirSeq);
			} else {
				_DirSeq_SetActBuf(pDirSeq, _DirSeq_Exec);
			}
		}
		//-----------------------------------------------------------------------------
		private static void _DirSeq_Exec(ST_DirSeq pDirSeq, params object[] args) {
			if(args.Length != 0) { throw new ApplicationException(); }	//バグ
			int iCh;
			for(iCh = 0; iCh < pDirSeq.nCh; iCh++) {
				//演出シーケンス処理を行う。
				_DirSeqCh_Exec1(pDirSeq, iCh, false/*bStop*/);
				//ロックバッファ処理を行う。
				_DirSeqCh_Exec2(pDirSeq, iCh, false/*bUnlock*/);
			}
		}
		//-----------------------------------------------------------------------------
		private static void _DirSeqCh_Exec1(ST_DirSeq pDirSeq, int iCh, bool bStop) {
			ST_DirSeqCh pDirSeqCh = _DirSeq_GetDirSeqCh(pDirSeq, iCh);
			int iCtx = pDirSeqCh.iCtxHead;
			//このチャネルのコンテキストチェインの、各コンテキストについて…
			while(iCtx != 255/*終端*/) {
				ST_DirSeqCtx pDirSeqCtx = _DirSeq_GetDirSeqCtx(pDirSeq, iCtx);
				//コンテキストを実行する。
				int iCtxNext = pDirSeqCtx.iCtxNext;	//────┐
				_DirSeqCtx_Exec1(pDirSeq, iCh, iCtx, bStop);	//├_DirSeqCtx_Exec1()の中で(iCtxNext==255)になる可能性が有るので、_DirSeqCtx_Exec1()を呼び出す前にiCtxNextを取り出しておく。
				iCtx = iCtxNext;	//←───────────┘
			}
			if(bStop && (pDirSeqCh.iCtxHead != 255/*終端*/)) { throw new ApplicationException(); }	//停止時の処理ならば、コンテキストチェインが空になったはずです。もしここでエラー停止したら、当モジュールのバグです。
		}
		//-----------------------------------------------------------------------------
		private static void _DirSeqCtx_Exec1(ST_DirSeq pDirSeq, int iCh, int iCtx, bool bStop) {
			ST_DirSeqCh pDirSeqCh = _DirSeq_GetDirSeqCh(pDirSeq, iCh);
			ST_DirSeqCtx pDirSeqCtx = _DirSeq_GetDirSeqCtx(pDirSeq, iCtx);
			int c1, c2;
			ST_DirSeqCtx.ST_RepRet RepRet;
			if(!pDirSeqCtx.pDirSeq) { throw new ApplicationException(); }	//既に(pDirSeq==null)であるコンテキストに対して、当関数が呼び出される事は無いはずです。もしここでエラー停止したら、当モジュールのバグです。
			for(;;) {
				//待ち時間が残っていたら…
				if(pDirSeqCtx.WaitCnt != 0) {
					//停止時の処理でなければ…
					if(!bStop) {
						//次回のために待ち時間を減らす。
						pDirSeqCtx.WaitCnt--;
						return;	//ここまで
					//停止時の処理ならば…
					} else {
						//待ち時間をクリアする。
						pDirSeqCtx.WaitCnt = 0;
					}
				}
				//1バイト目を取得する。
				c1 = pDirSeqCtx.pDirSeq.Read();
				switch(c1) {
				default:throw new ApplicationException();
				case DirSeq_Inst_end:	//end
					//スタックが有れば…
					if(pDirSeqCtx.TBL_Stack.Count != 0) {
						//スタックを減らす。
						RepRet = pDirSeqCtx.TBL_Stack.Pop();
						//Repeatスタックならば…
						if(RepRet.Rep != 0) {
							//停止時の処理でなければ…
							if(!bStop) {
								//繰り返し回数が無限(255)か,又は,繰り返し回数を1減らしてまだ残っていたら…
								if((  RepRet.Rep == 255/*無限*/) ||
								   (--RepRet.Rep != 0)) {
									//命令位置を設定する。
									pDirSeqCtx.pDirSeq = RepRet.Ret;
									//スタックを増やす。
									pDirSeqCtx.TBL_Stack.Push(RepRet);
								}
							}
						//Callスタックならば…
						} else {
							//命令位置を設定する。
							pDirSeqCtx.pDirSeq = RepRet.Ret;
						}
					//スタックが無ければ…
					} else {
						//コンテキストを解放する。
						_DirSeq_FreeDirSeqCtx(pDirSeq, iCh, iCtx);	//この中で、(pDirSeq==null)になります。
						return;	//ここまで
					}
					break;
				case DirSeq_Inst_join:		//join
					//停止時の処理でなければ…
					if(!bStop) {
						//自分以外の全てのコンテキストが終了するまで待つ。
						if((pDirSeqCh.iCtxHead != iCtx) ||		//自分が、このチャネルのコンテキストチェインの、最初のコンテキストでないか、又は、
						   (pDirSeqCtx.iCtxNext != 255/*終端*/)) {	//自分が、このチャネルのコンテキストチェインの、最後のコンテキストでなければ、自分以外のコンテキストが残っていると見なせる。
							pDirSeqCtx.pDirSeq--;			//命令位置を1バイト戻して、次回もこの'join'を処理するようにする。
							return;	//ここまで
						}
					}
					break;
				case DirSeq_Inst_hide:		//hide
					//隠れコンテキストフラグをセットする。
					pDirSeqCtx.bHide = true;
					break;
				case DirSeq_Inst_repeat:	//repeat
					//繰り返し回数を取得する。
					c2 = pDirSeqCtx.pDirSeq.Read();
					if(c2 == 0) { throw new ApplicationException(); }	//データバグ
					//繰り返し回数と命令位置を格納する。
					RepRet.Rep = (byte)c2;
					RepRet.Ret = pDirSeqCtx.pDirSeq;
					//スタックを増やす。
					pDirSeqCtx.TBL_Stack.Push(RepRet);
					break;
				case DirSeq_Inst_wait:		//wait
					//待ち時間を取得する。
					c2  = pDirSeqCtx.pDirSeq.Read();
					c2 |= pDirSeqCtx.pDirSeq.Read() << 8;
					if(c2 == 0) { throw new ApplicationException(); }	//データバグ
					//待ち時間を格納する。
					pDirSeqCtx.WaitCnt = (ushort)c2;
					break;
				case DirSeq_Inst_call:		//call
					//相対位置を取得する。
					c2  =        pDirSeqCtx.pDirSeq.Read();
					c2 |=        pDirSeqCtx.pDirSeq.Read() <<  8;
					c2 |= (sbyte)pDirSeqCtx.pDirSeq.Read() << 16;	//符号拡張、忘れずに!!
					//繰り返し回数(0=Callスタック)と命令位置を格納する。
					RepRet.Rep = 0;
					RepRet.Ret = pDirSeqCtx.pDirSeq;
					//スタックを増やす。
					pDirSeqCtx.TBL_Stack.Push(RepRet);
					//命令位置を設定する。
					pDirSeqCtx.pDirSeq += c2;
					break;
				case DirSeq_Inst_fork:		//fork
					//相対位置を取得する。
					c2  =        pDirSeqCtx.pDirSeq.Read();
					c2 |=        pDirSeqCtx.pDirSeq.Read() <<  8;
					c2 |= (sbyte)pDirSeqCtx.pDirSeq.Read() << 16;	//符号拡張、忘れずに!!
					{
						ST_DirSeqCtx pCtxFork;
						//コンテキストを確保する。
						_DirSeq_AllocDirSeqCtx(pDirSeq, iCh);
						pCtxFork = _DirSeq_GetDirSeqCtx(pDirSeq, pDirSeqCh.iCtxHead);
						//命令位置を設定する。
						pCtxFork.pDirSeq = pDirSeqCtx.pDirSeq;
						//元コンテキストの隠れコンテキストフラグを継承する。
						pCtxFork.bHide = pDirSeqCtx.bHide;
						//フォークと同時に、演出シーケンスを1フレーム,又は,最後まで実行する。
						_DirSeqCtx_Exec1(pDirSeq, iCh, pDirSeqCh.iCtxHead, bStop);
					}
					//命令位置を設定する。
					pDirSeqCtx.pDirSeq += c2;
					break;
				case DirSeq_Inst_act:		//act
				case DirSeq_Inst_act_flush:	//act flush
					//演出番号を取得する。
					c2 =  pDirSeqCtx.pDirSeq.Read();
					c2 |= pDirSeqCtx.pDirSeq.Read() <<  8;
					c2 |= pDirSeqCtx.pDirSeq.Read() << 16;
					//停止時の処理でないか,又は,停止時にも実行する命令(act flush)ならば…
					if(!bStop || (c1 == DirSeq_Inst_act_flush/*act flush*/)) {
						//演出番号を演出番号バッファに追加する。
						_DirSeq_AddActBuf(pDirSeq, iCh, c2);
					}
					break;
				}
			}
		}
		//-----------------------------------------------------------------------------
		private static void _DirSeqCh_Exec2(ST_DirSeq pDirSeq, int iCh, bool bUnlock) {
			ST_DirSeqCh pDirSeqCh = _DirSeq_GetDirSeqCh(pDirSeq, iCh);
			BytePtr p;
			//ロックバッファが空になるまで…
			while(pDirSeqCh.TBL_LockBuf.Count != 0) {
				//ロック時間が残っていたら…
				if(pDirSeqCh.LockCnt != 0) {
					//ロック解除時の処理でなければ…
					if(!bUnlock) { 
						//次回のためにロック時間を減らす。
						pDirSeqCh.LockCnt--;
						return;	//ここまで
					//ロック解除時の処理ならば…
					} else { 
						//ロック時間をクリアする。
						pDirSeqCh.LockCnt = 0;
					}
				}
				//ロックバッファの先頭要素を削除する。
				p = pDirSeqCh.TBL_LockBuf.Dequeue();
				//演出シーケンスを開始する。
				_DirSeqCh_Start(pDirSeq, iCh, p);
			}
			//ロック解除時の処理でなければ…
			if(!bUnlock) { 
				//次回のためにロック時間を減らす。
				if(pDirSeqCh.LockCnt != 0) { pDirSeqCh.LockCnt--; }
			//ロック解除時の処理ならば…
			} else { 
				//ロック時間をクリアする。
				pDirSeqCh.LockCnt = 0;
			}
		}
		//*****************************************************************************
		//	
		//*****************************************************************************
		public static void DirSeq_ClrTbl(ST_DirSeq pDirSeq) {
			if(pDirSeq.pActBuf != null) {
				_DirSeq_ClrTbl(pDirSeq);
			} else {
				_DirSeq_SetActBuf(pDirSeq, _DirSeq_ClrTbl);
			}
		}
		//-----------------------------------------------------------------------------
		private static void _DirSeq_ClrTbl(ST_DirSeq pDirSeq, params object[] args) {
			if(args.Length != 0) { throw new ApplicationException(); }	//バグ
			int iCh;
			for(iCh = 0; iCh < pDirSeq.nCh; iCh++) {
				DirSeqCh_SetTbl(pDirSeq, iCh, null);
			}
		}
		//*****************************************************************************
		//	
		//*****************************************************************************
		public static void DirSeqCh_SetTbl(ST_DirSeq pDirSeq, int iCh, BytePtr pDirTbl/*null=クリア*/) {
			if(pDirSeq.pActBuf != null) {
				_DirSeqCh_SetTbl(pDirSeq, iCh, pDirTbl);
			} else {
				_DirSeq_SetActBuf(pDirSeq, _DirSeqCh_SetTbl, iCh, pDirTbl);
			}
		}
		//-----------------------------------------------------------------------------
		private static void _DirSeqCh_SetTbl(ST_DirSeq pDirSeq, params object[] args) {
			if(args.Length != 2) { throw new ApplicationException(); }	//バグ
			int iCh         =     (int)args[0];
			BytePtr pDirTbl = (BytePtr)args[1];	//null=クリア
			ST_DirSeqCh pDirSeqCh = _DirSeq_GetDirSeqCh(pDirSeq, iCh);
			//'clr'トリガを送信する。
			DirSeqCh_SendTrig(pDirSeq, iCh, DirTrg_clr);
			//演出テーブルを設定する。
			pDirSeqCh.pDirTbl = pDirTbl;
			//'set'トリガを送信する。
			DirSeqCh_SendTrig(pDirSeq, iCh, DirTrg_set);
		}
		//*****************************************************************************
		//	
		//*****************************************************************************
		public static void DirSeq_SendTrig(ST_DirSeq pDirSeq, int iTrig) {
			if(pDirSeq.pActBuf != null) {
				_DirSeq_SendTrig(pDirSeq, iTrig);
			} else {
				_DirSeq_SetActBuf(pDirSeq, _DirSeq_SendTrig, iTrig);
			}
		}
		//-----------------------------------------------------------------------------
		private static void _DirSeq_SendTrig(ST_DirSeq pDirSeq, params object[] args) {
			if(args.Length != 1) { throw new ApplicationException(); }	//バグ
			int iTrig = (int)args[0];
			int iCh;
			for(iCh = 0; iCh < pDirSeq.nCh; iCh++) {
				DirSeqCh_SendTrig(pDirSeq, iCh, iTrig);
			}
		}
		//*****************************************************************************
		//	
		//*****************************************************************************
		public static void DirSeqCh_SendTrig(ST_DirSeq pDirSeq, int iCh, int iTrig) {
			if(pDirSeq.pActBuf != null) {
				_DirSeqCh_SendTrig(pDirSeq, iCh, iTrig);
			} else {
				_DirSeq_SetActBuf(pDirSeq, _DirSeqCh_SendTrig, iCh, iTrig);
			}
		}
		//-----------------------------------------------------------------------------
		private static void _DirSeqCh_SendTrig(ST_DirSeq pDirSeq, params object[] args) {
			if(args.Length != 2) { throw new ApplicationException(); }	//バグ
			int iCh   = (int)args[0];
			int iTrig = (int)args[1];
			ST_DirSeqCh pDirSeqCh = _DirSeq_GetDirSeqCh(pDirSeq, iCh);
			BytePtr p;
			int c1, c2;
			//演出テーブルを走査して…
			p = pDirSeqCh.pDirTbl;
			while(p) {
				//1バイト目を取得する。
				c1 = p.Read();
				switch(c1) {
				default:throw new ApplicationException();
				case DirSeq_Inst_end:		//end
					p = null;
					break;
				case DirSeq_Inst_on_goto_lock:		//on
				case DirSeq_Inst_on_unlock_goto_lock:	//on unlock
					//トリガ番号を取得する。
					c2 = p.Read();
					//トリガ番号が一致したら…
					if(c2 == iTrig) {
						//ロック解除する命令(on unlock)ならば…
						if(c1 == DirSeq_Inst_on_unlock_goto_lock/*on unlock*/) {
							//ロック解除する。
							DirSeqCh_Unlock(pDirSeq, iCh);
						}
						//ロック時間が残っていたら…
						if(pDirSeqCh.LockCnt != 0) {
							//ロックバッファに演出シーケンスを追加する。
							pDirSeqCh.TBL_LockBuf.Enqueue(p);
						//ロック時間が残っていなければ…
						} else {
							//演出シーケンスを開始する。
							_DirSeqCh_Start(pDirSeq, iCh, p);
						}
					}
					//相対位置とロック時間を読み飛ばす。
					p += (3/*相対位置*/ + 2/*ロック時間*/);
					break;
				}
			}
		}
		//*****************************************************************************
		//	
		//*****************************************************************************
		public static void DirSeq_Unlock(ST_DirSeq pDirSeq) {
			if(pDirSeq.pActBuf != null) {
				_DirSeq_Unlock(pDirSeq);
			} else {
				_DirSeq_SetActBuf(pDirSeq, _DirSeq_Unlock);
			}
		}
		//-----------------------------------------------------------------------------
		private static void _DirSeq_Unlock(ST_DirSeq pDirSeq, params object[] args) {
			if(args.Length != 0) { throw new ApplicationException(); }	//バグ
			int iCh;
			for(iCh = 0; iCh < pDirSeq.nCh; iCh++) {
				DirSeqCh_Unlock(pDirSeq, iCh);
			}
		}
		//*****************************************************************************
		//	
		//*****************************************************************************
		public static void DirSeqCh_Unlock(ST_DirSeq pDirSeq, int iCh) {
			if(pDirSeq.pActBuf != null) {
				_DirSeqCh_Unlock(pDirSeq, iCh);
			} else {
				_DirSeq_SetActBuf(pDirSeq, _DirSeqCh_Unlock, iCh);
			}
		}
		//-----------------------------------------------------------------------------
		private static void _DirSeqCh_Unlock(ST_DirSeq pDirSeq, params object[] args) {
			if(args.Length != 1) { throw new ApplicationException(); }	//バグ
			int iCh = (int)args[0];
			_DirSeqCh_Exec2(pDirSeq, iCh, true/*bUnlock*/);
		}
		//*****************************************************************************
		//	
		//*****************************************************************************
		public static void DirSeq_Stop(ST_DirSeq pDirSeq) {
			if(pDirSeq.pActBuf != null) {
				_DirSeq_Stop(pDirSeq);
			} else {
				_DirSeq_SetActBuf(pDirSeq, _DirSeq_Stop);
			}
		}
		//-----------------------------------------------------------------------------
		private static void _DirSeq_Stop(ST_DirSeq pDirSeq, params object[] args) {
			if(args.Length != 0) { throw new ApplicationException(); }	//バグ
			int iCh;
			for(iCh = 0; iCh < pDirSeq.nCh; iCh++) {
				DirSeqCh_Stop(pDirSeq, iCh);
			}
		}
		//*****************************************************************************
		//	
		//*****************************************************************************
		public static void DirSeqCh_Stop(ST_DirSeq pDirSeq, int iCh) {
			if(pDirSeq.pActBuf != null) {
				_DirSeqCh_Stop(pDirSeq, iCh);
			} else {
				_DirSeq_SetActBuf(pDirSeq, _DirSeqCh_Stop, iCh);
			}
		}
		//-----------------------------------------------------------------------------
		private static void _DirSeqCh_Stop(ST_DirSeq pDirSeq, params object[] args) {
			if(args.Length != 1) { throw new ApplicationException(); }	//バグ
			int iCh = (int)args[0];
			_DirSeqCh_Exec1(pDirSeq, iCh, true/*bStop*/);
		}
		//*****************************************************************************
		//	
		//*****************************************************************************
		//演出シーケンスが動作中であるチャネル数を返します。
		public static int DirSeq_GetStat(ST_DirSeq pDirSeq) {
			int iCh, nActive = 0;
			for(iCh = 0; iCh < pDirSeq.nCh; iCh++) {
				if(DirSeqCh_GetStat(pDirSeq, iCh)) { nActive++; }
			}
			return nActive;
		}
		//-----------------------------------------------------------------------------
		//このチャネルが動作中ならばtrue、停止中ならばfalseを返します。
		public static bool DirSeqCh_GetStat(ST_DirSeq pDirSeq, int iCh) {
			ST_DirSeqCh pDirSeqCh = _DirSeq_GetDirSeqCh(pDirSeq, iCh);
			int iCtx = pDirSeqCh.iCtxHead;
			//このチャネルのコンテキストチェインの、各コンテキストについて…
			while(iCtx != 255/*終端*/) {
				ST_DirSeqCtx pDirSeqCtx = _DirSeq_GetDirSeqCtx(pDirSeq, iCtx);
				//通常のコンテキストが一つでも有れば、このチャネルは動作中であると見なす。
				if(!pDirSeqCtx.bHide) { return true; }
				iCtx = pDirSeqCtx.iCtxNext;
			}
			//コンテキストチェインが空か,又は,全て隠れコンテキストならば、このチャネルは停止中であると見なす。
			return false;
		}
		//*****************************************************************************
		//	dDirSeqC.exeが出力したバイナリ形式を扱うためのユーティリティ関数
		//*****************************************************************************
		public static void DirSeqCh_SetTblNo(ST_DirSeq pDirSeq, int iCh, VoidPtr pDirSeqBin, int iDirTblNo) {
			Int32Ptr TBL_DirTblNo = pDirSeqBin;				//バイナリの先頭にアドレステーブルが有る。アドレスはデータ本体(BYTE配列)のインデクスである。
			int nDirTblNo = TBL_DirTblNo[0];				//アドレステーブルの先頭[0]に要素数が入っている。要素数はそれ自身([0])も含む。
			BytePtr TBL_DirSeq = (BytePtr)(TBL_DirTblNo + nDirTblNo);	//アドレステーブルの直後からデータ本体(BYTE配列)が開始する。
			if((iDirTblNo <= 0) || (iDirTblNo >= nDirTblNo)) { throw new ApplicationException(); }
			DirSeqCh_SetTbl(pDirSeq, iCh, TBL_DirSeq + TBL_DirTblNo[iDirTblNo]);
		}
	}
}
