/*
 *	dSnrSeqD - シナリオシーケンサ逆コンパイラ
 *	Copyright (C) 2017-2018 Naoyuki Sawa
 *
 *	* Sat Oct 14 22:22:29 JST 2017 Naoyuki Sawa
 *	- 1st リリース。
 *	* Sun Oct 29 23:57:57 JST 2017 Naoyuki Sawa
 *	- 'wait for'命令と'wait for not'命令を追加しました。
 *	* Sat Nov 04 23:56:59 JST 2017 Naoyuki Sawa
 *	- '-l'オプションを追加しました。
 *	  詳細は、/clip/tool/dSnrSeqC/winapp.h の同日のコメントを参照して下さい。
 *	* Sat Nov 11 23:59:59 JST 2017 Naoyuki Sawa
 *	- 'escape'命令を追加しました。
 *	   詳細は、/clip/clipsnrs.c の同日のコメントを参照して下さい。
 *	* Tue Nov 21 23:59:59 JST 2017 Naoyuki Sawa
 *	- 'on unlock','on exec','on unlock exec'命令を追加しました。
 *	   詳細は、/clip/clipsnrs.c の同日のコメントを参照して下さい。
 *	* Thu May 03 23:59:59 JST 2018 Naoyuki Sawa
 *	- Boehm GCを修正した事に伴い、当ツールもリビルドしました。
 *	  詳細は、/clip/tool/gc/README.TXTの'Wed May 02 23:59:59 JST 2018 Naoyuki Sawa'のコメントを参照して下さい。
 */
#include "app.h"
#define VERSION		"20180503"	//最終更新日
/****************************************************************************
 *	main
 ****************************************************************************/
int main(int argc, char* argv[]) {
	int exitCode = EXIT_SUCCESS;
#ifdef  _DEBUG
	remove("syslog.out");			//毎回ログを削除する事にした。
    #ifndef USE_JOB_DEBUG
	openlog(NULL, LOG_PERROR, LOG_USER);	//デバッグビルドでは、システムログを標準エラーにも出力する。ただし、デバッグジョブを使用する場合は、デバッグジョブのcurses表示と衝突しないよう、標準エラーには出力しない。
    #endif//USE_JOB_DEBUG
#else //_DEBUG
	setlogmask(LOG_UPTO(LOG_INFO));		//リリースビルドでは、LOG_DEBUGを抑制する。
#endif//_DEBUG
#if     (defined(GC_H) && !defined(USE_BISON_FLEX))
//	putenv("GC_LOG_FILE=CON");//ログ出力先を指定する。
//	GC_set_find_leak(1);//メモリリーク検出を開始する。
#endif//(defined(GC_H) && !defined(USE_BISON_FLEX))
#ifdef  __CLIP_SEH_H__
	SEH_init();
#endif//__CLIP_SEH_H__
	app_init(argc, argv);
	exitCode = app_main(argc, argv);
	app_exit();
#if     (defined(GC_H) && !defined(USE_BISON_FLEX))
//	CHECK_LEAKS();//メモリリークを検出する。
#endif//(defined(GC_H) && !defined(USE_BISON_FLEX))
	return exitCode;
}
/****************************************************************************
 *	usage
 ****************************************************************************/
__declspec(noreturn) static void usage() {
	fprintf(stderr, "dSnrSeqD.exe - シナリオシーケンサ逆コンパイラ (%s)\n", VERSION);
	fprintf(stderr, "Copyright (C) 2017-2018 Naoyuki Sawa\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "USAGE:\n");
	fprintf(stderr, "  dSnrSeqD.exe [option ...] SnrSeq.bin\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "OPTIONS:\n");
//{{2017/11/04追加:'-l'オプションを追加しました。
	fprintf(stderr, "  -l  ラベルテーブルから取得したラベル名を使用します。\n");
	fprintf(stderr, "      このオプションを指定しなければ、自動生成したラベル名を使用します。\n");
	fprintf(stderr, "      ラベルテーブルが格納されていない場合は、このオプションは無効です。\n");
//}}2017/11/04追加:'-l'オプションを追加しました。
	fprintf(stderr, "  -s  省略記法を使用します。\n");
	fprintf(stderr, "      このオプションを指定しなければ、省略記法を使用しません。\n");
	fprintf(stderr, "  -t  シナリオテーブル毎に出力します。\n");
	fprintf(stderr, "      このオプションを指定しなければ、一括で出力します。\n");
	fprintf(stderr, "  -h  使い方を表示します。\n");
	exit(EXIT_FAILURE);
}
/****************************************************************************
 *	
 ****************************************************************************/
void app_init(int argc, char* argv[]) {
	/** no job **/
}
/*--------------------------------------------------------------------------*/
void app_exit() {
	/** no job **/
}
/****************************************************************************
 *	
 ****************************************************************************/
static void printColumns(int indent, ...);
static const char* GetTrigName(int iSnrTrg);
static const char* GetFuncName(int iSnrFnc);
static const char* GetSnrTblName(int iSnrTbl);
static const char* GetSnrSeqName(unsigned char* pSnrSeq);
static const char* GetNakedArgs(unsigned char* pArgs);
static const char* GetParenthesizedArgs(unsigned char* pArgs);
static void DumpSnrTbl(int indent, unsigned char* pSnrTbl);
static void DumpSnrSeq(int indent, unsigned char* pSnrSeq);
/*--------------------------------------------------------------------------*/
//{{2017/11/04追加:'-l'オプションを追加しました。
static int opt_l = 0;	//0=自動生成したラベル名を使用します。1=ラベルテーブルから取得したラベル名を使用します。
//}}2017/11/04追加:'-l'オプションを追加しました。
static int opt_s = 0;	//0=省略記法を使用しません。1=省略記法を使用します。
static int opt_t = 0;	//0=シナリオテーブル毎に出力する。1=一括で出力する。
//{{2017/11/04追加:'-l'オプションを追加しました。
GPtrArray *array_pTableName, *array_pTrigName, *array_pFuncName;
//}}2017/11/04追加:'-l'オプションを追加しました。
GPtrArray* array_pSnrSeq;
/*--------------------------------------------------------------------------*/
int app_main(int argc, char* argv[]) {
	int* TBL_SnrTblNo;
	//コマンドラインオプションを処理する。
	{
		int opt;
		opterr = 0; //オプションエラーは当プログラム内で処理するので、getopt()内でのエラー表示は不要。
		while((opt = getopt(argc, argv, "lsth")) != -1) {
			switch(opt) {
//{{2017/11/04追加:'-l'オプションを追加しました。
			case 'l':
				opt_l = 1;	//ラベルテーブルから取得したラベル名を使用します。
				break;
//}}2017/11/04追加:'-l'オプションを追加しました。
			case 's':
				opt_s = 1;	//省略記法を使用します。
				break;
			case 't':
				opt_t = 1;	//シナリオテーブル毎に出力する。
				break;
			case 'h':
			default:
				usage();
			}
		}
		if(optind != (argc - 1)) { usage(); }	//引数の数が不正
	}
	//シナリオのバイナリデータを読み込む。
	{
		FILE* fp;
		fp = fopen(argv[optind], "rb");
		if(!fp) { error(EXIT_FAILURE, errno, "%s が開けません。", argv[optind]); }
		{
			struct stat st;
			if(fstat(fileno(fp), &st)) { DIE(); }
			if(!(TBL_SnrTblNo = malloc(st.st_size))) { DIE(); }
			if((int)fread(TBL_SnrTblNo, 1, st.st_size, fp) != (int)st.st_size) { DIE(); }	//fread()の戻り値はunsigned型，st.st_sizeは(なぜか)signed型なので、そのまま比較すると警告が出る。警告を抑制するには片方をキャストするだけでも良いが、判り易さのために両方をキャストして比較する事にした。
			fread(TBL_SnrTblNo, 1, st.st_size, fp);
		}
		fclose(fp);
	}
//{{2017/11/04追加:'-l'オプションを追加しました。
	//ラベルテーブルを読み込む。
	if(opt_l) {
		const char* p = SnrSeq_GetLabelTable(TBL_SnrTblNo);
		//シナリオテーブル名
		{
			array_pTableName = g_ptr_array_new();
			g_ptr_array_add(array_pTableName, NULL);	//0番はダミー
			while(*p) {
				g_ptr_array_add(array_pTableName, iscsymf(*p) ? (char*)p : strconcat("`", p, NULL));	//一文字目がCシンボル文字の先頭文字でなければ「`」を付与する。シナリオシーケンサスクリプトの書式です。
				p = strchr(p, '\0') + 1/*'\0'*/;
			}
			p++;	//'\0'
		}
		//トリガ名
		{
			array_pTrigName = g_ptr_array_new();
			g_ptr_array_add(array_pTrigName, NULL);		//0番はダミー
			while(*p) {
				g_ptr_array_add(array_pTrigName, iscsymf(*p) ? (char*)p : strconcat("`", p, NULL));	//一文字目がCシンボル文字の先頭文字でなければ「`」を付与する。シナリオシーケンサスクリプトの書式です。
				p = strchr(p, '\0') + 1/*'\0'*/;
			}
			p++;	//'\0'
		}
		//関数名
		{
			array_pFuncName = g_ptr_array_new();
			g_ptr_array_add(array_pFuncName, NULL);		//0番はダミー
			while(*p) {
				g_ptr_array_add(array_pFuncName, iscsymf(*p) ? (char*)p : strconcat("`", p, NULL));	//一文字目がCシンボル文字の先頭文字でなければ「`」を付与する。シナリオシーケンサスクリプトの書式です。
				p = strchr(p, '\0') + 1/*'\0'*/;
			}
			p++;	//'\0'
		}
	}
//}}2017/11/04追加:'-l'オプションを追加しました。
	if(opt_t) {
		//シナリオテーブルをダンプする。
		{
			int iSnrTblNo, nSnrTblNo = TBL_SnrTblNo[0];					//アドレステーブルの先頭[0]に要素数が入っている。要素数はそれ自身([0])も含む。
			unsigned char* const TBL_SnrTbl = (unsigned char*)&TBL_SnrTblNo[nSnrTblNo];	//アドレステーブルの直後からデータ本体(BYTE配列)が開始する。
			for(iSnrTblNo = 1; iSnrTblNo < nSnrTblNo; iSnrTblNo++) {
				array_pSnrSeq = g_ptr_array_new();
				puts("#//-----------------------------------------------------------------------------");
				if(opt_s) {
					printColumns(0,
						GetSnrTblName(iSnrTblNo),
						NULL);
				} else {
					printColumns(0,
						"def",
						GetSnrTblName(iSnrTblNo),
						NULL);
				}
				DumpSnrTbl(1, &TBL_SnrTbl[TBL_SnrTblNo[iSnrTblNo]]);
				printColumns(0,
					"end",
					NULL);
				//シナリオシーケンスをダンプする。
				{
					int iSnrSeq;
					for(iSnrSeq = 0; iSnrSeq < array_pSnrSeq->len; iSnrSeq++) {
						unsigned char* pSnrSeq = array_pSnrSeq->pdata[iSnrSeq];
						puts("#//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
						if(opt_s) {
							printColumns(0,
								GetSnrSeqName(pSnrSeq),
								NULL);
						} else {
							printColumns(0,
								"def",
								GetSnrSeqName(pSnrSeq),
								NULL);
						}
						DumpSnrSeq(1, pSnrSeq);	//この中でarray_pSnrSeq->lenが増える可能性も有ります。
						printColumns(0,
							"end",
							NULL);
					}
				}
			}
		}
	} else {
		//シナリオテーブルをダンプする。
		puts("#//-----------------------------------------------------------------------------");
		{
			int iSnrTblNo, nSnrTblNo = TBL_SnrTblNo[0];					//アドレステーブルの先頭[0]に要素数が入っている。要素数はそれ自身([0])も含む。
			unsigned char* const TBL_SnrTbl = (unsigned char*)&TBL_SnrTblNo[nSnrTblNo];	//アドレステーブルの直後からデータ本体(BYTE配列)が開始する。
			array_pSnrSeq = g_ptr_array_new();
			for(iSnrTblNo = 1; iSnrTblNo < nSnrTblNo; iSnrTblNo++) {
				if(iSnrTblNo != 1) {
					puts("#//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
				}
				if(opt_s) {
					printColumns(0,
						GetSnrTblName(iSnrTblNo),
						NULL);
				} else {
					printColumns(0,
						"def",
						GetSnrTblName(iSnrTblNo),
						NULL);
				}
				DumpSnrTbl(1, &TBL_SnrTbl[TBL_SnrTblNo[iSnrTblNo]]);
				printColumns(0,
					"end",
					NULL);
			}
		}
		//シナリオシーケンスをダンプする。
		puts("#//-----------------------------------------------------------------------------");
		{
			int iSnrSeq;
			for(iSnrSeq = 0; iSnrSeq < array_pSnrSeq->len; iSnrSeq++) {
				unsigned char* pSnrSeq = array_pSnrSeq->pdata[iSnrSeq];
				if(iSnrSeq != 0) {
					puts("#//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -");
				}
				if(opt_s) {
					printColumns(0,
						GetSnrSeqName(pSnrSeq),
						NULL);
				} else {
					printColumns(0,
						"def",
						GetSnrSeqName(pSnrSeq),
						NULL);
				}
				DumpSnrSeq(1, pSnrSeq);	//この中でarray_pSnrSeq->lenが増える可能性も有ります。
				printColumns(0,
					"end",
					NULL);
			}
		}
	}
	return EXIT_SUCCESS;
}
/*--------------------------------------------------------------------------*/
static void printColumns(int indent, ...) {
	char **v = strv_new(NULL), *s;
	va_list ap;
	va_start(ap, indent);
	while(--indent >= 0) {
		strv_extend(&v, "");
	}
	while((s = va_arg(ap, char*)) != NULL) {
		strv_extend(&v, s);
	}
	s = strv_join(v, "\t");
	s = strchomp(s);
	puts(s);
}
/*--------------------------------------------------------------------------*/
static const char* GetTrigName(int iSnrTrg) {
	if(iSnrTrg == SnrTrg_set) { return "set"; }
	if(iSnrTrg == SnrTrg_clr) { return "clr"; }
//{{2017/11/04追加:'-l'オプションを追加しました。
	if(opt_l && ((iSnrTrg >= 1) && (iSnrTrg <= array_pTrigName->len - 1))) {
		return g_ptr_array_index(array_pTrigName, iSnrTrg);
	}
//}}2017/11/04追加:'-l'オプションを追加しました。
	return strdup_printf("Trg%d", iSnrTrg);
}
/*--------------------------------------------------------------------------*/
static const char* GetFuncName(int iSnrFnc) {
//{{2017/11/04追加:'-l'オプションを追加しました。
	if(opt_l && ((iSnrFnc >= 1) && (iSnrFnc <= array_pFuncName->len - 1))) {
		return g_ptr_array_index(array_pFuncName, iSnrFnc);
	}
//}}2017/11/04追加:'-l'オプションを追加しました。
	return strdup_printf("Fnc%d", iSnrFnc);
}
/*--------------------------------------------------------------------------*/
static const char* GetSnrTblName(int iSnrTblNo) {
//{{2017/11/04追加:'-l'オプションを追加しました。
	if(opt_l && ((iSnrTblNo >= 1) && (iSnrTblNo <= array_pTableName->len - 1))) {
		return g_ptr_array_index(array_pTableName, iSnrTblNo);
	}
//}}2017/11/04追加:'-l'オプションを追加しました。
	return strdup_printf("Tbl%d", iSnrTblNo);
}
/*--------------------------------------------------------------------------*/
static const char* GetSnrSeqName(unsigned char* pSnrSeq) {
	int iSnrSeq;
	for(iSnrSeq = 0; iSnrSeq < array_pSnrSeq->len; iSnrSeq++) {
		if(array_pSnrSeq->pdata[iSnrSeq] == pSnrSeq) { break; }
	}
	if(iSnrSeq == array_pSnrSeq->len) {
		g_ptr_array_add(array_pSnrSeq, pSnrSeq);
	}
	if(opt_t) {
		return strdup_printf(".Seq%d", iSnrSeq + 1);
	} else {
		return strdup_printf( "Seq%d", iSnrSeq + 1);
	}
}
/*--------------------------------------------------------------------------*/
static const char* GetNakedArgs(unsigned char* pArgs) {
	const char* s = "";
	int n = *pArgs++;
	if((intptr_t)pArgs & 3) { DIE(); }	//引数リストの値の部分は4N境界に整列されているはず(/clip/tool/dSprFntC/winapp.cのArgs_CodeGen()内のコメント参照)。もしここで停止したらデータバグです。
	if(n) {
		s = strdup_printf("0x%02X", *pArgs++);
		while(--n) {
			s = strdup_printf("%s,0x%02X", s, *pArgs++);
		}
	}
	return s;
}
/*--------------------------------------------------------------------------*/
static const char* GetParenthesizedArgs(unsigned char* pArgs) {
	const char* s = GetNakedArgs(pArgs);
	if(*s || !opt_s) {
		s = strdup_printf("(%s)", s);
	}
	return s;
}
/*--------------------------------------------------------------------------*/
static void DumpSnrTbl(int indent, unsigned char* pSnrTbl) {
	for(;;) {
		int c = *pSnrTbl++;	//命令
		switch(c) {
		default:DIE();
		case SnrTbl_end:
			return;	//ここまで
		case SnrTbl_on_unlock:
			{
				int t;
				t = *pSnrTbl++;	//トリガ番号
				if(opt_s) {
					printColumns(indent,
						GetTrigName(t),
						"unlock",
						NULL);
				} else {
					printColumns(indent,
						"on",
						GetTrigName(t),
						"unlock",
						NULL);
				}
			}
			break;
		case SnrTbl_on_goto:
		case SnrTbl_on_unlock_goto:
			{
				int t, s, l;
				t =            *pSnrTbl++;	//トリガ番号
				s =      (char)*pSnrTbl++;	//シナリオシーケンスへの相対距離
				s = (s << 8) | *pSnrTbl++;
				s = (s << 8) | *pSnrTbl++;
				l =            *pSnrTbl++;	//ロック時間(0=ロック無し)
				l = (l << 8) | *pSnrTbl++;
				if(opt_s) {
					printColumns(indent,
						GetTrigName(t),
						(c == SnrTbl_on_unlock_goto) ? "unlock" : "",
						"",
						GetSnrSeqName(pSnrTbl + s),
						l ? strdup_printf("%d", l) : "",
						NULL);
				} else {
					printColumns(indent,
						"on",
						GetTrigName(t),
						(c == SnrTbl_on_unlock_goto) ? "unlock" : "",
						"goto",
						GetSnrSeqName(pSnrTbl + s),
						l ? strdup_printf("lock\t%d", l) : "",
						NULL);
				}
			}
			break;
		case SnrTbl_on_exec:
		case SnrTbl_on_unlock_exec:
			{
				int t, s, l;
				t =            *pSnrTbl++;	//トリガ番号
				s =      (char)*pSnrTbl++;	//シナリオシーケンスへの相対距離
				s = (s << 8) | *pSnrTbl++;
				s = (s << 8) | *pSnrTbl++;
				l =            *pSnrTbl++;	//ロック時間(0=ロック無し)
				l = (l << 8) | *pSnrTbl++;
				if(opt_s) {
					printColumns(indent,
						GetTrigName(t),
						(c == SnrTbl_on_unlock_exec) ? "unlock" : "",
						"exec",
						GetSnrSeqName(pSnrTbl + s),
						l ? strdup_printf("%d", l) : "",
						NULL);
				} else {
					printColumns(indent,
						"on",
						GetTrigName(t),
						(c == SnrTbl_on_unlock_exec) ? "unlock" : "",
						"exec",
						GetSnrSeqName(pSnrTbl + s),
						l ? strdup_printf("lock\t%d", l) : "",
						NULL);
				}
			}
			break;
		}
	}
}
/*--------------------------------------------------------------------------*/
static void DumpSnrSeq(int indent, unsigned char* pSnrSeq) {
	for(;;) {
		int c = *pSnrSeq++;	//命令
		switch(c) {
		default:DIE();
		case SnrSeq_end:
			return;	//ここまで
		case SnrSeq_exit:
			{
				printColumns(indent,
					"exit",
					NULL);
			}
			break;
		case SnrSeq_escape:
			{
				printColumns(indent,
					"escape",
					NULL);
			}
			break;
		case SnrSeq_wait:
			{
				int w;
				w =            *pSnrSeq++;	//待ち時間(0=エラー)
				w = (w << 8) | *pSnrSeq++;
				if(!w) { DIE(); }
				if(opt_s) {
					printColumns(indent,
						"wait",
						(w == 1) ? "" : strdup_printf("%d", w),
						NULL);
				} else {
					printColumns(indent,
						"wait",
						strdup_printf("%d", w),
						NULL);
				}
			}
			break;
		case SnrSeq_wait_for:
		case SnrSeq_wait_for_not:
			{
				int f, a;
				f =            *pSnrSeq++;	//関数番号
				a =      (char)*pSnrSeq++;	//引数リストへの相対距離
				a = (a << 8) | *pSnrSeq++;
				a = (a << 8) | *pSnrSeq++;
				if(opt_s) {
					printColumns(indent,
						"wait",
						(c == SnrSeq_wait_for_not) ? strdup_printf("not %s%s", GetFuncName(f), GetParenthesizedArgs(pSnrSeq + a))
						                           : strdup_printf(    "%s%s", GetFuncName(f), GetParenthesizedArgs(pSnrSeq + a)),
						NULL);
				} else {
					printColumns(indent,
						"wait for",
						(c == SnrSeq_wait_for_not) ? strdup_printf("not %s%s", GetFuncName(f), GetParenthesizedArgs(pSnrSeq + a))
						                           : strdup_printf(    "%s%s", GetFuncName(f), GetParenthesizedArgs(pSnrSeq + a)),
						NULL);
				}
			}
			break;
		case SnrSeq_fork:
			{
				int s;
				s =      (char)*pSnrSeq++;	//シナリオシーケンスボディへの相対距離
				s = (s << 8) | *pSnrSeq++;
				s = (s << 8) | *pSnrSeq++;
				printColumns(indent,
					"fork",
					NULL);
				DumpSnrSeq(indent + 1, pSnrSeq + s);
				printColumns(indent,
					"end",
					NULL);
			}
			break;
		case SnrSeq_hide:
			{
				printColumns(indent,
					"hide",
					NULL);
			}
			break;
		case SnrSeq_join:
			{
				printColumns(indent,
					"join",
					NULL);
			}
			break;
		case SnrSeq_goto:
			{
				int s;
				s =      (char)*pSnrSeq++;	//シナリオシーケンスボディへの相対距離
				s = (s << 8) | *pSnrSeq++;
				s = (s << 8) | *pSnrSeq++;
				printColumns(indent,
					"goto",
					GetSnrSeqName(pSnrSeq + s),
					NULL);
			}
			break;
		case SnrSeq_call:
			{
				int s;
				s =      (char)*pSnrSeq++;	//シナリオシーケンスボディへの相対距離
				s = (s << 8) | *pSnrSeq++;
				s = (s << 8) | *pSnrSeq++;
				printColumns(indent,
					"call",
					GetSnrSeqName(pSnrSeq + s),
					NULL);
			}
			break;
		case SnrSeq_if:
		case SnrSeq_if_not:
			{
				int f, a, s;
				f =            *pSnrSeq++;	//関数番号
				a =      (char)*pSnrSeq++;	//引数リストへの相対距離
				a = (a << 8) | *pSnrSeq++;
				a = (a << 8) | *pSnrSeq++;
				s =      (char)*pSnrSeq++;	//シナリオシーケンスボディへの相対距離
				s = (s << 8) | *pSnrSeq++;
				s = (s << 8) | *pSnrSeq++;
				printColumns(indent,
					"if",
					(c == SnrSeq_if_not) ? strdup_printf("not %s%s", GetFuncName(f), GetParenthesizedArgs(pSnrSeq + a))
					                     : strdup_printf(    "%s%s", GetFuncName(f), GetParenthesizedArgs(pSnrSeq + a)),
					NULL);
				DumpSnrSeq(indent + 1, pSnrSeq + s);
				for(;;) {
					c = *pSnrSeq++;	//命令
					if((c == SnrSeq_elif) || (c == SnrSeq_elif_not)) {
						f =            *pSnrSeq++;	//関数番号
						a =      (char)*pSnrSeq++;	//引数リストへの相対距離
						a = (a << 8) | *pSnrSeq++;
						a = (a << 8) | *pSnrSeq++;
						s =      (char)*pSnrSeq++;	//シナリオシーケンスへの相対距離
						s = (s << 8) | *pSnrSeq++;
						s = (s << 8) | *pSnrSeq++;
						printColumns(indent,
							"elif",
							(c == SnrSeq_elif_not) ? strdup_printf("not %s%s", GetFuncName(f), GetParenthesizedArgs(pSnrSeq + a))
							                       : strdup_printf(    "%s%s", GetFuncName(f), GetParenthesizedArgs(pSnrSeq + a)),
							NULL);
						DumpSnrSeq(indent + 1, pSnrSeq + s);
					} else if(c == SnrSeq_else) {
						s =      (char)*pSnrSeq++;	//シナリオシーケンスボディへの相対距離
						s = (s << 8) | *pSnrSeq++;
						s = (s << 8) | *pSnrSeq++;
						printColumns(indent,
							"else",
							NULL);
						DumpSnrSeq(indent + 1, pSnrSeq + s);
					} else {
						pSnrSeq--;
						break;
					}
				}
				printColumns(indent,
					"end",
					NULL);
			}
			break;
		case SnrSeq_case:
			{
				int f, a, s, v;
				f =            *pSnrSeq++;	//関数番号
				a =      (char)*pSnrSeq++;	//引数リストへの相対距離
				a = (a << 8) | *pSnrSeq++;
				a = (a << 8) | *pSnrSeq++;
				if(opt_s) {
					printColumns(indent,
						"case",
						strdup_printf("%s%s", GetFuncName(f), GetParenthesizedArgs(pSnrSeq + a)),
						NULL);
				} else {
					printColumns(indent,
						"case",
						strdup_printf("%s%s", GetFuncName(f), GetParenthesizedArgs(pSnrSeq + a)),
						"of",
						NULL);
				}
				if(*pSnrSeq != SnrSeq_of) { DIE(); }
				for(;;) {
					c = *pSnrSeq++;	//命令
					if(c == SnrSeq_of) {
						v =            *pSnrSeq++;	//値
						s =      (char)*pSnrSeq++;	//シナリオシーケンスへの相対距離
						s = (s << 8) | *pSnrSeq++;
						s = (s << 8) | *pSnrSeq++;
						printColumns(indent,
							strdup_printf("%d", v),
							NULL);
						DumpSnrSeq(indent + 1, pSnrSeq + s);
					} else if(c == SnrSeq_else) {
						s =      (char)*pSnrSeq++;	//シナリオシーケンスボディへの相対距離
						s = (s << 8) | *pSnrSeq++;
						s = (s << 8) | *pSnrSeq++;
						printColumns(indent,
							"else",
							NULL);
						DumpSnrSeq(indent + 1, pSnrSeq + s);
					} else {
						pSnrSeq--;
						break;
					}
				}
				printColumns(indent,
					"end",
					NULL);
			}
			break;
		case SnrSeq_repeat:
			{
				int r, s;
				r =            *pSnrSeq++;	//繰り返し回数(0=エラー,65535=無限)
				r = (r << 8) | *pSnrSeq++;
				s =      (char)*pSnrSeq++;	//シナリオシーケンスへの相対距離
				s = (s << 8) | *pSnrSeq++;
				s = (s << 8) | *pSnrSeq++;
				if(!r) { DIE(); }
				printColumns(indent,
					"repeat",
					(r == 65535/*無限*/) ? "" : strdup_printf("%d", r),
					NULL);
				DumpSnrSeq(indent + 1, pSnrSeq + s);
				printColumns(indent,
					"end",
					NULL);
			}
			break;
		case SnrSeq_break:
			{
				int n;
				n = *pSnrSeq++;	//ネストしたrepeatを抜ける数(0=エラー)
				if(!n) { DIE(); }
				if(opt_s) {
					printColumns(indent,
						"break",
						(n == 1) ? "" : strdup_printf("%d", n),
						NULL);
				} else {
					printColumns(indent,
						"break",
						strdup_printf("%d", n),
						NULL);
				}
			}
			break;
		case SnrSeq_while:
		case SnrSeq_while_not:
			{
				int f, a, s;
				f =            *pSnrSeq++;	//関数番号
				a =      (char)*pSnrSeq++;	//引数リストへの相対距離
				a = (a << 8) | *pSnrSeq++;
				a = (a << 8) | *pSnrSeq++;
				s =      (char)*pSnrSeq++;	//シナリオシーケンスへの相対距離
				s = (s << 8) | *pSnrSeq++;
				s = (s << 8) | *pSnrSeq++;
				printColumns(indent,
					"while",
					(c == SnrSeq_while_not) ? strdup_printf("not %s%s", GetFuncName(f), GetParenthesizedArgs(pSnrSeq + a))
					                        : strdup_printf(    "%s%s", GetFuncName(f), GetParenthesizedArgs(pSnrSeq + a)),
					NULL);
				DumpSnrSeq(indent + 1, pSnrSeq + s);
				printColumns(indent,
					"end",
					NULL);
			}
			break;
		case SnrSeq_act:
		case SnrSeq_act_ensure:
			{
				int f, a;
				f =            *pSnrSeq++;	//関数番号
				a =      (char)*pSnrSeq++;	//引数リストへの相対距離
				a = (a << 8) | *pSnrSeq++;
				a = (a << 8) | *pSnrSeq++;
				if(opt_s) {
					printColumns(indent,
						GetFuncName(f),
						GetNakedArgs(pSnrSeq + a),
						(c == SnrSeq_act_ensure) ? "ensure" : "",
						NULL);
				} else {
					printColumns(indent,
						"act",
						strdup_printf("%s%s", GetFuncName(f), GetParenthesizedArgs(pSnrSeq + a)),
						(c == SnrSeq_act_ensure) ? "ensure" : "",
						NULL);
				}
			}
			break;
		}
	}
}
