/*****************************************************************************
 * 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"を自動出力します。このオプションを指定しなければ、この属性を自動出力しません。
//{{2017/05/10追加:'-aloop','-aadd'オプションを追加しました。
	ATRS_LOOP,			//入力ファイル名にサフィックス'-loop'が含まれていたら、"loop=1"を自動出力します。このオプションを指定しなければ、この属性を自動出力しません。
	ATRS_ADD,			//入力ファイル名にサフィックス'-add'が含まれていたら、"add=1"を自動出力します。このオプションを指定しなければ、この属性を自動出力しません。
//}}2017/05/10追加:'-aloop','-aadd'オプションを追加しました。
};
static const char* const atrs_tokens[]={
	"scale",			//入力ファイル名にサフィックス'-1x'～'-9x'が含まれていたら、"scale=1"～"scale=9"を自動出力します。このオプションを指定しなければ、この属性を自動出力しません。
//{{2017/05/10追加:'-aloop','-aadd'オプションを追加しました。
	"loop",				//入力ファイル名にサフィックス'-loop'が含まれていたら、"loop=1"を自動出力します。このオプションを指定しなければ、この属性を自動出力しません。
	"add",				//入力ファイル名にサフィックス'-add'が含まれていたら、"add=1"を自動出力します。このオプションを指定しなければ、この属性を自動出力しません。
//}}2017/05/10追加:'-aloop','-aadd'オプションを追加しました。
NULL};//終端
int atrs;				//各属性(ATRS_*)について(atrs&(1<<ATRS_*))がセットされていたら、入力ファイル名にそのサフィックスが含まれていたら属性を自動出力します。
//}}2017/03/31追加:入力ファイル名のサフィックスによる属性の自動出力に対応しました。今回は自動スケーリング属性のサフィックス('-1x'～'-9x')だけに対応しました。今後必要に応じて増やして行く予定です。
//-----------------------------------------------------------------------------
//グローバル変数定義
//{{2017/07/05削除:グリッドの原点を指定出来るようにしました。
//int grid_w, grid_h;
//}}2017/07/05削除:グリッドの原点を指定出来るようにしました。
/*---------------------------------------------------------------------------*/
//ローカル変数定義
static ght_hash_table_t* ht_tex;	//テクスチャ名⇒(ST_Tex*)
static ght_hash_table_t* ht_spr;	//スプライト名⇒(ST_Spr*)
static char* sSprLabPrefix = "";	//'.'スプライト名のプレフィクス		//{{2017/12/01追加:「'.'スプライト名」の書式を追加しました。}}
//ラベル名のソート方法
int (*fnLabCmp)(const void*, const void*) = strcmp;	//'n'オプションが指定されたらstrnatcmpに変更する。
//{{進捗表示用
static int opt_q;
static int newMsgLen, oldMsgLen = 0;
//}}進捗表示用
//{{2017/07/02追加:'-m'オプションを追加しました。
static const char* opt_m;		//'-m[フォルダ名]'オプションが指定されたら、フォルダ名,又は,""を設定する。'-m[フォルダ名]'オプションが指定されなければ、NULLのままとする。
//}}2017/07/02追加:'-m'オプションを追加しました。
/*---------------------------------------------------------------------------*/
//エラーログ用
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);
//{{2017/08/26変更:'-t',及び,'-p'オプションを廃止しました。
//static void output(const char* sTexOut, const char* sSprOut, const char* sEnmOut, const char* sRegOut);
//↓2017/08/26変更:'-t',及び,'-p'オプションを廃止しました。
static void output(const char* sEnmOut, const char* sRegOut);
//}}2017/08/26変更:'-t',及び,'-p'オプションを廃止しました。
//{{2017/08/26削除:'-t',及び,'-p'オプションを廃止しました。
//static void output_tex(FILE* fp);
//static void output_spr(FILE* fp);
//}}2017/08/26削除:'-t',及び,'-p'オプションを廃止しました。
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'オプションが指定されなければ最適化を行う。
//{{2017/08/26削除:'-t',及び,'-p'オプションを廃止しました。
//	const char* sTexOut = NULL;	//'-t'オプションが指定されたらテクスチャ定義ヘッダのファイル名,又は,"-"(標準出力)を設定する。	┬P/ECE専用
//	const char* sSprOut = NULL;	//'-p'オプションが指定されたらスプライト定義ヘッダのファイル名,又は,"-"(標準出力)を設定する。	┘
//}}2017/08/26削除:'-t',及び,'-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, "du::r::enw::a::m::qh")) != -1) {
		switch(opt) {
		case 'd':
			bOptimize = 0;				//最適化を行わない。
			break;
//{{2017/08/26削除:'-t',及び,'-p'オプションを廃止しました。
//		case 't':
//			sTexOut = optarg ? optarg : "-";	//テクスチャ定義ヘッダのファイル名,又は,"-"(標準出力)を設定する。
//			break;
//		case 'p':
//			sSprOut = optarg ? optarg : "-";	//スプライト定義ヘッダのファイル名,又は,"-"(標準出力)を設定する。
//			break;
//}}2017/08/26削除:'-t',及び,'-p'オプションを廃止しました。
		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;
//{{2017/05/10追加:'-aloop','-aadd'オプションを追加しました。
					case ATRS_LOOP:
						if(valuep) { usage(); }
						atrs |= (1<<ATRS_LOOP);
						break;
					case ATRS_ADD:
						if(valuep) { usage(); }
						atrs |= (1<<ATRS_ADD);
						break;
//}}2017/05/10追加:'-aloop','-aadd'オプションを追加しました。
					default:
						usage();
					}
				}
			} else {
				atrs = -1;
			}
			break;
//}}2017/03/31追加:入力ファイル名のサフィックスによる属性の自動出力に対応しました。今回は自動スケーリング属性のサフィックス('-1x'～'-9x')だけに対応しました。今後必要に応じて増やして行く予定です。
//{{2017/07/02追加:'-m'オプションを追加しました。
		case 'm':
			opt_m = optarg ? optarg : "";
			break;
//}}2017/07/02追加:'-m'オプションを追加しました。
		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()の文字列の最後に余計な改行が付いていたのを削除しました。}}
	//--- 出力処理 ---
	//出力処理を行う。
//{{2017/08/26変更:'-t',及び,'-p'オプションを廃止しました。
//	output(sTexOut, sSprOut, sEnmOut, sRegOut);
//↓2017/08/26変更:'-t',及び,'-p'オプションを廃止しました。
	output(sEnmOut, sRegOut);
//}}2017/08/26変更:'-t',及び,'-p'オプションを廃止しました。
	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-2018 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");
//{{2017/08/26変更:'-t',及び,'-p'オプションを廃止しました。
//	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");
//↓2017/08/26変更:'-t',及び,'-p'オプションを廃止しました。
	fprintf(stderr, "  -u[ファイル名] 列挙定義スクリプトを、ファイルへ出力します。\n");
	fprintf(stderr, "                 ファイル名を指定しなければ、標準出力へ出力します。\n");
	fprintf(stderr, "                 このオプションを指定しなければ、列挙定義スクリプトを出力しません。\n");
	fprintf(stderr, "  -r[ファイル名] レジストリスクリプトを、ファイルへ出力します。\n");
	fprintf(stderr, "                 ファイル名を指定しなければ、標準出力へ出力します。\n");
	fprintf(stderr, "                 このオプションを指定しなければ、レジストリスクリプトを出力しません。\n");
//}}2017/08/26変更:'-t',及び,'-p'オプションを廃止しました。
	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/05/10追加:'-aloop','-aadd'オプションを追加しました。
	fprintf(stderr, "  -aloop         入力ファイル名にサフィックス'-loop'が含まれていたら、\"loop=1\"を自動出力します。\n");
	fprintf(stderr, "                 このオプションを指定しなければ、この属性を自動出力しません。\n");
	fprintf(stderr, "  -aadd          入力ファイル名にサフィックス'-add'が含まれていたら、\"add=1\"を自動出力します。\n");
	fprintf(stderr, "                 このオプションを指定しなければ、この属性を自動出力しません。\n");
//}}2017/05/10追加:'-aloop','-aadd'オプションを追加しました。
//}}2017/03/31追加:入力ファイル名のサフィックスによる属性の自動出力に対応しました。今回は自動スケーリング属性のサフィックス('-1x'～'-9x')だけに対応しました。今後必要に応じて増やして行く予定です。
//{{2017/07/02追加:'-m'オプションを追加しました。
	fprintf(stderr, "  -m[フォルダ名] スプライトのトリミングを行います。\n");
	fprintf(stderr, "                 このオプションを指定しなければ、スプライトのトリミングを行いません。\n");
	fprintf(stderr, "                 '-m'オプションが指定された場合、当ツールがテクスチャを読み込んで、透明な領域を判定します。\n");
	fprintf(stderr, "                 スクリプトで定義したテクスチャのパス名と、実際にテクスチャが存在するフォルダが異なる場合、'-m'オプションにフォルダ名を指定して下さい。\n");
	fprintf(stderr, "                 '-m'オプションにフォルダ名を指定した場合、指定されたフォルダからテクスチャをロードします。\n");
	fprintf(stderr, "                 '-m'オプションにフォルダ名を指定しなければ、カレントフォルダからテクスチャをロードします。\n");
//}}2017/07/02追加:'-m'オプションを追加しました。
	fprintf(stderr, "  -q             進捗表示を抑制します。\n");
	fprintf(stderr, "  -h             使い方を表示します。\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "EXAMPLE:\n");
//{{2017/08/26変更:'-t',及び,'-p'オプションを廃止しました。
//	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");
//↓2017/08/26変更:'-t',及び,'-p'オプションを廃止しました。
	fprintf(stderr, "  ①dTexDefC.exe -u -r TexDef.txt\n");
	fprintf(stderr, "  　TexDef.txtをコンパイルします。\n");
	fprintf(stderr, "  　列挙定義スクリプトとレジストリスクリプトを、標準出力へ出力します。\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");
//}}2017/08/26変更:'-t',及び,'-p'オプションを廃止しました。
	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 **/
			}
		}
//{{2017/08/25削除:リソースフラグを廃止しました。
//		//既存のテクスチャのリソースフラグに、今回のテクスチャのリソースフラグをマージする。
//		pTexHdr2->bResource	|= pTexHdr->bResource;
//}}2017/08/25削除:リソースフラグを廃止しました。
//{{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変更:テクスチャに汎用の属性リストを追加し、既存のループフラグも属性に含めました。
		//既存のテクスチャのスプライト配列に、今回のテクスチャのスプライト配列をマージする。
		{
//{{2017/07/04変更:一つのテクスチャ定義ブロック内で、グリッドサイズを切り替えて、複数のスプライト定義ブロックを書けるようにしました。			┐
//			pTexBody2 = realloc(pTexBody2, sizeof(ST_TexBody) + (sizeof pTexBody2->aSpr[0]) * (pTexBody2->nSpr + pTexBody->nSpr));		│
//			if(!pTexBody2) { DIE(); }													│2017/07/04コメント:本日追加したTexBody_addTexBody()関数は、この範囲の既存の処理を元にして作成しました。
//			memcpy(&pTexBody2->aSpr[pTexBody2->nSpr], &pTexBody->aSpr[0], (sizeof pTexBody2->aSpr[0]) * (pTexBody->nSpr));			│この範囲の既存の処理はそのままにしておいても構わなかったのですが、判り易さのためにTexBody_addTexBody()を呼び出すように変更しました。
//			pTexBody2->nSpr		+= pTexBody->nSpr;											│
//↓2017/07/04変更:一つのテクスチャ定義ブロック内で、グリッドサイズを切り替えて、複数のスプライト定義ブロックを書けるようにしました。			┘
			pTexBody2 = TexBody_addTexBody(pTexBody2, pTexBody);
//}}2017/07/04変更:一つのテクスチャ定義ブロック内で、グリッドサイズを切り替えて、複数のスプライト定義ブロックを書けるようにしました。
			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/08/25変更:リソースフラグを廃止しました。
ST_TexHdr* TexHdr_new(char* pTexLab/*片方のみNULL可*/, char* pPath/*片方のみNULL可*/, ST_AttrList* pAttrList) {
//}}2017/08/25変更:リソースフラグを廃止しました。
	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()を参照せよ。
//{{2017/08/25削除:リソースフラグを廃止しました。
//	pTexHdr->bResource	= bResource;
//}}2017/08/25削除:リソースフラグを廃止しました。
//{{2017/03/26変更:テクスチャに汎用の属性リストを追加し、既存のループフラグも属性に含めました。
//	pTexHdr->bLoop		= bLoop;
//↓2017/03/26変更:テクスチャに汎用の属性リストを追加し、既存のループフラグも属性に含めました。
	pTexHdr->pAttrList	= pAttrList;
//}}2017/03/26変更:テクスチャに汎用の属性リストを追加し、既存のループフラグも属性に含めました。
//{{2017/12/01追加:「'.'スプライト名」の書式を追加しました。
	sSprLabPrefix = pTexLab;
//}}2017/12/01追加:「'.'スプライト名」の書式を追加しました。
	return pTexHdr;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
//{{2017/07/05変更:グリッドの原点を指定出来るようにしました。
//ST_TexBody* TexBody_new(int iGridW, int iGridH) {
//↓2017/07/05変更:グリッドの原点を指定出来るようにしました。
ST_TexBody* TexBody_new() {
//}}2017/07/05変更:グリッドの原点を指定出来るようにしました。
	ST_TexBody* pTexBody = calloc(1, sizeof(ST_TexBody));
	if(!pTexBody) { DIE(); }
//{{2017/07/05削除:グリッドの原点を指定出来るようにしました。
//	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;
//}}2017/07/05削除:グリッドの原点を指定出来るようにしました。
	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;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
//{{2017/07/04追加:一つのテクスチャ定義ブロック内で、グリッドサイズを切り替えて、複数のスプライト定義ブロックを書けるようにしました。
ST_TexBody* TexBody_addTexBody(ST_TexBody* pTexBodyTo, ST_TexBody* pTexBodyFrom) {
	//pTexBodyToのスプライト配列に、pTexBodyFromのスプライト配列をマージする。
	pTexBodyTo = realloc(pTexBodyTo, sizeof(ST_TexBody) + (sizeof pTexBodyTo->aSpr[0]) * (pTexBodyTo->nSpr + pTexBodyFrom->nSpr));
	if(!pTexBodyTo) { DIE(); }
	memcpy(&pTexBodyTo->aSpr[pTexBodyTo->nSpr], &pTexBodyFrom->aSpr[0], (sizeof pTexBodyTo->aSpr[0]) * (pTexBodyFrom->nSpr));
	pTexBodyTo->nSpr	+= pTexBodyFrom->nSpr;
	return pTexBodyTo;
}
//}}2017/07/04追加:一つのテクスチャ定義ブロック内で、グリッドサイズを切り替えて、複数のスプライト定義ブロックを書けるようにしました。
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
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/12/01追加:「'.'スプライト名」の書式を追加しました。
//char* SprLab_AddPrefix(char* pDotLab) {
//	return strjoin("_", sSprLabPrefix, pDotLab, NULL);
//}
//↓2018/07/28コメント変更:テクスチャ名とスプライト名が同じ場合の簡略記法を追加しました。
char* SprLab_AddPrefix(char* pDotLab/*NULL可*/) {
	return strjoin("_", sSprLabPrefix, pDotLab, NULL);	//(pDotLab=NULL)で呼び出された場合は、「strjoin("_",sSprLabPrefix,NULL,NULL)」となり、「strjoin("_",sSprLabPrefix,NULL)」と同じであり、sSprLabPrefixと同じ文字列を返します。
}
//}}2018/07/28コメント変更:テクスチャ名とスプライト名が同じ場合の簡略記法を追加しました。
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
//{{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;
	}
//{{2017/05/10変更:同じ種類で同じ名前の属性が複数回指定されても、同じ値ならばエラーにしないようにしました。
//	//同じ種類で同じ名前の属性が登録されていない事を確認する。
//	if(g_tree_lookup_extended(pTree, pAttr->pAttrName, NULL, NULL)) {
//		yyerror("同じ種類で同じ名前の属性が登録されています。(%s)", pAttr->pAttrName);
//		yynerrs++;	//呼び出し元(main)に知らせるために。	//必要。YYERRORマクロ呼び出しでyynerrsは増やされないようだ。	←←←←←←←←←←■■■■重要■■■■
//		return NULL;	//y.tab.yの処理でYYERRORを行う。
//	}
//↓2017/05/10変更:同じ種類で同じ名前の属性が複数回指定されても、同じ値ならばエラーにしないようにしました。
	//同じ種類で同じ名前で違う値の属性が登録されていない事を確認する。
	{
		//同じ種類で同じ名前の属性が有れば…	上でツリーを選択しているので、見つかったら同じ種類である事は自明です。
		ST_Attr* pAttr2 = g_tree_lookup(pTree, pAttr->pAttrName);
		if(pAttr2) {
			//同じ値ならば属性リストを変更せずに返す。
			switch(pAttr->iAttrType) {
			default:DIE();
			case 0/*数値*/:   if(        pAttr2->iVal == pAttr->iVal ) { return pAttrList; } break;
			case 1/*文字列*/: if(!strcmp(pAttr2->pStr,   pAttr->pStr)) { return pAttrList; } break;
			}
			//同じ値でなければ、エラーとする。
			yyerror("同じ種類で同じ名前で違う値の属性が登録されています。(%s)", pAttr->pAttrName);
			yynerrs++;	//呼び出し元(main)に知らせるために。	//必要。YYERRORマクロ呼び出しでyynerrsは増やされないようだ。	←←←←←←←←←←■■■■重要■■■■
			return NULL;	//y.tab.yの処理でYYERRORを行う。
		}
	}
//}}2017/05/10変更:同じ種類で同じ名前の属性が複数回指定されても、同じ値ならばエラーにしないようにしました。
	//属性を登録する。
	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/05/10追加:'-aloop','-aadd'オプションを追加しました。
					//入力ファイル名にサフィックス'-add'が含まれていたら、"add=1"を自動出力します。
					if(atrs & (1<<ATRS_ADD)) {
						if(!strcmp(suffix, "add")) {
							ST_Attr* pAttr = Attr_New("add", 0/*数値*/, 1);
							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がそのまま返されるはずです。もしここで停止したら当モジュールのバグです。
						}
					}
					//入力ファイル名にサフィックス'-loop'が含まれていたら、"loop=1"を自動出力します。
					if(atrs & (1<<ATRS_LOOP)) {
						if(!strcmp(suffix, "loop")) {
							ST_Attr* pAttr = Attr_New("loop", 0/*数値*/, 1);
							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/05/10追加:'-aloop','-aadd'オプションを追加しました。
				}
			}
//}}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;
		}
	}
//{{2017/07/02追加:'-m'オプションを追加しました。
	//'-m'オプションが指定されていたら…
	if(opt_m) {
		ST_Tex* save_pTex = NULL;
		void* save_bitmap = NULL;
		//GDI+を初期化する。
		Gdiplus_Init();
		{
			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)) {
				//幅,又は,高さが0であるスプライトは、'-m'オプションの対象としない。
				if(pSpr->w && pSpr->h) {
//{{2017/08/08追加:スクリプトでテクスチャ定義の途中のgridに誤りが有り,かつ,'-m'を指定した場合に、正しくエラーを表示して終了せずに、一般保護エラーで終了してしまっていたバグを修正しました。
					// * Tue Aug 08 21:44:58 JST 2017 Naoyuki Sawa
					// - スクリプトでテクスチャ定義の途中のgridに誤りが有り,かつ,'-m'を指定した場合に、正しくエラーを表示して終了せずに、一般保護エラーで終了してしまっていたバグを修正しました。
					//   例えば、以下のような誤りを含んだスクリプトに対して…
					//   　│PAT "pat-2m.bmp"              ①
					//   　│  grid      1,1
					//   　│  FOO   0,0                   ②
					//   　│  grid  0,2,2,0/*h=0不可!!*/  ③
					//   　│  BAR   0,0
					//   　│end
					//   以下のように実行すると…
					//   　│dTexDefC.exe -m test.txt
					//   (pSpr->pTexLab==NULL)のままでここへ来てしまい、この下の'strlen(pSpr->pTexLab)'で一般保護エラーが発生してしまっていました。
					//   なぜなら、誤りを含んだスクリプトを処理した場合に、以下のような処理の流れになる事が有ったからです。
					//   前述の誤りを含んだスクリプトで、②の時点までは正常に実行されて、TexBody_addSpr()にてht_sprにスプライト'FOO'が登録されます。
					//   その後、③の時点でエラーを検出し、テクスチャ'PAT'の処理は打ち切られてaddTex()は呼び出されず、テクスチャ'PAT'はht_texに登録されません。
					//   すると、pass2()の前半の「各スプライトに、スプライトが属するテクスチャのテクスチャ名を格納する。」の処理において、スプライト'FOO'のpSpr->pTexLabは格納されません。
					//   上記の処理は、ht_texに登録されているテクスチャから辿って行くので、ht_texに登録されなかったテクスチャ'PAT'に属するスプライト'FOO'は辿られないからです。
					//   結果として、スプライト'FOO'の(pSpr->pTexLab==NULL)のままでここへ来てしまい、この下の'strlen(pSpr->pTexLab)'で一般保護エラーが発生してしまっていました。
					//   既に③にてyynerrsが増やされているので、最終的にエラーになる事に違い無いのですが、エラーメッセージを表示して終了せずに一般保護エラーで終了していたのが問題でした。
					//   根本的な原因としては、テクスチャ定義ブロック全体をエラー無くパースするよりも先に、個々のスプライトをht_texに登録してしまっていたのが良くなかったのですが、今から処理を大きく変えるのはエンバグが怖いので、小さな変更で対処する事にしました。q
					//   具体的には、ここで(pSpr->pTexLab==NULL)だった場合は、処理を飛ばして一般保護エラーを回避する事にしました。
					//   根本的な対策ではありませんが、動作上は、この対策でも問題無いはずです。
					//   尚、前述の通り、最終的にエラーになる事に違いは無く、これまでの動作でも、誤った結果が出力されていた訳ではありません。
					//   今回の修正の理由は、(見栄えの悪い)一般保護エラーを回避するためだけです。
					if(pSpr->pTexLab) {	//スプライトが属するテクスチャのテクスチャ名が格納されている場合のみ、この処理を行う。スプライトが属するテクスチャのテクスチャ名が格納されていない場合は、最終的にはエラー停止するが、ここへは来てしまうので、一般保護エラーを回避するためにここでは処理を飛ばす必要が有る。
//}}2017/08/08追加:スクリプトでテクスチャ定義の途中のgridに誤りが有り,かつ,'-m'を指定した場合に、正しくエラーを表示して終了せずに、一般保護エラーで終了してしまっていたバグを修正しました。
						//このスプライトが属するテクスチャを取得する。
						ST_Tex* pTex = ght_get(ht_tex,
							strlen(pSpr->pTexLab) + 1/*nul*/,
							       pSpr->pTexLab);
						if(!pTex) { DIE(); }	//当ツールのバグ
						{
							ST_TexHdr*  pTexHdr  = pTex->pTexHdr;
							ST_TexBody* pTexBody = pTex->pTexBody;
//{{2017/08/05追加:スクリプトでテクスチャのファイル名を設定を忘れ,かつ,'-m'を指定した場合に、正しくエラーを表示して終了せずに、一般保護エラーで終了してしまっていたバグを修正しました。
							if(pTexHdr->pPath) {	//テクスチャのファイル名が設定されている場合のみ、この処理を行う。テクスチャのファイル名が設定されていない場合は最終的にはエラー停止するが、この上の処理では全てのセマンティクスエラーを報告するために継続しているので、ここでももう一度確認する必要が有る。
//}}2017/08/05追加:スクリプトでテクスチャのファイル名を設定を忘れ,かつ,'-m'を指定した場合に、正しくエラーを表示して終了せずに、一般保護エラーで終了してしまっていたバグを修正しました。
								char path[MAX_PATH], ext[MAX_EXT];
								int trspMode;	//0=P/ECE環境,1=Win32環境,及び,汎用
								void* bitmap;
								_makepath(path, NULL, opt_m, pTexHdr->pPath, NULL);
								_splitpath(pTexHdr->pPath, NULL, NULL, NULL, ext);
								if(!strcasecmp(ext, ".bmp")) {		//bmp形式ならば…
									//bmp形式ならば、P/ECE環境と判断して、グレースケール以外を透明と見なす。
									trspMode = 0;
								} else if(!strcasecmp(ext, ".png")) {	//png形式ならば…
									//bmp形式以外(pngやgif等)ならば、Win32環境,及び,汎用と判断して、ビットマップのアルファ値が0ならば透明と見なす。
									trspMode = 1;
								} else {
									//それ以外の形式(usm等)ならば、'-m'オプションの対象としない。
									continue;	//ここまで
								}
								//前回のテクスチャと同じならば…
								if(pTex == save_pTex) {
									if(!save_pTex || !save_bitmap) { DIE(); }	//当ツールのバグ
									//前回のビットマップを引き続き使用する。
									bitmap = save_bitmap;
								//前回のテクスチャと同じでなければ…
								} else {
									//前回のビットマップが有れば、削除する。
									if(save_bitmap) { Bitmap_Free(save_bitmap); }
									//今回のテクスチャの、ビットマップをロードする。
									bitmap = Bitmap_Load(path);
									if(!bitmap) { error(EXIT_FAILURE, 0, "%s がロード出来ません。", path); }
									//次回のために、今回のテクスチャとビットマップを保持する。
									save_pTex   = pTex;
									save_bitmap = bitmap;
								}
								{
									int tex_w, tex_h, min_x, min_y, max_x, max_y, x, y;
									//テクスチャのサイズを取得する。
									if((tex_w = Bitmap_GetWidth( bitmap)) == -1) { error(EXIT_FAILURE, 0, "%s の幅が取得出来ません。",   path); }
									if((tex_h = Bitmap_GetHeight(bitmap)) == -1) { error(EXIT_FAILURE, 0, "%s の高さが取得出来ません。", path); }
									//このスプライトの範囲で、透明でない最小と最大のピクセル位置を求める。
									min_x = min_y = INT_MAX;
									max_x = max_y = INT_MIN;
									for(x = pSpr->x; x <= pSpr->x + pSpr->w - 1; x++) {
										if((unsigned)x < (unsigned)tex_w) {	//テクスチャの外は透明と見なす。
											for(y = pSpr->y; y <= pSpr->y + pSpr->h - 1; y++) {
												if((unsigned)y < (unsigned)tex_h) {	//テクスチャの外は透明と見なす。
													int r, g, b, a;
													if(Bitmap_GetPixel(bitmap, x, y, &r, &g, &b, &a)) { error(EXIT_FAILURE, 0, "%s のピクセルが取得出来ません。", path); }
													if(trspMode == 0) { a = (r == g) && (g == b); }	//bmp形式ならば、P/ECE環境と判断して、グレースケール以外を透明と見なす。
													if(a) {
														if(x < min_x) { min_x = x; }
														if(x > max_x) { max_x = x; }
														if(y < min_y) { min_y = y; }
														if(y > max_y) { max_y = y; }
													}
												}
											}
										}
									}
									//透明でないピクセルが一つでも有れば…
									if((min_x <= max_x) && (min_y <= max_y)) {
										int trim_left, trim_top, trim_right, trim_bottom;
										if(((min_x < pSpr->x) || (min_x > pSpr->x + pSpr->w - 1)) ||
										   ((min_y < pSpr->y) || (min_y > pSpr->y + pSpr->h - 1)) ||
										   ((max_x < pSpr->x) || (max_x > pSpr->x + pSpr->w - 1)) ||
										   ((max_y < pSpr->y) || (max_y > pSpr->y + pSpr->h - 1))) { DIE(); }	//当ツールのバグ
										trim_left   = min_x - pSpr->x;
										trim_top    = min_y - pSpr->y;
										trim_right  = pSpr->x + pSpr->w - 1 - max_x;
										trim_bottom = pSpr->y + pSpr->h - 1 - max_y;
										if((trim_left   < 0) ||
										   (trim_top    < 0) ||
										   (trim_right  < 0) ||
										   (trim_bottom < 0)) { DIE(); }	//当ツールのバグ
										pSpr->x  += trim_left;			//┐
										pSpr->w  -= trim_left + trim_right;	//├左右トリミング
										pSpr->cx -= trim_left;			//┘
										pSpr->y  += trim_top;			//┐
										pSpr->h  -= trim_top + trim_bottom;	//├上下トリミング
										pSpr->cy -= trim_top;			//┘
										if((pSpr->w <= 0) ||
										   (pSpr->h <= 0)) { DIE(); }		//当ツールのバグ
									//透明でないピクセルが一つも無ければ…
									} else {
										if((min_x != INT_MAX) ||
										   (max_x != INT_MIN) ||
										   (min_y != INT_MAX) ||
										   (max_y != INT_MIN)) { DIE(); }	//当ツールのバグ
										pSpr->w = 0;
										pSpr->h = 0;
									}
								}
//{{2017/08/05追加:スクリプトでテクスチャのファイル名を設定を忘れ,かつ,'-m'を指定した場合に、正しくエラーを表示して終了せずに、一般保護エラーで終了してしまっていたバグを修正しました。
							}
//}}2017/08/05追加:スクリプトでテクスチャのファイル名を設定を忘れ,かつ,'-m'を指定した場合に、正しくエラーを表示して終了せずに、一般保護エラーで終了してしまっていたバグを修正しました。
						}
//{{2017/08/08追加:スクリプトでテクスチャ定義の途中のgridに誤りが有り,かつ,'-m'を指定した場合に、正しくエラーを表示して終了せずに、一般保護エラーで終了してしまっていたバグを修正しました。
					}
//}}2017/08/08追加:スクリプトでテクスチャ定義の途中のgridに誤りが有り,かつ,'-m'を指定した場合に、正しくエラーを表示して終了せずに、一般保護エラーで終了してしまっていたバグを修正しました。
				}
			}
		}
		//前回のビットマップが有れば、削除する。
		if(save_bitmap) { Bitmap_Free(save_bitmap); }
		//GDI+を終了する。
		Gdiplus_Exit();
	}
//}}2017/07/02追加:'-m'オプションを追加しました。
}
/*---------------------------------------------------------------------------*/
//出力処理
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
//{{2017/08/26変更:'-t',及び,'-p'オプションを廃止しました。
//static void output(const char* sTexOut, const char* sSprOut, const char* sEnmOut, const char* sRegOut) {
//↓2017/08/26変更:'-t',及び,'-p'オプションを廃止しました。
static void output(const char* sEnmOut, const char* sRegOut) {
//}}2017/08/26変更:'-t',及び,'-p'オプションを廃止しました。
	int nOut = 0;
//{{2017/08/26削除:'-t',及び,'-p'オプションを廃止しました。
//	//テクスチャ定義ヘッダのファイル名,又は,"-"(標準出力)が指定されていたら…
//	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++;
//	}
//}}2017/08/26削除:'-t',及び,'-p'オプションを廃止しました。
	//列挙定義スクリプトのファイル名,又は,"-"(標準出力)を指定されていたら…
	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) {
//{{2017/08/26変更:'-t',及び,'-p'オプションを廃止しました。
//		fprintf(stderr, "-t/-p/-u/-rオプションが指定されなかったので、出力を行いませんでした。\n");
//↓2017/08/26変更:'-t',及び,'-p'オプションを廃止しました。
		fprintf(stderr, "-u/-rオプションが指定されなかったので、出力を行いませんでした。\n");
//}}2017/08/26変更:'-t',及び,'-p'オプションを廃止しました。
	}
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
//{{2017/08/26削除:'-t',及び,'-p'オプションを廃止しました。
////テクスチャ定義ヘッダを出力する。
//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;
////{{2017/08/25削除:リソースフラグを廃止しました。
////		if(pTexHdr->bResource) {
////}}2017/08/25削除:リソースフラグを廃止しました。
//			//サービスとして、パス名をP/ECEリソースに適したファイル名に変換して出力する。
//			char fname[MAX_FNAME];
//			splitpath(pTexHdr->pPath, NULL, NULL, fname, NULL);
//			fprintf(fp, "TEXTURE_RESOURCE_(%s,\"%s.pgd\")\n",
//				pTexLab,
//				fname);
////{{2017/08/25削除:リソースフラグを廃止しました。
////		} else {
////			fprintf(fp, "TEXTURE_(%s)\n",
////				pTexLab);
////		}
////}}2017/08/25削除:リソースフラグを廃止しました。
//	}
//}
///*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
////スプライト定義ヘッダを出力する。
//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);
//	}
//}
//}}2017/08/26削除:'-t',及び,'-p'オプションを廃止しました。
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
//列挙定義スクリプトを出力する。
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");
	}
}
