//
//	clipdirs.cs
//
//	演出シーケンサ
//
//	CLiP - Common Library for P/ECE
//	Copyright (C) 2017 Naoyuki Sawa
//
//	* Fri Mar 03 22:34:24 JST 2017 Naoyuki Sawa
//	- 1st リリース。
//
using System;
using System.Collections.Generic;
namespace org.piece_me {
	public static partial class libclip {
		//*****************************************************************************
		//	定数
		//*****************************************************************************
		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_DirSeqCh {
			public BytePtr				pDirTbl;					//現在設定されている演出テーブル		null=無し
			public BytePtr				pDirSeq;					//演出シーケンスの次に実行する命令位置		null=停止中
			public ushort				WaitCnt;					//残り待ち時間
			//--- ロック ---
			public ushort				LockCnt;					//残りロック時間
			public Queue<BytePtr>			TBL_LockBuf	= new Queue<BytePtr>();		//ロック終了を待っている演出シーケンス		'on'命令の3バイト目を指す
			//--- スタック ---
			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_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; } }	//演出シーケンサチャネル数		一般的に演出シーケンサチャネル数は高々十数個程度だから、nChフィールドの型はuint8_tでも充分なのだが、そうしてもアライメントのためメモリ節約にならないので、とりあえずint型にしておく事にした。もし今後当構造体にフィールドを追加する必要が生じたら、nChフィールドの型をuint8_t,又は,uint16_tに変更して空きスペースを作れ。
			public ST_DirSeqCh[/*nCh*/]		TBL_DirSeqCh;					//演出シーケンサチャネル配列
		}
		//*****************************************************************************
		//	
		//*****************************************************************************
		public static ST_DirSeq DirSeq_New(Action<ST_DirSeq/*pDirSeq*/,int/*iCh*/,int/*ActNo*/,bool/*bOverFlow*/> fnAct, int nCh) {
			ST_DirSeq pDirSeq;
			//構造体のメモリを確保する。
			pDirSeq = new ST_DirSeq();
			//構造体を初期化する。
			DirSeq_Init(pDirSeq, fnAct, nCh);
			return pDirSeq;
		}
		//-----------------------------------------------------------------------------
		public static void DirSeq_Init(ST_DirSeq pDirSeq, Action<ST_DirSeq/*pDirSeq*/,int/*iCh*/,int/*ActNo*/,bool/*bOverFlow*/> fnAct, int nCh) {
			//構造体をクリアする。
		//不要	pDirSeq.fnAct     = null;	//演出実行関数へのポインタ
			pDirSeq.pActBuf   = null;	//演出番号バッファへのポインタ
			pDirSeq.ActBufCnt = 0;		//演出番号バッファの格納数
			pDirSeq.ActBufPos = 0;		//演出番号バッファの読出し位置
		//不要	TBL_DirSeqCh = null;		//演出シーケンサチャネル配列
			//演出実行関数を格納する。
			pDirSeq.fnAct = fnAct;
			//演出実行関数を格納する。
			pDirSeq.fnAct = fnAct;
			//演出シーケンサチャネル配列を確保する。
			pDirSeq.TBL_DirSeqCh = new ST_DirSeqCh[nCh];
			//演出シーケンサチャネルを作成する。
			for(int iCh = 0; iCh < nCh; iCh++) {
				pDirSeq.TBL_DirSeqCh[iCh] = new ST_DirSeqCh();
			}
		}
		//*****************************************************************************
		//	
		//*****************************************************************************
		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;
			int c1, c2;
			//演出シーケンサチャネルを取得する。
			if((uint)iCh >= (uint)pDirSeq.nCh) { throw new ApplicationException(); }	//バグ
			pDirSeqCh = pDirSeq.TBL_DirSeqCh[iCh];
			//実行中の演出シーケンスが有れば停止する。
			_DirSeqCh_Stop(pDirSeq, iCh);
			//相対位置とロック時間を取得する。
			c1  =        p.Read();
			c1 |=        p.Read() <<  8;
			c1 |= (sbyte)p.Read() << 16;	//符号拡張、忘れずに!!
			c2  =        p.Read();
			c2 |=        p.Read() <<  8;
			//命令位置とロック時間を設定する。
			pDirSeqCh.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;
			int c1, c2;
			ST_DirSeqCh.ST_RepRet RepRet;
			//演出シーケンサチャネルを取得する。
			if((uint)iCh >= (uint)pDirSeq.nCh) { throw new ApplicationException(); }	//バグ
			pDirSeqCh = pDirSeq.TBL_DirSeqCh[iCh];
			//停止中になるまで…
			while(pDirSeqCh.pDirSeq) {
				//停止時の処理でなければ…
				if(!bStop) {
					//待ち時間が残っていたら、抜ける。
					if(pDirSeqCh.WaitCnt != 0) { break; }
				//停止時の処理ならば…
				} else {
					//待ち時間をクリアする。
					pDirSeqCh.WaitCnt = 0;
				}
				//1バイト目を取得する。
				c1 = pDirSeqCh.pDirSeq.Read();
				switch(c1) {
				case 0x00://end
					//スタックが有れば…
					if(pDirSeqCh.TBL_Stack.Count != 0) {
						//スタックを減らす。
						RepRet = pDirSeqCh.TBL_Stack.Pop();
						//Repeatスタックならば…
						if(RepRet.Rep != 0) {
							//停止時の処理でなければ…
							if(!bStop) {
								//繰り返し回数が無限(255)か,又は,繰り返し回数を1減らしてまだ残っていたら…
								if((  RepRet.Rep == 255/*無限*/) ||
								   (--RepRet.Rep != 0)) {
									//命令位置を設定する。
									pDirSeqCh.pDirSeq = RepRet.Ret;
									//スタックを増やす。
									pDirSeqCh.TBL_Stack.Push(RepRet);
								}
							}
						//Callスタックならば…
						} else {
							//命令位置を設定する。
							pDirSeqCh.pDirSeq = RepRet.Ret;
						}
					//スタックが無ければ…
					} else {
						//停止中にする。
						pDirSeqCh.pDirSeq = null;
					}
					break;
				case 0x01://repeat
					//繰り返し回数を取得する。
					c2 = pDirSeqCh.pDirSeq.Read();
					if(c2 == 0) { throw new ApplicationException(); }	//データバグ
					//繰り返し回数と命令位置を格納する。
					RepRet.Rep = (byte)c2;
					RepRet.Ret = pDirSeqCh.pDirSeq;
					//スタックを増やす。
					pDirSeqCh.TBL_Stack.Push(RepRet);
					break;
				case 0x02://wait
					//待ち時間を取得する。
					c2  = pDirSeqCh.pDirSeq.Read();
					c2 |= pDirSeqCh.pDirSeq.Read() << 8;
					if(c2 == 0) { throw new ApplicationException(); }	//データバグ
					//待ち時間を格納する。
					pDirSeqCh.WaitCnt = (ushort)c2;
					break;
				case 0x03://call
					//相対位置を取得する。
					c2  =        pDirSeqCh.pDirSeq.Read();
					c2 |=        pDirSeqCh.pDirSeq.Read() <<  8;
					c2 |= (sbyte)pDirSeqCh.pDirSeq.Read() << 16;	//符号拡張、忘れずに!!
					//繰り返し回数(0=Callスタック)と命令位置を格納する。
					RepRet.Rep = 0;
					RepRet.Ret = pDirSeqCh.pDirSeq;
					//スタックを増やす。
					pDirSeqCh.TBL_Stack.Push(RepRet);
					//命令位置を設定する。
					pDirSeqCh.pDirSeq += c2;
					break;
				case 0x04://act
				case 0x05://act flush
					//演出番号を取得する。
					c2 =  pDirSeqCh.pDirSeq.Read();
					c2 |= pDirSeqCh.pDirSeq.Read() <<  8;
					c2 |= pDirSeqCh.pDirSeq.Read() << 16;
					//停止時の処理でないか,又は,停止時にも実行する命令(act flush)ならば…
					if(!bStop || (c1 == 0x05/*act flush*/)) {
						//演出番号を演出番号バッファに追加する。
						_DirSeq_AddActBuf(pDirSeq, iCh, c2);
					}
					break;
				default:throw new ApplicationException();
				}
			}
			//停止時の処理でなければ…
			if(!bStop) {
				//次回のために待ち時間を減らす。
				if(pDirSeqCh.WaitCnt != 0) { pDirSeqCh.WaitCnt--; }
			//停止時の処理ならば…
			} else {
				//待ち時間をクリアする。
				pDirSeqCh.WaitCnt = 0;
			}
		}
		//-----------------------------------------------------------------------------
		private static void _DirSeqCh_Exec2(ST_DirSeq pDirSeq, int iCh, bool bUnlock) {
			ST_DirSeqCh pDirSeqCh;
			BytePtr p;
			//演出シーケンサチャネルを取得する。
			if((uint)iCh >= (uint)pDirSeq.nCh) { throw new ApplicationException(); }	//バグ
			pDirSeqCh = pDirSeq.TBL_DirSeqCh[iCh];
			//ロックバッファが空になるまで…
			while(pDirSeqCh.TBL_LockBuf.Count != 0) {
				//ロック解除時の処理でなければ…
				if(!bUnlock) { 
					//ロック時間が残っていたら、抜ける。
					if(pDirSeqCh.LockCnt != 0) { break; }
				//ロック解除時の処理ならば…
				} 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;
			//演出シーケンサチャネルを取得する。
			if((uint)iCh >= (uint)pDirSeq.nCh) { throw new ApplicationException(); }	//バグ
			pDirSeqCh = pDirSeq.TBL_DirSeqCh[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;
			BytePtr p;
			int c1, c2;
			//演出シーケンサチャネルを取得する。
			if((uint)iCh >= (uint)pDirSeq.nCh) { throw new ApplicationException(); }	//バグ
			pDirSeqCh = pDirSeq.TBL_DirSeqCh[iCh];
			//演出テーブルを走査して…
			p = pDirSeqCh.pDirTbl;
			while(p) {
				//1バイト目を取得する。
				c1 = p.Read();
				switch(c1) {
				case 0x00://end
					p = null;
					break;
				case 0x06://on
				case 0x07://on unlock
					//トリガ番号を取得する。
					c2 = p.Read();
					//トリガ番号が一致したら…
					if(c2 == iTrig) {
						//ロック解除する命令(on unlock)ならば…
						if(c1 == 0x07/*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;
				default:throw new ApplicationException();
				}
			}
		}
		//*****************************************************************************
		//	
		//*****************************************************************************
		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;
			if((uint)iCh >= (uint)pDirSeq.nCh) { throw new ApplicationException(); }	//バグ
			pDirSeqCh = pDirSeq.TBL_DirSeqCh[iCh];
			return pDirSeqCh.pDirSeq;
		}
		//*****************************************************************************
		//	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]);
		}
	}
}
