/*
 *	app.cpp
 *
 *	dpsprsht.exe - スプライトシート作成ツール
 *	Copyright (C) 2016 Naoyuki Sawa
 *
 *	* Mon Feb 22 21:41:36 JST 2016 Naoyuki Sawa
 *	- 1st リリース。
 *	- 以下の資料を参考にさせて頂きました。
 *	  「ブレ男のブログ」(http://ktaobo.blogspot.jp/)さんの、『TexturePackerを自作した』(http://ktaobo.blogspot.jp/2013/10/texture-packer.html)の記事
 *	  「blackpawn」(http://www.blackpawn.com/)さんの、『Packing Lightmaps』(http://www.blackpawn.com/texts/lightmaps/default.html)の記事
 *	- ■■■既知の問題点■■■ 2016/02/22 1st リリースverには、以下の問題点があります。	⇒{{2016/02/23追記:解決しました。}}
 *	  P/ECE用としてパレット付きBMP形式を作成するImageMagickコマンドで作成されたビットマップは、そのままでは、P/ECEのパレットと並びが一致していない。
 *	  本来は、白,明,暗,黒,緑,紫,...と並んでいるのが望ましいのですが、ImageMagickではパレット順を指定出来ないようで、並び順は不定になってしまいます。
 *	  現在の吐露、ImageMagickコマンドで作成されたビットマップを、手作業でパレットの並び順を修正するしかありません。
 *	  或いは、dpbmpcnv.exe側にパレットの内容を見て自動リマップする昨日を追加する案も有りますが、まだ実装していません。
 *	  どうするのが最善か、検討してから実装しようと思います。				⇒{{2016/02/23追記:dpbmpcnv.exeにパレットリマップ機能を追加しました。}}
 *	* Tue Feb 23 21:00:55 JST 2016 Naoyuki Sawa
 *	- dpbmpcnv.exeにパレットリマップ機能を追加しました。
 *	  当ツールが生成したImageMagickコマンドで生成したBMPを、P/ECE形式に変換する場合は、dpbmpcnv.exeに「-m」オプションを指定して下さい。
 *	  詳細は、/clip/tool/dpbmpcnv/dpbmpcnv.c の同日のコメントを参照して下さい。
 *	* Wed Feb 24 21:12:31 JST 2016 Naoyuki Sawa
 *	- dump_sprite()において、スプライト名の一文字目がCシンボルの一文字目として無効な文字(多分数字)ならば、スプライト名の先頭に「`」を付与するようにしました。
 *	* Mon Mar 14 21:59:15 JST 2016 Naoyuki Sawa
 *	- 画像処理の方法を、「ImageMagick」⇒「GDI+」に変更しました。
 *	- Sprite_compar()において、高さも幅も同じ場合は、スプライト名の自然順の昇順にソートする事にしました。
 *	  結果を安定させるためだけであり、領域の使用効率には影響有りません。
 *	* Wed Mar 30 21:41:00 JST 2016 Naoyuki Sawa
 *	- 前回まで古いコードをコメントアウトして残していたのを、削除して整理しました。
 *	  古いコードを参照する必要が有る場合は、前回のアーカイプを参照して下さい。
 *	- '-p'オプションに、'-p1','-p2','-p4'を追加しました。'-p'は'-p2'と同等としました。
 *	  これまでは、P/ECE用のパレット付きBMP形式ファイルを生成する時は、16諧調固定で生成し、2諧調や4諧調にする時はdpbmpcnv.exeの「-m」オプションに任せていました。
 *	  しかし、アプリケーションプロジェクトのMakefileの中で、dpbmpcnv.exeに「-m」オプションを指定するかしないかの切り替えが難しい問題が生じました。
 *	  そこで、dpbmpcnv.exeの「-m」オプションに頼らずに、当ツール自身で2諧調用/4諧調用/16諧調用のパレットを切り替えてBMP形式ファイルを生成するようにしました。
 *	  尚、dpbmpcnv.exeの「-m」オプションは、当初の目的としてはもう不要になったのですが、別の用途で使えるかも知れないので残しておくことにしました。
 *	- 上記の変更に伴って、P/ECE用のパレット付きBMP形式ファイルを生成する時に、サフィックス"-1m"/"-2m"/"-4m"を付加するようにしました。
 *	  これによって、dpbmpcnv.exeが諧調を自動判別できるようになり、アプリケーションプロジェクトで明示的にdpbmpcnv.exeに対して諧調を指定する必要が無くなりました。
 *	* Sun Apr 17 18:12:58 JST 2016 Naoyuki Sawa
 *	- '-f'オプションを追加しました。
 *	  '-f'オプションを追加した理由は、(むしろ)'-f'オプションを使わずに、コマンドラインで直接画像ファイル名を指定出来るようにするためです。
 *	  (逆に)これまでと同様にリストファイルで画像ファイルを指定する場合に、'-f'オプションを指定して下さい。
 *	* Sun May 08 23:30:59 JST 2016 Naoyuki Sawa
 *	- バックスラッシュの変換問題を、clipunic.cモジュール内で吸収した事により、個別の処理が不要になりました。
 *	  この変更に伴い、'ビットマップをロードする'処理の特例処理を削除しました。
 *	* Sun May 15 21:18:41 JST 2016 Naoyuki Sawa
 *	- '-e'オプションを追加しました。('auto Enlarge the canvas'の'e'です)
 *	  '-e'オプションを指定すると、指定されたキャンバスサイズよりも大きい画像が有ったら、自動的に指定サイズを超えるキャンバスを作成します。
 *	  このオプションを追加した理由は、ごく一部の大きなテクスチャのために、全てのキャンバスを大きくする必要が生じる事を避けるためです。
 *	  全てのキャンバスを必要以上に大きくしてしまうと、小さなスプライトを描画するためだけに、大きなスプライトシートをロードする事が多くなってしまうからです。
 *	* Mon Jul 25 21:36:27 JST 2016 Naoyuki Sawa
 *	- 2016/06/14に、/clip/clipstd.hに'#pragma comment(lib, "liblua.lib")'を追加した事に伴い、当プロジェクトにもlibluaのライブラリパスの設定を追加しました。
 *	  現在の所、当プロジェクトはlibluaを使用していませんが、インクルードパスとライブラリパスの設定を追加しないとビルドエラーになるからです。
 *	  尚、libluaのインクルードパスは以前から設定していたので、今回、追加したのはlibluaのライブラリパスのみです。
 *	- 2016/07/01に、/clip/libclip/extclip.hに'#include <sqlite3.h>'と'#pragma comment(lib, "sqlite3.lib")'を追加した事に伴い、当プロジェクトにもsqlite3のインクルードパスとライブラリパスの設定を追加しました。
 *	  現在の所、当プロジェクトはsqlite3を使用していませんが、インクルードパスとライブラリパスの設定を追加しないとビルドエラーになるからです。
 *	- 2016/06/17に、clipstd.hの「using namespace Gdiplus;」を削除した事に伴い、明示的に'Gdiplus::'を指定するように変更しました。
 *	  詳細は、clipstd.hの同日のコメントを参照して下さい。
 *	- '-r'オプションを追加しました。
 *	  変更前は、スプライトシートのプレフィクス名は「SprSht」固定でしたが、変更後は、'-r'オプションで指定出来るようになりました。
 *	  '-r'オプションを追加した理由は、ゲームシーン毎に個別のスプライトシートグループにまとめる際に、dpsprsht.exeを複数回実行したいと思ったからです。
 *	  変更前は、dpsprsht.exeを実行する度にスプライトシート名が「SprSht0～」となってしまい、名前が衝突してしまう問題が有りました。
 *	  変更後は、異なるプレフィクス名を指定すれば、dpsprsht.exeを複数回実行してもスプライトシート名が衝突しなくなりました。
 *	  尚、'-r'オプションを指定しなければ、デフォルトのプレフィクス名は「SprSht」であり、変更前と同じ動作となります。
 */
#include "app.h"
#define VERSION		"20160725"	//最終更新日
/****************************************************************************
 *	main
 ****************************************************************************/
int main(int argc, char* argv[]) {
	int exitCode = EXIT_SUCCESS;
#if     (defined(GC_H) && !defined(USE_BISON_FLEX))
//	putenv("GC_LOG_FILE=CON");//ログ出力先を指定する。
//	GC_set_find_leak(1);//メモリリーク検出を開始する。
#endif//(defined(GC_H) && !defined(USE_BISON_FLEX))
#if     (defined(GC_H) && defined(PTHREAD_H))
	GC_init();//この中でGCスレッドが起動する。メインスレッドをpthread_exit()等で終了するとGCスレッドが残ってプロセスが終了しないので注意せよ。
	GC_allow_register_threads();
#endif//(defined(GC_H) && defined(PTHREAD_H))
#ifdef  __CLIP_SEH_H__
	SEH_init();
#endif//__CLIP_SEH_H__
#ifdef  USE_GDIPLUS
	Gdiplus_Init();
#endif//USE_GDIPLUS
	app_init(argc, argv);
	exitCode = app_main(argc, argv);
	app_exit();
#ifdef  USE_GDIPLUS
	Gdiplus_Exit();
#endif//USE_GDIPLUS
#if     (defined(GC_H) && !defined(USE_BISON_FLEX))
//	CHECK_LEAKS();//メモリリークを検出する。
#endif//(defined(GC_H) && !defined(USE_BISON_FLEX))
	return exitCode;
}
/****************************************************************************
 *	usage
 ****************************************************************************/
#define CANVAS_W_DEFAULT	256
#define CANVAS_H_DEFAULT	256
#define GAP_W_DEFAULT		0
#define GAP_H_DEFAULT		0
/*--------------------------------------------------------------------------*/
__declspec(noreturn) static void usage() {
	fprintf(stderr, "dpsprsht.exe - スプライトシート作成ツール (%s)\n", VERSION);
	fprintf(stderr, "Copyright (C) 2016 Naoyuki Sawa\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "USAGE:\n");
	fprintf(stderr, "  dpsprsht.exe [option ...] [画像ファイル名 ...]\n");
	fprintf(stderr, "  ・画像ファイル名は、ワイルドカードによる複数指定も可能です。\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "OPTIONS:\n");
	fprintf(stderr, "  -fリストファイル名 リストファイルを指定します。\n");
	fprintf(stderr, "                     リストファイルには、一行に一個づつ、画像ファイル名を記述して下さい。\n");
	fprintf(stderr, "                     リストファイルに記述された画像ファイル名の、ワイルドカードは展開されません。\n");
	fprintf(stderr, "                     リストファイルには、空行が含まれていても構いません。\n");
	fprintf(stderr, "                     -fオプションは、複数指定する事も出来ます。\n");
	fprintf(stderr, "                     -fオプションと、コマンドラインの画像ファイル名指定を、併用する事も出来ます。\n");
	fprintf(stderr, "  -cサイズ           キャンバスサイズを幅,高さ共通で指定します。\n");
	fprintf(stderr, "  -c幅x高さ          キャンバスサイズを幅,高さ個別に指定します。\n");
	fprintf(stderr, "                     キャンバスサイズを指定しなければ%dx%dとなります。\n", CANVAS_W_DEFAULT, CANVAS_H_DEFAULT);
    //{{2016/05/15追加:'-e'オプションを追加しました。
	fprintf(stderr, "  -e                 指定されたキャンバスサイズよりも大きい画像が有ったら、自動的に指定サイズを超えるキャンバスを作成します。\n");
	fprintf(stderr, "                     このオプションを指定しなければ、指定されたキャンバスサイズよりも大きい画像が有ったら、エラー停止します。\n");
    //}}2016/05/15追加:'-e'オプションを追加しました。
	fprintf(stderr, "  -gサイズ           ギャップサイズを幅,高さ共通で指定します。\n");
	fprintf(stderr, "  -g幅x高さ          ギャップサイズを幅,高さ個別に指定します。\n");
	fprintf(stderr, "                     ギャップサイズを指定しなければ%dx%dとなります。\n", GAP_W_DEFAULT, GAP_H_DEFAULT);
	fprintf(stderr, "  -s出力ファイル名   dTexDefC.exe形式のスプライト定義を、ファイルへ出力します。\n");
	fprintf(stderr, "                     このオプションを指定しなければ、標準出力へ出力します。\n");
	fprintf(stderr, "  -p[1|2|4]          このオプションを指定すると、パレット付きBMP形式のスプライトシートを作成します。\n");
	fprintf(stderr, "                     '-p1'ならば2諧調、'-p',又は,'-p2'ならば4諧調、'-p4'ならば16諧調のパレットを生成します。\n");
	fprintf(stderr, "                     このオプションを指定しなければ、αチャネル付きフルカラーPNG形式のスプライトシートを作成します。\n");
    //{{2016/07/25追加:'-r'オプションを追加しました。
	fprintf(stderr, "  -rプレフィクス名   スプライトシートのプレフィクス名を指定します。\n");
	fprintf(stderr, "                     スプライトシートのファイル名は、「プレフィクス名+連番.png」又は「プレフィクス名+連番+'-'+形式.bmp」です。\n");
	fprintf(stderr, "                     スプライト定義に出力する名前は、「プレフィクス名を大文字化した名前+連番」です。\n");
	fprintf(stderr, "                     このオプションを指定しなければ、プレフィクス名は「SprSht」となります。\n");
    //}}2016/07/25追加:'-r'オプションを追加しました。
	fprintf(stderr, "  -h                 使い方を表示します。\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "EXAMPLE:\n");
	fprintf(stderr, "  ①カレントフォルダに有る*.pngを、スプライトシートにまとめます。\n");
	fprintf(stderr, "  │REM DirectXやOpenGLで使用する場合、隣の色を引き込まないように、'-g1'以上を指定するのが良いと思います。\n");
	fprintf(stderr, "  │DIR /B *.png > SprSht.txt\n");
	fprintf(stderr, "  │dpsprsht.exe -c1024 -g1 -sSprDef.txt -fSprSht.txt\n");
	fprintf(stderr, "  又は\n");
	fprintf(stderr, "  │dpsprsht.exe -c1024 -g1 -sSprDef.txt *.png\n");
	fprintf(stderr, "  ②カレントフォルダに有る*.bmpを、スプライトシートにまとめます。\n");
	fprintf(stderr, "  │REM P/ECEで使用する場合、回転描画を使わない限りは隣の色を引き込む事は無いので、'-g1'の指定は不要です。\n");
	fprintf(stderr, "  │DIR /B *.bmp > SprSht.txt\n");
	fprintf(stderr, "  │dpsprsht.exe -p -sSprDef.txt -fSprSht.txt\n");
	fprintf(stderr, "  又は\n");
	fprintf(stderr, "  │dpsprsht.exe -p -sSprDef.txt *.bmp\n");
//{{2016/03/30削除:当ツール自身で2諧調用/4諧調用/16諧調用のパレットを生成するようになったので、dpbmpcnv.exeへの「-m」オプション指定は不要になりました。
//	fprintf(stderr, "  │REM 上記の方法で生成したBMPを、P/ECE形式に変換する場合は、dpbmpcnv.exeに「-m」オプションを指定して下さい。\n");	//{{2016/02/23追記:dpbmpcnv.exeにパレットリマップ機能を追加しました。}}
//}}2016/03/30削除:当ツール自身で2諧調用/4諧調用/16諧調用のパレットを生成するようになったので、dpbmpcnv.exeへの「-m」オプション指定は不要になりました。
	exit(EXIT_FAILURE);
}
/****************************************************************************
 *	
 ****************************************************************************/
typedef struct _ST_Sprite {
	char*			name;		//スプライト名
	char*			path;		//画像パス名
	int			w, h;		//幅,高さ
	Gdiplus::Bitmap*	bitmap;		//ビットマップ
} ST_Sprite;
ght_hash_table_t*	ht_sprite;	//スプライト名⇒ST_Sprite*
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
//スプライトを高さの降順にソートする。
//それに加えて、高さが同じならば、幅の降順にソートする事にした。
//幅の降順にソートする事にした理由は、結果を安定させるためだけです。領域の使用効率には影響無いと思います。
//高さも幅も同じ場合は、スプライト名の自然順の昇順にソートする事にしました。
//結果を安定させるためだけであり、領域の使用効率には影響有りません。
static int Sprite_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);
	ST_Sprite* pSprite1 = (ST_Sprite*)x->p_entry_data;
	ST_Sprite* pSprite2 = (ST_Sprite*)y->p_entry_data;
	if(pSprite1->h < pSprite2->h) { return  1; }	//┬高さの降順
	if(pSprite1->h > pSprite2->h) { return -1; }	//┘
	if(pSprite1->w < pSprite2->w) { return  1; }	//┬幅の降順
	if(pSprite1->w > pSprite2->w) { return -1; }	//┘
	return strnatcmp(pSprite1->name, pSprite2->name);	//スプライト名の自然順の昇順
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
int	canvas_w = CANVAS_W_DEFAULT;	//キャンバスの幅
int	canvas_h = CANVAS_H_DEFAULT;	//キャンバスの高さ
int	gap_w = GAP_W_DEFAULT;		//ギャップの幅		//ギャップは配置したスプライトの右側だけに作成する。左側には作成しない。つまりスプライトとスプライトの間隔はギャップ×2ではなくギャップ×1である。
int	gap_h = GAP_H_DEFAULT;		//ギャップの高さ	//ギャップは配置したスプライトの下側だけに作成する。上側には作成しない。つまりスプライトとスプライトの間隔はギャップ×2ではなくギャップ×1である。
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
typedef struct _ST_Region {
	int			x, y;		//領域の原点座標				ギャップサイズによってキャンバスを超えた場合は、原点座標がキャンバスサイズを超える値にに成り得る。
	int			w, h;		//領域の幅,高さ					ギャップサイズによってキャンバスを超えた場合は、領域の幅,高さがマイナスに成り得る。
	ST_Sprite*		pSprite;	//この領域に配置されたスプライト		まだこの領域にスプライトを配置していなければNULL
	struct _ST_Region*	rRight;		//スプライトを配置した後、右側に出来た領域	まだこの領域にスプライトを配置していなければNULL
	struct _ST_Region*	rDown;		//スプライトを配置した後、下側に出来た領域	まだこの領域にスプライトを配置していなければNULL
} ST_Region;
ST_Region**	TBL_Region/*[nCanvas]*/;	//各キャンバスの領域ツリー
int		nCanvas;			//キャンバス数
/*--------------------------------------------------------------------------*/
int add_region(ST_Region* pRegion, ST_Sprite* pSprite) {
	//この領域に配置されたスプライトが有れば…
	if(pRegion->pSprite) {
		return add_region(pRegion->rRight, pSprite) ||	//右側の領域に対して追加を試みる。右側の領域に対して追加出来なければ…
		       add_region(pRegion->rDown,  pSprite);	//下側の領域に対して追加を試みる。
	//この領域に配置されたスプライトが無ければ…
	} else {
		//この領域のサイズがスプライトサイズ未満ならば、失敗を返す。
		// - pRegion->w,pRegion->hはマイナスである可能性も有るので、符号付きで比較する必要が有る。
		if((pRegion->w < pSprite->w) || (pRegion->h < pSprite->h)) { return 0; }
		//この領域のサイズがスプライトサイズ以上ならば、スプライトを配置する。
		pRegion->pSprite = pSprite;
		//右側に出来た領域を作成する。
		//┌─────┬────┐
		//│スプライト│この部分│
		//├─────┴────┤
		//│　　　　　　　　　　│
		//└──────────┘
		pRegion->rRight = (ST_Region*)calloc(sizeof(ST_Region), 1);
		if(!pRegion->rRight) { DIE(); }
		pRegion->rRight->x = pRegion->x + (pSprite->w + gap_w);
		pRegion->rRight->y = pRegion->y;
		pRegion->rRight->w = pRegion->w - (pSprite->w + gap_w);
		pRegion->rRight->h = pSprite->h;//(pSprite->h + gap_h)ではないことに注意!!
		//下側に出来た領域を作成する。
		//┌─────┬────┐
		//│スプライト│　　　　│
		//├─────┴────┤
		//│　　　この部分　　　│
		//└──────────┘
		pRegion->rDown = (ST_Region*)calloc(sizeof(ST_Region), 1);
		if(!pRegion->rDown) { DIE(); }
		pRegion->rDown->x = pRegion->x;
		pRegion->rDown->y = pRegion->y + (pSprite->h + gap_h);
		pRegion->rDown->w = pRegion->w;
		pRegion->rDown->h = pRegion->h - (pSprite->h + gap_h);
		return 1;	//成功
	}
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
void get_sprsht_size(ST_Region* pRegion, int* sprsht_w, int* sprsht_h) {
	ST_Sprite* pSprite = pRegion->pSprite;
	//この領域にスプライトが配置されていたら…
	if(pSprite) {
		//このスプライトの右下座標(グリッド座標系)がスプライトシートサイズを超えていたら、スプライトシートサイズを更新する。
		int w = pRegion->x + pSprite->w;
		int h = pRegion->y + pSprite->h;
		if(w > *sprsht_w) { *sprsht_w = w; }
		if(h > *sprsht_h) { *sprsht_h = h; }
		get_sprsht_size(pRegion->rRight, sprsht_w, sprsht_h);	//右側の領域について再帰する。
		get_sprsht_size(pRegion->rDown,  sprsht_w, sprsht_h);	//下側の領域について再帰する。
	}
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
void dump_convert(Gdiplus::Bitmap* sprsht, ST_Region* pRegion) {
	ST_Sprite* pSprite = pRegion->pSprite;
	//この領域にスプライトが配置されていたら…
	if(pSprite) {
		//スプライトの画像を、スプライトシートにコピーする。
		Gdiplus::Bitmap* bitmap = pSprite->bitmap;
#if 0	//×DrawImage()を使って画像をコピーする場合はこちら(問題有り)
		{
			Graphics g(sprsht);
		//{{2016/03/14暫定修正													//│ * Mon Mar 14 21:59:15 JST 2016 Naoyuki Sawa
		//不可	if(g.DrawImage(bitmap, pRegion->x, pRegion->y)) { DIE(); }	//起きないはず。				//│ - 何故か2引数版のDrawImage()を使うと、正しい描画がされず、勝手に拡大されたり縮小されたりする事が有るようです。画像サイズに含まれた、実寸(DPI)が影響したりしているのだろうか?
		/*ok*/	if(g.DrawImage(bitmap, pRegion->x, pRegion->y, pSprite->w, pSprite->h)) { DIE(); }	//起きないはず。	//│   まだ原因を解明出来ていないのですが、とりあえず、4引数版のDrawImage()を使う方が安全なようです。
		//}}2016/03/14暫定修正													//│   尚、多分この問題は、ファイルから読み込んだ画像を描画する時だけだと思います。常に4引数版のDrawImage()を使うのは面倒なので、普段は2引数版のDrawImage()で構わないと思います。
		}
#else	//○GetPixel()/SetPixel()を使って画像をコピーする場合はこちら(問題無し)
//│ * Mon Mar 14 21:59:15 JST 2016 Naoyuki Sawa
//│ - 上述の'2016/03/14暫定修正'の他にも、DrawImage()を使って画像をコピーする方法には、問題が有る。
//│   コピー元画像のアルファ値が255でない場合、コピー元のRGB値にアルファ値を掛けた値が、コピー先に書き込まれるようである事です。
//│   表示用のレンダリングとしてはそれで正しいのですが、画像作成用としては問題が有ります。
//│   コピー元画像のアルファ値もRGB値も全てそのままの値を、コピー先に書き込むべきです。
//│   そうするためには、DrawImage()は使用出来ず、GetPixel()/SetPixel()を使う必要が有ります。
//│ - もう一つ問題が有ります。
//│   上で、'コピー元のRGB値にアルファ値を掛けた値が、コピー先に書き込まれる'と書いたのですが、厳密にはそうでもないみたいです。
//│   よくわからないのですが、元のカラー値と微妙に異なる値が、コピー先に書き込まれる事が有るようでした。(255⇒254)
//│   この問題も、DrawImage()は表示用に最適化されているためだと思います。フィルタリングとかの影響かな?
//│ - 結論としては、画像を作成する場合は、DrawImage()を使わずに、GetPixel()/SetPixel()を使うべきです。
//│   DrawImage()よりも、GetPixel()/SetPixel()の方がオーバーヘッドが大きいので、多少遅くなりますが、せいぜい2～3倍遅い程度のようで、速度的には許容範囲だと思います。
		{
			int x, y;
			for(y = 0; y < pSprite->h; y++) {
				for(x = 0; x < pSprite->w; x++) {
					Gdiplus::Color c;
					if(bitmap->GetPixel(             x,              y, &c)) { DIE(); }	//起きないはず。
					if(sprsht->SetPixel(pRegion->x + x, pRegion->y + y,  c)) { DIE(); }	//起きないはず。
				}
			}
		}
#endif
		dump_convert(sprsht, pRegion->rRight);	//右側の領域について再帰する。
		dump_convert(sprsht, pRegion->rDown);	//下側の領域について再帰する。
	}
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
void dump_sprite(FILE* fp, ST_Region* pRegion) {
	ST_Sprite* pSprite = pRegion->pSprite;
	//この領域にスプライトが配置されていたら…
	if(pSprite) {
		//スプライト名の一文字目がCシンボルの一文字目として有効な文字ならば…
		if(iscsymf(*pSprite->name)) {
			//スプライト定義を出力する。
			fprintf(fp, "\t%s\t%d,%d,%d,%d,50%%,50%%\n",
				pSprite->name,			//スプライト名
				pRegion->x, pRegion->y,		//配置座標
				pSprite->w, pSprite->h);	//スプライトサイズ
		//スプライト名の一文字目がCシンボルの一文字目として無効な文字(多分数字)ならば…
		} else {
			//スプライト定義を出力する。
			fprintf(fp, "\t`%s\t%d,%d,%d,%d,50%%,50%%\n",	//スプライト名の先頭に「`」を付与する。dTexDefC.exeのためです。
				pSprite->name,			//スプライト名
				pRegion->x, pRegion->y,		//配置座標
				pSprite->w, pSprite->h);	//スプライトサイズ
		}
		dump_sprite(fp, pRegion->rRight);	//右側の領域について再帰する。
		dump_sprite(fp, pRegion->rDown);	//下側の領域について再帰する。
	}
}
/****************************************************************************
 *	
 ****************************************************************************/
void app_init(int argc, char* argv[]) {
	//ハッシュテーブルを作成する。
	ght_set_rehash((ht_sprite = ght_create(0)), 1);
}
/*--------------------------------------------------------------------------*/
void app_exit() {
	/** no job **/
}
/*--------------------------------------------------------------------------*/
#define MAX_FILE 99999			//画像ファイルの数の最大数(調整可)
static char* TBL_File[MAX_FILE];	//画像ファイルリスト
static int nFile;			//画像ファイルの数
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
int app_main(int argc, char* argv[]) {
	int exitCode = EXIT_SUCCESS;
	char* opt_s = NULL;	//スプライト定義を出力するファイル名		NULLならば標準出力へ出力する。
	int opt_p = 0;		//0ならばWindows用を想定してαチャネル付きフルカラーPNG形式ファイルを生成します。1/2/4ならばP/ECE用を想定してパレット付きBMP形式ファイルを生成します。1ならば1階調,2ならば2階調,4ならば4階調のグレースケール+緑(透過)のパレットを設定します。
    //{{2016/05/15追加:'-e'オプションを追加しました。
	int opt_e = 0;		//0ならば、指定されたキャンバスサイズよりも大きい画像が有ったら、エラー停止します。1ならば、指定されたキャンバスサイズよりも大きい画像が有ったら、自動的に指定サイズを超えるキャンバスを作成します。
    //}}2016/05/15追加:'-e'オプションを追加しました。
    //{{2016/07/25追加:'-r'オプションを追加しました。
	char* opt_r = "SprSht";	//スプライトシートのプレフィクス名
    //}}2016/07/25追加:'-r'オプションを追加しました。
	{
		char* glbuf = NULL;	//┬getline()用バッファ
		size_t glcap = 0;	//┘
		char* endptr;
		int opt;
		opterr = 0; //オプションエラーは当プログラム内で処理するので、getopt()内でのエラー表示は不要。
		while((opt = getopt(argc, argv, "f:c:eg:s:p::r:h")) != -1) {	//コマンドラインオプションを処理する。
			switch(opt) {
			case 'f':
				{
					//リストファイルを開く。
					FILE* fp = fopen(optarg, "r");
					if(!fp) { die("%s が開けません。", optarg); }
					//リストファイルの各行を読み込んで…
					while(getline(&glbuf, &glcap, fp) != EOF) {
						//前後の空白文字を取り除く。
						strstrip(glbuf);
						//空行でなければ…
						if(glbuf[0]) {
							//リストファイルで指定された画像ファイル名を、画像ファイルリストに追加する。
							if(nFile >= MAX_FILE) { die("画像ファイル数が制限を超えました。"); }
							TBL_File[nFile++] = strdup(glbuf);	//呼び出し元のバッファは次のgetline()で破壊されるのでstrdup()必要。
						}
					}
					//リストファイルを閉じる。
					fclose(fp);
				}
				break;
			case 'c':
				canvas_w = strtoul(optarg, &endptr, 0);
				if(*endptr == 'x') {	//「-c幅x高さ」又は「-c 幅x高さ」の場合
					optarg = endptr + 1/*'x'*/;
					canvas_h = strtoul(optarg, &endptr, 0);
				} else {		//「-cサイズ」又は「-c サイズ」の場合
					canvas_h = canvas_w;
				}
				if(!canvas_w || !canvas_h || *endptr) { usage(); }	//キャンバスサイズは0不可
				break;
			case 'e':
				opt_e = 1;
				break;
			case 'g':
				gap_w = strtoul(optarg, &endptr, 0);
				if(*endptr == 'x') {	//「-g幅x高さ」又は「-g 幅x高さ」の場合
					optarg = endptr + 1/*'x'*/;
					gap_h = strtoul(optarg, &endptr, 0);
				} else {		//「-gサイズ」又は「-g サイズ」の場合
					gap_h = gap_w;
				}
				if(/*!gap_w || !gap_h ||*/ *endptr) { usage(); }	//ギャップサイズは0可(デフォルトと同じ)
				break;
			case 's':
				opt_s = optarg;
				break;
			case 'p':
				if(optarg) {
					     if(!strcmp(optarg, "1")) { opt_p = 1; }	//'-p1'ならば1階調とする。
					else if(!strcmp(optarg, "2")) { opt_p = 2; }	//'-p2'ならば2階調とする。
					else if(!strcmp(optarg, "4")) { opt_p = 4; }	//'-p4'ならば4階調とする。
					else { usage(); }
				} else {
					opt_p = 2;	//'-p'ならば'-p2'と同じとする。
				}
				break;
		    //{{2016/07/25追加:'-r'オプションを追加しました。
			case 'r':
				opt_r = optarg;
				break;
		    //}}2016/07/25追加:'-r'オプションを追加しました。
			case 'h':
			default:
				usage();
			}
		}
		//コマンドラインで指定された画像ファイル名を、画像ファイルリストに追加する。
		while(optind < argc) {
			if(nFile >= MAX_FILE) { die("画像ファイル数が制限を超えました。"); }
			TBL_File[nFile++] = argv[optind++];	//argv[]の文字列は破壊されないのでstrdup()不要。
		}
		//'-f'オプション,又は,コマンドラインの画像ファイル名指定によって、少なくとも一個以上、画像ファイルが指定されていなければいけない。
		if(!nFile) { error(EXIT_FAILURE, 0, "画像ファイルが指定されていません。"); }
		//getline()用バッファを開放する。
		free(glbuf);
	}
	{
		//画像ファイルリストの各画像ファイルについて…
		int iFile;
		for(iFile = 0; iFile < nFile; iFile++) {
			char* path = TBL_File[iFile];
			wchar_t* wPath;
			Gdiplus::Bitmap* bitmap;
			int i, w, h;
			char name[MAX_FNAME];
			ST_Sprite* pSprite;
			//ビットマップをロードする。
			wPath = strdup_ShiftJisToUnicode(path);
	    //{{2016/05/08削除:バックスラッシュの変換問題を、clipunic.cモジュール内で吸収した事により、個別の処理が不要になりました。この変更に伴い、'ビットマップをロードする'処理の特例処理を削除しました。
	    //	//{{特例処理
	    //		{
	    //			// * Mon Mar 14 21:59:15 JST 2016 Naoyuki Sawa
	    //			// - 自作のstrdup_ShiftJisToUnicode()は、'http://unicode.org/Public/MAPPINGS/OBSOLETE/EASTASIA/JIS/SHIFTJIS.TXT'の変換表を使っているので、'\'文字をシフトJIS⇒Unicodeに変換すると、CP932とのズレが生じます。
	    //			//   CP932として見た場合、0x5C⇒0x005Cに変換されるべきですが、自作のstrdup_ShiftJisToUnicode()は、0x5C⇒0x00ACに変換してしまいます。
	    //			//   □参照資料
	    //			//   │「HyperWorkBook」(http://hwb.ecc.u-tokyo.ac.jp/current/)さんの、「18.4.1.3 Unicode - YEN SIGN 問題」(http://hwb.ecc.u-tokyo.ac.jp/current/information/coding/character/chrcode/unicode/)の記事
	    //			// - これは必ずしもバグという訳ではなく、解釈の違いの問題なのですが、とは言え、WindowsはUnicodeの0x00ACをパスセパレータとして認識しないので、問題が生じます。
	    //			//   例えば、"grp\sample.bmp"というパス名をUnicodeに変換してBitmap()コンストラクタに指定すると、'\'が0x00ACに変換されてしまい、不正なパス名と見なされてBitmap()コンストラクタが失敗してしまいます。
	    //			//   ちなみに、"grp/sample.bmp"ならば失敗しません。'/'の変換には、上記のような曖昧さが無いからです。
	    //			// - 解決策としては、当ツールの入力ファイルの、パスセパレータに'\'ではなく'/'を使うように強制する方法も有りますが、実際の所それは難しいです。
	    //			//   当ツールの入力ファイルは、'DIR /S'コマンド等を使って作成する事が多く、'DIR /S'コマンドはパスセパレータに'\'を出力するからです。
	    //			//   従って、変換後のUnicode文字列に対して、ここで0x00ACを0x005Cに置き換えて、問題を回避する事にしました。
	    //			// - strdup_ShiftJisToUnicode()自体を修正する案も考えたのですが、それはしない事にしました。
	    //			//   上述の通り、0x5Cを0x005Cに変換するか0x00ACに変換するかは、解釈の違いであり、当ツールの都合のためにstrdup_ShiftJisToUnicode()を変更するのは、良くないと思ったからです。
	    //			//   strdup_ShiftJisToUnicode()はあくまでも、'http://unicode.org/Public/MAPPINGS/OBSOLETE/EASTASIA/JIS/SHIFTJIS.TXT' に準拠する、としておく方が、混乱が生じないと思います。
	    //			wchar_t* p = wPath;
	    //			while(*p) {
	    //				if(*p == 0x00A5) { *p = 0x005C; }
	    //				p++;
	    //			}
	    //		}
	    //	//}}特例処理
	    //}}2016/05/08削除:バックスラッシュの変換問題を、clipunic.cモジュール内で吸収した事により、個別の処理が不要になりました。この変更に伴い、'ビットマップをロードする'処理の特例処理を削除しました。
			bitmap = new Gdiplus::Bitmap(wPath);
			if(bitmap->GetLastStatus()) { error(EXIT_FAILURE, 0, "%sが読み込めません。", path); }
			free(wPath);
			//幅と高さを取得する。
			w = bitmap->GetWidth();
			if(bitmap->GetLastStatus()) { DIE(); }	//起きないはず。
			h = bitmap->GetHeight();
			if(bitmap->GetLastStatus()) { DIE(); }	//起きないはず。
		    //{{2016/05/15追加:'-e'オプションを追加しました。
		      if(!opt_e) {	//'-e'オプションが指定されていなければ、ここで、画像サイズが指定されたキャンバスサイズを超えていない事を確認する。これ以降の処理では、'-e'オプションの指定の有無による違いは無い。
		    //}}2016/05/15追加:'-e'オプションを追加しました。
			if(((unsigned)w > (unsigned)canvas_w) ||
			   ((unsigned)h > (unsigned)canvas_h)) { error(EXIT_FAILURE, 0, "キャンバスサイズよりも大きい画像が有ります: %s", path); }	//ここでギャップを考慮する必要は無い。ぴったりキャンバスサイズと同じスプライトは配置可能です。
		    //{{2016/05/15追加:'-e'オプションを追加しました。
		      }
		    //}}2016/05/15追加:'-e'オプションを追加しました。
			//画像パス名から、スプライト名を作成する。
			splitpath(path, NULL, NULL, name, NULL);
			for(i = 0; name[i]; i++) {
				name[i] = toupper(name[i]);		//大文字化する事にした。
				if(!iscsym(name[i])) { name[i] = '_'; }	//シンボル文字以外は'_'に置き換える事にした。
			}
			//スプライトを登録する。
			pSprite = (ST_Sprite*)malloc(sizeof(ST_Sprite));
			if(!pSprite) { DIE(); }
			pSprite->name	= strdup(name);
			pSprite->path	= strdup(path);
			pSprite->w	= w;
			pSprite->h	= h;
			pSprite->bitmap	= bitmap;
			if(ght_insert(ht_sprite,
				pSprite,
				strlen(name) + 1/*nul*/,
				name)) { error(EXIT_FAILURE, 0, "スプライト名が重複しています: %s", path); }
		}
	}
	{
		//入力行が無ければ、使い方を表示して終了する。
		if(!ght_size(ht_sprite)) { usage(); }
		//スプライトを高さの降順にソートする。
		// - add_region()において、Tcl/Tkで言うところの'top'でパックするので、配置したスプライトの右側に出来る領域の方が下側に出来る領域よりも小さくなる。
		//   従って、右側に出来る領域を効率良く使うためには、スプライトを高さの降順でパックして行くのが良いからです。
		SortList(&ht_sprite->le_iterator, Sprite_compar);	//■■■注意：ght_hash_table_tの実装依存の処理です。■■■
	}
	{
		ght_iterator_t it;
		char* name;
		ST_Sprite* pSprite;
		ST_Region* pRegion;
		//各スプライトについて…
		for(pSprite = (ST_Sprite*)ght_first(ht_sprite, &it, (const void**)&name);
		    pSprite;
		    pSprite = (ST_Sprite*)ght_next( ht_sprite, &it, (const void**)&name)) {
			int iCanvas;
			//作成済みのキャンバスに対して、追加を試みる。
			for(iCanvas = 0; iCanvas < nCanvas; iCanvas++) {
				pRegion = TBL_Region[iCanvas];
				if(add_region(pRegion, pSprite)) { break; }	//追加出来たら抜ける。
			}
			//作成済みのキャンバスに対して、追加出来なければ…
			if(iCanvas == nCanvas) {
				//新しいキャンバスを作成する。
				pRegion = (ST_Region*)calloc(sizeof(ST_Region), 1);
				if(!pRegion) { DIE(); }
				pRegion->w = canvas_w;
				pRegion->h = canvas_h;
			    //{{2016/05/15追加:'-e'オプションを追加しました。
				//画像サイズが、指定されたキャンバスサイズ(幅)を超えていたら…
				if(pSprite->w > pRegion->w) {
					if(!opt_e) { DIE(); }	//'-e'オプションが指定されなかった場合は、ビットマップロード処理でエラー停止しているはず。もしここで停止したらバグです。
					//自動的に指定サイズ(幅)を超えるキャンバスを作成します。
					pRegion->w = pSprite->w;
				}
				//画像サイズが、指定されたキャンバスサイズ(高さ)を超えていたら…
				if(pSprite->h > pRegion->h) {
					if(!opt_e) { DIE(); }	//'-e'オプションが指定されなかった場合は、ビットマップロード処理でエラー停止しているはず。もしここで停止したらバグです。
					//自動的に指定サイズ(高さ)を超えるキャンバスを作成します。
					pRegion->h = pSprite->h;
				}
			    //}}2016/05/15追加:'-e'オプションを追加しました。
				//新しいキャンバスを、テーブルに追加する。
				TBL_Region = (ST_Region**)realloc(TBL_Region, (sizeof TBL_Region[0]) * (nCanvas + 1));
				if(!TBL_Region) { DIE(); }
				TBL_Region[nCanvas++] = pRegion;
				//新しいキャンバスに対して、追加する。
				if(!add_region(pRegion, pSprite)) { DIE(); }	//キャンバスサイズよりも大きい画像は読み込み時に検出しているのでここで失敗する事はないはず。もしここで停止したら当ツールのバグです。
			}
		}
	}
	{
		int iCanvas;
		{
			//スプライトシートをSave()するための、エンコーダのクラスIDを求めておく。
			const char* ext = opt_p ? "bmp" : "png";
			CLSID clsid;
			GetEncoderClsid(ext, &clsid);
			//各キャンバスについて…
			for(iCanvas = 0; iCanvas < nCanvas; iCanvas++) {
				ST_Region* pRegion = TBL_Region[iCanvas];
			    //{{2016/07/25変更:'-r'オプションを追加しました。
			    //	char* path = opt_p ? strdup_printf("SprSht%d-%dm.%s", iCanvas + 1, opt_p, ext)	//P/ECE用のパレット付きBMP形式ファイルを生成する時は、2諧調/4諧調/16諧調に応じてサフィックス"-1m"/"-2m"/"-4m"を付加する。
			    //	                   : strdup_printf("SprSht%d.%s",     iCanvas + 1,        ext);
			    //↓2016/07/25変更:'-r'オプションを追加しました。
				char* path = opt_p ? strdup_printf("%s%d-%dm.%s", opt_r, iCanvas + 1, opt_p, ext)	//P/ECE用のパレット付きBMP形式ファイルを生成する時は、2諧調/4諧調/16諧調に応じてサフィックス"-1m"/"-2m"/"-4m"を付加する。
				                   : strdup_printf("%s%d.%s",     opt_r, iCanvas + 1,        ext);
			    //}}2016/07/25変更:'-r'オプションを追加しました。
				wchar_t* wPath = strdup_ShiftJisToUnicode(path);
				fprintf(stderr, "%sを作成中です...\n", path);
				//スプライトシートサイズ(キャンバスの中で実際にスプライトが配置された矩形)を求める。
				int sprsht_w = 0, sprsht_h = 0;
				get_sprsht_size(pRegion, &sprsht_w, &sprsht_h);
				//スプライトシートを作成する。
				Gdiplus::Bitmap sprsht(sprsht_w, sprsht_h/*, PixelFormat32bppARGB*/);	//実験したところ、このコンストラクタで作成したビットマップの初期状態は、全てのピクセルが透明(A=0,R=0,G=0,B=0)であるようなので、ビットマップ作成後に明示的にクリアする必要は無いようです。尚、'PixelFormat32bppARGB'はデフォルト引数で指定されるので、ピクセルフォーマットを明示的に指定する必要も有りません。
				if(sprsht.GetLastStatus()) { DIE(); }	//起きないはず。
				dump_convert(&sprsht, pRegion);
				if(opt_p) {	//P/ECE用
					const int palNum = (1 << opt_p);	//グレースケールのパレットエントリ数 (緑の1エントリは含めない数)	(opt_p=1)⇒  2、(opt_p=2)⇒ 4、(opt_p=4)⇒16
					const int palMax = palNum - 1;		//グレースケールの最後のパレットエントリの番号 (緑の1つ前の番号)	(opt_p=1)⇒  1、(opt_p=2)⇒ 3、(opt_p=4)⇒15
					const int palDiv = 255 / palMax;	//グレースケールの輝度間隔						(opt_p=1)⇒255、(opt_p=2)⇒85、(opt_p=4)⇒17
					//αチャネル付きフルカラービットマップを、パレット付きBMP形式のビットマップに変換する。
					Gdiplus::Bitmap sprsht2(sprsht_w, sprsht_h, PixelFormat8bppIndexed);
					if(sprsht2.GetLastStatus()) { DIE(); }	//起きないはず。
					{
						//当ツールが出力する、パレット付きBMPのパレットは、P/ECE用の'高速切替・16階調表示用パレット'とする事にしました。
						// - もし最終的に、16諧調ではなく4諧調のpgdを生成したい場合は、dpbmpcnv.exeの「-m」オプションによって変換して下さい。
						int size = sprsht2.GetPaletteSize();
						if(sprsht2.GetLastStatus()) { DIE(); }	//起きないはず。
						Gdiplus::ColorPalette* pal = (Gdiplus::ColorPalette*)malloc(size);
						if(!pal) { DIE(); }	//メモリ不足
						if(sprsht2.GetPalette(pal, size)) { DIE(); }
						pal->Count = palNum/*グレースケール*/ + 1/*緑*/;	//(2+1) or (4+1) or (16+1)
						int i;
						for(i = 0; i < (int)pal->Count; i++) {			//(2+1) or (4+1) or (16+1)
							if(i < palNum) {				//(i<2) or (i<4) or (i<16)
								int v = (palMax - i) * palDiv;		//((1-i)*255) or ((3-i)*85) or ((15-i)*17)
								pal->Entries[i] = Gdiplus::Color::MakeARGB(255, v, v, v);	//グレースケール
							} else {
								pal->Entries[i] = Gdiplus::Color::Lime;	//緑	Gdiplus::Color::Greenではない事に注意せよ。Gdiplus::Color::Greenは(0,128,0)、Gdiplus::Color::Limeが(0,255,0)である。もっとも、見た目だけの違いだけであり、Gdiplus::Color::Greenにしてもdpbmpcnv.exeで変換した結果は同じになるのだが。
							}
						}
						if(sprsht2.SetPalette(pal)) { DIE(); }
						free(pal);
						//パレットを維持するためには、SetPixel()は使えない。
						//SetPixel()を使うと、ハーフトーンパレットにリセットされてしまう。
						//パレットを維持したままピクセルを書き込むためには、LockBits()を使う必要が有る。
						//□参照資料
						//│「GDI+を使用して新しい色テーブルに.gifファイルを保存する方法」(https://support.microsoft.com/ja-jp/kb/315780)
						//│「MFCでGDI+を使う(2) LockBits()について考える」(http://blueclouds.blog.so-net.ne.jp/2011-04-29)
						//■■注意!!■■
						//上記の参照資料には書かれていないが、ビットマップ自身と違うピクセルフォーマットを指定して、かつ、ImageLockModeWriteフラグを指定してLockBits()した場合も、ハーフトーンパレットにリセットされてしまうようだ。(実験より)
						//当関数の処理では、ビットマップ自身と違うピクセルフォーマットでLockBits()する恐れはないが、今後別のプログラムを作る時は要注意だ。
						Gdiplus::BitmapData data;
						if(sprsht2.LockBits(NULL,	//LockBits()の第一引数にNULLを指定すれば全体ロックになる。リファレンスに載っていないようだが、多くのサンプルソースで使用されているし、Win32やDirectXの一般的な関数仕様でも同様なので、間違いないだろう。
							Gdiplus::ImageLockModeWrite,
							PixelFormat8bppIndexed,
							&data)) { DIE(); }
						if((int)data.Width  != sprsht_w              ) { DIE(); }	//起きないはず。
						if((int)data.Height != sprsht_h              ) { DIE(); }	//起きないはず。
						if(data.PixelFormat != PixelFormat8bppIndexed) { DIE(); }	//起きないはず。
						if(data.Stride < 0) { DIE(); }	//LockBits()の仕様上は、(Stride<0)も有り得る。その場合Scan0は左下のピクセルを指し、ボトムアップ形式である事を意味する。しかし実験によると、ここではトップダウンに決め打って大丈夫みたいだ。ここでロックするsprsht2は、メモリ上に作成したビットマップであり、ファイル形式等に依存しないので、動作が変わる事も無いだろう。そんな訳で、簡単のために、ボトムアップに対応するためのコードは書かずに、トップダウンに決め打って処理する事にした。
						int x, y;
						for(y = 0; y < sprsht_h; y++) {
							for(x = 0; x < sprsht_w; x++) {
								Gdiplus::Color c;
								if(sprsht.GetPixel(x, y, &c)) { DIE(); }	//起きないはず。
								int a = c.GetAlpha();
								int r = c.GetRed();
								int g = c.GetGreen();
								int b = c.GetBlue();
								int i = palNum/*緑*/;				//2 or 4 or 16
								if(a && ((r == g) && (g == b))) {		//アルファ値が0でなく,かつ,RGB値が同じピクセルを、不透明ピクセルと見なす。
									i = (r + (palDiv / 2)) / palDiv;	//((r+(255/2))/255) or ((r+(85/2))/85) or ((r+(17/2))/17)	//┐
									i = palMax - i;				//(1-i) or (3-i) or (15-i)					//├/clip/tool/dpbmpcnv/dpbmpcnv.cのreadbmp()の'パレットリマップ処理'と同じアリゴリズムです。
									if(i < 0) { i = 0; }											//┘
								}
								*((uint8_t*)data.Scan0 + (data.Stride * y) + x) = i;
							}
						}
						if(sprsht2.UnlockBits(&data)) { DIE(); }	//忘れないで!!
					}
					//パレット付きBMP形式のビットマップを、パレット付きBMP形式ファイルとしてセーブする。
					sprsht2.Save(wPath, &clsid);
					if(sprsht2.GetLastStatus()) { error(EXIT_FAILURE, errno, "%sが作成出来ません。", path); }
				} else {	//Windows用
					//αチャネル付きフルカラービットマップを、αチャネル付きフルカラーPNG形式ファイルとしてセーブする。
					sprsht.Save(wPath, &clsid);
					if(sprsht.GetLastStatus()) { error(EXIT_FAILURE, errno, "%sが作成出来ません。", path); }
				}
				free(wPath);
				free(path);
			}
		}
		{
			FILE* fp = stdout;
		    //{{2016/07/25追加:'-r'オプションを追加しました。
			//スプライトシートのプレフィクス名を大文字化した名前を作成する。
			// - opt_rは(char*)であるが、const文字列を指している可能性が有るので、そのままstrupr()してはいけない。
			//   '-r'オプションが指定されなかった場合、当関数の最初に「char* opt_r = "SprSht";」として初期化した文字列のままである可能性が有るからです。
			char* label = strdup(opt_r);
			if(!label) { DIE(); }	//メモリ不足
			strupr(label);
		    //}}2016/07/25追加:'-r'オプションを追加しました。
			//スプライト定義を出力するファイル名が指定されていたら、ファイルを作成する。
			if(opt_s) {
				fp = fopen(opt_s, "w");
				if(!fp) { error(EXIT_FAILURE, errno, "%sが作成出来ません。", opt_s); }
			}
			//各キャンバスについて…
			for(iCanvas = 0; iCanvas < nCanvas; iCanvas++) {
				ST_Region* pRegion = TBL_Region[iCanvas];
				//スプライト定義を出力する。
			    //{{2016/07/25変更:'-r'オプションを追加しました。
			    //	fprintf(fp, "SPRSHT%d\n",
			    //		iCanvas + 1);		//キャンバス番号
			    //↓2016/07/25変更:'-r'オプションを追加しました。
				fprintf(fp, "%s%d\n",
					label, iCanvas + 1);	//プレフィクス名を大文字化した名前+キャンバス番号
			    //}}2016/07/25変更:'-r'オプションを追加しました。
				fprintf(fp, "\tgrid\t%d,%d\n",
					1, 1);			//グリッドサイズ(1x1ピクセル固定)
				dump_sprite(fp, pRegion);
				fprintf(fp, "end\n");
			}
			//スプライト定義を出力するファイル名が指定されていたら、ファイルを閉じる。
			if(opt_s) {
				fclose(fp);
			}
		    //{{2016/07/25追加:'-r'オプションを追加しました。
			//スプライトシートのプレフィクス名を大文字化した名前を解放する。
			free(label);
		    //}}2016/07/25追加:'-r'オプションを追加しました。
		}
	}
	return exitCode;
}
