/*****************************************************************************
 * 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である事を示します。
//-----------------------------------------------------------------------------
//{{2017/03/31追加:入力ファイル名のサフィックスによる属性の自動出力に対応しました。今回は自動スケーリング属性のサフィックス('-1x'～'-9x')だけに対応しました。今後必要に応じて増やして行く予定です。
//入力ファイル名のサフィックスによる属性の自動出力
enum {
	ATRS_SCALE,			//入力ファイル名にサフィックス'-1x'～'-9x'が含まれていたら、"scale=1"～"scale=9"を自動出力します。このオプションを指定しなければ、この属性を自動出力しません。
};
static const char* const atrs_tokens[]={
	"scale",			//入力ファイル名にサフィックス'-1x'～'-9x'が含まれていたら、"scale=1"～"scale=9"を自動出力します。このオプションを指定しなければ、この属性を自動出力しません。
NULL};//終端
int atrs;				//各属性(ATRS_*)について(atrs&(1<<ATRS_*))がセットされていたら、入力ファイル名にそのサフィックスが含まれていたら属性を自動出力します。
//}}2017/03/31追加:入力ファイル名のサフィックスによる属性の自動出力に対応しました。今回は自動スケーリング属性のサフィックス('-1x'～'-9x')だけに対応しました。今後必要に応じて増やして行く予定です。
//-----------------------------------------------------------------------------
//グローバル変数定義
int grid_w, grid_h;
/*---------------------------------------------------------------------------*/
//ローカル変数定義
static ght_hash_table_t* ht_tex;	//テクスチャ名⇒(ST_Tex*)
static ght_hash_table_t* ht_spr;	//スプライト名⇒(ST_Spr*)
//ラベル名のソート方法
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* sTexOut, const char* sSprOut, const char* sEnmOut, const char* sRegOut);
static void output_tex(FILE* fp);
static void output_spr(FILE* fp);
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* sTexOut = NULL;	//'-t'オプションが指定されたらテクスチャ定義ヘッダのファイル名,又は,"-"(標準出力)を設定する。	┬P/ECE専用
	const char* sSprOut = NULL;	//'-p'オプションが指定されたらスプライト定義ヘッダのファイル名,又は,"-"(標準出力)を設定する。	┘
	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, "dt::p::u::r::enw::a::qh")) != -1) {
		switch(opt) {
		case 'd':
			bOptimize = 0;				//最適化を行わない。
			break;
		case 't':
			sTexOut = optarg ? optarg : "-";	//テクスチャ定義ヘッダのファイル名,又は,"-"(標準出力)を設定する。
			break;
		case 'p':
			sSprOut = optarg ? optarg : "-";	//スプライト定義ヘッダのファイル名,又は,"-"(標準出力)を設定する。
			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;
//{{2017/03/31追加:入力ファイル名のサフィックスによる属性の自動出力に対応しました。今回は自動スケーリング属性のサフィックス('-1x'～'-9x')だけに対応しました。今後必要に応じて増やして行く予定です。
		case 'a':
			if(optarg) {
				char *optionp = optarg, *valuep;
				while(*optionp) {
					switch(getsubopt(&optionp, (char* const*)atrs_tokens, &valuep)) {
					case ATRS_SCALE:
						if(valuep) { usage(); }
						atrs |= (1<<ATRS_SCALE);
						break;
					default:
						usage();
					}
				}
			} else {
				atrs = -1;
			}
			break;
//}}2017/03/31追加:入力ファイル名のサフィックスによる属性の自動出力に対応しました。今回は自動スケーリング属性のサフィックス('-1x'～'-9x')だけに対応しました。今後必要に応じて増やして行く予定です。
		case 'q':
			opt_q = 1;
			break;
		case 'h':
		default:
			usage();
		}
	}
	//ハッシュテーブルを作成する。
	ght_set_rehash((ht_tex = ght_create(0)), 1);
	ght_set_rehash((ht_spr = 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(sTexOut, sSprOut, 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, "dTexDefC - テクスチャ定義コンパイラ (%s)\n", VERSION);
	fprintf(stderr, "Copyright (C) 2016-2017 Naoyuki Sawa\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "USAGE:\n");
	fprintf(stderr, "  dTexDefC [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, "  -t[ファイル名] P/ECE用のテクスチャ定義ヘッダを、ファイルへ出力します。\n");
	fprintf(stderr, "                 ファイル名を指定しなければ、標準出力へ出力します。\n");
	fprintf(stderr, "                 このオプションを指定しなければ、テクスチャ定義ヘッダを出力しません。\n");
	fprintf(stderr, "  -p[ファイル名] P/ECE用のスプライト定義ヘッダを、ファイルへ出力します。\n");
	fprintf(stderr, "                 ファイル名を指定しなければ、標準出力へ出力します。\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");
//{{2017/03/31追加:入力ファイル名のサフィックスによる属性の自動出力に対応しました。今回は自動スケーリング属性のサフィックス('-1x'～'-9x')だけに対応しました。今後必要に応じて増やして行く予定です。
	fprintf(stderr, "  -a             入力ファイル名のサフィックスによる属性の自動出力を全て有効にします。\n");
	fprintf(stderr, "                 このオプションを指定しなければ、個別に設定した属性のみが有効になります。\n");
	fprintf(stderr, "  -ascale        入力ファイル名にサフィックス'-1x'～'-9x'が含まれていたら、\"scale=1\"～\"scale=9\"を自動出力します。\n");
	fprintf(stderr, "                 このオプションを指定しなければ、この属性を自動出力しません。\n");
//}}2017/03/31追加:入力ファイル名のサフィックスによる属性の自動出力に対応しました。今回は自動スケーリング属性のサフィックス('-1x'～'-9x')だけに対応しました。今後必要に応じて増やして行く予定です。
	fprintf(stderr, "  -q             進捗表示を抑制します。\n");
	fprintf(stderr, "  -h             使い方を表示します。\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "EXAMPLE:\n");
	fprintf(stderr, "  ①dTexDefC.exe -t -p TexDef.txt\n");
	fprintf(stderr, "  　TexDef.txtをコンパイルします。\n");
	fprintf(stderr, "  　P/ECE用のテクスチャ定義ヘッダとスプライト定義ヘッダを、標準出力へ出力します。\n");
	fprintf(stderr, "  ②dTexDefC.exe -ttexture.h -psprite.h TexDef.txt\n");
	fprintf(stderr, "  　TexDef.txtをコンパイルします。\n");
	fprintf(stderr, "  　P/ECE用のテクスチャ定義ヘッダを、texture.hへ出力します。\n");
	fprintf(stderr, "  　P/ECE用のスプライト定義ヘッダを、sprite.hへ出力します。\n");
	fprintf(stderr, "  ③dTexDefC.exe -uEnmTexDef.txt -rRegTexDef.txt TexDef.txt\n");
	fprintf(stderr, "  　TexDef.txtをコンパイルします。\n");
	fprintf(stderr, "  　汎用の列挙定義スクリプトを、EnmTexDef.txtへ出力します。\n");
	fprintf(stderr, "  　汎用のレジストリスクリプトを、RegTexDef.txtへ出力します。\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "※注意\n");
	fprintf(stderr, "  P/ECE用のテクスチャ番号,及び,スプライト番号は既存のマクロ仕様のため0ベースです。\n");
	fprintf(stderr, "  汎用のテクスチャ番号,及び,スプライト番号は使用性を考慮して1ベースとしてあります。\n");
	fprintf(stderr, "  両者の番号は一致していないので一つのアプリケーション内で両者を併用しないで下さい。\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 addTex(ST_Tex* pTex) {
	ST_TexHdr*  pTexHdr  = pTex->pTexHdr;
	ST_TexBody* pTexBody = pTex->pTexBody;
	ST_Tex* pTex2;
    //{{進捗表示用
	if(!opt_q) {
		newMsgLen = fprintf(stderr, "\rpass1 %s", pTexHdr->pTexLab);
		if(oldMsgLen > newMsgLen) { fprintf(stderr, "%*s", oldMsgLen - newMsgLen, ""); } oldMsgLen = newMsgLen;
	}
    //}}進捗表示用
	//同じ名前のテクスチャが既に登録されていたら…
	pTex2 = ght_get(ht_tex,
		strlen(pTexHdr->pTexLab) + 1/*nul*/,
		       pTexHdr->pTexLab);
	if(pTex2) {
		ST_TexHdr*  pTexHdr2  = pTex2->pTexHdr;
		ST_TexBody* pTexBody2 = pTex2->pTexBody;
		if(pTexHdr->pPath) {
			//今回のテクスチャと、既存のテクスチャの、両方ともファイル名が設定されていたら…
			if(pTexHdr2->pPath) {
				//ファイル名が違っていたら…
				if(strcmp(pTexHdr->pPath, pTexHdr2->pPath)) {
					yyerror("テクスチャ名が同じでファイル名が違っています。(%s)", pTexHdr->pTexLab);
					yynerrs++;	//呼び出し元(main)に知らせるために。
					return;		//全てのセマンティクスエラーを報告するために継続する。
				}
			//今回のテクスチャにファイル名が設定されていて、既存のテクスチャにファイル名が設定されていなければ…
			} else {
				//既存のテクスチャにファイル名を設定する。
				pTexHdr2->pPath		= pTexHdr->pPath;
			}
		} else {
			//今回のテクスチャにファイル名が設定されていなくて、既存のテクスチャにファイル名が設定されていたら…
			if(pTexHdr2->pPath) {
				/** no job **/
			//今回のテクスチャと、既存のテクスチャの、両方ともファイル名が設定されていなければ…
			} else {
				/** no job **/
			}
		}
		//既存のテクスチャのリソースフラグに、今回のテクスチャのリソースフラグをマージする。
		pTexHdr2->bResource	|= pTexHdr->bResource;
//{{2017/03/26変更:テクスチャに汎用の属性リストを追加し、既存のループフラグも属性に含めました。
//		//既存のテクスチャのループフラグに、今回のテクスチャのループフラグをマージする。
//		pTexHdr2->bLoop		|= pTexHdr->bLoop;
//↓2017/03/26変更:テクスチャに汎用の属性リストを追加し、既存のループフラグも属性に含めました。
		//既存のテクスチャの属性リストに、今回のテクスチャの属性リストをマージする。
		{
			GTreeIteratorNP it;
			ST_Attr* pAttr;
			//数値
			if(g_tree_first_np(pTexHdr->pAttrList->pAttrValTree, &it, NULL, &pAttr)) {
				do {
					ST_AttrList* pAttrList = AttrList_AddAttr(pTexHdr2->pAttrList, pAttr);
					if(!pAttrList) { return; }			//AttrList_AddAttr()が失敗した時は、yyerror(～),及び,yynerrs++はAttrList_AddAttr()にて行っているので、ここで行う必要は有りません。
					if(pTexHdr2->pAttrList != pAttrList) { DIE(); }	//AttrList_AddAttr()が成功した時は、引数pAttrListがそのまま返されるはずです。もしここで停止したら当モジュールのバグです。
				} while(g_tree_next_np(pTexHdr->pAttrList->pAttrValTree, &it, NULL, &pAttr));
			}
			//文字列
			if(g_tree_first_np(pTexHdr->pAttrList->pAttrStrTree, &it, NULL, &pAttr)) {
				do {
					ST_AttrList* pAttrList = AttrList_AddAttr(pTexHdr2->pAttrList, pAttr);
					if(!pAttrList) { return; }			//AttrList_AddAttr()が失敗した時は、yyerror(～),及び,yynerrs++はAttrList_AddAttr()にて行っているので、ここで行う必要は有りません。
					if(pTexHdr2->pAttrList != pAttrList) { DIE(); }	//AttrList_AddAttr()が成功した時は、引数pAttrListがそのまま返されるはずです。もしここで停止したら当モジュールのバグです。
				} while(g_tree_next_np(pTexHdr->pAttrList->pAttrStrTree, &it, NULL, &pAttr));
			}
		}
//}}2017/03/26変更:テクスチャに汎用の属性リストを追加し、既存のループフラグも属性に含めました。
		//既存のテクスチャのスプライト配列に、今回のテクスチャのスプライト配列をマージする。
		{
			pTexBody2 = realloc(pTexBody2, sizeof(ST_TexBody) + (sizeof pTexBody2->aSpr[0]) * (pTexBody2->nSpr + pTexBody->nSpr));
			if(!pTexBody2) { DIE(); }
			memcpy(&pTexBody2->aSpr[pTexBody2->nSpr], &pTexBody->aSpr[0], (sizeof pTexBody2->aSpr[0]) * (pTexBody->nSpr));
			pTexBody2->nSpr		+= pTexBody->nSpr;
			pTex2->pTexBody		 = pTexBody2;	//忘れないで!!
		}
	//同じ名前のテクスチャがまだ登録されていなければ…
	} else {
		//このテクスチャを登録する。
		if(ght_insert(ht_tex,
			       pTex,
			strlen(pTexHdr->pTexLab) + 1/*nul*/,
			       pTexHdr->pTexLab)) { DIE(); }	//ここで停止する事は有りえないはず。
	}
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_Tex* Tex_new(ST_TexHdr* pTexHdr, ST_TexBody* pTexBody) {
	ST_Tex* pTex = calloc(1, sizeof(ST_Tex));
	if(!pTex) { DIE(); }
	pTex->pTexHdr		= pTexHdr;
	pTex->pTexBody		= pTexBody;
	return pTex;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
//{{2017/03/26変更:テクスチャに汎用の属性リストを追加し、既存のループフラグも属性に含めました。
//ST_TexHdr* TexHdr_new(char* pTexLab/*片方のみNULL可*/, char* pPath/*片方のみNULL可*/, int bResource, int bLoop) {
//↓2017/03/26変更:テクスチャに汎用の属性リストを追加し、既存のループフラグも属性に含めました。
ST_TexHdr* TexHdr_new(char* pTexLab/*片方のみNULL可*/, char* pPath/*片方のみNULL可*/, int bResource, ST_AttrList* pAttrList) {
//}}2017/03/26変更:テクスチャに汎用の属性リストを追加し、既存のループフラグも属性に含めました。
	ST_TexHdr* pTexHdr = calloc(1, sizeof(ST_TexHdr));
	if(!pTexHdr) { DIE(); }
	if(!pTexLab && !pPath) { DIE(); }	//両方NULLで呼び出される事は無いはず。もしここで停止したらy.tab.yのバグである。
	//ラベルが省略されていたら、ファイル名からラベルを自動生成する。
	if(!pTexLab) {
		char fname[MAX_FNAME];
		int i;
		splitpath(pPath, NULL, NULL, fname, NULL);
//{{2016/01/27変更:ファイル名からラベルを自動生成する処理を変更しました。
//		for(i = 0; iscsym(fname[i]); i++) { /** no job **/ }
//↓2016/01/27変更:ファイル名からラベルを自動生成する処理を変更しました。
		// * Wed Jan 27 22:56:05 JST 2016 Naoyuki Sawa
		// - ファイル名からラベルを自動生成する処理を変更しました。
		// - 変更前は、単純に、Cシンボル以外の文字が出現した所で打ち切っていました。
		//   しかしそれだと、'foo 001.bmp','foo 002.bmp','foo 003.bmp',…のような空白を含むファイル名に対して、個別のラベルが上手く生成出来ない問題が生じました。
		//   「AVI2JPG」(http://www.gigafree.net/media/conv/avi2jpg.html)等のフリーソフトで連番画像を作成すると、上記のようにファイル名に空白が含まれるからです。
		// - そこで、ファイル名からラベルを自動生成する処理を、以下のように変更しました。
		//   ①'-'が出現するまでの間、Cシンボル以外の文字が出現したら、'_'に置き換えて継続する。
		//   ②'-'が出現したら、そこで打ち切る。
		//   (※②はdpbmpcnv.exeに準拠するための仕様です。Win32プロジェクトで使用する場合は必ずしも適切ではないのですが、P/ECEプロジェクトでの使い易さを優先する事にしました。)
		// - 変更後の処理で、前述の例のファイル名は、以下のようなラベルに自動変換されます。
		//   'foo 001.bmp'⇒FOO_001,'foo 002.bmp'⇒FOO_002,'foo 003.bmp'⇒FOO_003
		//   別の例として、dpbmpcnv.exe用のフォーマット指定が付いているファイル名の場合、以下のようにラベルが自動変換されます。
		//   'bar 001-2m.bmp'⇒BAR_001,'bar 002-2m.bmp'⇒BAR_002,'bar 003-2m.bmp'⇒BAR_003
		for(i = 0; fname[i]; i++) {
			if(fname[i] == '-') { break; }			//②'-'が出現したら、そこで打ち切る。							┬この順序は必須です。
			if(!iscsym(fname[i])) { fname[i] = '_'; }	//①'-'が出現するまでの間、Cシンボル以外の文字が出現したら、'_'に置き換えて継続する。	┘
		}
//}}2016/01/27変更:ファイル名からラベルを自動生成する処理を変更しました。
		if(!i) {
			yyerror("ファイル名からラベルを自動生成出来ません。(%s)", pPath);
			yynerrs++;	//呼び出し元(main)に知らせるために。	//必要。YYERRORマクロ呼び出しでyynerrsは増やされないようだ。	←←←←←←←←←←■■■■重要■■■■
			return NULL;	//y.tab.yの処理でYYERRORを行う。
		}
		pTexLab = strndup(fname, i);
		if(!pTexLab) { DIE(); }
		strupr(pTexLab);
	}
	pTexHdr->pTexLab	= pTexLab;	//この時点でpTexLabはNULLではない。
	pTexHdr->pPath		= pPath;	//この時点でpPathはNULLの可能性有り。別途、同じ名前のテクスチャ定義行でファイル名を設定する。addTex()を参照せよ。
	pTexHdr->bResource	= bResource;
//{{2017/03/26変更:テクスチャに汎用の属性リストを追加し、既存のループフラグも属性に含めました。
//	pTexHdr->bLoop		= bLoop;
//↓2017/03/26変更:テクスチャに汎用の属性リストを追加し、既存のループフラグも属性に含めました。
	pTexHdr->pAttrList	= pAttrList;
//}}2017/03/26変更:テクスチャに汎用の属性リストを追加し、既存のループフラグも属性に含めました。
	return pTexHdr;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_TexBody* TexBody_new(int iGridW, int iGridH) {
	ST_TexBody* pTexBody = calloc(1, sizeof(ST_TexBody));
	if(!pTexBody) { DIE(); }
	if((iGridW <= 0) || (iGridH <= 0)) {
		yyerror("グリッドサイズが範囲外です。(%d,%d)", iGridW, iGridH);
		yynerrs++;	//呼び出し元(main)に知らせるために。	//必要。YYERRORマクロ呼び出しでyynerrsは増やされないようだ。	←←←←←←←←←←■■■■重要■■■■
		return NULL;	//y.tab.yの処理でYYERRORを行う。
	}
	grid_w			= iGridW;
	grid_h			= iGridH;
	return pTexBody;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_TexBody* TexBody_addSpr(ST_TexBody* pTexBody, ST_Spr* pSpr) {
	pTexBody = realloc(pTexBody, sizeof(ST_TexBody) + (sizeof pTexBody->aSpr[0]) * (pTexBody->nSpr + 1));
	if(!pTexBody) { DIE(); }
	pTexBody->aSpr[pTexBody->nSpr++]	= pSpr;
	//スプライト名が重複していない事を確認し、スプライトを登録する。
	if(ght_insert(ht_spr,
		       pSpr,
		strlen(pSpr->pSprLab) + 1/*nul*/,
		       pSpr->pSprLab)) {
		yyerror("スプライト名が重複しています。(%s)", pSpr->pSprLab);
		yynerrs++;	//呼び出し元(main)に知らせるために。	//必要。YYERRORマクロ呼び出しでyynerrsは増やされないようだ。	←←←←←←←←←←■■■■重要■■■■
		return NULL;	//y.tab.yの処理でYYERRORを行う。
	}
	return pTexBody;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_Spr* Spr_new(char* pSprLab, int x, int y, int w, int h, int cx, int cy, char* Param) {
	ST_Spr* pSpr = calloc(1, sizeof(ST_Spr));
	if(!pSpr) { DIE(); }
	if((x < 0) || (y < 0)) {
		yyerror("スプライトの座標が不正です。(%s)", pSprLab);
		yynerrs++;	//呼び出し元(main)に知らせるために。	//必要。YYERRORマクロ呼び出しでyynerrsは増やされないようだ。	←←←←←←←←←←■■■■重要■■■■
		return NULL;	//y.tab.yの処理でYYERRORを行う。
	}
	if((w <= 0) || (h <= 0)) {
		yyerror("スプライトのサイズが不正です。(%s)", pSprLab);
		yynerrs++;	//呼び出し元(main)に知らせるために。	//必要。YYERRORマクロ呼び出しでyynerrsは増やされないようだ。	←←←←←←←←←←■■■■重要■■■■
		return NULL;	//y.tab.yの処理でYYERRORを行う。
	}
	pSpr->pSprLab		= pSprLab;
	pSpr->x			= x;
	pSpr->y			= y;
	pSpr->w			= w;
	pSpr->h			= h;
	pSpr->cx		= cx;
	pSpr->cy		= cy;
	pSpr->Param		= Param;
	return pSpr;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_Spr* Spr_newP(char* pSprLab, int x, int y, int w, int h, int px, int py, char* Param) {
	return Spr_new(pSprLab,
		x,
		y,
		w,
		h,
		(w * px) / 100,	//幅に対するパーセンテージ
		(h * py) / 100,	//高さに対するパーセンテージ
		Param);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
//{{2017/03/26追加:テクスチャに汎用の属性リストを追加し、既存のループフラグも属性に含めました。
ST_AttrList* AttrList_NewBlank() {
	ST_AttrList* pAttrList = calloc(1, sizeof(ST_AttrList));
	if(!pAttrList) { DIE(); }
	pAttrList->pAttrValTree	= g_tree_new(strcmp);	//属性名の並び順は関係無い(最終的にdRegTblC.exeが並び替える)ので'-n'オプションは影響しない。当ツールが出力するレジストリスクリプトの読み易さのためだけに辞書順にソートする事にした。
	pAttrList->pAttrStrTree	= g_tree_new(strcmp);	//属性名の並び順は関係無い(最終的にdRegTblC.exeが並び替える)ので'-n'オプションは影響しない。当ツールが出力するレジストリスクリプトの読み易さのためだけに辞書順にソートする事にした。
	return pAttrList;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_AttrList* AttrList_New(ST_Attr* pAttr) {
	ST_AttrList* pAttrList = AttrList_NewBlank();
	return AttrList_AddAttr(pAttrList, pAttr);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_AttrList* AttrList_AddAttr(ST_AttrList* pAttrList, ST_Attr* pAttr) {
	GTree* pTree;
	//属性の種類によって、ツリーを選択する。
	switch(pAttr->iAttrType) {
	default:DIE();
	case 0/*数値*/:   pTree = pAttrList->pAttrValTree; break;
	case 1/*文字列*/: pTree = pAttrList->pAttrStrTree; break;
	}
	//同じ種類で同じ名前の属性が登録されていない事を確認する。
	if(g_tree_lookup_extended(pTree, pAttr->pAttrName, NULL, NULL)) {
		yyerror("同じ種類で同じ名前の属性が登録されています。(%s)", pAttr->pAttrName);
		yynerrs++;	//呼び出し元(main)に知らせるために。	//必要。YYERRORマクロ呼び出しでyynerrsは増やされないようだ。	←←←←←←←←←←■■■■重要■■■■
		return NULL;	//y.tab.yの処理でYYERRORを行う。
	}
	//属性を登録する。
	g_tree_insert(pTree, pAttr->pAttrName, pAttr);
	return pAttrList;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
ST_Attr* Attr_New(char* pAttrName, int iAttrType, intptr_t iVal_pStr) {
	ST_Attr* pAttr = calloc(1, sizeof(ST_Attr));
	if(!pAttr) { DIE(); }
    //{{基本的には属性名は任意の名前が使えますが、唯一、"Path"という名前の文字列属性だけは定義出来ません。output_reg()にてテクスチャのキーに対して出力する"Path"と衝突してしまうからです。
	if(!strcmp(pAttrName, "Path") && (iAttrType == 1/*文字列*/)) {
		yyerror("\"%s\"という名前の文字列属性を定義する事は出来ません。", pAttrName);
		yynerrs++;	//呼び出し元(main)に知らせるために。	//必要。YYERRORマクロ呼び出しでyynerrsは増やされないようだ。	←←←←←←←←←←■■■■重要■■■■
		return NULL;	//y.tab.yの処理でYYERRORを行う。
	}
    //}}基本的には属性名は任意の名前が使えますが、唯一、"Path"という名前の文字列属性だけは定義出来ません。output_reg()にてテクスチャのキーに対して出力する"Path"と衝突してしまうからです。
	pAttr->pAttrName	= pAttrName;
	pAttr->iAttrType	= iAttrType;
	switch(iAttrType) {
	default:DIE();
	case 0/*数値*/:   pAttr->iVal =        iVal_pStr; break;
	case 1/*文字列*/: pAttr->pStr = (char*)iVal_pStr; break;
	}
	return pAttr;
}
//}}2017/03/26追加:テクスチャに汎用の属性リストを追加し、既存のループフラグも属性に含めました。
/*---------------------------------------------------------------------------*/
//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 void pass2(int bOptimize) {
	//テクスチャを名前の昇順にソートする。		//■■■注意：ght_hash_table_tの実装依存の処理です。■■■
	SortList(&ht_tex->le_iterator, Label_compar);
	{
		ght_iterator_t it;
		ST_Tex* pTex;
		char* pTexLab;
		int iTexNo = 0;
		//各テクスチャについて…
		for(pTex = ght_first(ht_tex, &it, &pTexLab);
		    pTex;
		    pTex = ght_next( ht_tex, &it, &pTexLab)) {
			ST_TexHdr*  pTexHdr  = pTex->pTexHdr;
			ST_TexBody* pTexBody = pTex->pTexBody;
			//テクスチャ番号(1ベース)を格納する。
			pTex->iTexNo		= ++iTexNo;
			//テクスチャのファイル名が設定されている事を確認する。
			// - テクスチャのファイル名を省略する形式でスプライトを定義した場合は、別途、テクスチャ定義行でテクスチャのファイル名を指定しなければいけない。
			{
				if(!pTexHdr->pPath) {
					yyerror("テクスチャのファイル名が設定されていません。(%s)", pTexLab);
					yynerrs++;	//呼び出し元(main)に知らせるために。
					//全てのセマンティクスエラーを報告するために継続する。
				}
			}
			//各スプライトに、スプライトが属するテクスチャのテクスチャ名を格納する。
			{
				int iSpr;
				for(iSpr = 0; iSpr < pTexBody->nSpr; iSpr++) {
					ST_Spr* pSpr = pTexBody->aSpr[iSpr];
					pSpr->pTexLab		= pTexLab;
				}
			}
//{{2017/03/31追加:入力ファイル名のサフィックスによる属性の自動出力に対応しました。今回は自動スケーリング属性のサフィックス('-1x'～'-9x')だけに対応しました。今後必要に応じて増やして行く予定です。
			//入力ファイル名のサフィックスによる属性の自動出力処理
			if(pTexHdr->pPath) {	//テクスチャのファイル名が設定されている場合のみ、この処理を行う。テクスチャのファイル名が設定されていない場合は最終的にはエラー停止するが、この上の処理では全てのセマンティクスエラーを報告するために継続しているので、ここでももう一度確認する必要が有る。
				char fname[MAX_FNAME], **suffixes, *suffix;
				//テクスチャのファイル名を、'-'で分割する。
				splitpath(pTexHdr->pPath, NULL, NULL, fname, NULL);
				suffixes = strsplit(fname, "-", 0);
				//'-'で分割した個々の要素の内、二つ目から最後までをサフィックスと見なすので、最初の要素は飛ばす。
				if(*suffixes) { suffixes++; }	//有り得ないとは思うが万一空文字列だった場合のために、最初の要素が有る場合のみ進める。
				//個々のサフィックスについて…
				while((suffix = *suffixes++)) {
					//入力ファイル名にサフィックス'-1x'～'-9x'が含まれていたら、"scale=1"～"scale=9"を自動出力します。
					if(atrs & (1<<ATRS_SCALE)) {
						regexp* prog = regcomp("^([1-9])x$");
						if(regexec(prog, suffix)) {
							ST_Attr* pAttr = Attr_New("scale", 0/*数値*/, *prog->startp[1] - '0');
							ST_AttrList* pAttrList = AttrList_AddAttr(pTexHdr->pAttrList, pAttr);
							if(!pAttrList) { continue; }			//AttrList_AddAttr()が失敗した時は、yyerror(～),及び,yynerrs++はAttrList_AddAttr()にて行っているので、ここで行う必要は有りません。
							if(pTexHdr->pAttrList != pAttrList) { DIE(); }	//AttrList_AddAttr()が成功した時は、引数pAttrListがそのまま返されるはずです。もしここで停止したら当モジュールのバグです。
						}
					}
				}
			}
//}}2017/03/31追加:入力ファイル名のサフィックスによる属性の自動出力に対応しました。今回は自動スケーリング属性のサフィックス('-1x'～'-9x')だけに対応しました。今後必要に応じて増やして行く予定です。
		}
	}
	//スプライトを名前の昇順にソートする。		//■■■注意：ght_hash_table_tの実装依存の処理です。■■■
	SortList(&ht_spr->le_iterator, Label_compar);
	{
		ght_iterator_t it;
		ST_Spr* pSpr;
		char* pSprLab;
		int iSprNo = 0;
		//各スプライトについて…
		for(pSpr = ght_first(ht_spr, &it, &pSprLab);
		    pSpr;
		    pSpr = ght_next( ht_spr, &it, &pSprLab)) {
			//スプライト番号(1ベース)を格納する。
			pSpr->iSprNo		= ++iSprNo;
		}
	}
}
/*---------------------------------------------------------------------------*/
//出力処理
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
static void output(const char* sTexOut, const char* sSprOut, const char* sEnmOut, const char* sRegOut) {
	int nOut = 0;
	//テクスチャ定義ヘッダのファイル名,又は,"-"(標準出力)が指定されていたら…
	if(sTexOut) {
		FILE* fp = strcmp(sTexOut, "-") ? fopen(sTexOut, "w") : stdout;
		if(!fp) { error(EXIT_FAILURE, errno, "%s が作成出来ません。", sTexOut); }	//{{2016/09/21変更:die(～)で終了していた箇所を、error(EXIT_FAILURE,～)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
		output_tex(fp);
		if(fp != stdout) { fclose(fp); }
		nOut++;
	}
	//スプライト定義ヘッダのファイル名,又は,"-"(標準出力)が指定されていたら…
	if(sSprOut) {
		FILE* fp = strcmp(sSprOut, "-") ? fopen(sSprOut, "w") : stdout;
		if(!fp) { error(EXIT_FAILURE, errno, "%s が作成出来ません。", sSprOut); }	//{{2016/09/21変更:die(～)で終了していた箇所を、error(EXIT_FAILURE,～)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
		output_spr(fp);
		if(fp != stdout) { fclose(fp); }
		nOut++;
	}
	//列挙定義スクリプトのファイル名,又は,"-"(標準出力)を指定されていたら…
	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, "-t/-p/-u/-rオプションが指定されなかったので、出力を行いませんでした。\n");
	}
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
//テクスチャ定義ヘッダを出力する。
static void output_tex(FILE* fp) {
	ght_iterator_t it;
	ST_Tex* pTex;
	char* pTexLab;
	//各テクスチャについて…
	for(pTex = ght_first(ht_tex, &it, &pTexLab);
	    pTex;
	    pTex = ght_next( ht_tex, &it, &pTexLab)) {
		ST_TexHdr*  pTexHdr  = pTex->pTexHdr;
		ST_TexBody* pTexBody = pTex->pTexBody;
		if(pTexHdr->bResource) {
			//サービスとして、パス名をP/ECEリソースに適したファイル名に変換して出力する。
			char fname[MAX_FNAME];
			splitpath(pTexHdr->pPath, NULL, NULL, fname, NULL);
			fprintf(fp, "TEXTURE_RESOURCE_(%s,\"%s.pgd\")\n",
				pTexLab,
				fname);
		} else {
			fprintf(fp, "TEXTURE_(%s)\n",
				pTexLab);
		}
	}
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
//スプライト定義ヘッダを出力する。
static void output_spr(FILE* fp) {
	ght_iterator_t it;
	ST_Spr* pSpr;
	char* pSprLab;
	//各スプライトについて…
	for(pSpr = ght_first(ht_spr, &it, &pSprLab);
	    pSpr;
	    pSpr = ght_next( ht_spr, &it, &pSprLab)) {
		//※TODO:範囲チェックしろ。
		fprintf(fp, "SPRITE_(SPR_%s,TEX_%s,%d,%d,%d,%d,%d,%d,%s)\n",
			pSprLab,
			pSpr->pTexLab,
			pSpr->x, pSpr->y,
			pSpr->w, pSpr->h,
			pSpr->cx, pSpr->cy,
			pSpr->Param);
	}
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
//列挙定義スクリプトを出力する。
static void output_enm(FILE* fp) {
	//テクスチャ
	if(ght_size(ht_tex)) {
		ght_iterator_t it;
		ST_Tex* pTex;
		char* pTexLab;
		fprintf(fp, "TexNo\n");
		//各テクスチャについて…
		for(pTex = ght_first(ht_tex, &it, &pTexLab);
		    pTex;
		    pTex = ght_next( ht_tex, &it, &pTexLab)) {
			ST_TexHdr*  pTexHdr  = pTex->pTexHdr;
			ST_TexBody* pTexBody = pTex->pTexBody;
			//テクスチャ名の一文字目がCシンボルの一文字目として有効な文字ならば…
			if(iscsymf(*pTexLab)) {
				fprintf(fp, "\t%s", pTexLab);
			//テクスチャ名の一文字目がCシンボルの一文字目として無効な文字(多分数字)ならば…
			} else {
				fprintf(fp, "\t`%s", pTexLab);	//テクスチャ名の先頭に「`」を付与する。dEnmDefC.exeのためです。
			}
			if(pTex->iTexNo == 1) { fprintf(fp, "\t%d", pTex->iTexNo); }	//1行目
			fputc('\n', fp);
		}
		fprintf(fp, "end\n");
	}
	//スプライト
	if(ght_size(ht_spr)) {
		ght_iterator_t it;
		ST_Spr* pSpr;
		char* pSprLab;
		fprintf(fp, "SprNo\n");
		//各スプライトについて…
		for(pSpr = ght_first(ht_spr, &it, &pSprLab);
		    pSpr;
		    pSpr = ght_next( ht_spr, &it, &pSprLab)) {
			//スプライト名の一文字目がCシンボルの一文字目として有効な文字ならば…
			if(iscsymf(*pSprLab)) {
				fprintf(fp, "\t%s", pSprLab);
			//スプライト名の一文字目がCシンボルの一文字目として無効な文字(多分数字)ならば…
			} else {
				fprintf(fp, "\t`%s", pSprLab);	//スプライト名の先頭に「`」を付与する。dEnmDefC.exeのためです。
			}
			if(pSpr->iSprNo == 1) { fprintf(fp, "\t%d", pSpr->iSprNo); }	//1行目
			fputc('\n', fp);
		}
		fprintf(fp, "end\n");
	}
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
//レジストリスクリプトを出力する。
static void output_reg(FILE* fp) {
	//テクスチャ
	if(ght_size(ht_tex)) {
		ght_iterator_t it;
		ST_Tex* pTex;
		char* pTexLab;
		fprintf(fp, "%s\t\t\t\t#//\t%s\n", "TexDef", "TexDef");
		//各テクスチャについて…
		for(pTex = ght_first(ht_tex, &it, &pTexLab);
		    pTex;
		    pTex = ght_next( ht_tex, &it, &pTexLab)) {
			ST_TexHdr*  pTexHdr  = pTex->pTexHdr;
			ST_TexBody* pTexBody = pTex->pTexBody;
			fprintf(fp, "\t%d\t\t\t#//\t\t%s\n", pTex->iTexNo, pTexLab);
			fprintf(fp, "\t\t%s\t= \"%s\"\t#//\t\t\t%s\t= \"%s\"\n", "Path", pTexHdr->pPath, "Path", pTexHdr->pPath);
//{{2017/03/26変更:テクスチャに汎用の属性リストを追加し、既存のループフラグも属性に含めました。
//			if(pTexHdr->bLoop) {
//				fprintf(fp, "\t\t%s\t= %d\t#//\t\t\t%s\t= %d\n", "Loop", 1, "Loop", 1);		//ループフラグが1の場合のみ'Loop=1'を出力する。'Loop=0'は出力しない。
//			}
//↓2017/03/26変更:テクスチャに汎用の属性リストを追加し、既存のループフラグも属性に含めました。
			{
				GTreeIteratorNP it;
				ST_Attr* pAttr;
				//数値
				if(g_tree_first_np(pTexHdr->pAttrList->pAttrValTree, &it, NULL, &pAttr)) {
					do {
						fprintf(fp, "\t\t%s\t= %d\t#//\t\t\t%s\t= %d\n", pAttr->pAttrName, pAttr->iVal, pAttr->pAttrName, pAttr->iVal);
					} while(g_tree_next_np(pTexHdr->pAttrList->pAttrValTree, &it, NULL, &pAttr));
				}
				//文字列
				if(g_tree_first_np(pTexHdr->pAttrList->pAttrStrTree, &it, NULL, &pAttr)) {
					do {
						fprintf(fp, "\t\t%s\t= \"%s\"\t#//\t\t\t%s\t= \"%s\"\n", pAttr->pAttrName, pAttr->pStr, pAttr->pAttrName, pAttr->pStr);
					} while(g_tree_next_np(pTexHdr->pAttrList->pAttrStrTree, &it, NULL, &pAttr));
				}
			}
//}}2017/03/26変更:テクスチャに汎用の属性リストを追加し、既存のループフラグも属性に含めました。
			fprintf(fp, "\t%s\t\t\t#//\t\t%s\n", "end", "end");
		}
		fprintf(fp, "%s\t\t\t\t#//\t%s\n", "end", "end");
	}
	//スプライト
	if(ght_size(ht_spr)) {
		ght_iterator_t it;
		ST_Spr* pSpr;
		char* pSprLab;
		fprintf(fp, "%s\t\t\t\t#//\t%s\n", "SprDef", "SprDef");
		//各スプライトについて…
		for(pSpr = ght_first(ht_spr, &it, &pSprLab);
		    pSpr;
		    pSpr = ght_next( ht_spr, &it, &pSprLab)) {
			//このスプライトが属するテクスチャを取得する。
			ST_Tex* pTex = ght_get(ht_tex,
				strlen(pSpr->pTexLab) + 1/*nul*/,
				       pSpr->pTexLab);
			if(!pTex) { DIE(); }	//当ツールのバグ
			fprintf(fp, "\t%d\t\t\t#//\t\t%s\n", pSpr->iSprNo, pSprLab);
			fprintf(fp, "\t\t%s\t= %d\t#//\t\t\t%s\t= %s\n", "TexNo", pTex->iTexNo, "TexNo", pSpr->pTexLab);
			fprintf(fp, "\t\t%s\t= %d\t#//\t\t\t%s\t= %d\n", "x",     pSpr->x,      "x",     pSpr->x);
			fprintf(fp, "\t\t%s\t= %d\t#//\t\t\t%s\t= %d\n", "y",     pSpr->y,      "y",     pSpr->y);
			fprintf(fp, "\t\t%s\t= %d\t#//\t\t\t%s\t= %d\n", "w",     pSpr->w,      "w",     pSpr->w);
			fprintf(fp, "\t\t%s\t= %d\t#//\t\t\t%s\t= %d\n", "h",     pSpr->h,      "h",     pSpr->h);
//{{2016/01/27変更:レジストリスクリプトでも、マイナスのcx,cyを表現出来るようにしました。アプリケーション側では、取得したcx,cyを(((int)(x<<8))>>8)のように符号拡張して使用して下さい。
//			fprintf(fp, "\t\t%s\t= %d\t#//\t\t\t%s\t= %d\n", "cx",    pSpr->cx,     "cx",    pSpr->cx);
//			fprintf(fp, "\t\t%s\t= %d\t#//\t\t\t%s\t= %d\n", "cy",    pSpr->cy,     "cy",    pSpr->cy);
//↓2016/01/27変更:レジストリスクリプトでも、マイナスのcx,cyを表現出来るようにしました。アプリケーション側では、取得したcx,cyを(((int)(x<<8))>>8)のように符号拡張して使用して下さい。
			fprintf(fp, "\t\t%s\t= (%d)&(~(-1<<24))\t#//\t\t\t%s\t= %d\n", "cx",    pSpr->cx,     "cx",    pSpr->cx);
			fprintf(fp, "\t\t%s\t= (%d)&(~(-1<<24))\t#//\t\t\t%s\t= %d\n", "cy",    pSpr->cy,     "cy",    pSpr->cy);
//}}2016/01/27変更:レジストリスクリプトでも、マイナスのcx,cyを表現出来るようにしました。アプリケーション側では、取得したcx,cyを(((int)(x<<8))>>8)のように符号拡張して使用して下さい。
			fprintf(fp, "\t\t%s\t= %s\t#//\t\t\t%s\t= %s\n", "Param", pSpr->Param,  "Param", pSpr->Param);
			fprintf(fp, "\t%s\t\t\t#//\t\t%s\n", "end", "end");
		}
		fprintf(fp, "%s\t\t\t\t#//\t%s\n", "end", "end");
	}
}
