/*****************************************************************************
 * Bison/Flexプログラム部分をテストするための、Windowsアプリケーションの雛型 *
 * □ビルド・実行方法:                                                       *
 * ┃"C:\Program Files (x86)\Microsoft Visual Studio\VC98\Bin\VCVARS32.BAT"  *
 * ┃make -f winapp.mk run                                                   *
 *****************************************************************************/
#include "winapp.h"
//-----------------------------------------------------------------------------
//警告定義
enum {
	WARN_CAST,			//暗黙の型変換で誤差が生じたら、警告を表示し最終的にエラー終了します。このオプションを指定しなければ、警告表示もエラー終了もしません。
};
static const char* const warn_tokens[]={
	"cast",				//暗黙の型変換で誤差が生じたら、警告を表示し最終的にエラー終了します。このオプションを指定しなければ、警告表示もエラー終了もしません。
NULL};//終端
int warn;				//各警告(WARN_*)について(warn&(1<<WARN_*))がセットされていたらその警告がOnである事を示します。
/*---------------------------------------------------------------------------*/
//ローカル変数定義
static ght_hash_table_t* ht_Symbol;		//シンボルラベル⇒(ST_Symbol*)
static ght_hash_table_t* ht_GameMode;		//ゲームモードラベル⇒(ST_GameMode*)
//ラベル名のソート方法
int (*fnLabCmp)(const void*, const void*) = strcmp;	//'n'オプションが指定されたらstrnatcmpに変更する。
//{{進捗表示用
static int opt_q;
static int newMsgLen, oldMsgLen = 0;
//}}進捗表示用
/*---------------------------------------------------------------------------*/
//エラーログ用
static char err_path[_MAX_PATH];	//'-e'オプションが指定されたら、エラーログファイル名を格納する。
static FILE* fp_err;			//最初のエラーログを記録する時に、エラーログファイルを作成する。
//{{2016/09/24追加:エラーメッセージに、現在の行番号だけでなく、現在のファイル名も表示するようにしました。
typedef struct _ST_Location {
	const char*	path;					//push_location()が呼び出された直前のファイル名
	int		line;					//push_location()が呼び出された直前の行番号
} ST_Location;
static ST_Location TBL_Location[16/*調整可*/];			//ロケーションスタック
static nLocation;						//ロケーションスタックの格納数
static const char* filename = "-";				//現在のファイル名(popen時はコマンド名)。現在の行番号はyylinenoを参照して下さい。
static void init_location(const char* path) {
	nLocation = 0;						//ロケーションスタックを空にする。
	filename = strdup(path);				//現在のファイル名をコピーして保持する。
	yylineno = 1;						//現在の行番号を初期化する。	yylex_init()でも初期化しているのでこの行は無くても構わないのだが、判り易さのためにここでも初期化する事にした。
}
void push_location(const char* path) {
	if(nLocation >= ARRAY_SIZE(TBL_Location)) { DIE(); }	//ロケーションスタックがフルでない事を確認する。
	TBL_Location[nLocation].path = filename;		//現在のファイル名をロケーションスタックに退避する。
	TBL_Location[nLocation].line = yylineno;		//現在の行番号をロケーションスタックに退避する。
	nLocation++;						//ロケーションスタックの格納数を増やす。
	filename = strdup(path);				//現在のファイル名をコピーして保持する。
	yylineno = 1;						//現在の行番号を初期化する。
}
void pop_location() {
	if(!nLocation) { DIE(); }				//ロケーションスタックが空でない事を確認する。
	nLocation--;						//ロケーションスタックの格納数を減らす。
	filename = TBL_Location[nLocation].path;		//現在のファイル名をロケーションスタックから復元する。
	yylineno = TBL_Location[nLocation].line;		//現在の行番号をロケーションスタックから復元する。
}
//}}2016/09/24追加:エラーメッセージに、現在の行番号だけでなく、現在のファイル名も表示するようにしました。
void yyerror(const char* fmt, ...) {	//←{{2015/03/06:'-e'オプション対応のためにwinapp.cから移動しました。}}
	//'-e'オプションが指定されていて、最初のエラーログを記録する時ならば…
	if(*err_path && !fp_err) {
		//エラーログファイルを作成する。
		fp_err = fopen(err_path, "w");
		if(!fp_err) { error(EXIT_FAILURE, errno, "%s が作成できません。", err_path); }	//{{2016/09/21変更:die(〜)で終了していた箇所を、error(EXIT_FAILURE,〜)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
		//エラーログファイルの出力ストリームは、バッファリング無しとする。
		// - die()で終了した時(abort()時)に、バッファ分がフラッシュされないようなので、こうしておく必要が有る。
		//   別の案として、この下の処理で毎回fflush()する方法も有るが、バッファリング無しとする方が簡単だろう。
		setbuf(fp_err, NULL);
	}
	//エラーメッセージ(,及び,エラーログ)を出力する。
//{{2016/09/24変更:エラーメッセージに、現在の行番号だけでなく、現在のファイル名も表示するようにしました。
//	             fprintf(stderr, "### error at line %d : ", yylineno);	//エラーメッセージ
//	if(fp_err) { fprintf(fp_err, "### error at line %d : ", yylineno); }	//エラーログ
//↓2016/09/24変更:エラーメッセージに、現在の行番号だけでなく、現在のファイル名も表示するようにしました。
	             fprintf(stderr, "%s(%d) : ", filename, yylineno);		//エラーメッセージ
	if(fp_err) { fprintf(fp_err, "%s(%d) : ", filename, yylineno); }	//エラーログ
//}}2016/09/24変更:エラーメッセージに、現在の行番号だけでなく、現在のファイル名も表示するようにしました。
	{
		va_list ap;
		va_start(ap, fmt);
		             vfprintf(stderr, fmt, ap);		//エラーメッセージ
		if(fp_err) { vfprintf(fp_err, fmt, ap); }	//エラーログ
		va_end(ap);
	}
	             fputc('\n', stderr);	//エラーメッセージ
	if(fp_err) { fputc('\n', fp_err); }	//エラーログ
}
/*---------------------------------------------------------------------------*/
//ローカル関数宣言
__declspec(noreturn) static void usage();
static int subr(int argc, char* argv[]);
static void pass2(int bOptimize);
static void output(const char* sEnmOut, const char* sRegOut);
static void output_enm(FILE* fp);
static void output_reg(FILE* fp);
/*---------------------------------------------------------------------------*/
int main(int argc, char* argv[]) {
	int exitCode;
	SEH_init();
	exitCode = subr(argc, argv);
	return exitCode;
}
/*---------------------------------------------------------------------------*/
static int subr(int argc, char* argv[]) {
	int opt, bOptimize = 1;		//'-d'オプションが指定されなければ最適化を行う。
	const char* sEnmOut = NULL;	//'-u'オプションが指定されたら列挙定義スクリプトのファイル名,又は,"-"(標準出力)を設定する。
	const char* sRegOut = NULL;	//'-r'オプションが指定されたらレジストリスクリプトのファイル名,又は,"-"(標準出力)を設定する。
#if     (YYDEBUG!=0)
//	yydebug = 1; //nonzero means print parse trace
#endif/*(YYDEBUG!=0)*/
	//コマンドラインオプションを処理する。
	opterr = 0; //オプションエラーは当プログラム内で処理するので、getopt()内でのエラー表示は不要。
	while((opt = getopt(argc, argv, "du::r::enw::qh")) != -1) {
		switch(opt) {
		case 'd':
			bOptimize = 0;				//最適化を行わない。
			break;
		case 'u':
			sEnmOut = optarg ? optarg : "-";	//列挙定義スクリプトのファイル名,又は,"-"(標準出力)を設定する。
			break;
		case 'r':
			sRegOut = optarg ? optarg : "-";	//レジストリスクリプトのファイル名,又は,"-"(標準出力)を設定する。
			break;
		case 'e':
			{
				//エラーログファイル名を作成する。
				// - カレントフォルダに'実行ファイル名.err'という名前で生成する事にした。この動作は、'lk33.exe'の仕様を参考にした。
				char fname[_MAX_FNAME];
				_splitpath(argv[0], NULL, NULL, fname, NULL);
				_makepath(err_path, NULL, NULL, fname, ".err");
				//'-e'オプションが指定されたら、最初に確実に、既存のエラーログファイルを削除しておく。
				//前回に生成したエラーログが残っていて、今回生成したエラーログと混同する危険を避けるためである。
				//尚、'-e'オプションが指定されなかった場合は、既存のエラーログファイルを削除する処理は行わない。
				remove(err_path);
			}
			break;
		case 'n':
			fnLabCmp = strnatcmp;	//ラベル名のソートにstrnatcmp()を使用する。
			break;
		case 'w':
			if(optarg) {
				char *optionp = optarg, *valuep;
				while(*optionp) {
					switch(getsubopt(&optionp, (char* const*)warn_tokens, &valuep)) {
					case WARN_CAST:
						if(valuep) { usage(); }
						warn |= (1<<WARN_CAST);
						break;
					default:
						usage();
					}
				}
			} else {
				warn = -1;
			}
			break;
		case 'q':
			opt_q = 1;
			break;
		case 'h':
		default:
			usage();
		}
	}
	//ハッシュテーブルを作成する。
	ght_set_rehash((ht_Symbol   = ght_create(0)), 1);
	ght_set_rehash((ht_GameMode = ght_create(0)), 1);
	//--- 1パス目の処理 ---
	//ファイルが指定されていなければ、標準入力から読み込む。
	if(optind == argc) {
		init_location("-");	//{{2016/09/24追加:エラーメッセージに、現在の行番号だけでなく、現在のファイル名も表示するようにしました。}}
		yylex_init(stdin);
		if((yyparse() != 0) /*|| (yynerrs != 0)*/) { error(EXIT_FAILURE, 0, "エラーが発生しました。"); }	//{{2016/09/21変更:die(〜)で終了していた箇所を、error(EXIT_FAILURE,〜)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
	//ファイルが指定されていたら、各ファイルを読み込む。
	} else {
		int i;
		for(i = optind; i < argc; i++) {
			FILE* fp = fopen(argv[i], "r");
			if(!fp) { error(EXIT_FAILURE, errno, "%s が開けません。", argv[i]); }	//{{2016/09/21変更:die(〜)で終了していた箇所を、error(EXIT_FAILURE,〜)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
			init_location(argv[i]);	//{{2016/09/24追加:エラーメッセージに、現在の行番号だけでなく、現在のファイル名も表示するようにしました。}}
			yylex_init(fp);
//{{2017/03/16変更:複数の入力ファイルを指定した時に、最後以外のファイルでエラーが発生した場合、当ツールの終了ステータスが0になっていたバグを修正しました。
//			if((yyparse() != 0) /*|| (yynerrs != 0)*/) { error(EXIT_FAILURE, 0, "%s: エラーが発生しました。", argv[i]); }	//{{2016/09/21変更:die(〜)で終了していた箇所を、error(EXIT_FAILURE,〜)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
//↓2017/03/16変更:複数の入力ファイルを指定した時に、最後以外のファイルでエラーが発生した場合、当ツールの終了ステータスが0になっていたバグを修正しました。
			{
				int save_yynerrs = yynerrs;	//yyparse()がyynerrsをクリアするので、その前に現在のyynerrsを保存しておく。
				if((yyparse() != 0) /*|| (yynerrs != 0)*/) { error(EXIT_FAILURE, 0, "%s: エラーが発生しました。", argv[i]); }	//{{2016/09/21変更:die(〜)で終了していた箇所を、error(EXIT_FAILURE,〜)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
				yynerrs += save_yynerrs;	//今回のyyparse()が0から数えたであろうyynerrsに対して、先ほど保存しておいたyynerrsを足し込む。
			}
//}}2017/03/16変更:複数の入力ファイルを指定した時に、最後以外のファイルでエラーが発生した場合、当ツールの終了ステータスが0になっていたバグを修正しました。
			fclose(fp);
		}
	}
    //{{進捗表示用
	if(!opt_q) {
		newMsgLen = fprintf(stderr, "\rpass1 ok");
		if(oldMsgLen > newMsgLen) { fprintf(stderr, "%*s", oldMsgLen - newMsgLen, ""); } putc('\n', stderr); oldMsgLen = 0;
	}
    //}}進捗表示用
	//1パス目の処理でエラーが発生しても、2パス目の処理を行って、全てのセマンティクスエラーを報告する。
	//--- 2パス目の処理 ---
	//2パス目の処理を行う。
	pass2(bOptimize);
    //{{進捗表示用
	if(!opt_q) {
		newMsgLen = fprintf(stderr, "\rpass2 ok");
		if(oldMsgLen > newMsgLen) { fprintf(stderr, "%*s", oldMsgLen - newMsgLen, ""); } putc('\n', stderr); oldMsgLen = 0;
	}
    //}}進捗表示用
	if(yynerrs != 0) { error(EXIT_FAILURE, 0, "エラーが発生しました。"); }	//1パス目,又は,2パス目の処理でエラーが発生していたら、出力処理を行わない。	//{{2016/09/21変更:die(〜)で終了していた箇所を、error(EXIT_FAILURE,〜)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
	//--- 出力処理 ---
	//出力処理を行う。
	output(sEnmOut, sRegOut);
	if(yynerrs != 0) { error(EXIT_FAILURE, 0, "エラーが発生しました。"); }	//{{2016/09/21変更:die(〜)で終了していた箇所を、error(EXIT_FAILURE,〜)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
	return EXIT_SUCCESS;
}
/*---------------------------------------------------------------------------*/
//使い方を表示して終了する。
__declspec(noreturn) static void usage() {
	fprintf(stderr, "dVslDefC - ビデオスロットゲーム定義コンパイラ (%s)\n", VERSION);
	fprintf(stderr, "Copyright (C) 2017-2018 Naoyuki Sawa\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "USAGE:\n");
	fprintf(stderr, "  dVslDefC [option ...] [filename ...] \n");
	fprintf(stderr, "  ・filenameを指定しなければ、標準入力から読み込みます。\n");
	fprintf(stderr, "  ・filenameは、ワイルドカードによる複数指定も可能です。\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "OPTIONS:\n");
	fprintf(stderr, "  -d             最適化を行いません。\n");
	fprintf(stderr, "                 このオプションを指定しなければ、最適化を行います。\n");
	fprintf(stderr, "  -u[ファイル名] 列挙定義スクリプトを、ファイルへ出力します。\n");
	fprintf(stderr, "                 ファイル名を指定しなければ、標準出力へ出力します。\n");
	fprintf(stderr, "                 このオプションを指定しなければ、列挙定義スクリプトを出力しません。\n");
	fprintf(stderr, "  -r[ファイル名] レジストリスクリプトを、ファイルへ出力します。\n");
	fprintf(stderr, "                 ファイル名を指定しなければ、標準出力へ出力します。\n");
	fprintf(stderr, "                 このオプションを指定しなければ、レジストリスクリプトを出力しません。\n");
	fprintf(stderr, "  -e             エラーログファイルを生成します。\n");
	fprintf(stderr, "                 このオプションを指定しても、エラーが発生しなければ生成されません。\n");
	fprintf(stderr, "  -n             ラベル名のソートに、strnatcmp()を使用します。\n");
	fprintf(stderr, "                 このオプションを指定しなければ、strcmp()を使用します。\n");
	fprintf(stderr, "  -w             全ての警告を有効にします。\n");
	fprintf(stderr, "                 このオプションを指定しなければ、個別に設定した警告のみが有効になります。\n");
	fprintf(stderr, "  -wcast         暗黙の型変換で誤差が生じたら、警告を表示し最終的にエラー終了します。\n");
	fprintf(stderr, "                 このオプションを指定しなければ、警告表示もエラー終了もしません。\n");
	fprintf(stderr, "  -q             進捗表示を抑制します。\n");
	fprintf(stderr, "  -h             使い方を表示します。\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "EXAMPLE:\n");
	fprintf(stderr, "  dVslDefC.exe -uEnmVslDef.txt -rRegVslDef.txt VslDef.txt\n");
	fprintf(stderr, "  VslDef.txtをコンパイルします。\n");
	fprintf(stderr, "  列挙定義スクリプトを、EnmVslDef.txtへ出力します。\n");
	fprintf(stderr, "  レジストリスクリプトを、RegVslDef.txtへ出力します。\n");
	exit(EXIT_FAILURE);
}
/*---------------------------------------------------------------------------*/
//1パス目の処理
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
int cast_dbl_to_int(double d) {
	int i = d;
	if(i != d) {
		if(warn & (1<<WARN_CAST)) {
			yyerror("切り捨て誤差が発生しました: %f → %d", d, i);
			yynerrs++;	//呼び出し元(main)に知らせるために。
			//全てのセマンティクスエラーを報告するために継続する。
		}
	}
	return i;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
void addSymbolRow(ST_Symbol* pSymbolDef) {
	if(!pSymbolDef->pLabel || !pSymbolDef->pDesc) { DIE(); }	//バグ
    //{{進捗表示用
	if(!opt_q) {
		newMsgLen = fprintf(stderr, "\rpass1 %s", pSymbolDef->pLabel);
		if(oldMsgLen > newMsgLen) { fprintf(stderr, "%*s", oldMsgLen - newMsgLen, ""); } oldMsgLen = newMsgLen;
	}
    //}}進捗表示用
	//このシンボルを登録する。
	if(ght_insert(ht_Symbol,
		       pSymbolDef,
		strlen(pSymbolDef->pLabel) + 1/*nul*/,
		       pSymbolDef->pLabel)) {
		yyerror("シンボルラベルが重複しています。(%s)", pSymbolDef->pLabel);
		yynerrs++;	//呼び出し元(main)に知らせるために。
		return;		//全てのセマンティクスエラーを報告するために継続する。
	}
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
void addGameMode(ST_GameMode* pGameMode) {
	if(!pGameMode->pLabel || !pGameMode->pGameModeBody) { DIE(); }	//バグ
    //{{進捗表示用
	if(!opt_q) {
		newMsgLen = fprintf(stderr, "\rpass1 %s", pGameMode->pLabel);
		if(oldMsgLen > newMsgLen) { fprintf(stderr, "%*s", oldMsgLen - newMsgLen, ""); } oldMsgLen = newMsgLen;
	}
    //}}進捗表示用
	//このゲームモードを登録する。
	if(ght_insert(ht_GameMode,
		       pGameMode,
		strlen(pGameMode->pLabel) + 1/*nul*/,
		       pGameMode->pLabel)) {
		yyerror("ゲームモードラベルが重複しています。(%s)", pGameMode->pLabel);
		yynerrs++;	//呼び出し元(main)に知らせるために。
		return;		//全てのセマンティクスエラーを報告するために継続する。
	}
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_Symbol* SymbolDef_New(char* pLabel, char* pDesc) {	//両方ともNULL不可
	ST_Symbol* pSymbol = calloc(1, sizeof(ST_Symbol));
	if(!pSymbol) { DIE(); }	//メモリ不足
	if(!pLabel || !pDesc) { DIE(); }	//バグ
	pSymbol->pLabel		= pLabel;
	pSymbol->pDesc		= pDesc;
	return pSymbol;
}
ST_Symbol* SymbolRef_New(char* pLabel, char* pDesc) {	//片方だけNULLとする事
	ST_Symbol* pSymbol = calloc(1, sizeof(ST_Symbol));
	if(!pSymbol) { DIE(); }	//メモリ不足
	if((pLabel && pDesc) || (!pLabel && !pDesc)) { DIE(); }	//バグ
	pSymbol->pLabel		= pLabel;
	pSymbol->pDesc		= pDesc;
	return pSymbol;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_GameMode* GameMode_New(char* pLabel, struct _ST_GameModeBody* pGameModeBody) {
	ST_GameMode* pGameMode = calloc(1, sizeof(ST_GameMode));
	if(!pGameMode) { DIE(); }	//メモリ不足
	if(!pLabel || !pGameModeBody) { DIE(); }	//バグ
	pGameMode->pLabel		= pLabel;
	pGameMode->pGameModeBody	= pGameModeBody;
	return pGameMode;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_GameModeBody* GameModeBody_New(struct _ST_ReelStrip* pReelStrip, struct _ST_LineDef* pLineDef, struct _ST_PayTable* pPayTable) {
	ST_GameModeBody* pGameModeBody = calloc(1, sizeof(ST_GameModeBody));
	if(!pGameModeBody) { DIE(); }	//メモリ不足
	if(!pReelStrip || !pPayTable) { DIE(); }	//バグ
	pGameModeBody->pReelStrip	= pReelStrip;
	pGameModeBody->pLineDef		= pLineDef;
	pGameModeBody->pPayTable	= pPayTable;
	return pGameModeBody;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_ReelStrip* ReelStrip_New(struct _ST_ReelStripRow* pReelStripRow) {
	ST_ReelStrip* pReelStrip = calloc(1, sizeof(ST_ReelStrip));
	if(!pReelStrip) { DIE(); }	//メモリ不足
	if(!pReelStripRow) { DIE(); }	//バグ
	return ReelStrip_AddReelStripRow(pReelStrip, pReelStripRow);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_ReelStrip* ReelStrip_AddReelStripRow(ST_ReelStrip* pReelStrip, struct _ST_ReelStripRow* pReelStripRow) {
	if(!pReelStrip || !pReelStripRow) { DIE(); }	//バグ
	pReelStrip->sl_pReelStripRow	= g_slist_append(pReelStrip->sl_pReelStripRow, pReelStripRow);
	return pReelStrip;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_ReelStripRow* ReelStripRow_New(struct _ST_ReelStripRowBody* pReelStripRowBody) {
	ST_ReelStripRow* pReelStripRow = calloc(1, sizeof(ST_ReelStripRow));
	if(!pReelStripRow) { DIE(); }	//メモリ不足
	if(!pReelStripRowBody) { DIE(); }	//バグ
	pReelStripRow->pReelStripRowBody	= pReelStripRowBody;
	return pReelStripRow;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_ReelStripRowBody* ReelStripRowBody_New(ST_Symbol* pSymbolRef) {
	ST_ReelStripRowBody* pReelStripRowBody = calloc(1, sizeof(ST_ReelStripRowBody));
	if(!pReelStripRowBody) { DIE(); }	//メモリ不足
	return ReelStripRowBody_AddSymbol(pReelStripRowBody, pSymbolRef);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_ReelStripRowBody* ReelStripRowBody_AddSymbol(ST_ReelStripRowBody* pReelStripRowBody, ST_Symbol* pSymbolRef) {
	if(!pReelStripRowBody) { DIE(); }
	pReelStripRowBody->sl_pSymbolRef	= g_slist_append(pReelStripRowBody->sl_pSymbolRef, pSymbolRef);
	return pReelStripRowBody;
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ST_LineDef* LineDef_New(struct _ST_LineDefRow* pLineDefRow) {
	ST_LineDef* pLineDef = calloc(1, sizeof(ST_LineDef));
	if(!pLineDef) { DIE(); }	//メモリ不足
	if(!pLineDefRow) { DIE(); }	//バグ
	return LineDef_AddLineDefRow(pLineDef, pLineDefRow);
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ST_LineDef* LineDef_AddLineDefRow(ST_LineDef* pLineDef, struct _ST_LineDefRow* pLineDefRow) {
	if(!pLineDef || !pLineDefRow) { DIE(); }	//バグ
	pLineDef->sl_pLineDefRow	= g_slist_append(pLineDef->sl_pLineDefRow, pLineDefRow);
	return pLineDef;
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ST_LineDefRow* LineDefRow_New(struct _ST_LineDefRowBody* pLineDefRowBody) {
	ST_LineDefRow* pLineDefRow = calloc(1, sizeof(ST_LineDefRow));
	if(!pLineDefRowBody) { DIE(); }	//メモリ不足
	pLineDefRow->pLineDefRowBody	= pLineDefRowBody;
	return pLineDefRow;
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ST_LineDefRowBody* LineDefRowBody_New(int iRow) {
	ST_LineDefRowBody* pLineDefRowBody = calloc(1, sizeof(ST_LineDefRowBody));
	if(!pLineDefRowBody) { DIE(); }	//メモリ不足
	return LineDefRowBody_AddRow(pLineDefRowBody, iRow);
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ST_LineDefRowBody* LineDefRowBody_AddRow(ST_LineDefRowBody* pLineDefRowBody, int iRow) {
	if(!pLineDefRowBody) { DIE(); }	//バグ
	pLineDefRowBody->sl_iRow	= g_slist_append(pLineDefRowBody->sl_iRow, (void*)(intptr_t)iRow);
	return pLineDefRowBody;
}
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int Row_New(int iRow) {
	//値がbyteの範囲内であるかを検査する。
	if((unsigned)iRow > UINT8_MAX) {
		yyerror("行番号が想定外です。(%d)", iRow);
		yynerrs++;	//呼び出し元(main)に知らせるために	//必要。YYERRORマクロ呼び出しでyynerrsは増やされないようだ。	←←←←←←←←←←■■■■重要■■■■
		return -1;	//y.tab.yの処理でYYERRORを行う。
	}
	return iRow;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_PayTable* PayTable_New(struct _ST_PayTableRow* pPayTableRow) {
	ST_PayTable* pPayTable = calloc(1, sizeof(ST_PayTable));
	if(!pPayTable) { DIE(); }	//メモリ不足
	if(!pPayTableRow) { DIE(); }	//バグ
	return PayTable_AddPayTableRow(pPayTable, pPayTableRow);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_PayTable* PayTable_AddPayTableRow(ST_PayTable* pPayTable, struct _ST_PayTableRow* pPayTableRow) {
	if(!pPayTable || !pPayTableRow) { DIE(); }	//バグ
	pPayTable->sl_pPayTableRow	= g_slist_append(pPayTable->sl_pPayTableRow, pPayTableRow);
	return pPayTable;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_PayTableRow* PayTableRow_New(struct _ST_PayTableRowBody* pPayTableRowBody) {
	ST_PayTableRow* pPayTableRow = calloc(1, sizeof(ST_PayTableRow));
	if(!pPayTableRowBody) { DIE(); }	//メモリ不足
	pPayTableRow->pPayTableRowBody	= pPayTableRowBody;
	return pPayTableRow;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_PayTableRowBody* PayTableRowBody_New(ST_Symbol* pSymbolRef, int nPay) {
	ST_PayTableRowBody* pPayTableRowBody = calloc(1, sizeof(ST_PayTableRowBody));
	if(!pPayTableRowBody) { DIE(); }	//メモリ不足
	if(!pSymbolRef) { DIE(); }	//バグ
	pPayTableRowBody->pSymbolRef	= pSymbolRef;
	return PayTableRowBody_AddPay(pPayTableRowBody, nPay);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_PayTableRowBody* PayTableRowBody_AddPay(ST_PayTableRowBody* pPayTableRowBody, int nPay) {
	if(!pPayTableRowBody) { DIE(); }	//バグ
	pPayTableRowBody->sl_nPay	= g_slist_append(pPayTableRowBody->sl_nPay, (void*)(intptr_t)nPay);
	return pPayTableRowBody;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
int Pay_New(int nPay) {
	//値がexp10の範囲内であるかを検査する。
	if(Exp10_Encode(nPay) == -1) {
		yyerror("配当額がexp10の範囲外です。(%d)", nPay);
		yynerrs++;	//呼び出し元(main)に知らせるために	//必要。YYERRORマクロ呼び出しでyynerrsは増やされないようだ。	←←←←←←←←←←■■■■重要■■■■
		return -1;	//y.tab.yの処理でYYERRORを行う。
	}
//{{2018/04/08コメント変更:dRegTblC.exeに便利なBLOBの書式を追加した事に伴い、当ツールが出力するレジストリスクリプトも便利なBLOBの書式を利用するように変更しました。
//	return nPay;	//Exp10_Encode()で変換した値を返すのではなく、元の値をそのまま返す。当関数は、値がexp10の範囲内であるかを検査するだけです。当関数は{exp10 値,exp10 値,exp10 値,...}の形式でレジストリスクリプトを出力し、実際に変換するのはdRegTblC.exeに任せます。
//↓2018/04/08コメント変更:dRegTblC.exeに便利なBLOBの書式を追加した事に伴い、当ツールが出力するレジストリスクリプトも便利なBLOBの書式を利用するように変更しました。
	return nPay;	//Exp10_Encode()で変換した値を返すのではなく、元の値をそのまま返す。当関数は、値がexp10の範囲内であるかを検査するだけです。当関数はexp10 {値,値,値,...}の形式でレジストリスクリプトを出力し、実際に変換するのはdRegTblC.exeに任せます。
//}}2018/04/08コメント変更:dRegTblC.exeに便利なBLOBの書式を追加した事に伴い、当ツールが出力するレジストリスクリプトも便利なBLOBの書式を利用するように変更しました。
}
/*---------------------------------------------------------------------------*/
//2パス目の処理
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
static int Label_compar(LIST_ENTRY* _x, LIST_ENTRY* _y) {
	//当関数の処理は、ght_hash_table.h/cモジュールの動作に依存しています。
	//外部からght_hash_table_t.le_iteratorの順番を並び替えても、動作上問題有りません。
	ght_hash_entry_t* x = CONTAINING_RECORD(_x, ght_hash_entry_t, le_iterator);
	ght_hash_entry_t* y = CONTAINING_RECORD(_y, ght_hash_entry_t, le_iterator);
	return (*fnLabCmp)(x->key_data, y->key_data);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//ラベルに対応する、唯一のシンボル定義を検索する。
static ST_Symbol* Symbol_LookupByLabel(char* pLabel) {
	//ラベルが唯一である事は、addSymbolRow()での登録時に検査しているので、当関数では検査する必要は無い。
	//ラベルが一致するシンボル定義が有れば、それがラベルに対応する唯一のシンボル定義である。
	ST_Symbol* pSymbol = ght_get(ht_Symbol,
		strlen(pLabel) + 1/*nul*/,
		       pLabel);
	return pSymbol;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//説明文に対応する、唯一のシンボル定義を検索する。
static ST_Symbol* Symbol_LookupByDesc(char* pDesc) {
	ght_iterator_t it;
	ST_Symbol *pSymbol, *pSymbolMatch = NULL;
	//説明文が唯一かどうかは、addSymbolRow()での登録時には検査していない。
	//重複する説明文も登録可能である。
	//ただし、説明文によるシンボル参照を使用する場合は、その説明文に対応するシンボル定義は唯一でなくてはならない。
	//説明文によるシンボル参照が使用された場合のみ、当関数にて、その説明文に対応するシンボル定義が唯一である事を確認する事にした。
	for(pSymbol = ght_first(ht_Symbol, &it, NULL);
	    pSymbol;
	    pSymbol = ght_next( ht_Symbol, &it, NULL)) {
		if(!strcmp(pSymbol->pDesc, pDesc)) {		//ここではfnLabCmpを使用してはいけない。文字列が厳密に一致しているかを判断する目的だから。
			if(pSymbolMatch) { return NULL; }	//この説明文に対応するシンボル定義が唯一でなければ、NULLを返す。
			pSymbolMatch = pSymbol;
		}
	}
	return pSymbolMatch;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static int PayTableRow_compar(const void* a, const void* b) {
	const ST_PayTableRow* pPayTableRow1 = a;
	ST_PayTableRowBody* pPayTableRowBody1 = pPayTableRow1->pPayTableRowBody;
	ST_Symbol* pSymbolDef1 = pPayTableRowBody1->pSymbolRef;	//既に、各ペイテーブル行の処理で、シンボル定義に置き換えられている。
	const ST_PayTableRow* pPayTableRow2 = b;
	ST_PayTableRowBody* pPayTableRowBody2 = pPayTableRow2->pPayTableRowBody;
	ST_Symbol* pSymbolDef2 = pPayTableRowBody2->pSymbolRef;	//既に、各ペイテーブル行の処理で、シンボル定義に置き換えられている。
	if(pSymbolDef1->iSymbolNo < pSymbolDef2->iSymbolNo) { return -1; }
	if(pSymbolDef1->iSymbolNo > pSymbolDef2->iSymbolNo) { return  1; }
	                                                    { return  0; }
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static void pass2(int bOptimize) {
	//シンボルをラベルの昇順にソートする。		//■■■注意：ght_hash_table_tの実装依存の処理です。■■■
	SortList(&ht_Symbol->le_iterator, Label_compar);
	{
		ght_iterator_t it;
		ST_Symbol* pSymbol;
		int iSymbolNo = 0;
		//各シンボルについて…
		for(pSymbol = ght_first(ht_Symbol, &it, NULL);
		    pSymbol;
		    pSymbol = ght_next( ht_Symbol, &it, NULL)) {
			//シンボル番号(1ベース)を格納する。
			pSymbol->iSymbolNo	= ++iSymbolNo;
		}
	}
	//ゲームモードをラベルの昇順にソートする。	//■■■注意：ght_hash_table_tの実装依存の処理です。■■■
	SortList(&ht_GameMode->le_iterator, Label_compar);
	{
		ght_iterator_t it;
		ST_GameMode* pGameMode;
		int iGameModeNo = 0;
		//各ゲームモードについて…
		for(pGameMode = ght_first(ht_GameMode, &it, NULL);
		    pGameMode;
		    pGameMode = ght_next( ht_GameMode, &it, NULL)) {
			int bErr = 0;	//このゲームモードの処理でエラーが発生したら1にする。
			//ゲームモード番号(1ベース)を格納する。
			pGameMode->iGameModeNo	= ++iGameModeNo;
			//このゲームモードの、リールストリップについて…
			{
				ST_ReelStrip* pReelStrip = pGameMode->pGameModeBody->pReelStrip;
				GSList *list1, *list2;
				int nCols, iCol, iRow = 0;
				//各リールストリップ行について…
				for(list1 = pReelStrip->sl_pReelStripRow, iRow = 0;
				    list1;
				    list1 = g_slist_next(list1),          iRow++) {
					ST_ReelStripRow* pReelStripRow = list1->data;
					ST_ReelStripRowBody* pReelStripRowBody = pReelStripRow->pReelStripRowBody;
					//列数を取得する。
					nCols = g_slist_length(pReelStripRowBody->sl_pSymbolRef);
					if((nCols < 1) || (nCols > ARRAY_SIZE(pReelStrip->TBL_ReelStripLen))) {
						yyerror("リールストリップの列数が想定外です。(%s)", pGameMode->pLabel);
						DIE();	//継続しても無駄なので終了する。
					}
					//一行目ならば…
					if(!iRow) {
						//リールストリップの一行目の列数(=リール数)を格納する。
						pReelStrip->nReel = nCols;
					//二行目以降ならば…
					} else {
						//リールストリップの一行目の列数(=リール数)よりも多い列数であってはならない。
						//厳密には…
						//│Pa Pb Pc	#//一行目が3列
						//│Pa Pb Pc ""	#//二行目が4列だが、""なのでセーフ
						//…ならばセーフなのだが、わざわざ意図的にそのような書き方をする事も無いと思うので、それもエラーにする事にした。
						if(nCols > pReelStrip->nReel) {
							yyerror("リールストリップの二行目以降に、一行目よりも多い列数が出現しました。(%s)", pGameMode->pLabel);
							yynerrs++;	//呼び出し元(main)に知らせるために。
							bErr = 1;	//このゲームモードの処理でエラーが発生した事を記憶する。
							//全てのセマンティクスエラーを報告するために継続する。
						}
					}
					//このリールストリップ行の、各列のシンボル参照について…
					for(list2 = pReelStripRowBody->sl_pSymbolRef, iCol = 0;
					    list2;
					    list2 = g_slist_next(list2),              iCol++) {
						ST_Symbol* pSymbolRef = list2->data;
						//このシンボル参照が、NULLのシンボル参照(=説明文による参照の形式で,かつ,その説明文が空文字列)ならば…
						if(pSymbolRef->pDesc && !*pSymbolRef->pDesc) {
							//一行目にNULLのシンボル参照を書いてはいけない。(空の列になるので)
							if(!iRow) {
								yyerror("リールストリップ内で、一行目に\"\"が出現しました。(%s)", pGameMode->pLabel);
								yynerrs++;	//呼び出し元(main)に知らせるために。
								bErr = 1;	//このゲームモードの処理でエラーが発生した事を記憶する。
								//全てのセマンティクスエラーを報告するために継続する。
							}
						//このシンボル参照が、NULLのシンボル参照("")でなければ…
						} else {
							//既にNULLのシンボル参照が出現した列(=増やす前の列の長さと行番号が一致しない列)ならば、それよりも下の行にNULLのシンボル参照が出現してはいけない。
							//言い換えると、NULLのシンボル参照が出現した列は、それよりも下の行は、NULLのシンボル参照を書くか、又は、空白(=上の行よりも短い列数)にしなければならない。
							if(pReelStrip->TBL_ReelStripLen[iCol]++ != iRow) {	//各列の長さを増やすのと同時に、上記の検査を行う。
								yyerror("リールストリップ内で、\"\"よりも下にシンボルが出現しました。(%s)", pGameMode->pLabel);
								yynerrs++;	//呼び出し元(main)に知らせるために。
								bErr = 1;	//このゲームモードの処理でエラーが発生した事を記憶する。
								//全てのセマンティクスエラーを報告するために継続する。
							}
							//このシンボル参照が、ラベルによる参照ならば…
							if(pSymbolRef->pLabel) {
								//このラベルに対応する、唯一のシンボル定義が有れば…
								ST_Symbol* pSymbolDef = Symbol_LookupByLabel(pSymbolRef->pLabel);
								if(pSymbolDef) {
									//シンボル参照を、シンボル定義へのポインタに置き換える。
									list2->data = pSymbolDef;
								//このラベルに対応する、唯一のシンボル定義が無ければ…
								} else {
									yyerror("ラベルによるシンボル参照が不正です。(%s)", pSymbolRef->pLabel);
									yynerrs++;	//呼び出し元(main)に知らせるために。
									bErr = 1;	//このゲームモードの処理でエラーが発生した事を記憶する。
									//全てのセマンティクスエラーを報告するために継続する。
								}
							//このシンボル参照が、説明文による参照ならば…
							} else if(pSymbolRef->pDesc) {
								//この説明文に対応する、唯一のシンボル定義が有れば…
								ST_Symbol* pSymbolDef = Symbol_LookupByDesc(pSymbolRef->pDesc);
								if(pSymbolDef) {
									//シンボル参照を、シンボル定義へのポインタに置き換える。
									list2->data = pSymbolDef;
								//この説明文に対応する、唯一のシンボル定義が無ければ…
								} else {
									yyerror("説明文によるシンボル参照が不正です。(%s)", pSymbolRef->pDesc);
									yynerrs++;	//呼び出し元(main)に知らせるために。
									bErr = 1;	//このゲームモードの処理でエラーが発生した事を記憶する。
									//全てのセマンティクスエラーを報告するために継続する。
								}
							} else {
								DIE();	//バグ
							}
						}
					}
					if(iCol != nCols) { DIE(); }	//バグ
				}
				if(!iRow) { DIE(); }	//バグ
			}
			//このゲームモードの、ライン定義について…
			{
				ST_LineDef* pLineDef = pGameMode->pGameModeBody->pLineDef;
				GSList* list;
				int nReel = -1;	//最初のライン定義行のリール数を格納し、それ以降のライン定義行のリール数も全て同じである事を確認する。
				//各ライン定義行について…
				for(list = pLineDef->sl_pLineDefRow;
				    list;
				    list = g_slist_next(list)) {
					ST_LineDefRow* pLineDefRow = list->data;
					ST_LineDefRowBody* pLineDefRowBody = pLineDefRow->pLineDefRowBody;
					//最初のライン定義行ならば…
					if(nReel == -1) {
						//リール数を格納する。
						nReel = g_slist_length(pLineDefRowBody->sl_iRow);
					//最初のライン定義行でなければ…
					} else {
						//一つのゲームモードに属するライン定義のリール数が、全て同じである事を確認する。
						// - 一般的には、(リールストリップのリール数 == ライン定義のリール数)ですが、それは確認しない事にしました。
						//   リールストリップのリール数を、画面に表示するリール数よりも多く定義し、アプリケーションが選択して使用する可能性も有るからです。
						//   従って、一つのゲームモードに属するライン定義のリール数が、全て同じである事だけを確認する事にしました。
						// - 尚、一つのゲームモード内で、ライン定義のリール数が変動する事は想定していません。
						//   もしそのような必要が有る場合は、個別のゲームモードとして定義して下さい。
						if(g_slist_length(pLineDefRowBody->sl_iRow) != nReel) {
							yyerror("一つのゲームモードに属するライン定義のリール数は、全て同じでなくてはなりません。(%s)", pGameMode->pLabel);
							yynerrs++;	//呼び出し元(main)に知らせるために。
							bErr = 1;	//このゲームモードの処理でエラーが発生した事を記憶する。
							//全てのセマンティクスエラーを報告するために継続する。
						}
					}
				}
			}
			//このゲームモードの、ペイテーブルについて…
			{
				ST_PayTable* pPayTable = pGameMode->pGameModeBody->pPayTable;
				GSList* list;
				//各ペイテーブル行について…
				for(list = pPayTable->sl_pPayTableRow;
				    list;
				    list = g_slist_next(list)) {
					ST_PayTableRow* pPayTableRow = list->data;
					ST_PayTableRowBody* pPayTableRowBody = pPayTableRow->pPayTableRowBody;
					ST_Symbol* pSymbolRef = pPayTableRowBody->pSymbolRef;
					//このシンボル参照が、ラベルによる参照ならば…
					if(pSymbolRef->pLabel) {
						//このラベルに対応する、唯一のシンボル定義が有れば…
						ST_Symbol* pSymbolDef = Symbol_LookupByLabel(pSymbolRef->pLabel);
						if(pSymbolDef) {
							//シンボル参照を、シンボル定義へのポインタに置き換える。
							pPayTableRowBody->pSymbolRef = pSymbolDef;
						//このラベルに対応する、唯一のシンボル定義が無ければ…
						} else {
							yyerror("ラベルによるシンボル参照が不正です。(%s)", pSymbolRef->pLabel);
							yynerrs++;	//呼び出し元(main)に知らせるために。
							bErr = 1;	//このゲームモードの処理でエラーが発生した事を記憶する。
							//全てのセマンティクスエラーを報告するために継続する。
						}
					//このシンボル参照が、説明文による参照ならば…
					} else if(pSymbolRef->pDesc) {
						//この説明文に対応する、唯一のシンボル定義が有れば…
						ST_Symbol* pSymbolDef = Symbol_LookupByDesc(pSymbolRef->pDesc);
						if(pSymbolDef) {
							//シンボル参照を、シンボル定義へのポインタに置き換える。
							pPayTableRowBody->pSymbolRef = pSymbolDef;
						//この説明文に対応する、唯一のシンボル定義が無ければ…
						} else {
							yyerror("説明文によるシンボル参照が不正です。(%s)", pSymbolRef->pDesc);
							yynerrs++;	//呼び出し元(main)に知らせるために。
							bErr = 1;	//このゲームモードの処理でエラーが発生した事を記憶する。
							//全てのセマンティクスエラーを報告するために継続する。
						}
					} else {
						DIE();	//バグ
					}
				}
				//このゲームモードの処理で、エラーが発生していなければ…
				if(!bErr) {
					//ペイテーブル行を、シンボル番号の昇順にソートする。
					pPayTable->sl_pPayTableRow = g_slist_sort(pPayTable->sl_pPayTableRow, PayTableRow_compar);
					//ペイテーブル内で、シンボルが重複していない事を確認する。
					list = pPayTable->sl_pPayTableRow;
					for(;;) {
						GSList* next = g_slist_next(list);
						if(!next) { break; }
						{
							ST_PayTableRow* pPayTableRow1 = list->data;
							ST_PayTableRowBody* pPayTableRowBody1 = pPayTableRow1->pPayTableRowBody;
							ST_Symbol* pSymbolDef1 = pPayTableRowBody1->pSymbolRef;	//既に、各ペイテーブル行の処理で、シンボル定義に置き換えられている。
							ST_PayTableRow* pPayTableRow2 = next->data;
							ST_PayTableRowBody* pPayTableRowBody2 = pPayTableRow2->pPayTableRowBody;
							ST_Symbol* pSymbolDef2 = pPayTableRowBody2->pSymbolRef;	//既に、各ペイテーブル行の処理で、シンボル定義に置き換えられている。
							if(pSymbolDef1 == pSymbolDef2) {
								yyerror("ペイテーブル内で、シンボルが重複しています。(%s)", pSymbolDef1->pLabel);
								yynerrs++;	//呼び出し元(main)に知らせるために。
								bErr = 1;	//このゲームモードの処理でエラーが発生した事を記憶する。
								//全てのセマンティクスエラーを報告するために継続する。
							}
						}
						list = next;
					}
				}
			}
			//【NOTE】リールストリップとペイテーブルの、整合性の検査は行わない事にしました。
			// - たとえば、リールストリップに存在しないシンボルのペイテーブルが設定されていたとしても、エラーにはしない事にしました。
			//   なぜなら、ゲーム固有の特殊ルール(シンボル変化等)によって、リールストリップ上には存在しないシンボルが出現して、揃う可能性も有るからです。
			//   上記の例のような、ゲーム固有の特殊ルールは、当スクリプトでは判断出来ないので、当ツールでは、リールストリップとペイテーブルの、整合性の検査は行わない事にしました。
			// - もし、ゲーム固有の特殊ルールを考慮せずに整合性の検査を行うとすると、単純に、ペイテーブルが設定されているシンボルがリールストリップ上に存在するかだけでは不足です。
			//   たとえば、リールストリップの1リール目に存在しないシンボルが(ワイルドや特殊ルールを考慮しなければ)2 of a kind以上では揃う事は無い、等も検査する必要が有るからです。
			//   今回は、前述の通り、整合性の検査を行わない事にしたのですが、もし今後、検査する事にした場合は、それらの複雑な検査も行うようにして下さい。
			//【NOTE】ペイテーブルの列数がリールストリップの列数に一致しているかや、全てのペイテーブル行の列数が一致しているかの検査は、行わない事にした。
			// - たとえば、リールストリップを10列定義しておいて、プログラムによってその中から5列選択して使用するような、特殊な使い方も考えられるからです。
			//   また、たとえば、5リールだが、リールストリップにスキャッターを3個しか配置しない場合に、スキャッターのペイを3oakまでしか設定しない事も考えられるからです。
			// - 以上の理由により、ペイテーブルは、行数にも列数にも特に制限の無い、自由な形式とする事にしました。
		}
	}
}
/*---------------------------------------------------------------------------*/
//出力処理
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
static void output(const char* sEnmOut, const char* sRegOut) {
	int nOut = 0;
	//列挙定義スクリプトのファイル名,又は,"-"(標準出力)を指定されていたら…
	if(sEnmOut) {
		FILE* fp = strcmp(sEnmOut, "-") ? fopen(sEnmOut, "w") : stdout;
		if(!fp) { error(EXIT_FAILURE, errno, "%s が作成出来ません。", sEnmOut); }	//{{2016/09/21変更:die(〜)で終了していた箇所を、error(EXIT_FAILURE,〜)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
		output_enm(fp);
		if(fp != stdout) { fclose(fp); }
		nOut++;
	}
	//レジストリスクリプトのファイル名,又は,"-"(標準出力)が指定されていたら…
	if(sRegOut) {
		FILE* fp = strcmp(sRegOut, "-") ? fopen(sRegOut, "w") : stdout;
		if(!fp) { error(EXIT_FAILURE, errno, "%s が作成出来ません。", sRegOut); }	//{{2016/09/21変更:die(〜)で終了していた箇所を、error(EXIT_FAILURE,〜)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
		output_reg(fp);
		if(fp != stdout) { fclose(fp); }
		nOut++;
	}
	if(!nOut) {
		fprintf(stderr, "-u/-rオプションが指定されなかったので、出力を行いませんでした。\n");
	}
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
//列挙定義スクリプトを出力する。
static void output_enm(FILE* fp) {
	//シンボル
	{
		ght_iterator_t it;
		ST_Symbol* pSymbol;
		int iSymbolNo = 0;
	    //{{2017/02/24変更:シンボルの列挙定義名を"Symbol_*"→"VslSym_*"に変更しました。ゲームモードの列挙定義名を"GameMode_*"→"VslGmd_*"に変更しました。
	    //	fprintf(fp, "Symbol\n");
	    //↓2017/02/24変更:シンボルの列挙定義名を"Symbol_*"→"VslSym_*"に変更しました。ゲームモードの列挙定義名を"GameMode_*"→"VslGmd_*"に変更しました。
		fprintf(fp, "VslSym\n");
	    //}}2017/02/24変更:シンボルの列挙定義名を"Symbol_*"→"VslSym_*"に変更しました。ゲームモードの列挙定義名を"GameMode_*"→"VslGmd_*"に変更しました。
		//各シンボルについて…
		for(pSymbol = ght_first(ht_Symbol, &it, NULL);
		    pSymbol;
		    pSymbol = ght_next( ht_Symbol, &it, NULL)) {
			if(++iSymbolNo != pSymbol->iSymbolNo) { DIE(); }	//バグ
			//ラベルの一文字目がCシンボルの一文字目として有効な文字ならば…
			if(iscsymf(*pSymbol->pLabel)) {
				fprintf(fp, "\t%s", pSymbol->pLabel);
			//ラベルの一文字目がCシンボルの一文字目として無効な文字(多分数字)ならば…
			} else {
				fprintf(fp, "\t`%s", pSymbol->pLabel);	//ラベルの先頭に「`」を付与する。dEnmDefC.exeのためです。
			}
			//一行目ならば…
			if(iSymbolNo == 1) {
				fprintf(fp, "\t%d", iSymbolNo);
			//二行目以降ならば…
			} else {
				fprintf(fp, "\t");
			}
			//説明文
			fprintf(fp, "\t\"%s\"", pSymbol->pDesc);
			fputc('\n', fp);
		}
		fprintf(fp, "end\n");
	}
	//ゲームモード
	{
		ght_iterator_t it;
		ST_GameMode* pGameMode;
		int iGameModeNo = 0;
	    //{{2017/02/24変更:シンボルの列挙定義名を"Symbol_*"→"VslSym_*"に変更しました。ゲームモードの列挙定義名を"GameMode_*"→"VslGmd_*"に変更しました。
	    //	fprintf(fp, "GameMode\n");
	    //↓2017/02/24変更:シンボルの列挙定義名を"Symbol_*"→"VslSym_*"に変更しました。ゲームモードの列挙定義名を"GameMode_*"→"VslGmd_*"に変更しました。
		fprintf(fp, "VslGmd\n");
	    //}}2017/02/24変更:シンボルの列挙定義名を"Symbol_*"→"VslSym_*"に変更しました。ゲームモードの列挙定義名を"GameMode_*"→"VslGmd_*"に変更しました。
		//各ゲームモードについて…
		for(pGameMode = ght_first(ht_GameMode, &it, NULL);
		    pGameMode;
		    pGameMode = ght_next( ht_GameMode, &it, NULL)) {
			if(++iGameModeNo != pGameMode->iGameModeNo) { DIE(); }	//バグ
			//ラベルの一文字目がCシンボルの一文字目として有効な文字ならば…
			if(iscsymf(*pGameMode->pLabel)) {
				fprintf(fp, "\t%s", pGameMode->pLabel);
			//ラベルの一文字目がCシンボルの一文字目として無効な文字(多分数字)ならば…
			} else {
				fprintf(fp, "\t`%s", pGameMode->pLabel);	//ラベルの先頭に「`」を付与する。dEnmDefC.exeのためです。
			}
			//一行目ならば…
			if(iGameModeNo == 1) {
				fprintf(fp, "\t%d", iGameModeNo);
			}
			fputc('\n', fp);
		}
		fprintf(fp, "end\n");
	}
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
//レジストリスクリプトを出力する。
static void output_reg(FILE* fp) {
	fprintf(fp, "%s\t\t\t\t\t#//\t%s\n", "VslDef", "VslDef");
	//ゲームモード
	{
		ght_iterator_t it;
		ST_GameMode* pGameMode;
		int iGameModeNo = 0;
		//各ゲームモードについて…
		for(pGameMode = ght_first(ht_GameMode, &it, NULL);
		    pGameMode;
		    pGameMode = ght_next( ht_GameMode, &it, NULL)) {
			if(++iGameModeNo != pGameMode->iGameModeNo) { DIE(); }	//バグ
			//ゲームモード
			fprintf(fp, "\t%d\t\t\t\t#//\t\t%s\n", iGameModeNo, pGameMode->pLabel);
			{
				//リールストリップ
				fprintf(fp, "\t\t%d\t\t\t#//\t\t\t%s\n", VslDef_ReelStrip, "ReelStrip");
				{
					ST_ReelStrip* pReelStrip = pGameMode->pGameModeBody->pReelStrip;
					int iCol, iRow;
					for(iCol = 0; iCol < pReelStrip->nReel; iCol++) {
						if(!pReelStrip->TBL_ReelStripLen[iCol]) { DIE(); }	//バグ
						fprintf(fp, "\t\t\t%d\t= {\t#//\t\t\t\t%d\t= {\n", iCol, iCol);	//リール番号は0ベースとする事にした。ゲームモード番号とシンボル番号は1ベースだが、リール番号は0ベースとした理由は、次の通りです。ゲームモード番号とシンボル番号は特に意味の無い列挙値なので、初期化漏れを防ぐためにも1ベースの方が良いと思ってそうした。リール番号はアプリケーションプログラム内でインデクス計算すると思うので、0ベースの方が扱い易いと思ってそうした。
						//この列の、各リールストリップ行について…
						for(iRow = 0; iRow < pReelStrip->TBL_ReelStripLen[iCol]; iRow++) {
							ST_ReelStripRow* pReelStripRow = g_slist_nth_data(pReelStrip->sl_pReelStripRow, iRow);
							ST_ReelStripRowBody* pReelStripRowBody = pReelStripRow->pReelStripRowBody;
							ST_Symbol* pSymbolDef = g_slist_nth_data(pReelStripRowBody->sl_pSymbolRef, iCol);	//既に、各ペイテーブル行の処理で、シンボル定義に置き換えられている。
							if(iRow < pReelStrip->TBL_ReelStripLen[iCol] - 1) {
								fprintf(fp, "\t\t\t\t  %d,\t#//\t\t\t\t\t  %s,\t#//pos %d\n", pSymbolDef->iSymbolNo, pSymbolDef->pLabel, iRow);	//シンボルの種類は255種類以下だろうと想定して、暗黙のbyte配列とする事にした。万一、(まず有り得ないが)シンボルの種類が256以上になった場合は、half配列にしろ。
							} else {
								fprintf(fp, "\t\t\t\t  %d\t#//\t\t\t\t\t  %s\t#//pos %d\n", pSymbolDef->iSymbolNo, pSymbolDef->pLabel, iRow);	//シンボルの種類は255種類以下だろうと想定して、暗黙のbyte配列とする事にした。万一、(まず有り得ないが)シンボルの種類が256以上になった場合は、half配列にしろ。
							}
						}
						fprintf(fp, "\t\t\t\t  }\t#//\t\t\t\t\t  }\n");
					}
				}
				fprintf(fp, "\t\t%s\t\t\t#//\t\t\t%s\n", "end", "end");
				//ライン定義
				fprintf(fp, "\t\t%d\t\t\t#//\t\t\t%s\n", VslDef_LineDef, "LineDef");
				{
					ST_LineDef* pLineDef = pGameMode->pGameModeBody->pLineDef;
					GSList *list1, *list2;
					int iLine, iReel;
					//各ライン定義行について…
					for(list1 = pLineDef->sl_pLineDefRow, iLine = 0;
					    list1;
					    list1 = g_slist_next(list1),      iLine++) {
						ST_LineDefRow* pLineDefRow = list1->data;
						ST_LineDefRowBody* pLineDefRowBody = pLineDefRow->pLineDefRowBody;
						fprintf(fp, "\t\t\t%d\t= {\t#//\t\t\t\t%d\t= {\n", iLine, iLine);	//ライン番号は0ベースとする事にした。ゲームモード番号とシンボル番号は1ベースだが、ライン番号は0ベースとした理由は、次の通りです。ゲームモード番号とシンボル番号は特に意味の無い列挙値なので、初期化漏れを防ぐためにも1ベースの方が良いと思ってそうした。ライン番号はアプリケーションプログラム内でインデクス計算すると思うので、0ベースの方が扱い易いと思ってそうした。
						//各行番号について…
						for(list2 = pLineDefRowBody->sl_iRow, iReel = 0;
						    list2;
						    list2 = g_slist_next(list2),      iReel++) {
							int iRow = (intptr_t)list2->data;
							if(g_slist_next(list2)) {
								fprintf(fp, "\t\t\t\t  %d,\t#//\t\t\t\t\t  %d,\t#//reel %d\n", iRow, iRow, iReel);
							} else {
								fprintf(fp, "\t\t\t\t  %d\t#//\t\t\t\t\t  %d\t#//reel %d\n", iRow, iRow, iReel);
							}
						}
						fprintf(fp, "\t\t\t\t  }\t#//\t\t\t\t\t  }\n");
					}
				}
				fprintf(fp, "\t\t%s\t\t\t#//\t\t\t%s\n", "end", "end");
				//ペイテーブル
				fprintf(fp, "\t\t%d\t\t\t#//\t\t\t%s\n", VslDef_PayTable, "PayTable");
				{
					ST_PayTable* pPayTable = pGameMode->pGameModeBody->pPayTable;
					GSList *list1, *list2;
					//各ペイテーブル行について…
					for(list1 = pPayTable->sl_pPayTableRow;
					    list1;
					    list1 = g_slist_next(list1)) {
						ST_PayTableRow* pPayTableRow = list1->data;
						ST_PayTableRowBody* pPayTableRowBody = pPayTableRow->pPayTableRowBody;
						ST_Symbol* pSymbolDef = pPayTableRowBody->pSymbolRef;	//既に、各ペイテーブル行の処理で、シンボル定義に置き換えられている。
						int nOfAKind;
//{{2018/04/08変更:dRegTblC.exeに便利なBLOBの書式を追加した事に伴い、当ツールが出力するレジストリスクリプトも便利なBLOBの書式を利用するように変更しました。
//						fprintf(fp, "\t\t\t%d\t= {\t#//\t\t\t\t%s\t= {\n", pSymbolDef->iSymbolNo, pSymbolDef->pLabel);
//↓2018/04/08変更:dRegTblC.exeに便利なBLOBの書式を追加した事に伴い、当ツールが出力するレジストリスクリプトも便利なBLOBの書式を利用するように変更しました。
						fprintf(fp, "\t\t\t%d\t= exp10 {\t#//\t\t\t\t%s\t= exp10 {\n", pSymbolDef->iSymbolNo, pSymbolDef->pLabel);
//}}2018/04/08変更:dRegTblC.exeに便利なBLOBの書式を追加した事に伴い、当ツールが出力するレジストリスクリプトも便利なBLOBの書式を利用するように変更しました。
						//各配当額について…
						for(list2 = pPayTableRowBody->sl_nPay, nOfAKind = 1;
						    list2;
						    list2 = g_slist_next(list2),       nOfAKind++) {
							int nPay = (intptr_t)list2->data;
							if(g_slist_next(list2)) {
//{{2018/04/08変更:dRegTblC.exeに便利なBLOBの書式を追加した事に伴い、当ツールが出力するレジストリスクリプトも便利なBLOBの書式を利用するように変更しました。
//								fprintf(fp, "\t\t\t\t  exp10 %d,\t#//\t\t\t\t\t  %d,\t#//%d of a kind\n", nPay, nPay, nOfAKind);
//↓2018/04/08変更:dRegTblC.exeに便利なBLOBの書式を追加した事に伴い、当ツールが出力するレジストリスクリプトも便利なBLOBの書式を利用するように変更しました。
								fprintf(fp, "\t\t\t\t  %d,\t#//\t\t\t\t\t  %d,\t#//%d of a kind\n", nPay, nPay, nOfAKind);
//}}2018/04/08変更:dRegTblC.exeに便利なBLOBの書式を追加した事に伴い、当ツールが出力するレジストリスクリプトも便利なBLOBの書式を利用するように変更しました。
							} else {
//{{2018/04/08変更:dRegTblC.exeに便利なBLOBの書式を追加した事に伴い、当ツールが出力するレジストリスクリプトも便利なBLOBの書式を利用するように変更しました。
//								fprintf(fp, "\t\t\t\t  exp10 %d\t#//\t\t\t\t\t  %d\t#//%d of a kind\n", nPay, nPay, nOfAKind);
//↓2018/04/08変更:dRegTblC.exeに便利なBLOBの書式を追加した事に伴い、当ツールが出力するレジストリスクリプトも便利なBLOBの書式を利用するように変更しました。
								fprintf(fp, "\t\t\t\t  %d\t#//\t\t\t\t\t  %d\t#//%d of a kind\n", nPay, nPay, nOfAKind);
//}}2018/04/08変更:dRegTblC.exeに便利なBLOBの書式を追加した事に伴い、当ツールが出力するレジストリスクリプトも便利なBLOBの書式を利用するように変更しました。
							}
						}
						fprintf(fp, "\t\t\t\t  }\t#//\t\t\t\t\t  }\n");
					}
				}
				fprintf(fp, "\t\t%s\t\t\t#//\t\t\t%s\n", "end", "end");
			}
			fprintf(fp, "\t%s\t\t\t\t#//\t\t%s\n", "end", "end");
		}
	}
	fprintf(fp, "%s\t\t\t\t\t#//\t%s\n", "end", "end");
}
