/*
 *	dpbmpcnv - BMP to PGD コンバータ
 *	Copyright (C) 2002 Naoyuki Sawa <nsawa@north.hokkai.net>
 *
 *	* ??? May 11 09:13:00 JST 2002 Naoyuki Sawa <nsawa@north.hokkai.net>
 *	- 1st リリース。
 *	* Sat Jun 01 11:42:00 JST 2002 Naoyuki Sawa <nsawa@north.hokkai.net>
 *	- 最大イメージサイズ(MAX_SIZE)を1024->4096に変更。
 *	* Tue Jun 18 19:00:00 JST 2002 Naoyuki Sawa <nsawa@north.hokkai.net>
 *	- アイコンデータ、スプライトデータ作成機能を実装。
 *	* Sat Jun 23 21:38:00 JST 2002 Naoyuki Sawa <nsawa@north.hokkai.net>
 *	- 画像サイズ調整時は、パディング部分を透明に。
 *	- 試験的な減色機能追加。透明色との併用はお勧めしません。
 *	* Sat Jun 27 22:14:00 JST 2002 Naoyuki Sawa <nsawa@north.hokkai.net>
 *	- カラー0〜3反転モード追加。(0:黒,1:暗,2:明,3:白とみなす)
 *	  P/ECE開発環境のsimpleサンプル付属のビットマップ(test.bmp)が、
 *	  上記のようなパレット構成になっていたので、対応しました。
 *	- バージョン表示を追加しました。
 *	* Tue Jul  9 03:34:00 JST 2002 Naoyuki Sawa <nsawa@north.hokkai.net>
 *	- バイナリ出力をデフォルトにしました。
 *	  プロジェクト名と同じ名前のアイコンファイルを生成しようとして、
 *	  うっかりproject.bmpからproject.c(テキスト形式のアイコンデータ)を生成してしまい、
 *	  project.c(メインプログラムソース)を上書きしてしまう事故が多発したからです。
 *	- 今回の変更により、テキスト出力をデフォルトと想定していたMakefileなどに
 *	  影響が出るかも知れません。気付いたら、適宜修正する予定です。
 *	* Tue Jul 16 19:00:00 JST 2002 Naoyuki Sawa <nsawa@north.hokkai.net>
 *	- 入力ファイル名のサフィックスによる、暗黙の出力フォーマット指定を追加しました。
 *	  "-f"オプションが指定された場合は、暗黙の出力フォーマット指定よりも優先します。
 *	- このように変更した理由は、Makefileの中で.bmpから.cへのサフィックスルールを一つ定義すれば、
 *	  ファイル名によって出力フォーマットを自動的に切り替えられるようにしたいと思ったからです。
 *	- これまでは、"-f"オプションが指定されなかった場合は、常に2ビットMASK付きで出力していました。
 *	  今回の変更により、"-f"オプションを指定しておらず、2ビットMASK付きの出力を期待していて、
 *	  かつ、たまたまサフィックスがフォーマット指定サフィックスに一致するような変換を行っていた
 *	  プロジェクトに影響が出るかも知れません。気付いたら、適宜修正する予定です。
 *	- メッセージを日本語に変更しました。
 *	* Thu Aug  8 06:48:00 JST 2002 Naoyuki Sawa <nsawa@north.hokkai.net>
 *	- フォーマット指定のためのファイル名サフィックスを変更。
 *	  これまでi,s,1,1m,2,2mでしたが、-i,-s,-1,-1m,-2,-2mに変更しました。
 *	- ファイル名サフィックスからフォーマットを自動決定し、かつ出力ラベルが明示されていないときは、
 *	  出力ラベルからファイル名サフィックスの部分を省くように変更しました。
 *	  例: ファイル名="test-2m.bmp"→フォーマット=2bitマスク付き、出力ラベル="TEST"
 *	- 入力ファイル名が"pat"で、出力フォーマットが明示されていなければ、スプライト形式としました。
 *	  たぶん、シンプルライブラリ用のスプライトデータと思われるからです。
 *	* Fri Nov 22 06:58:00 JST 2002 Naoyuki Sawa
 *	- ファイル名サフィックスによる指定「-2d」(2bitマスクなしへの減色)を追加しました。
 *	- 減色モードなら「OUT FMT:」の表示に"(DEC)"を追加するようにしました。
 *	* Mon Dec 2 05:48:00 JST 2002 Naoyuki Sawa
 *	- 入力ファイル名が"font"で、出力フォーマットが明示されていなければ、1ビットマスクなし形式としました。
 *	* Fri Apr 2 12:30:00 JST 2004 Naoyuki Sawa
 *	- CHIP-8/SCHIPスプライトデータ出力に対応しました。
 *	  コマンドラインオプション"-fc8","-fsc"または、ファイル名サフィックス"-c8","-sc"にて指定可能です。
 *	  これらの指定は暗黙に、Chipperアセンブラ用出力形式の指示も含んでいます。
 *	  TEXT/BINARYの出力形式指定と併用することはできません。
 *	* Sun Jun 6 21:21:00 JST 2004 Naoyuki Sawa
 *	- 16階調のPieceBitmap独自拡張形式(-4,-4m)に対応しました。
 *	* Fri Jun 25 05:30:00 JST 2004 Naoyuki Sawa
 *	- 2ビット4階調→4ビット16階調拡張形式を追加しました。(単独オプション-i、形式オプション-f4i-f4im、サフィックス-4i,-4im)
 *	  2ビット用のビットマップ画像から、4ビット用のPGDを作成します。
 *	  具体的には、入力ビットマップのカラーコード{0,1,2,3}を、カラーコード{0,5,10,15}に変換してから処理しているだけです。
 *	* Wed May 11 19:30:00 JST 2005 Naoyuki Sawa
 *	- Signetics 2637 UVI (バイダイ社アルカディアのCRTC) Graphics Mode = 1, Color Mode = 0 キャラクタ出力に対応しました。
 *	  コマンドラインオプション"-fuvi"または、ファイル名サフィックス"-uvi"にて指定可能です。
 *	  これらの指定は暗黙に、VACSアセンブラ用出力形式の指示も含んでいます。
 *	  TEXT/BINARYの出力形式指定と併用することはできません。
 *	* Thu Dec 27 13:11:37 JST 2007 Naoyuki Sawa
 *	- これまでの8->2bit減色に加えて、8->4bit減色を追加しました。
 */
#define STRICT
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void usage();
void cmdline(int argc, char* argv[]);
void readbmp(FILE* fp);
void writepgd(FILE* fp, int bpp, int mask);
void writepsp(FILE* fp);
void writepid(FILE* fp);
void writechip8(FILE* fp);	/* 2004/04/02追加 */
void writeschip(FILE* fp);	/* 2004/04/02追加 */
void writeuvi(FILE* fp);	/* 2005/05/11追加 */

/****************************************************************************
 *
 ****************************************************************************/

/* /usr/piece/include/piece_bmp.hより */
typedef struct _PBMP_FILEHEADER {
	unsigned long head;		/* + 0,4 シグネチャ'PBMP' */
	unsigned long fsize;		/* + 4,4 ファイルサイズ   */
	unsigned char bpp;		/* + 8,1 ビット深度       */
	unsigned char mask;		/* + 9,1 マスク深度       */
	short w;			/* +10,2 幅               */
	short h;			/* +12,2 高さ             */
					/* +14,2 パディング       */
	unsigned long buf_size;		/* +16,4 データサイズ     */
					/* =20                    */
} PBMP_FILEHEADER;

/****************************************************************************
 *	
 ****************************************************************************/

#define VERSION	"20071227"

#define NULDEV	"NUL"
FILE* msg = stdout;

#define TYPE_TEXT	0
#define TYPE_BINARY	1
#define TYPE_CHIPPER	2	/* 2004/04/02追加 */
#define TYPE_VACS	3	/* 2005/05/11追加 */
char* out_type_desc[] = {
	"TEXT",
	"BINARY",
	"CHIPPER",		/* 2004/04/02追加 */
	"VACS",			/* 2005/05/11追加 */
};
int out_type = -1;

#define FMT_ICON	 0
#define FMT_SPRITE	 1
#define FMT_1BIT	 2
#define FMT_1BITMASK	 3
#define FMT_2BIT	 4
#define FMT_2BITMASK	 5
#define FMT_4BIT	 6	/* 2004/06/06追加 */
#define FMT_4BITMASK	 7	/* 2004/06/06追加 */
#define FMT_CHIP8	 8	/* 2004/04/02追加 */
#define FMT_SCHIP	 9	/* 2004/04/02追加 */
#define FMT_UVI		10	/* 2005/05/11追加 */
char* out_fmt_desc[] = {
	"ICON",
	"SPRITE",
	"1BIT",
	"1BIT MASK",
	"2BIT",
	"2BIT MASK",
	"4BIT",			/* 2004/06/06追加 */
	"4BIT MASK",		/* 2004/06/06追加 */
	"CHIP8",		/* 2004/04/02追加 */
	"SCHIP",		/* 2004/04/02追加 */
	"UVI",			/* 2005/05/11追加 */
};
int out_fmt = -1;

char out_label[32];
char out_prefix[32];

char in_path[_MAX_PATH];
char out_path[_MAX_PATH];
char drive[_MAX_DRIVE];
char dir[_MAX_DIR];
char fname[_MAX_FNAME];
char ext[_MAX_EXT];

#define MAX_SIZE	4096
unsigned char in_data[MAX_SIZE][MAX_SIZE];
int in_width;
int in_height;
int out_width;
int out_height;

const int DITHER2[8][8] = {
	 0,32, 8,40, 2,34,10,42,
	48,16,56,24,50,18,58,26,
	12,44, 4,36,14,46, 6,38,
	60,28,52,20,62,30,54,22,
	 3,35,11,43, 1,33, 9,41,
	51,19,59,27,49,17,57,25,
	15,47, 7,39,13,45, 5,37,
	63,31,55,23,61,29,53,21,
};
const int DITHER4[4][4] = {
	 0, 8, 2,10,
	12, 4,14, 6,
	 3,11, 1, 9,
	15, 7,13, 5,
};
int dec_color; /* 「-d」オプションが指定されたら1 */
int inc_color; /* 「-i」オプションが指定されたら1 */
int rev_pal;   /* 「-r」オプションが指定されたら1 */

/****************************************************************************
 *	main
 ****************************************************************************/

int
main(int argc, char* argv[])
{
	FILE* fp;

	/* コマンドラインを解析します。 */
	cmdline(argc, argv);

	/* ビットマップを読み込みます。 */
	fp = fopen(in_path, "rb");
	if(fp == NULL) {
		fprintf(stderr, "### %s ファイルが開けません。\n", in_path);
		exit(1);
	}
	readbmp(fp);
	fclose(fp);

	/* 出力ファイルを作成します。 */
	switch(out_type) {
	case TYPE_TEXT:
	case TYPE_CHIPPER:	/* 2004/04/02追加 */
	case TYPE_VACS:		/* 2005/05/11追加 */
		fp = fopen(out_path, "wt");
		break;
	case TYPE_BINARY:
		fp = fopen(out_path, "wb");
		break;
	default:
		abort();
	}
	if(fp == NULL) {
		fprintf(stderr, "### %s ファイルが作成できません。\n", out_path);
		exit(1);
	}

	/* データを出力します。 */
	switch(out_fmt) {
	case FMT_ICON:
		writepid(fp);
		break;
	case FMT_SPRITE:
		writepsp(fp);
		break;
	case FMT_1BIT:
		writepgd(fp, 1, 0);
		break;
	case FMT_1BITMASK:
		writepgd(fp, 1, 1);
		break;
	case FMT_2BIT:
		writepgd(fp, 2, 0);
		break;
	case FMT_2BITMASK:
		writepgd(fp, 2, 1);
		break;
	case FMT_4BIT:		/* 2004/06/06追加 */
		writepgd(fp, 4, 0);
		break;
	case FMT_4BITMASK:	/* 2004/06/06追加 */
		writepgd(fp, 4, 1);
		break;
	case FMT_CHIP8:		/* 2004/04/02追加 */
		writechip8(fp);
		break;
	case FMT_SCHIP:		/* 2004/04/02追加 */
		writeschip(fp);
		break;
	case FMT_UVI:		/* 2005/05/11追加 */
		writeuvi(fp);
		break;
	default:
		abort();
	}

	/* 出力ファイルを閉じます。 */
	fclose(fp);

	return 0;
}

/****************************************************************************
 *	usage
 ****************************************************************************/

void
usage()
{
	fprintf(stderr, "dpbmpcnv - BMP to PGD converter (%s)\n", VERSION);
	fprintf(stderr, "Copyright (C) 2002-2007 Naoyuki Sawa\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "USAGE:\n");
	fprintf(stderr, "    dpbmpcnv [options] <filename.bmp>\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "TYPE OPTIONS:\n");
	fprintf(stderr, "    -t         テキスト\n");
	fprintf(stderr, "  * -b         バイナリ\n");
	fprintf(stderr, "FORMAT OPTIONS:\n");
	fprintf(stderr, "    -fi        アイコン\n");
	fprintf(stderr, "    -fs        スプライト\n");
	fprintf(stderr, "    -f1        １ビット\n");
	fprintf(stderr, "    -f1m       １ビットマスク付き（独自拡張）\n");
	fprintf(stderr, "    -f2        ２ビット\n");
	fprintf(stderr, "  * -f2m       ２ビットマスク付き\n");
	fprintf(stderr, "    -f4        ４ビット（独自拡張）\n");
	fprintf(stderr, "    -f4m       ４ビットマスク付き（独自拡張）\n");
	fprintf(stderr, "    -f4i       ２ビット→４ビット拡張（独自拡張）\n");
	fprintf(stderr, "    -f4im      ２ビット→４ビット拡張、マスク付き（独自拡張）\n");
	fprintf(stderr, "    -fc8       CHIP-8（TYPE OPTIONSとの併用不可）\n");
	fprintf(stderr, "    -fsc       SCHIP （TYPE OPTIONSとの併用不可）\n");
	fprintf(stderr, "    -fuvi      Signetics 2637 UVI （TYPE OPTIONSとの併用不可）\n");
	fprintf(stderr, "    ファイル名サフィックスによるフォーマット指定も可能です。\n");
	fprintf(stderr, "    (-i,-s,-1,-1m,-2,-2m,-2d,-4,-4m,-4i,-4im,-4d,-c8,-sc,-uvi)\n");
	fprintf(stderr, "    オプション指定はサフィックス指定よりも優先されます。\n");
	fprintf(stderr, "OTHER OPTIONS:\n");
	fprintf(stderr, "    -d         ８ビットカラー→２/４ビット減色（試験的な実装）\n");
	fprintf(stderr, "    -i         ２ビット→４ビット拡張（試験的な実装）\n");
	fprintf(stderr, "    -o<fname>  出力ファイル名\n");
	fprintf(stderr, "    -l<label>  ラベル名（テキスト時のみ）\n");
	fprintf(stderr, "    -p<prefix> ラベルプレフィクス（テキスト時のみ）\n");
	fprintf(stderr, "    -r         パレット反転\n");
	fprintf(stderr, "    -q         メッセージ抑制\n");
	fprintf(stderr, "（* 初期値）\n");
	exit(1);
}

/****************************************************************************
 *	cmdline
 ****************************************************************************/

void
cmdline(int argc, char* argv[])
{
	int i, len;
	char* opt;

	for(i = 1; i < argc; i++) {
		opt = argv[i];
		if(opt[0] != '-') {
			if(strlen(in_path) != 0) usage();
			strcpy(in_path, opt);
		} else {
			switch(opt[1]) {
			case 't':
				if(out_type != -1) usage();
				out_type = TYPE_TEXT;
				break;
			case 'b':
				if(out_type != -1) usage();
				out_type = TYPE_BINARY;
				break;
			case 'f':
				if(out_fmt != -1) usage();
				       if(strcmp(&opt[2], "i") == 0) {
					out_fmt = FMT_ICON;
				} else if(strcmp(&opt[2], "s") == 0) {
					out_fmt = FMT_SPRITE;
				} else if(strcmp(&opt[2], "1") == 0) {
					out_fmt = FMT_1BIT;
				} else if(strcmp(&opt[2], "1m") == 0) {
					out_fmt = FMT_1BITMASK;
				} else if(strcmp(&opt[2], "2") == 0) {
					out_fmt = FMT_2BIT;
				} else if(strcmp(&opt[2], "2m") == 0) {
					out_fmt = FMT_2BITMASK;
				} else if(strcmp(&opt[2], "4") == 0) {		/* 2004/06/06追加 */
					out_fmt = FMT_4BIT;
				} else if(strcmp(&opt[2], "4m") == 0) {		/* 2004/06/06追加 */
					out_fmt = FMT_4BITMASK;
				} else if(strcmp(&opt[2], "c8") == 0) {		/* 2004/04/02追加 */
					if(out_type != -1) usage();
					out_type = TYPE_CHIPPER;
					out_fmt = FMT_CHIP8;
				} else if(strcmp(&opt[2], "sc") == 0) {		/* 2004/04/02追加 */
					if(out_type != -1) usage();
					out_type = TYPE_CHIPPER;
					out_fmt = FMT_SCHIP;
				} else if(strcmp(&opt[2], "uvi") == 0) {	/* 2005/05/11追加 */
					if(out_type != -1) usage();
					out_type = TYPE_VACS;
					out_fmt = FMT_UVI;
				} else {
					usage();
				}
				break;
			case 'd':
				if(dec_color) usage();
				dec_color = 1;
				break;
			case 'i':	/* 2004/06/25追加 */
				if(inc_color) usage();
				inc_color = 1;
				break;
			case 'o':
				if(strlen(out_path) != 0) usage();
				strcpy(out_path, &opt[2]);
				break;
			case 'l':
				if(strlen(out_label) != 0) usage();
				strcpy(out_label, &opt[2]);
				break;
			case 'p':
				if(strlen(out_prefix) != 0) usage();
				strcpy(out_prefix, &opt[2]);
				break;
			case 'q':
				msg = fopen(NULDEV, "wt");
				break;
			case 'r':
				if(rev_pal) usage();
				rev_pal = 1;
				break;
			default:
				usage();
			}
		}
	}

	/* 入力ファイル名は必須です。 */
	if(strlen(in_path) == 0) usage();

	/* 入力ファイル名の拡張子が省略されていたら、「.bmp」とします。 */
	_splitpath(in_path, drive, dir, fname, ext);
	if(strlen(ext) == 0) {
		_makepath(in_path, drive, dir, fname, ".bmp");
	}

	/* 出力フォーマットが指定されていなければ... */
	if(out_fmt == -1) {
		_splitpath(in_path, drive, dir, fname, ext);
		/* 入力ファイル名のサフィックスから、出力フォーマットを決定します。
		 * 入力ファイル名のサフィックスから出力フォーマットを自動決定した場合は、
		 * 出力ラベルにサフィックス部分を含めません。
		 * * Fri Nov 22 06:58:00 JST 2002 Naoyuki Sawa
		 * - ファイル名サフィックスによる指定「-2d」(2bitマスクなしへの減色)を追加しました。
		 */
		len = strlen(fname);
		       if(len >= 2 && strcmpi(&fname[len - 2], "-i" ) == 0) {
			out_fmt = FMT_ICON;
			if(strlen(out_label) == 0) {
				memcpy(out_label, fname, len - 2);
				_strupr(out_label);
			}
		} else if(len >= 2 && strcmpi(&fname[len - 2], "-s" ) == 0) {
			out_fmt = FMT_SPRITE;
			if(strlen(out_label) == 0) {
				memcpy(out_label, fname, len - 2);
				_strupr(out_label);
			}
		} else if(len >= 2 && strcmpi(&fname[len - 2], "-1" ) == 0) {
			out_fmt = FMT_1BIT;
			if(strlen(out_label) == 0) {
				memcpy(out_label, fname, len - 2);
				_strupr(out_label);
			}
		} else if(len >= 3 && strcmpi(&fname[len - 3], "-1m") == 0) {
			out_fmt = FMT_1BITMASK;
			if(strlen(out_label) == 0) {
				memcpy(out_label, fname, len - 3);
				_strupr(out_label);
			}
		} else if(len >= 2 && strcmpi(&fname[len - 2], "-2" ) == 0) {
			out_fmt = FMT_2BIT;
			if(strlen(out_label) == 0) {
				memcpy(out_label, fname, len - 2);
				_strupr(out_label);
			}
		} else if(len >= 3 && strcmpi(&fname[len - 3], "-2m") == 0) {
			out_fmt = FMT_2BITMASK;
			if(strlen(out_label) == 0) {
				memcpy(out_label, fname, len - 3);
				_strupr(out_label);
			}
		} else if(len >= 3 && strcmpi(&fname[len - 3], "-2d") == 0) {	/* 2002/11/22追加 */
			out_fmt = FMT_2BIT;
			dec_color = 1;
			if(strlen(out_label) == 0) {
				memcpy(out_label, fname, len - 3);
				_strupr(out_label);
			}
		} else if(len >= 2 && strcmpi(&fname[len - 2], "-4" ) == 0) {	/* 2004/06/06追加 */
			out_fmt = FMT_4BIT;
			if(strlen(out_label) == 0) {
				memcpy(out_label, fname, len - 2);
				_strupr(out_label);
			}
		} else if(len >= 3 && strcmpi(&fname[len - 3], "-4m") == 0) {	/* 2004/06/06追加 */
			out_fmt = FMT_4BITMASK;
			if(strlen(out_label) == 0) {
				memcpy(out_label, fname, len - 3);
				_strupr(out_label);
			}
		} else if(len >= 2 && strcmpi(&fname[len - 3], "-4i" ) == 0) {	/* 2004/06/25追加 */
			out_fmt = FMT_4BIT;
			inc_color = 1;
			if(strlen(out_label) == 0) {
				memcpy(out_label, fname, len - 3);
				_strupr(out_label);
			}
		} else if(len >= 3 && strcmpi(&fname[len - 4], "-4im") == 0) {	/* 2004/06/25追加 */
			out_fmt = FMT_4BITMASK;
			inc_color = 1;
			if(strlen(out_label) == 0) {
				memcpy(out_label, fname, len - 4);
				_strupr(out_label);
			}
		} else if(len >= 3 && strcmpi(&fname[len - 3], "-4d") == 0) {	/* 2007/12/27追加 */
			out_fmt = FMT_4BIT;
			dec_color = 1;
			if(strlen(out_label) == 0) {
				memcpy(out_label, fname, len - 3);
				_strupr(out_label);
			}
		} else if(len >= 3 && strcmpi(&fname[len - 3], "-c8") == 0) {	/* 2004/04/02追加 */
			if(out_type != -1) usage();
			out_type = TYPE_CHIPPER;
			out_fmt = FMT_CHIP8;
			if(strlen(out_label) == 0) {
				memcpy(out_label, fname, len - 3);
				_strupr(out_label);
			}
		} else if(len >= 3 && strcmpi(&fname[len - 3], "-sc") == 0) {	/* 2004/04/02追加 */
			if(out_type != -1) usage();
			out_type = TYPE_CHIPPER;
			out_fmt = FMT_SCHIP;
			if(strlen(out_label) == 0) {
				memcpy(out_label, fname, len - 3);
				_strupr(out_label);
			}
		} else if(len >= 4 && strcmpi(&fname[len - 4], "-uvi") == 0) {	/* 2005/05/11追加 */
			if(out_type != -1) usage();
			out_type = TYPE_VACS;
			out_fmt = FMT_UVI;
			if(strlen(out_label) == 0) {
				memcpy(out_label, fname, len - 4);
				_strupr(out_label);
			}
		} else if(strcmpi(fname, "pat") == 0) { /* たぶんシンプルライブラリ用のスプライトデータ */
			out_fmt = FMT_SPRITE;
		} else if(strcmpi(fname, "font") == 0) { /* たぶんフォントデータなので1ビットマスクなし */
			out_fmt = FMT_1BIT;
		} else {
			fprintf(stderr, "### 警告 出力フォーマットが指定されていません。\n");
			out_fmt = FMT_2BITMASK; /* どれにも一致しなければデフォルト値を設定 */
		}
	}

	/* タイプが指定されていなければ、デフォルト値を設定します。 */
	if(out_type == -1) out_type = TYPE_BINARY;

	/* 出力ファイル名が省略されていたら、入力ファイル名から生成します。 */
	if(strlen(out_path) == 0) {
		_splitpath(in_path, drive, dir, fname, ext);
		switch(out_type) {
		case TYPE_TEXT:
			_makepath(out_path, drive, dir, fname, ".c");
			break;
		case TYPE_BINARY:
			switch(out_fmt) {
			case FMT_ICON:
				_makepath(out_path, drive, dir, fname, ".pid");
				break;
			case FMT_SPRITE:
				_makepath(out_path, drive, dir, fname, ".psp");
				break;
			default:
				_makepath(out_path, drive, dir, fname, ".pgd");
				break;
			}
			break;
		case TYPE_CHIPPER:	/* 2004/04/02追加 */
			_makepath(out_path, drive, dir, fname, ".chp");
			break;
		case TYPE_VACS:		/* 2005/05/11追加 */
			_makepath(out_path, drive, dir, fname, ".asm");
			break;
		default:
			abort();
		}
	}

	/* 出力ラベルが省略されていたら、入力ファイル名から生成します。 */
	if(strlen(out_label) == 0) {
		_splitpath(in_path, NULL, NULL, out_label, NULL);
		_strupr(out_label);
	}

	/* 出力プレフィクスが指定されていたら、ラベルに追加します。 */
	if(strlen(out_prefix) != 0) {
		strcat(out_prefix, out_label);
		strcpy(out_label, out_prefix);
	}

	/* * Fri Nov 22 06:58:00 JST 2002 Naoyuki Sawa
	 * - 減色モードなら「OUT FMT:」の表示に"(DEC)"を追加するようにしました。
	 * * Fri Jun 25 05:30:00 JST 2004 Naoyuki Sawa
	 * - ２ビット→４ビット拡張モードなら「OUT FMT:」の表示に"(INC)"を追加するようにしました。
	 */
	fprintf(msg, "IN  FILE : %s\n", in_path);
	fprintf(msg, "OUT FILE : %s\n", out_path);
	fprintf(msg, "OUT TYPE : %s\n", out_type_desc[out_type]);
	if(dec_color) {
		fprintf(msg, "OUT FMT  : %s (DEC)\n", out_fmt_desc[out_fmt]);
	} else if(inc_color) {
		fprintf(msg, "OUT FMT  : %s (INC)\n", out_fmt_desc[out_fmt]);
	} else {
		fprintf(msg, "OUT FMT  : %s\n", out_fmt_desc[out_fmt]);
	}
	fprintf(msg, "OUT LABEL: %s\n", out_label);
}

/****************************************************************************
 *	readbmp
 ****************************************************************************/

void
readbmp(FILE* fp)
{
	int retval, pitch, x, y, n, c, r, g, b;
	BITMAPFILEHEADER bf;
	BITMAPINFOHEADER bi;
	RGBQUAD palette[256];

	/* サイズ調整された場合、パディング部分を透明にするために、
	 * in_dataを透明値でクリアしておきます。
	 */
	memset(in_data, -1, sizeof in_data);

	/* ファイルヘッダを読みます。 */
	retval = fread(&bf, 1, sizeof(BITMAPFILEHEADER), fp);
	if(retval != sizeof(BITMAPFILEHEADER)) goto ERR;
	if(strncmp((char*)&bf.bfType, "BM", 2) != 0) goto ERR;

	/* ビットマップヘッダを読みます。 */
	retval = fread(&bi, 1, sizeof(BITMAPINFOHEADER), fp);
	if(retval != sizeof(BITMAPINFOHEADER)) goto ERR;
	if(bi.biSize < sizeof(BITMAPINFOHEADER)) goto ERR;
	if(bi.biWidth > MAX_SIZE) goto ERR;
	if(bi.biHeight > MAX_SIZE) goto ERR;
	if(bi.biPlanes != 1) goto ERR;   /* 256色パレットBMPのみ対応 */
	if(bi.biBitCount != 8) goto ERR; /* 256色パレットBMPのみ対応 */
	if(bi.biCompression != BI_RGB) goto ERR;

	/* 減色ありなら、パレットを読み込みます。 */
	if(dec_color) {
		n = bi.biClrUsed != 0 ? bi.biClrUsed
		                      : bi.biPlanes << bi.biBitCount;
		if(n < 0 || 256 < n) goto ERR; /* 壊れてる？ */
		retval = fread(palette, sizeof(RGBQUAD), n, fp);
		if(retval != n) goto ERR;
	}

	/* 入力画像サイズとラインストライドを求めます。 */
	in_width = bi.biWidth;
	in_height = bi.biHeight;
	pitch = in_width + 3 & ~3; /* DIBの仕様 */

	/* 入力画像を読み込みます。 */
	retval = fseek(fp, bf.bfOffBits, SEEK_SET);
	if(retval != 0) goto ERR;
	for(y = in_height - 1; y >= 0; y--) { /* ボトムアップ */
		retval = fread(&in_data[y][0], 1, in_width, fp); /* pitch[byte]読んでしまうとゴミが入る恐れアリ */
		if(retval != in_width) goto ERR;
		retval = fseek(fp, pitch - in_width, SEEK_CUR);
		if(retval != 0) goto ERR;
	}

	/* 減色処理。 */
	if(dec_color) {
		for(y = 0; y < in_height; y++) {
			for(x = 0; x < in_width; x++) {
				c = in_data[y][x];					/* c=色番号(0〜255) */
				r = palette[c].rgbRed;					/* r=赤成分(0〜255) */
				g = palette[c].rgbGreen;				/* g=緑成分(0〜255) */
				b = palette[c].rgbBlue;					/* b=青成分(0〜255) */
				c = (r * 3 + g * 5 + b * 2) / 10;			/* c=輝度  (0〜255) */
				switch(out_fmt) {
				case FMT_2BIT:
				case FMT_2BITMASK:
					c = (c + DITHER2[y & 7][x & 7] + (85 - 63) / 2) / 85;	/* c=輝度  (0〜3) */
					if(c > 3) c = 3;					/* ありえないはずだが、念のため */
					c = 3 - c;						/* 液晶では輝度が逆 */
					break;
				case FMT_4BIT:
				case FMT_4BITMASK:
					c = (c + DITHER4[y & 3][x & 3] + (17 - 15) / 2) / 17;	/* c=輝度  (0〜15) */
					if(c > 15) c = 15;					/* ありえないはずだが、念のため */
					c = 15 - c;						/* 液晶では輝度が逆 */
					break;
				default:
					/** ※TODO: 2007/12/27現在は、何もしません。 */
					break;
				}
				in_data[y][x] = c;
			}
		}
	}

	/* パレット反転処理。 */
	if(rev_pal) {
		for(y = 0; y < in_height; y++) {
			for(x = 0; x < in_width; x++) {
				c = in_data[y][x];
				switch(out_fmt) {
				case FMT_2BIT:
				case FMT_2BITMASK:
					if(0 <= c && c <= 3) { /* カラー0〜3のみ反転 */
						c = 3 - c;
					}
					break;
				case FMT_4BIT:
				case FMT_4BITMASK:
					if(0 <= c && c <= 15) { /* カラー0〜15のみ反転 */
						c = 15 - c;
					}
					break;
				default:
					/** ※TODO: 2007/12/27現在は、何もしません。 */
					break;
				}
				in_data[y][x] = c;
			}
		}
	}

	/* ２ビット→４ビット拡張処理。 */
	if(inc_color) {
		for(y = 0; y < in_height; y++) {
			for(x = 0; x < in_width; x++) {
				c = in_data[y][x];
				if(c >= 0 && c <= 3) {
					c = c << 2 | c;	/* {0,1,2,3}→{0,5,10,15} */
				} else {
					c = 255;	/* {4〜255}→{255} */
				}
				in_data[y][x] = c;
			}
		}
	}

	/* サイズ調整。 */
	switch(out_fmt) {
	case FMT_ICON:
		/* アイコンの仕様により、32x32ピクセルに固定します。
		 * 元データの大きい部分は無視、足りない部分はゴミが入ります。
		 */
		out_width  = 32;
		out_height = 32;
		break;
	case FMT_SPRITE:
		/* スプライトの仕様により、幅と高さを8ピクセル単位に切り上げます。
		 * 元データの足りない部分は、ゴミが入ります。
		 */
		out_width  = in_width  + 7 & ~7;
		out_height = in_height + 7 & ~7;
		break;
	case FMT_1BIT:
	case FMT_1BITMASK:
	case FMT_2BIT:
	case FMT_2BITMASK:
	case FMT_4BIT:		/* 2004/06/06追加 */
	case FMT_4BITMASK:	/* 2004/06/06追加 */
		/* PIECE_BMPの仕様により、幅を8ピクセル単位に切り上げます。
		 * 元データの足りない部分は、ゴミが入ります。
		 * 高さは自由なので、元データそのままとします。
		 */
		out_width  = in_width + 7 & ~7;
		out_height = in_height;
		break;
	case FMT_CHIP8:		/* 2004/04/02追加 */
		/* CHIP-8の"DRW VX,VY,Nibble"命令の仕様により、幅を8ピクセル単位に切上げます。
		 * 元データの足りない部分は、ゴミが入ります。
		 * 高さは自由なので、元データそのままとします。
		 */
		out_width  = in_width + 7 & ~7;
		out_height = in_height;
		break;
	case FMT_SCHIP:		/* 2004/04/02追加 */
		/* SCHIPの"DRW VX,VY,0"命令の仕様により、幅と高さを16ピクセル単位に切上げます。
		 * 元データの足りない部分は、ゴミが入ります。
		 */
		out_width  = in_width  + 15 & ~15;
		out_height = in_height + 15 & ~15;
		break;
	case FMT_UVI:		/* 2005/05/11追加 */
		/* UVIのGraphics Modeの仕様により、幅を3ピクセル単位、高さを2ピクセル単位に切り上げます。
		 * 元データの足りない部分は、ゴミが入ります。
		 */
		out_width  = (in_width  + 2) / 3 * 3;
		out_height = (in_height + 1) / 2 * 2;
		break;
	default:
		abort();
	}
	if(out_width != in_width || out_height != in_height) {
		fprintf(stderr, "### 警告 出力イメージサイズを調整しました。\n");
		/* 継続 */
	}

	return;
ERR:
	fprintf(stderr, "### ビットマップの読み込みに失敗しました。\n");
	exit(1);
}

/****************************************************************************
 *	writepid
 ****************************************************************************/

void
writepid(FILE* fp)
{
	int x, y, i;
	unsigned char in_byte, out_byte;

	/* C言語配列の開始。 */
	if(out_type == TYPE_TEXT) {
		fprintf(fp, "const unsigned char %s[] = {\n", out_label);
	}

	/* カラーデータを出力します。 */
	for(y = 0; y < out_height; y++) {
		for(x = 0; x < out_width; x += (8 / 2)) {
			out_byte = 0;
			for(i = 0; i < (8 / 2); i++) {
				out_byte <<= 2;
				in_byte = in_data[y][x + i];
				if(in_byte < 4) {
					out_byte |= in_byte;
				}
			}
			if(out_type == TYPE_TEXT) {
				fprintf(fp, "0x%02x,", out_byte);
			} else {
				fwrite(&out_byte, 1, 1, fp);
			}
		}
		if(out_type == TYPE_TEXT) {
			fprintf(fp, "\n");
		}
	}

	/* C言語配列の終了。 */
	if(out_type == TYPE_TEXT) {
		fprintf(fp, "};\n");
	}
}

/****************************************************************************
 *	writepsp
 ****************************************************************************/

void
writepsp(FILE* fp)
{
	int i, x1, y1, x2, y2;
	unsigned char in_byte, out_bit0, out_bit1, out_mask;

	/* C言語配列の開始。 */
	if(out_type == TYPE_TEXT) {
		fprintf(fp, "const unsigned char %s[] = {\n", out_label);
	}

	/* 各キャラクタについて... */
	i = 0;
	for(y1 = 0; y1 < out_height; y1 += 8) {
		for(x1 = 0; x1 < out_width; x1 += 8) {
			/* 各ラインについて... */
			for(y2 = 0; y2 < 8; y2++) {
				out_bit0 = 0;
				out_bit1 = 0;
				out_mask = 0;
				/* 各ピクセルについて... */
				for(x2 = 0; x2 < 8; x2++) {
					in_byte = in_data[y1 + y2][x1 + x2];
					/* 左:LSB<->MSB:右 */
					out_bit0 >>= 1;
					out_bit1 >>= 1;
					out_mask >>= 1;
					if(in_byte < 4) {
						if(in_byte & 1) out_bit0 |= 0x80;
						if(in_byte & 2) out_bit1 |= 0x80;
					} else {
						out_mask |= 0x80; /* 透明が1 */
					}
				}
				/* bit0と1の順番に注意！bit1が先。直感と逆です。 */
				if(out_type == TYPE_TEXT) {
					fprintf(fp, "0x%02x,", out_bit1);
					fprintf(fp, "0x%02x,", out_bit0);
					fprintf(fp, "0x%02x,", out_mask);
				} else {
					fwrite(&out_bit1, 1, 1, fp);
					fwrite(&out_bit0, 1, 1, fp);
					fwrite(&out_mask, 1, 1, fp);
				}
			}
			if(out_type == TYPE_TEXT) {
				fprintf(fp, " /*%03x*/\n", i++);
			}
		}
	}

	/* C言語配列の終了。 */
	if(out_type == TYPE_TEXT) {
		fprintf(fp, "};\n");
	}
}

/****************************************************************************
 *	writepgd
 ****************************************************************************/

void
writepgd(FILE* fp, int bpp, int mask)
{
	PBMP_FILEHEADER hdr;
	int x, y, i;
	unsigned char in_byte, out_byte;

	/* C言語配列の開始。 */
	if(out_type == TYPE_TEXT) {
		fprintf(fp, "const unsigned char %s[] = {\n", out_label);
	}

	/* ヘッダを出力します。 */
	memset(&hdr, 0, sizeof hdr);
	memcpy(&hdr.head, "PMBP", 4); /* シグネチャ */
	         hdr.fsize = sizeof hdr + out_width * out_height / (8 / bpp); /* ヘッダとカラーのサイズ */
	if(mask) hdr.fsize +=             out_width * out_height /  8;        /* マスクのサイズを追加   */
	hdr.bpp  = bpp;
	hdr.mask = mask;
	hdr.w = out_width;
	hdr.h = out_height;
	         hdr.buf_size =  out_width * out_height / (8 / bpp); /* カラーのサイズ       */
	if(mask) hdr.buf_size += out_width * out_height /  8;        /* マスクのサイズを追加 */
	if(out_type == TYPE_TEXT) {
		for(i = 0; i < sizeof hdr; i++) {
			fprintf(fp, "0x%02x,", ((unsigned char*)&hdr)[i]);
		}
		fprintf(fp, "\n");
	} else {
		fwrite(&hdr, 1, sizeof hdr, fp);
	}

	/* カラーデータを出力します。 */
	if(out_type == TYPE_TEXT) {
		fprintf(fp, "/*buf*/\n");
	}
	for(y = 0; y < out_height; y++) {
		for(x = 0; x < out_width; x += (8 / bpp)) {
			out_byte = 0;
			for(i = 0; i < (8 / bpp); i++) {
				out_byte <<= bpp;
				in_byte = in_data[y][x + i];
				//if(in_byte < 4) {
				//	if(bpp == 1 && in_byte != 0) in_byte = 1; /* 1bit時は、色1〜3は1と見なす */
				//	out_byte |= in_byte;
				//}
				//↓2004/06/06変更。1bit時の色2以降は透過色とみなすようになりました。
				if(in_byte < (1 << bpp)) {
					out_byte |= in_byte;
				}
			}
			if(out_type == TYPE_TEXT) {
				fprintf(fp, "0x%02x,", out_byte);
			} else {
				fwrite(&out_byte, 1, 1, fp);
			}
		}
		if(out_type == TYPE_TEXT) {
			fprintf(fp, "\n");
		}
	}

	/* マスクデータを出力します。 */
	if(mask) {
		if(out_type == TYPE_TEXT) {
			fprintf(fp, "/*mask*/\n");
		}
		for(y = 0; y < out_height; y++) {
			for(x = 0; x < out_width; x += 8) {
				out_byte = 0;
				for(i = 0; i < 8; i++) {
					out_byte <<= 1;
					in_byte = in_data[y][x + i];
					//if(in_byte < 4) {
					//	out_byte |= 1;
					//}
					//↓2004/06/06変更
					if(in_byte < (1 << bpp)) {
						out_byte |= 1;
					}
				}
				if(out_type == TYPE_TEXT) {
					fprintf(fp, "0x%02x,", out_byte);
				} else {
					fwrite(&out_byte, 1, 1, fp);
				}
			}
			if(out_type == TYPE_TEXT) {
				fprintf(fp, "\n");
			}
		}
	}

	/* C言語配列の終了。 */
	if(out_type == TYPE_TEXT) {
		fprintf(fp, "};\n");
	}
}

/****************************************************************************
 *	writechip8
 ****************************************************************************/

void
writechip8(FILE* fp)	/* 2004/04/02追加 */
{
	int i, y, x1, x2;
	unsigned char in_byte, out_byte;

	if(out_type != TYPE_CHIPPER) abort();

	/* サイズ出力。 */
	fprintf(fp, "%s_W = %d\n", out_label, out_width);
	fprintf(fp, "%s_H = %d\n", out_label, out_height);

	/* ラベル出力。 */
	fprintf(fp, "%s:\n", out_label);

	/* 各カラムについて... */
	i = 0;
	for(x1 = 0; x1 < out_width; x1 += 8) {
		/* 各ラインについて... */
		for(y = 0; y < out_height; y++) {
			out_byte = 0;
			/* 各ピクセルについて... */
			for(x2 = 0; x2 < 8; x2++) {
				in_byte = in_data[y][x1 + x2];
				if(in_byte) in_byte = 1; /* カラー0以外は全て1と見なす */
				/* 左:MSB<->LSB:右 */
				out_byte = (out_byte << 1) | in_byte;
			}
			/* 1バイト出力。16バイト毎に改行します。 */
			if(!(i & 15)) {
				fprintf(fp, "\tdb ");
			} else {
				fprintf(fp, ",");
			}
			fprintf(fp, "#%02x", out_byte); /* ChipperではHex定数を「#xx」と表記します */
			i++;
			if(!(i & 15)) {
				fprintf(fp, "\n");
			}
		}
	}

	/* 最終行が未改行ならば、改行を付加します。 */
	if(i & 15) {
		fprintf(fp, "\n");
	}
}

/****************************************************************************
 *	writeschip
 ****************************************************************************/

void
writeschip(FILE* fp)	/* 2004/04/02追加 */
{
	int i, y, x1, x2;
	unsigned char in_byte;
	unsigned short out_half;

	if(out_type != TYPE_CHIPPER) abort();

	/* サイズ出力。 */
	fprintf(fp, "%s_W = %d\n", out_label, out_width);
	fprintf(fp, "%s_H = %d\n", out_label, out_height);

	/* ラベル出力。 */
	fprintf(fp, "%s:\n", out_label);

	/* 各カラムについて... */
	i = 0;
	for(x1 = 0; x1 < out_width; x1 += 16) {
		/* 各ラインについて... */
		for(y = 0; y < out_height; y++) {
			out_half = 0;
			/* 各ピクセルについて... */
			for(x2 = 0; x2 < 16; x2++) {
				in_byte = in_data[y][x1 + x2];
				if(in_byte) in_byte = 1; /* カラー0以外は全て1と見なす */
				/* 左:MSB<->LSB:右 */
				out_half = (out_half << 1) | in_byte;
			}
			/* 1ハーフワード出力。16ハーフワード毎に改行します。 */
			if(!(i & 15)) {
				fprintf(fp, "\tdw ");
			} else {
				fprintf(fp, ",");
			}
			fprintf(fp, "#%04x", out_half); /* ChipperではHex定数を「#xxxx」と表記します */
			i++;
			if(!(i & 15)) {
				fprintf(fp, "\n");
			}
		}
	}

	/* 最終行が未改行ならば、改行を付加します。 */
	if(i & 15) {
		fprintf(fp, "\n");
	}
}

/****************************************************************************
 *	writeuvi
 ****************************************************************************/

void
writeuvi(FILE* fp)	/* 2005/05/11追加 */
{
	int i;
	int c;
	int x0;
	int y0;
	int x1;
	int y1;
	int out_byte;
	int count[8];	/* カラー8未満(前景色:Cc)の出現カウント */
	int opaque;	/* 不透明キャラクタ? */

	if(out_type != TYPE_VACS) abort();

	/* サイズ出力。 */
	fprintf(fp, "%s_W\tequ\t%d\n", out_label, out_width  / 3);
	fprintf(fp, "%s_H\tequ\t%d\n", out_label, out_height / 2);

	/* ラベル出力。 */
	fprintf(fp, "%s:\n", out_label);

	/* 各ROWについて... */
	for(y0 = 0; y0 < out_height; y0 += 2) {
		fprintf(fp, "\tdb\t");
		/* 各COLについて... */
		for(x0 = 0; x0 < out_width; x0 += 3) {
			/*
			 *	Graphics mode character code B7 B6 D5 D4 D3 D2 D1 D0
			 *				     --+-- --------+--------
			 *	 M = 0: Color mode 0	       |           |
			 *	+-------------------+	       |           |
			 *	| Font              |	       |           |
			 *	| Video C1  C2  C3  |	       |           |
			 *	| ----------------- |          |           |
			 *	|    1  B7  B6  Cc3 | <--------+	   |
			 *	|    0  Cs1 Cs2 Cs3 |	                   |
			 *	+-------------------+	                   |
			 *				                   |
			 *	+----+----+----+	                   |
			 *	| D2 | D1 | D0 |	                   |
			 *	+----+----+----+ <-------------------------+
			 *	| D5 | D4 | D3 |
			 *	+----+----+----+
			 *
			 *	------	------
			 *	コード	カラー
			 *	------	------
			 *	 0	 白
			 *	 1	 黄
			 *	 2	 空色
			 *	 3	 緑
			 *	 4	 紫色
			 *	 5	 赤
			 *	 6	 青
			 *	 7	 黒
			 *	 8	 不透明背景色 ... 独自仕様
			 *	 9以上	 透明(背景色) ... 独自仕様
			 *	------	------
			 */
			opaque = 0;
			out_byte = 0;
			memset(count, 0, sizeof count);
			for(y1 = 1; y1 >= 0; y1--) {		/* y1 = 1,0   */
				for(x1 = 0; x1 <= 2; x1++) {	/* x1 = 0,1,2 */
					c = in_data[y0 + y1][x0 + x1];
					out_byte <<= 1;
					if(c < 9) {
						/* カラーコード#8は、不透明背景色と見なすことにします。
						 * (このツールの独自仕様です。UVIの仕様ではありません。)
						 * カラーコード#0..7を含むキャラは、もちろん不透明です。
						 */
						opaque = 1;
						if(c < 8) { /* 前景色(#0..7)ならば、出現数をカウント */
							out_byte |= 1;
							count[c]++;
						}
					}
				}
			}
			/* 前景色(0..7)のうち、最多出現色を求めます。 */
			c = 0;
			for(i = 1; i <= 7; i++) {
				if(count[i] > count[c]) {
					c = i;
				}
			}
			/* 最多出現色のD2:1を、キャラクタコードのD7:6とします。 */
			out_byte |= (c & 6) << 5;
			/* キャラ全体が不透明背景色の場合は、不透明を示すためにD7:6にダミービットを立てます。 */
			if(!out_byte && opaque) {
				out_byte |= 0xc0;
			}
			/* 1バイト出力します。 */
			if(x0) {
				fprintf(fp, ",", out_byte);
			}
			fprintf(fp, "$%02x", out_byte);
		}
		fprintf(fp, "\n");
	}
	fprintf(fp, "\tend\n");
}

