/*
 *	clipenm.c
 *
 *	列挙定義
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2016 Naoyuki Sawa
 *
 *	* Fri Jan 01 21:14:34 JST 2016 Naoyuki Sawa
 *	- 1st リリース。
 *	* Sat Jan 02 21:26:28 JST 2016 Naoyuki Sawa
 *	- 使用例を追記しました。
 *	* Wed May 11 21:35:57 JST 2016 Naoyuki Sawa
 *	- EnmDef_GetVal()を追加しました。
 *	* Thu May 12 22:56:34 JST 2016 Naoyuki Sawa
 *	- EnmDef_GetNum(),EnmDef_GetMin(),EnmDef_GetMax(),EnmDef_GetNth(),EnmDef_GetIdx()を追加しました。
 *	* Thu Jul 07 21:12:07 JST 2016 Naoyuki Sawa
 *	- EnmDef_IncVal(),EnmDef_DecVal()を追加しました。
 *	* Mon Aug 15 21:37:25 JST 2016 Naoyuki Sawa
 *	- 列挙型の名前の比較関数を用いて、列挙型の名前の文字列から列挙型の値を逆引きするユーティリティ関数群を追加しました。
 *	  詳細は、EnmDef_GetVal_UseCompar(),EnmDef_GetCanonicalNameCompar(),EnmDef_IsSuitableNameCompar()のコメントを参照して下さい。
 *	* Mon Sep 05 21:24:05 JST 2016 Naoyuki Sawa
 *	- (int)値を(void*)にキャストして整数キーとして使用する箇所で、64ビット版ではコンパイラの警告が出ていた問題を回避しました。
 *	  詳細は、EnmDef_GetIdx_compar(),EnmDef_GetIdx(),EnmDef_GetVal_UseCompar_compar(),EnmDef_GetVal_UseCompar()内のコメントを参照して下さい。
 */
#include "clip.h"
/****************************************************************************
 *	基本的な関数
 ****************************************************************************/
//名前の文字列を返す。
const char* EnmDef_GetName(const void* pEnmKey, int iEnmDef, int iVal) {
	const void* pEnmDef;
	const char* pName;
	//列挙型の値のキーを取得する。
	pEnmDef = REG_open_key_l(pEnmKey, iEnmDef, iVal, -1);
	if(!pEnmDef) { DIE(); }		//列挙型の値のキーが無ければエラー。iEnmDef,又は,iValが間違っている。多分呼び出し側のバグ。
	//名前の文字列を取得する。
	pName = REG_get_string(pEnmDef, Enm_name);
	if(!pName) { DIE(); }		//名前の文字列が無ければエラー。dEnmDefC.exeは名前の文字列を必ず格納しているはず。多分データ破損,又は,ツールバグ。
	//名前の文字列を返す。
	return pName;
}
/*--------------------------------------------------------------------------*/
//説明文の文字列を返す。説明文の文字列が格納されていなければNULLを返す。
const char* EnmDef_GetDesc(const void* pEnmKey, int iEnmDef, int iVal) {
	const void* pEnmDef;
	const char* pDesc;
	//列挙型の値のキーを取得する。
	pEnmDef = REG_open_key_l(pEnmKey, iEnmDef, iVal, -1);
	if(!pEnmDef) { DIE(); }		//列挙型の値のキーが無ければエラー。iEnmDef,又は,iValが間違っている。多分呼び出し側のバグ。
	//説明文の文字列を取得する。
	pDesc = REG_get_string(pEnmDef, Enm_desc);
//不要	if(!pDesc) { DIE(); }		//説明文の文字列は無い可能性も有る。列挙定義スクリプトにて説明文が指定されなかった場合はdEnmDefC.exeは説明文の文字列を格納しない。
	//説明文の文字列を返す。説明文の文字列が格納されていなければNULLを返す。
	return pDesc;
}
/*--------------------------------------------------------------------------*/
//説明文の文字列を返す。説明文の文字列が格納されていなければ名前の文字列を返す。
const char* EnmDef_ToStr(const void* pEnmKey, int iEnmDef, int iVal) {
	const void* pEnmDef;
	const char* pStr;
	//列挙型の値のキーを取得する。
	pEnmDef = REG_open_key_l(pEnmKey, iEnmDef, iVal, -1);
	if(!pEnmDef) { DIE(); }	//列挙型の値のキーが無ければエラー。iEnmDef,又は,iValが間違っている。多分呼び出し側のバグ。
	//説明文の文字列を取得する。
	pStr = REG_get_string(pEnmDef, Enm_desc);
	//説明文の文字列が格納されていなければ…
	if(!pStr) {			//説明文の文字列は無い可能性も有る。列挙定義スクリプトにて説明文が指定されなかった場合はdEnmDefC.exeは説明文の文字列を格納しない。
		//名前の文字列を取得する。
		pStr = REG_get_string(pEnmDef, Enm_name);
		if(!pStr) { DIE(); }	//名前の文字列が無ければエラー。dEnmDefC.exeは名前の文字列を必ず格納しているはず。多分データ破損,又は,ツールバグ。
	}
	//説明文の文字列を返す。説明文の文字列が格納されていなければ名前の文字列を返す。
	return pStr;
}
/****************************************************************************
 *	ユーティリティ関数
 ****************************************************************************/
//列挙型に属する値の数を返す。
int EnmDef_GetNum(const void* pEnmKey, int iEnmDef) {
	int n = REG_num_key_l(pEnmKey, iEnmDef, -1);	//REG_num_value_l()ではない事に注意せよ。列挙型のレジストリ構造はpEnmKey⇒列挙型のキー⇒列挙型の値のキー⇒値の名前[,説明文]という構造である。
	if(!n) { DIE(); }	//列挙型の値のキーが無ければエラー。iEnmDefが間違っている。多分呼び出し側のバグ。
	return n;
}
/*--------------------------------------------------------------------------*/
//列挙型に属する値のうち、最小値を返す。
int EnmDef_GetMin(const void* pEnmKey, int iEnmDef) {
	return EnmDef_GetNth(pEnmKey, iEnmDef, 0);
}
/*--------------------------------------------------------------------------*/
//列挙型に属する値のうち、最大値を返す。
int EnmDef_GetMax(const void* pEnmKey, int iEnmDef) {
	return EnmDef_GetNth(pEnmKey, iEnmDef,
	       EnmDef_GetNum(pEnmKey, iEnmDef) - 1);
}
/*--------------------------------------------------------------------------*/
//列挙型に属する値のうち、index番目の値を返す。index番目の値が無ければ、エラー停止する。
int EnmDef_GetNth(const void* pEnmKey, int iEnmDef, int index) {
	int iVal;
	if(!REG_open_nth_key_l(pEnmKey, index, &iVal, iEnmDef, -1)) {	//REG_get_nth_value_l()ではない事に注意せよ。列挙型のレジストリ構造はpEnmKey⇒列挙型のキー⇒列挙型の値のキー⇒値の名前[,説明文]という構造である。
		DIE();	//列挙型の値のキーが無ければエラー。iEnmDef,又は,indexが間違っている。多分呼び出し側のバグ。
	}
	return iVal;
}
/*--------------------------------------------------------------------------*/
typedef struct _ST_EnmDef_GetIdx_Param {
	const void*	pEnmKey;
	int		iEnmDef;
} ST_EnmDef_GetIdx_Param;
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static int EnmDef_GetIdx_compar(const void* key, const void* data, void* arg) {
	ST_EnmDef_GetIdx_Param* param = arg;
//{{2016/09/05変更:(int)値を(void*)にキャストして整数キーとして使用する箇所で、64ビット版ではコンパイラの警告が出ていた問題を回避しました。
//	int iVal1 = (int)key;
//	int index = (int)data - 1/*オフセット*/;
//↓2016/09/05変更:(int)値を(void*)にキャストして整数キーとして使用する箇所で、64ビット版ではコンパイラの警告が出ていた問題を回避しました。
#ifndef _WIN64
	int iVal1 = (int)key;				//┬32ビット版は、これまでと変更有りません。32ビット版も64ビット版と同じ書き方にしても動作は変わらないのですが、簡潔さを優先してこれまでのままとする事にしました。
	int index = (int)data - 1/*オフセット*/;	//┘
#else //_WIN64
	int iVal1 = (intptr_t)key;			//┬64ビット版は、(void*)(64ビット)⇒(int)(32ビット)にキャストしようとすると警告が出ます。元の値が32ビットの整数パラメータなので(int)にキャストしても安全ではあるのですが、警告を回避するために(void*)(64ビット)⇒(intptr_t)(64ビット)⇒暗黙の(int)(32ビット)へのキャスト、という手順を取る事にしました。
	int index = (intptr_t)data - 1/*オフセット*/;	//┘
#endif//_WIN64
//}}2016/09/05変更:(int)値を(void*)にキャストして整数キーとして使用する箇所で、64ビット版ではコンパイラの警告が出ていた問題を回避しました。
	int iVal2 = EnmDef_GetNth(param->pEnmKey, param->iEnmDef, index);
	return iVal1 - iVal2;	//列挙型の値(=レジストリキーの名前)は符号無し16ビットなので、オーバーフローの心配は無く単純な減算結果を返せば良い。
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//指定された値が、この列挙型における何番目の値であるかを逆引きする。この列挙型に属する値でなければ、(-1)を返す。
// - この操作は列挙型だけでなく、レジストリ一般にも(ほぼそのままで)適用出来る操作なのだが、敢えてclipreg.cには実装せず、列挙型特有の操作とする事にした。
//   そうした理由は、以下の通りです。
//   ・もしclipreg.cに実装すると、関数の対称性上、キーだけでなく値,文字列,BLOBも検索出来るようにすべきであり、関数の数が増えてコードサイズが増大する問題が有るから。
//   ・何番目の値であるかを検索するのは、列挙型以外では(まず)必要無いと思うので、clipreg.cを無駄に複雑化するよりも、列挙型特有の操作にする方が判り易いと思ったから。
int EnmDef_GetIdx(const void* pEnmKey, int iEnmDef, int iVal) {
	ST_EnmDef_GetIdx_Param param = { pEnmKey, iEnmDef };
	//bsearch()を番号に対して使用するテクニックです。アドレスをインデクスと見なして(index=0〜(n-1))に対して検索を行います。厳密にはインデクスを+1オフセットして扱います。詳細は下記のコメントを参照して下さい。
//{{2016/09/05変更:(int)値を(void*)にキャストして整数キーとして使用する箇所で、64ビット版ではコンパイラの警告が出ていた問題を回避しました。
//	return (int)bsearch_r((void*)iVal,
//		(void*)(0/*index*/ + 1/*オフセット*/),	//0番目の値に一致した時のbsearch_r()の戻り値と、どの値にも一致しなかった時のbsearch_r()の戻り値NULL(0)を区別するために、インデクスを+1オフセットして扱う事にした。
//		EnmDef_GetNum(pEnmKey, iEnmDef),
//		1,
//		EnmDef_GetIdx_compar,
//		&param) - 1/*オフセット*/;		//インデクスの+1オフセットを相殺する。どの値にも一致しなかった場合は、bsearch_r()の戻り値がNULL(0)となり、ここで-1する事で当関数の戻り値(-1)となる。
//↓2016/09/05変更:(int)値を(void*)にキャストして整数キーとして使用する箇所で、64ビット版ではコンパイラの警告が出ていた問題を回避しました。
#ifndef _WIN64
	return (int)bsearch_r(				//32ビット版は、これまでと変更有りません。32ビット版も64ビット版と同じ書き方にしても動作は変わらないのですが、簡潔さを優先してこれまでのままとする事にしました。
		(void*)iVal,				//32ビット版は、これまでと変更有りません。32ビット版も64ビット版と同じ書き方にしても動作は変わらないのですが、簡潔さを優先してこれまでのままとする事にしました。
		(void*)(0/*index*/ + 1/*オフセット*/),	//0番目の値に一致した時のbsearch_r()の戻り値と、どの値にも一致しなかった時のbsearch_r()の戻り値NULL(0)を区別するために、インデクスを+1オフセットして扱う事にした。
		EnmDef_GetNum(pEnmKey, iEnmDef),
		1,
		EnmDef_GetIdx_compar,
		&param) - 1/*オフセット*/;		//インデクスの+1オフセットを相殺する。どの値にも一致しなかった場合は、bsearch_r()の戻り値がNULL(0)となり、ここで-1する事で当関数の戻り値(-1)となる。
#else //_WIN64
	return (intptr_t)bsearch_r(			//64ビット版は、(void*)(64ビット)⇒(int)(32ビット)にキャストしようとすると警告が出ます。元の値が32ビットの整数パラメータなので(int)にキャストしても安全ではあるのですが、警告を回避するために(void*)(64ビット)⇒(intptr_t)(64ビット)⇒暗黙の(int)(32ビット)へのキャスト、という手順を取る事にしました。
		(void*)(intptr_t)iVal,			//64ビット版は、(int)(32ビット)⇒(void*)(64ビット)に直接キャストしようとすると警告が出ます。(int)(32ビット)⇒(intptr_t)(64ビット)⇒(void*)(64ビット)の順でキャストすれば警告は出ません。
		(void*)(0/*index*/ + 1/*オフセット*/),	//0番目の値に一致した時のbsearch_r()の戻り値と、どの値にも一致しなかった時のbsearch_r()の戻り値NULL(0)を区別するために、インデクスを+1オフセットして扱う事にした。
		EnmDef_GetNum(pEnmKey, iEnmDef),
		1,
		EnmDef_GetIdx_compar,
		&param) - 1/*オフセット*/;		//インデクスの+1オフセットを相殺する。どの値にも一致しなかった場合は、bsearch_r()の戻り値がNULL(0)となり、ここで-1する事で当関数の戻り値(-1)となる。
#endif//_WIN64
//}}2016/09/05変更:(int)値を(void*)にキャストして整数キーとして使用する箇所で、64ビット版ではコンパイラの警告が出ていた問題を回避しました。
}
/*--------------------------------------------------------------------------*/
//列挙型の名前の文字列から、列挙型の値を逆引きする。指定された名前の文字列に一致する値が無かったら、(-1)を返す。
// - この関数は単純なリニアサーチを行うので低速です。
//   値の数が多い列挙型に対して当関数使用すると、極端に長い時間がかかる恐れが有るので注意して下さい。
// - この関数がリニアサーチを行っている理由は、列挙型の値の順序と名前の並び順序に関する決まりは無いからです。
//   例えば、1:AAA,2:CCC,3:BBB,...のような並びである可能性もあるので、単純にリニアサーチするしか有りません。
// - もし、特定の列挙型の名前の並び順が想定出来る場合は、当関数を使わずに、個別に処理する方が高速化出来ます。
//   例えば、スプライトの列挙型に対する名前⇒値の逆引きは、スプライト名が昇順で有る事を利用して高速化出来ます。
//   その実装例は、clipafxb.cのAfxBin_Draw_GetFootage()を参照して下さい。
// - 結論としては、当関数は、速度が重要でないケースに対してのみ、ユーティリティー関数として利用して下さい。
//   <使用例>
//   │int iVal = EnmDef_GetVal(pEnmKey, RegKey_Color, "Black");
//   │if(iVal != -1) { printf("'Black'の値は%dです。", iVal); }
int EnmDef_GetVal(const void* pEnmKey, int iEnmDef, const char* pName) {
	int index, n = EnmDef_GetNum(pEnmKey, iEnmDef);
	for(index = 0; index < n; index++) {
		int iVal = EnmDef_GetNth(pEnmKey, iEnmDef, index);
		if(!strcmp(EnmDef_GetName(pEnmKey, iEnmDef, iVal), pName)) { return iVal; }
	}
	return -1;
}
/*--------------------------------------------------------------------------*/
//この列挙型における、指定された値の次の値を取得する。
//指定された値が最大値ならば、指定された値をそのまま返す。
int EnmDef_IncVal(const void* pEnmKey, int iEnmDef, int iVal) {
	int index = EnmDef_GetIdx(pEnmKey, iEnmDef, iVal);
	if(index == -1) { DIE(); }	//指定された値が、この列挙型に属する値でなければ、エラー停止する。
	if(index < EnmDef_GetNum(pEnmKey, iEnmDef) - 1) {
		iVal = EnmDef_GetNth(pEnmKey, iEnmDef, index + 1);
	}
	return iVal;
}
/*--------------------------------------------------------------------------*/
//この列挙型における、指定された値の前の値を取得する。
//指定された値が最小値ならば、指定された値をそのまま返す。
int EnmDef_DecVal(const void* pEnmKey, int iEnmDef, int iVal) {
	int index = EnmDef_GetIdx(pEnmKey, iEnmDef, iVal);
	if(index == -1) { DIE(); }	//指定された値が、この列挙型に属する値でなければ、エラー停止する。
	if(index > 0) {
		iVal = EnmDef_GetNth(pEnmKey, iEnmDef, index - 1);
	}
	return iVal;
}
/*--------------------------------------------------------------------------*/
//{{2016/08/15追加:列挙型の名前の比較関数を用いて、列挙型の名前の文字列から列挙型の値を逆引きするユーティリティ関数群を追加しました。
typedef struct _ST_EnmDef_GetVal_UseCompar_Param {
	const void*	pEnmKey;
	int		iEnmDef;
	int		(*compar)(const char*, const char*);
} ST_EnmDef_GetVal_UseCompar_Param;
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static int EnmDef_GetVal_UseCompar_compar(const void* key, const void* data, void* arg) {
	ST_EnmDef_GetVal_UseCompar_Param* param = arg;
	const char* pName1 = key;
//{{2016/09/05変更:(int)値を(void*)にキャストして整数キーとして使用する箇所で、64ビット版ではコンパイラの警告が出ていた問題を回避しました。
//	int index          = (int)data - 1/*オフセット*/;
//↓2016/09/05変更:(int)値を(void*)にキャストして整数キーとして使用する箇所で、64ビット版ではコンパイラの警告が出ていた問題を回避しました。
#ifndef _WIN64
	int index          = (int)data - 1/*オフセット*/;	//32ビット版は、これまでと変更有りません。32ビット版も64ビット版と同じ書き方にしても動作は変わらないのですが、簡潔さを優先してこれまでのままとする事にしました。
#else //_WIN64
	int index          = (intptr_t)data - 1/*オフセット*/;	//64ビット版は、(void*)(64ビット)⇒(int)(32ビット)にキャストしようとすると警告が出ます。元の値が32ビットの整数パラメータなので(int)にキャストしても安全ではあるのですが、警告を回避するために(void*)(64ビット)⇒(intptr_t)(64ビット)⇒暗黙の(int)(32ビット)へのキャスト、という手順を取る事にしました。
#endif//_WIN64
//}}2016/09/05変更:(int)値を(void*)にキャストして整数キーとして使用する箇所で、64ビット版ではコンパイラの警告が出ていた問題を回避しました。
	int iVal           = EnmDef_GetNth(param->pEnmKey, param->iEnmDef, index);
	const char* pName2 = EnmDef_GetName(param->pEnmKey, param->iEnmDef, iVal);
	return (*param->compar)(pName1, pName2);
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//この列挙型の名前の比較関数として適切な比較関数を用いて、列挙型の名前の文字列から、列挙型の値を逆引きする。(EnmDef_GetVal()よりも高速です。)
//指定された名前の文字列に一致する値が無かったら、(-1)を返す。
//comparは、予めEnmDef_IsSuitableNameCompar(),又は,EnmDef_GetCanonicalNameCompar()で、取得しておいて下さい。
//又は、この列挙型の名前の並び順のルールが、アプリケーションにて既知ならば、特定の比較関数を決め打ちで指定しても構いません。
//□使用例
//│int (*compar)(const char*, const char*) = EnmDef_GetCanonicalNameCompar(pEnmKey, RegKey_SprNo); //実際には初期化時に一度だけ求めておく。
//│int iSprNo_Player = EnmDef_GetVal_UseCompar(pEnmKey, RegKey_SprNo, "PLAYER", compar, NULL);
//│int iSprNo_Enemy  = EnmDef_GetVal_UseCompar(pEnmKey, RegKey_SprNo, "ENEMY",  compar, NULL);
//│int iSprNo_Num1   = EnmDef_GetVal_UseCompar(pEnmKey, RegKey_SprNo, "NUM1",   compar, NULL);
//│int iSprNo_Num2   = EnmDef_GetVal_UseCompar(pEnmKey, RegKey_SprNo, "NUM2",   compar, NULL);
//│int iSprNo_Num3   = EnmDef_GetVal_UseCompar(pEnmKey, RegKey_SprNo, "NUM3",   compar, NULL);
int EnmDef_GetVal_UseCompar(const void* pEnmKey, int iEnmDef, const char* pName, int (*compar)(const char*, const char*)) {
	ST_EnmDef_GetVal_UseCompar_Param param = { pEnmKey, iEnmDef, compar };
	//アルゴリズムは、EnmDef_GetIdx()とほぼ同じです。
	//ただし、EnmDef_GetIdx()はインデクスを返すが、当関数は列挙値を返すという点が異なります。
//{{2016/09/05変更:(int)値を(void*)にキャストして整数キーとして使用する箇所で、64ビット版ではコンパイラの警告が出ていた問題を回避しました。
//	int index = (int)bsearch_r(pName,
//		(void*)(0/*index*/ + 1/*オフセット*/),	//0番目の値に一致した時のbsearch_r()の戻り値と、どの値にも一致しなかった時のbsearch_r()の戻り値NULL(0)を区別するために、インデクスを+1オフセットして扱う事にした。
//		EnmDef_GetNum(pEnmKey, iEnmDef),
//		1,
//		EnmDef_GetVal_UseCompar_compar,
//		&param) - 1/*オフセット*/;		//インデクスの+1オフセットを相殺する。どの値にも一致しなかった場合は、bsearch_r()の戻り値がNULL(0)となり、ここで-1する事で当関数の戻り値(-1)となる。
//↓2016/09/05変更:(int)値を(void*)にキャストして整数キーとして使用する箇所で、64ビット版ではコンパイラの警告が出ていた問題を回避しました。
#ifndef _WIN64
	int index = (int)bsearch_r(pName,		//32ビット版は、これまでと変更有りません。32ビット版も64ビット版と同じ書き方にしても動作は変わらないのですが、簡潔さを優先してこれまでのままとする事にしました。
		(void*)(0/*index*/ + 1/*オフセット*/),	//0番目の値に一致した時のbsearch_r()の戻り値と、どの値にも一致しなかった時のbsearch_r()の戻り値NULL(0)を区別するために、インデクスを+1オフセットして扱う事にした。
		EnmDef_GetNum(pEnmKey, iEnmDef),
		1,
		EnmDef_GetVal_UseCompar_compar,
		&param) - 1/*オフセット*/;		//インデクスの+1オフセットを相殺する。どの値にも一致しなかった場合は、bsearch_r()の戻り値がNULL(0)となり、ここで-1する事で当関数の戻り値(-1)となる。
#else //_WIN64
	int index = (intptr_t)bsearch_r(pName,		//64ビット版は、(void*)(64ビット)⇒(int)(32ビット)にキャストしようとすると警告が出ます。元の値が32ビットの整数パラメータなので(int)にキャストしても安全ではあるのですが、警告を回避するために(void*)(64ビット)⇒(intptr_t)(64ビット)⇒暗黙の(int)(32ビット)へのキャスト、という手順を取る事にしました。
		(void*)(0/*index*/ + 1/*オフセット*/),	//0番目の値に一致した時のbsearch_r()の戻り値と、どの値にも一致しなかった時のbsearch_r()の戻り値NULL(0)を区別するために、インデクスを+1オフセットして扱う事にした。
		EnmDef_GetNum(pEnmKey, iEnmDef),
		1,
		EnmDef_GetVal_UseCompar_compar,
		&param) - 1/*オフセット*/;		//インデクスの+1オフセットを相殺する。どの値にも一致しなかった場合は、bsearch_r()の戻り値がNULL(0)となり、ここで-1する事で当関数の戻り値(-1)となる。
#endif//_WIN64
//}}2016/09/05変更:(int)値を(void*)にキャストして整数キーとして使用する箇所で、64ビット版ではコンパイラの警告が出ていた問題を回避しました。
	return (index < 0) ? index/*=-1*/ : EnmDef_GetNth(pEnmKey, iEnmDef, index)/*≧0*/;
}
/*--------------------------------------------------------------------------*/
//この列挙型の名前の比較関数として適切な、標準的な比較関数を取得する。
//標準的な比較関数のいずれも適切でなかった場合は、NULLを返す。
//当関数がNULLを返した場合、続けて標準的な比較関数以外も比較関数の候補として調査する場合は、アプリケーションが明示的にEnmDef_IsSuitableNameCompar()を呼び出して比較関数を決定して下さい。
int (*EnmDef_GetCanonicalNameCompar(const void* pEnmKey, int iEnmDef))(const char*, const char*) {
	//標準的な比較関数の候補。追加可能です。
	static int (* const TBL_Compar[])(const char*, const char*) = {
		strcmp,		//大文字小文字を区別する。
		strcasecmp,	//大文字小文字を区別しない。
		strnatcmp,	//大文字小文字を区別する、自然順比較。
		strnatcasecmp,	//大文字小文字を区別しない、自然順比較。
		//※TODO:追加可能です。
	};
	int i;
	for(i = 0; i < ARRAY_SIZE(TBL_Compar); i++) {
		int (*compar)(const char*, const char*) = TBL_Compar[i];
		if(EnmDef_IsSuitableNameCompar(pEnmKey, iEnmDef, compar)) { return compar; }	//この比較関数が適切ならば、この比較関数を返す。
	}
	return NULL;	//標準的な比較関数のいずれも適切でなかった場合は、NULLを返す。
}
/*--------------------------------------------------------------------------*/
//指定された比較関数comparが、この列挙型の名前の比較関数として、適切であるかどうかを調べる。
//具体的には、この列挙型の全ての、N番目の値の名前pName1と、(N+1)番目の値の名前pName2に対して、((*compar)(pName1,pName2)<0)が成立するならば、比較関数comparが、この列挙型の名前の比較関数として、適切であると見なす。
//【注意】
//│dEnmDefC.exeが生成する列挙型の名前の並び順は、列挙値の昇順であり、必ずしも名前の並び順にルールが存在する訳ではない。
//│従って、この列挙型の名前の比較関数として適切な比較関数が、存在するとは限らない事に注意せよ。
//│列挙型の名前の並び順にルールが存在し得るのは、dTexDefC.exeやdPhrDefC.exe等のツールで生成した列挙定義スクリプトや、アプリケーションが意図的に名前の昇順に定義した列挙定義スクリプトの場合だけである。
int EnmDef_IsSuitableNameCompar(const void* pEnmKey, int iEnmDef, int (*compar)(const char*, const char*)) {
	int index, n = EnmDef_GetNum(pEnmKey, iEnmDef);
	for(index = 0; index < n; index++) {
		const char* pName1 = NULL;
		for(index = 0; index < n; index++) {
			int iVal           = EnmDef_GetNth(pEnmKey, iEnmDef, index);
			const char* pName2 = EnmDef_GetName(pEnmKey, iEnmDef, iVal);
			if(pName1) {
				if(((*compar)(pName1, pName2) >= 0) ||			//一か所でも((*compar)(pName1,pName2)<0)が成立しなければ、適切でないと見なして0を返す。
				   ((*compar)(pName2, pName1) <= 0)) { return 0; }	//本当は上の検査だけで十分なのですが、念のために比較順序を逆にしての検査も行う事にしました。万一、comparが常に-1を返すような関数だった場合に、誤って適切であると判断してしまう事を避けるためです。
			}
			pName1 = pName2;
		}
	}
	return 1;	//全ての箇所で((*compar)(pName1,pName2)<0)が成立するならば、適切であると見なして0以外の値を返す。
}
//}}2016/08/15追加:列挙型の名前の比較関数を用いて、列挙型の名前の文字列から列挙型の値を逆引きするユーティリティ関数群を追加しました。
/****************************************************************************
 *	使用例
 ****************************************************************************/
#if 0
□EnmDef.txt
│Color
│  White  "白"
│  GlayW  "灰(淡)"
│  GlayB  "灰(濃)"
│  Black  "黒"
│  Mask   "マスク"
│end
□↓dEnmDefC.exe -rRegEnmDef.txt EnmDef.txt > EnmDef.h
│#define Color_White  0  //白
│#define Color_GlayW  1  //灰(淡)
│#define Color_GlayB  2  //灰(濃)
│#define Color_Black  3  //黒
│#define Color_Mask   4  //マスク
│#define Color_Max    4
□↓dRegTblC.exe -pRegKey_ -oRegTbl.bin RegEnmDef.txt > RegTbl.h
│#define RegKey_Color   5
│#define RegKey_EnmDef  6
□使用例
│#include "RegTbl.h"
│#include "EnmDef.h"
│void sample() {
│  const void* pEnmKey=REG_open_key(TBL_RegTbl,RegKey_EnmDef);
│  int iVal;
│  for(iVal=0;iVal<=Color_Max;iVal++) {
│    puts(EnmDef_ToStr(pEnmKey,RegKey_Color,iVal);
│  } //                        ~~~~~~~~~~~~ ~~~~
│}   //iEnmDefはRegTbl.hにて定義されます｡↑ ↑iValはEnmDef.hにて定義されます｡
#endif
