/*
 *	proutes.c
 *
 *	「Routes」PGDコンバータ
 *	Copyright (C) 2003 Naoyuki Sawa
 *
 *	* Sun Mar 2 16:43:00 JST 2003 Naoyuki Sawa
 *	- 作成開始。
 *	* Mon Mar 3 05:40:00 JST 2003 Naoyuki Sawa
 *	- BMP直接出力(-b1)時は、ビットマップ構造体のフィールドがおかしくても出力するよう変更。
 */
#define STRICT
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

/****************************************************************************
 *	マクロ・定数
 ****************************************************************************/

#define VERSION		"20030303"

#define REG_KEY		"Software\\Leaf\\Routes"
#define REG_VALUE	"InstallDir"

#define SIGNATURE	'PACK'
#define FNAME_LEN	24

typedef struct _PACK_INFO {	/* パック情報 */
	unsigned long head;	/* シグネチャ */
	long famount;		/* ファイル数 */
} PACK_INFO;

typedef struct _FILE_INFO {	/* ファイル情報                         */
	unsigned long type;	/* 0:圧縮なし/1:圧縮あり/それ以外:不明  */
	char fname[FNAME_LEN];	/* ファイル名                           */
	unsigned long offset;	/* オフセット(pakファイル先頭を0とする) */
	unsigned long size;	/* ファイルサイズ                       */
} FILE_INFO;

/* BMPエントリが含まれているパックファイル名リスト */
const char* const pack_list[] = {
	"grp.pak",
	"bak.pak",
};
#define PACK_LIST_COUNT	(sizeof pack_list / sizeof *pack_list)

/****************************************************************************
 *	グローバル変数
 ****************************************************************************/

char app_dir[_MAX_DIR];		/* 「Routes」のフォルダ名 */
char item_name[_MAX_PATH];	/* 抽出するアイテム名（bak0000aなど） */

int opt_bmp;			/* BMP出力（テスト用） */
int opt_chr;			/* 立ち絵モード（下切り取り） */

/****************************************************************************
 *	関数宣言
 ****************************************************************************/

void err(const char* fmt, ...);
void usage();
void extract(const char* item_name);
void decode(unsigned char* dst, unsigned char* src);
unsigned char* bmp8to32(unsigned char* in_buf);
unsigned char* bmp24to32(unsigned char* in_buf);
unsigned char* bmp32to8(unsigned char* in_buf);
unsigned char* reduce(unsigned char* in_buf);

/****************************************************************************
 *	err
 ****************************************************************************/

void
err(const char* fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	vfprintf(stderr, fmt, ap);
	va_end(ap);

	exit(1);
}

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

void
usage()
{
	fprintf(stderr, "proutes - 「Routes（Leaf/AQUAPLUS）」PGDコンバータ（%s）\n", VERSION);
	fprintf(stderr, "Copyright (C) 2003 Naoyuki Sawa\n");
	fprintf(stderr, "使い方：\n");
	fprintf(stderr, "  proutes [options] <item-name>\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "  アイテム名に「*」と指定すると、抽出可能な全てのアイテムを抽出します。\n");
	fprintf(stderr, "  ＜例＞ proutes -b *\n");
	fprintf(stderr, "         全てのファイルをBMPファイルとして抽出します。\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "オプション：\n");
	fprintf(stderr, "  -b        -b2と同じです。\n");
	fprintf(stderr, "  -b1       ビットマップファイルをそのまま出力します。\n");
	fprintf(stderr, "  -b2       ビットマップファイルを縮小して出力します。\n");
	fprintf(stderr, "  -b3       ビットマップファイルを縮小・減色して出力します。\n");
	fprintf(stderr, "  -b/-b1/-b2/-b3のどれも指定しなければ、PGDファイルを出力します。\n");
	fprintf(stderr, "  -c        縮小時、縦にはみ出す場合は、下側を切り取ります。\n");
	fprintf(stderr, "  -d<path>  Routesフォルダ指定 （初期値＝レジストリから取得）\n");
	exit(1);
}

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

int
main(int argc, char* argv[])
{
	int retval, i, n;
	char* p;
	HKEY hKey;

	/* コマンドライン解析。 */
	for(i = 1; i < argc; i++) {
		p = argv[i];
		if(p[0] == '-') { /* オプション */
			switch(p[1]) {
			case 'b': /* BMP出力 */
				if(opt_bmp) usage(); /* 二重指定 */
				if(p[2]) {
					opt_bmp = p[2] - '0';
				} else {
					opt_bmp = 2; /* -bのみなら2 */
				}
				if(opt_bmp < 1 || 3 < opt_bmp) usage();
				break;
			case 'c': /* 立ち絵モード */
				if(opt_chr) usage(); /* 二重指定 */
				opt_chr = 1;
				break;
			case 'd': /* 「Routes」インストールフォルダ指定 */
				if(strlen(app_dir) != 0) usage(); /* 二重指定 */
				strncpy(app_dir, &p[2], sizeof app_dir);
				break;
			default:
				usage();
			}
		} else {
			/* 抽出するアイテム名 */
			if(strlen(item_name) != 0) usage(); /* 二重指定 */
			strncpy(item_name, p, sizeof item_name);
		}
	}

	/* 「Routes」インストールフォルダを調べます。 */
	if(strlen(app_dir) == 0) {
		retval = RegOpenKeyEx(HKEY_CURRENT_USER, REG_KEY, 0, KEY_QUERY_VALUE, &hKey);
		if(retval == 0) {
			n = sizeof app_dir;
			retval = RegQueryValueEx(hKey, REG_VALUE, 0, NULL, (unsigned char*)app_dir, (unsigned long*)&n);
			RegCloseKey(hKey);
		}
		if(retval != 0) err("「Routes」がインストールされていないようです。");
	}

	/* アイテム名が指定されたら単独展開、指定されていなければ全展開へ。 */
	if(strlen(item_name) == 0) usage();
	if(strcmp(item_name, "*") == 0) {
		extract(NULL);
	} else {
		extract(item_name);
	}

	return 0;
}

/****************************************************************************
 *	extract_all
 ****************************************************************************/

void
extract(const char* item_name)
{
	int retval, done, alpha, i_pack, i_file;
	FILE *fp_in, *fp_out;
	PACK_INFO pack_info;
	FILE_INFO *file_info, *fi;
	char pack_path[_MAX_PATH];
	char bmp_path[_MAX_PATH];
	char fname[_MAX_PATH];
	unsigned char *in_buf, *out_buf;
	int in_size, out_size;
	BITMAPFILEHEADER* bf;
	BITMAPINFOHEADER* bi;
	char cmd[_MAX_PATH];

	done = 0;	/* 単独展開時、指定エントリ展開後に1になります。 */

	/* 各パックファイルについて... */
	for(i_pack = 0; !done && i_pack < PACK_LIST_COUNT; i_pack++) {
		/* パックファイルを開きます。 */
		_makepath(pack_path, NULL, app_dir, pack_list[i_pack], NULL);
		fp_in = fopen(pack_path, "rb");
		if(fp_in == NULL) err("%s: ファイルが開けません。", pack_list[i_pack]);

		/* パック情報を読み込みます。 */
		retval = fread(&pack_info, 1, sizeof(PACK_INFO), fp_in);
		if(retval != sizeof(PACK_INFO)) err("%s: パック情報が読み込めません。", pack_list[i_pack]);

		/* ファイル情報を読み込みます。 */
		file_info = (FILE_INFO*)calloc(pack_info.famount, sizeof(FILE_INFO));
		if(file_info == NULL) err("%s: ファイル情報を読み込むためのメモリが確保できません。", pack_list[i_pack]);
		retval = fread(file_info, sizeof(FILE_INFO), pack_info.famount, fp_in);
		if(retval != pack_info.famount) err("%s: ファイル情報が読み込めません。", pack_list[i_pack]);

		/* 各ファイルについて... */
		for(i_file = 0; !done && i_file < pack_info.famount; i_file++/**, printf("\n")**/) {
			fi = &file_info[i_file];
			/**printf("%s(%d): %s(%d) ", pack_list[i_pack], i_file, fi->fname, fi->type);**/

			/* 0:無圧縮/1:圧縮のいずれかの形式でなければ飛ばします。 */
			if(fi->type != 0 && fi->type != 1) { /**printf("** SKIP **");**/ continue; }

			/* 全展開か、または指定エントリに一致したら... */
			_splitpath(fi->fname, NULL, NULL, fname, NULL);
			if(item_name == NULL || _strcmpi(fname, item_name) == 0) {
				/* 全展開なら、処理中のファイルを表示。 */
				if(item_name == NULL) printf("%s(%4d): %-24s(%d)\n", pack_list[i_pack], i_file, fi->fname, fi->type);

				/* 入力バッファを確保します。 */
				in_size = fi->size;
				in_buf = (unsigned char*)malloc(in_size);
				if(in_buf == NULL) err("入力バッファが確保できません。");

				/* データを読み込みます。 */
				retval = fseek(fp_in, fi->offset, SEEK_SET);
				if(retval != 0) err("シークエラーです。");
				retval = fread(in_buf, 1, in_size, fp_in);
				if(retval != in_size) err("データが読み込めません。");

				/* 0:無圧縮 */
				if(!fi->type) {
					out_size = in_size;
					out_buf = (unsigned char*)malloc(out_size);
					if(out_buf == NULL) err("出力バッファが確保できません。");
					memcpy(out_buf, in_buf, out_size);

				/* 1:圧縮 */
				} else {
					out_size = *(int*)(in_buf + 4);
					out_buf = (unsigned char*)malloc(out_size);
					if(out_buf == NULL) err("出力バッファが確保できません。");
					decode(out_buf, in_buf + 4);
				}

				/* ビットマップでなければ飛ばします。 */
				if(out_buf[0] != 'B' || out_buf[1] != 'M') continue;

				if(opt_bmp == 1) goto L10;	/* BMP出力へ */

				/*{{高さが負のファイル(BJR)は暗号化されている？*/
				bf = (BITMAPFILEHEADER*)(out_buf);
				bi = (BITMAPINFOHEADER*)(out_buf + sizeof(BITMAPFILEHEADER));
				if(bi->biHeight < 0) continue;
				/*}}*/

				/* ビットマップ形式によって... */
				bf = (BITMAPFILEHEADER*)(out_buf);
				bi = (BITMAPINFOHEADER*)(out_buf + sizeof(BITMAPFILEHEADER));
				alpha = 0; /* 警告抑制 */
				switch(bi->biBitCount) {
				case 8:
					/* 8→32ビット変換。 */
					out_buf = bmp8to32(out_buf);
					bf = (BITMAPFILEHEADER*)(out_buf);
					bi = (BITMAPINFOHEADER*)(out_buf + sizeof(BITMAPFILEHEADER));
					out_size = bf->bfSize;
					//alpha = 1;	/* 透過あり */
					alpha = 0;	/* パーツは透過あり、グラフィックは透過なし。とりあえずなしとしておく */
					break;
				case 24:
					/* 24→32ビット変換。 */
					out_buf = bmp24to32(out_buf);
					bf = (BITMAPFILEHEADER*)(out_buf);
					bi = (BITMAPINFOHEADER*)(out_buf + sizeof(BITMAPFILEHEADER));
					out_size = bf->bfSize;
					alpha = 0;	/* 透過なし */
					break;
				case 32:
					alpha = 1;	/* 透過あり */
					break;
				default:
					err("未対応のビットマップ形式です。");
				}

				/* 縮小。 */
				out_buf = reduce(out_buf);
				bf = (BITMAPFILEHEADER*)(out_buf);
				bi = (BITMAPINFOHEADER*)(out_buf + sizeof(BITMAPFILEHEADER));
				out_size = bf->bfSize;

				if(opt_bmp == 2) goto L10;	/* BMP出力へ */

				/* グレイスケール変換。 */
				out_buf = bmp32to8(out_buf);
				bf = (BITMAPFILEHEADER*)(out_buf);
				bi = (BITMAPINFOHEADER*)(out_buf + sizeof(BITMAPFILEHEADER));
				out_size = bf->bfSize;
L10:
				/* ビットマップを出力します。 */
				_splitpath(fi->fname, NULL, NULL, fname, NULL);
				_makepath(bmp_path, NULL, NULL, fname, ".bmp");
				fp_out = fopen(bmp_path, "wb");
				retval = fwrite(out_buf, 1, out_size, fp_out);
				if(retval != out_size) err("ビットマップ出力に失敗しました。");
				fclose(fp_out);

				/* PGD変換。 */
				if(opt_bmp == 0) {
					if(alpha) {	/* 2bitマスク付き */
						sprintf(cmd, "dpbmpcnv -q -b -f2m \"%s\"", bmp_path); /* -q必要! */
					} else {	/* 2bitマスクなし */
						sprintf(cmd, "dpbmpcnv -q -b -f2  \"%s\"", bmp_path); /* -q必要! */
					}
					if(system(cmd) != 0) err("PGD変換に失敗しました。");
					remove(fi->fname);	/* BMPはもういらないので削除 */
				}

				/* 単独展開なら、完了マーク。 */
				if(item_name != NULL) done = 1;

				/* バッファを開放します。 */
				free(out_buf);
				free(in_buf);
			}
		}

		/* ファイル情報を開放します。 */
		free(file_info);

		/* パックファイルを閉じます。 */
		fclose(fp_in);
	}

	/* 単独展開で、指定されたエントリが見つからなければ、エラー。 */
	if(item_name && !done) err("%sが見つかりません。", item_name);
}

/****************************************************************************
 *	decode
 ****************************************************************************/

#define		N	4096
#define		F	18

static unsigned char tmp_buf[N + F];

void decode(unsigned char* dst, unsigned char* src)
{
	int i, j, k, r;
	int size, flags, pos, len;

	/* スライド辞書バッファを初期化。 */
	memset(tmp_buf, 0, sizeof tmp_buf);
	r = N - F; /* 初期位置は後ろの方？ */

	/* 出力サイズを取得。 */
	size = *(unsigned int*)src;
	src += 4;

	/* カウンタが出力サイズに達するまで... */
	i = 0;
	while(i < size) {
		/* チャンクの1バイト目はフラグ。 */
		flags = *(src++);
		for(j = 0; j < 8; j++) { /* フラグのbit0〜7が0:無圧縮/1:圧縮に対応 */
			if(i >= size) break;
			if(flags & 1) { /* ＜＜＜ここがおでマルとは違う！ */
				/* 0:無圧縮 */
				*(tmp_buf + r++) = *(dst++) = *(src++);
				r &= (N - 1);
				i++;
			} else {
				/* 1:圧縮 */
				pos = *(src++);	/* pppp_pppp: p=スライド辞書位置のbit 7〜0           */
				len = *(src++);	/* pppp_llll: p=スライド辞書位置のbit11〜8、l=長さ-2 */
				pos |= ((len & 0xf0) << 4);
				len = (len & 0x0f) + 2;
				for(k = 0; k <= len; k++) {
					if(i >= size) break;
					*(tmp_buf + r++) = *(dst++) = *(tmp_buf + ((pos + k) & (N - 1)));
					r &= (N - 1);
					i++;
				}
			}
			flags >>= 1; /* ＜＜＜ここがおでマルとは違う！ */
		}
	}
}

/****************************************************************************
 *	bmp8to32
 ****************************************************************************/

unsigned char*
bmp8to32(unsigned char* in_buf)
{
	int w, h, x, y, c;
	RGBQUAD* rgb;

	BITMAPFILEHEADER* bf_in;
	BITMAPINFOHEADER* bi_in;
	RGBQUAD* rgb_in;
	int stride_in;
	unsigned char *bits_in, *pin;

	int out_size;
	unsigned char* out_buf;
	BITMAPFILEHEADER* bf_out;
	BITMAPINFOHEADER* bi_out;
	int stride_out;
	unsigned char *bits_out, *pout;

	/* 入力ビットマップ情報を取得。 */
	bf_in   = (BITMAPFILEHEADER*)(in_buf);
	bi_in   = (BITMAPINFOHEADER*)(in_buf + sizeof(BITMAPFILEHEADER));
	rgb_in  = (RGBQUAD         *)(in_buf + sizeof(BITMAPFILEHEADER) + bi_in->biSize);
	bits_in = (unsigned char   *)(in_buf + bf_in->bfOffBits);
	w = bi_in->biWidth;
	h = bi_in->biHeight;
	stride_in = (w + 3) & ~3;

	/* 出力ビットマップ情報を作成。 */
	stride_out = 4 * w;
	out_size = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + stride_out * h;
	out_buf = (unsigned char*)calloc(out_size, 1);
	if(out_buf == NULL) err("ビットマップ変換用バッファが確保できません。");
	bf_out   = (BITMAPFILEHEADER*)(out_buf);
	bi_out   = (BITMAPINFOHEADER*)(out_buf + sizeof(BITMAPFILEHEADER));
	bits_out = (unsigned char   *)(out_buf + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER));

	bf_out->bfType = 'MB';
	bf_out->bfSize = out_size;
	bf_out->bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

	bi_out->biSize = sizeof(BITMAPINFOHEADER);
	bi_out->biWidth = w;
	bi_out->biHeight = h;
	bi_out->biPlanes = 1;
	bi_out->biBitCount = 32;

	for(y = 0; y < h; y++) {
		for(x = 0; x < w; x++) {
			pin  = bits_in  + stride_in  * (h - 1 - y) + 1 * x;
			pout = bits_out + stride_out * (h - 1 - y) + 4 * x;
			;
			c = *pin;
			rgb = &rgb_in[c];
			;
			*pout++ = rgb->rgbBlue;
			*pout++ = rgb->rgbGreen;
			*pout++ = rgb->rgbRed;
			//*pout++ = c == 0 ? 0 : 255;	/* カラー#0を透過と仮定 */
			*pout++ = 255;			/* パーツは透過あり、グラフィックは透過なし。とりあえずなしとしておく */
		}
	}

	/* 入力ビットマップを開放し、代わりに出力ビットマップを返します。 */
	free(in_buf);
	return out_buf;
}

/****************************************************************************
 *	bmp24to32
 ****************************************************************************/

unsigned char*
bmp24to32(unsigned char* in_buf)
{
	int w, h, x, y;

	BITMAPFILEHEADER* bf_in;
	BITMAPINFOHEADER* bi_in;
	int stride_in;
	unsigned char *bits_in, *pin;

	int out_size;
	unsigned char* out_buf;
	BITMAPFILEHEADER* bf_out;
	BITMAPINFOHEADER* bi_out;
	int stride_out;
	unsigned char *bits_out, *pout;

	/* 入力ビットマップ情報を取得。 */
	bf_in   = (BITMAPFILEHEADER*)(in_buf);
	bi_in   = (BITMAPINFOHEADER*)(in_buf + sizeof(BITMAPFILEHEADER));
	bits_in = (unsigned char   *)(in_buf + bf_in->bfOffBits);
	w = bi_in->biWidth;
	h = bi_in->biHeight;
	stride_in = (3 * w + 3) & ~3;

	/* 出力ビットマップ情報を作成。 */
	stride_out = 4 * w;
	out_size = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + stride_out * h;
	out_buf = (unsigned char*)calloc(out_size, 1);
	if(out_buf == NULL) err("ビットマップ変換用バッファが確保できません。");
	bf_out   = (BITMAPFILEHEADER*)(out_buf);
	bi_out   = (BITMAPINFOHEADER*)(out_buf + sizeof(BITMAPFILEHEADER));
	bits_out = (unsigned char   *)(out_buf + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER));

	bf_out->bfType = 'MB';
	bf_out->bfSize = out_size;
	bf_out->bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

	bi_out->biSize = sizeof(BITMAPINFOHEADER);
	bi_out->biWidth = w;
	bi_out->biHeight = h;
	bi_out->biPlanes = 1;
	bi_out->biBitCount = 32;

	for(y = 0; y < h; y++) {
		for(x = 0; x < w; x++) {
			pin  = bits_in  + stride_in  * (h - 1 - y) + 3 * x;
			pout = bits_out + stride_out * (h - 1 - y) + 4 * x;
			;
			*pout++ = *pin++;
			*pout++ = *pin++;
			*pout++ = *pin++;
			*pout++ = 255;
		}
	}

	/* 入力ビットマップを開放し、代わりに出力ビットマップを返します。 */
	free(in_buf);
	return out_buf;
}

/****************************************************************************
 *	bmp8to32
 ****************************************************************************/

const int DITHER[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,
};

unsigned char*
bmp32to8(unsigned char* in_buf)
{
	int w, h, x, y, r, g, b, a, c;

	BITMAPFILEHEADER* bf_in;
	BITMAPINFOHEADER* bi_in;
	int stride_in;
	unsigned char *bits_in, *pin;

	int out_size;
	unsigned char* out_buf;
	BITMAPFILEHEADER* bf_out;
	BITMAPINFOHEADER* bi_out;
	RGBQUAD* rgb_out;
	int stride_out;
	unsigned char *bits_out, *pout;

	/* 入力ビットマップ情報を取得。 */
	bf_in   = (BITMAPFILEHEADER*)(in_buf);
	bi_in   = (BITMAPINFOHEADER*)(in_buf + sizeof(BITMAPFILEHEADER));
	bits_in = (unsigned char   *)(in_buf + bf_in->bfOffBits);
	w = bi_in->biWidth;
	h = bi_in->biHeight;
	stride_in = 4 * w;

	/* 出力ビットマップ情報を作成。 */
	stride_out = (w + 3) & ~3;
	out_size = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * 256 + stride_out * h;
	out_buf = (unsigned char*)calloc(out_size, 1);
	if(out_buf == NULL) err("ビットマップ変換用バッファが確保できません。");
	bf_out   = (BITMAPFILEHEADER*)(out_buf);
	bi_out   = (BITMAPINFOHEADER*)(out_buf + sizeof(BITMAPFILEHEADER));
	rgb_out  = (RGBQUAD         *)(out_buf + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER));
	bits_out = (unsigned char   *)(out_buf + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * 256);

	bf_out->bfType = 'MB';
	bf_out->bfSize = out_size;
	bf_out->bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * 256;

	bi_out->biSize = sizeof(BITMAPINFOHEADER);
	bi_out->biWidth = w;
	bi_out->biHeight = h;
	bi_out->biPlanes = 1;
	bi_out->biBitCount = 8;

	for(c = 0; c < 4; c++) {
		rgb_out[c].rgbRed       = 85 * (3 - c);
		rgb_out[c].rgbGreen     = 85 * (3 - c);
		rgb_out[c].rgbBlue      = 85 * (3 - c);
		rgb_out[c].rgbReserved  = 0;
	}
	for(c = 4; c < 256; c++) {
		rgb_out[c].rgbRed       = 0;
		rgb_out[c].rgbGreen     = 255;
		rgb_out[c].rgbBlue      = 0;
		rgb_out[c].rgbReserved  = 0;
	}

	for(y = 0; y < h; y++) {
		for(x = 0; x < w; x++) {
			pin  = bits_in  + stride_in  * (h - 1 - y) + 4 * x;
			pout = bits_out + stride_out * (h - 1 - y) + 1 * x;
			;
			b = *pin++;
			g = *pin++;
			r = *pin++;
			a = *pin++;
			;
			if(a) {
				c = (r * 3 + g * 5 + b * 2) / 10;
				c = (c + DITHER[y & 7][x & 7] + (85 - 63) / 2) / 85;
				if(c > 3) c = 3;
				c = 3 - c;
			} else {
				c = 4;
			}
			;
			*pout++ = c;
		}
	}

	/* 入力ビットマップを開放し、代わりに出力ビットマップを返します。 */
	free(in_buf);
	return out_buf;
}

/****************************************************************************
 *	reduce
 ****************************************************************************/

#define WIDTH_LIMIT	128
#define HEIGHT_LIMIT	 88

unsigned char*
reduce(unsigned char* in_buf)
{
	int x_in, y_in, x_out, y_out, r, g, b, a, n, x_base, y_base;
	double REDUCE_LEVEL;

	int w_in;
	int h_in;
	BITMAPFILEHEADER* bf_in;
	BITMAPINFOHEADER* bi_in;
	int stride_in;
	unsigned char *bits_in, *pin;

	int out_size;
	int w_out;
	int h_out;
	unsigned char* out_buf;
	BITMAPFILEHEADER* bf_out;
	BITMAPINFOHEADER* bi_out;
	int stride_out;
	unsigned char *bits_out, *pout;

	/* 入力ビットマップ情報を取得。 */
	bf_in   = (BITMAPFILEHEADER*)(in_buf);
	bi_in   = (BITMAPINFOHEADER*)(in_buf + sizeof(BITMAPFILEHEADER));
	bits_in = (unsigned char   *)(in_buf + bf_in->bfOffBits);
	w_in = bi_in->biWidth;
	h_in = bi_in->biHeight;
	stride_in = 4 * w_in;

	/* 立ち絵モード時は比率固定。 */
	if(opt_chr) {
		REDUCE_LEVEL = 800.0 / WIDTH_LIMIT;
	/* 背景モード時は比率可変。 */
	} else {
		double rl1 = w_in / WIDTH_LIMIT;
		double rl2 = h_in / HEIGHT_LIMIT;
		REDUCE_LEVEL = rl1 <= rl2 ? rl1 : rl2; /* 画面いっぱいになるように */
	}

	/* 出力ビットマップ情報を作成。 */
	w_out = (int)(w_in / REDUCE_LEVEL);
	h_out = (int)(h_in / REDUCE_LEVEL);
	x_base = 0;
	y_base = 0;
	if(w_out > WIDTH_LIMIT) {
		x_base = (int)((w_out - WIDTH_LIMIT) / 2 * REDUCE_LEVEL);
		w_out = WIDTH_LIMIT;
	}
	if(h_out > HEIGHT_LIMIT) {
		if(!opt_chr) y_base = (int)((h_out - HEIGHT_LIMIT) / 2 * REDUCE_LEVEL);
		h_out = HEIGHT_LIMIT;
	}
	stride_out = 4 * w_out;
	out_size = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + stride_out * h_out;
	out_buf = (unsigned char*)calloc(out_size, 1);
	if(out_buf == NULL) err("ビットマップ変換用バッファが確保できません。");
	bf_out   = (BITMAPFILEHEADER*)(out_buf);
	bi_out   = (BITMAPINFOHEADER*)(out_buf + sizeof(BITMAPFILEHEADER));
	bits_out = (unsigned char   *)(out_buf + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER));

	bf_out->bfType = 'MB';
	bf_out->bfSize = out_size;
	bf_out->bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

	bi_out->biSize = sizeof(BITMAPINFOHEADER);
	bi_out->biWidth = w_out;
	bi_out->biHeight = h_out;
	bi_out->biPlanes = 1;
	bi_out->biBitCount = 32;

	/* 縮小処理。 */
	for(y_out = 0; y_out < h_out; y_out++) {
		for(x_out = 0; x_out < w_out; x_out++) {
			/* 縮小元のピクセルを加算。 */
			r = 0;
			g = 0;
			b = 0;
			a = 0;
			n = 0;
			for(y_in = y_base + (int)(y_out * REDUCE_LEVEL); y_in < y_base + (int)((y_out + 1) * REDUCE_LEVEL); y_in++) {
				if(y_in < 0 || h_in - 1 < y_in) continue;
				for(x_in = x_base + (int)(x_out * REDUCE_LEVEL); x_in < x_base + (int)((x_out + 1) * REDUCE_LEVEL); x_in++) {
					if(x_in < 0 || w_in - 1 < x_in) continue;
					pin  = bits_in  + stride_in  * (h_in - 1 - y_in) + 4 * x_in;
					b += *pin++;
					g += *pin++;
					r += *pin++;
					a += *pin++;
					n++;
				}
			}
			/* 平均して縮小後のピクセルへ。 */
			pout  = bits_out + stride_out  * (h_out - 1 - y_out) + 4 * x_out;
			if(n != 0) {
				r = (r + n / 2) / n;
				g = (g + n / 2) / n;
				b = (b + n / 2) / n;
				a = (a + n / 2) / n;
			}
			*pout++ = b;
			*pout++ = g;
			*pout++ = r;
			*pout++ = a;
		}
	}

	/* 入力ビットマップを開放し、代わりに出力ビットマップを返します。 */
	free(in_buf);
	return out_buf;
}
