view2fontx.c

2ちゃんねるフィルタのテスト用。元のソースコードは、こちらを参照のこと。


/*
	view2fontx.c

	code: SASANO Takayoshi
	---- freeware, no warranty.
 
	[charset=EUC-JP]

	●目的

	MSX-View用漢字ROMのROMイメージから12×8および
	12×12ドットフォントを切り出して、FONTX2形式のフォントに変換する。

	●動作条件

	gccが動く環境であれば基本的に問題は無いはずですが、
	intが32bitでなかったりbig-endianな環境ではどうなるか分かりません。
	開発環境はFreeBSD-4.7R-i386で動くgcc-3.4.2を使っていますので、
	これと似たようなものであれば動作する確率が高いと思われます。

	●コンパイル方法

		gcc view2fontx.c

	で十分です。得られたa.outを使用して作業を行います。

	●変換対象

	以下に示す、MSX-View用漢字ROMのROMイメージが変換対象となります。

		MSX-View付属の漢字ROM (256Kbyte)
		FS-A1GT内蔵のMSX-View用漢字ROM (2048Kbyte)

	これら以外のものには対応していません。

	●使用方法

	引数を指定せずに実行した場合は、簡単な使用方法を表示します。

	実行例)
		uaa@rouge:~$ ./a.out 
		usage: ./a.out [filename]
		uaa@rouge:~$ 

	filenameには、ターゲットとなるROMイメージのファイル名を指定します。
	ファイルが見つからなかった場合を除き、メッセージは特に表示されません。
	shellなりなんなりのプロンプトに戻って、4つのファイルが生成されていれば
	作業は終了です。

	実行例)
		uaa@rouge:~$ ls
		a.out*          gt2mb.rom
		uaa@rouge:~$ 
		uaa@rouge:~$ ./a.out gt2mb.rom 
		uaa@rouge:~$ 
		uaa@rouge:~$ ls
		a.out*          vgh12x12.fnt    vgz12x12.fnt
		gt2mb.rom       vgh12x8.fnt     vgz12x8.fnt
		uaa@rouge:~$ 

	●生成ファイル

	FONTX2形式に変換されたフォントのファイル名は、
	使用したROMイメージによって変わります。

	・MSX-View付属の漢字ROMを使用した場合

			半角		全角
		12×8	vkh12x8.fnt	vkz12x8.fnt
		12×12	vkh12x12.fnt	vkz12x12.fnt

	・FS-A1GT内蔵のMSX-View用漢字ROM を使用した場合

			半角		全角
		12×8	vgh12x8.fnt	vgz12x8.fnt
		12×12	vgh12x12.fnt	vgz12x12.fnt

	●制限事項

	変換対象となるASCIIコード領域は、0x20〜0x7eおよび0xa0〜0xdfです。
	これら以外のものに関しては対応するデータが
	MSX-View用漢字ROM内に存在しないため、空白としています。
	よって変換結果をDOS/V等でお使いになる場合は、
	必要に応じて不足する記号や罫線の類を各自で追加して下さい。

	変換対象となるSJISコード領域は、このプログラム中の
	CodeTable_SJIS1[]およびCodeTable_SJIS2[]に記述してあります。
	基本的なものに関しては変換するようになっていますが、
	いわゆる機種依存文字がありそうな領域や一部の領域
	(罫線や♪等の記号がある辺り)は、文字の並びが異なっていたり
	空白だったりするために変換対象から外しています。
	これらは必要に応じ、各自で変換対象領域の再設定や
	文字コード変換テーブル等を作成して対処して下さい。

	基本的には以下に示した違いを除き、
	MSX-View用漢字ROMに格納されているフォントデータは同一のようです。

	・MSX-View付属の漢字ROM

		12×8全角のJIS第二水準フォントはありません。
		12×12半角のASCII 0x7e(~)は空白となります。
		12×12全角のJIS第二水準フォントには並びのおかしな部分が
		一部見られます(この部分は補正して変換を行うため、
		変換結果に影響はないものと思われます)。

	・FS-A1GT内蔵のMSX-View用漢字ROM

		12×8全角のJIS第二水準フォントはありません。
		12×12全角のJIS第二水準フォントもありません。

	●参考文献

	Let's use "MSX-View付属ROM" 第六版
	    (http://www.yo.rim.or.jp/~anaka/AtoC/labo/labo32.htm)
	FONTXテクニカルリファレンス
	    (http://www.cfc.co.jp/lepton/fontx.html#application)
	漢字コードに関する資料・SJISからJISへ
	    (http://www.net.is.uec.ac.jp/~ueno/material/kanji/sjis2jis.html)

	…他、SONYの漢字コード一覧表とかMSX-Datapack Volume1とか。  

	●このプログラムの取り扱いに関して

	このプログラム自体の使用や配布に関する条件はありません。
	その代わり、不具合とか起こった場合も自分で何とかして下さい。
	ていうか、どなたかもっとマトモなコンバータ作って下さいお願いします。

	フォントデータだのROMイメージだのに関してうだうだ書くのは
	趣味じゃないんですが、著作権が存在する以上はそれに従って下さい。
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


/* FONTX2 header */
#define CODETYPE_ASCII	0
#define	CODETYPE_KANJI	1

/* common */
typedef struct {
	unsigned char	identifier[6];
	unsigned char	fontname[8];
	unsigned char	xsize;
	unsigned char	ysize;
	unsigned char	codetype;
} __attribute__((packed)) FONTX2_HEADER;

/* ascii */
typedef struct {
	FONTX2_HEADER	h;

	unsigned char	d[1];
} __attribute__((packed)) FONTX2_ASCII;

/* sjis */
typedef struct {
	FONTX2_HEADER	h;

	unsigned char	tnum;
	unsigned char	d[1];
} __attribute__((packed)) FONTX2_KANJI;

typedef	struct {
	unsigned short	start;
	unsigned short	end;
} __attribute__((packed)) FONTX2_CODETABLE;

/* MSX-View font offsets */
#define	MSXViewKROM_Offset	0x0010

typedef	struct	_fontoffset {
	int	ascii_20_12x8;
	int	ascii_a0_12x8;
	int	jis_1_12x8;
	int	jis_2_12x8;
	int	ascii_20_12x12;
	int	ascii_a0_12x12;
	int	jis_1_12x12;
	int	jis_2_12x12;
	char	*fontname;
	char	*name_ascii_12x8;
	char	*name_jis_12x8;
	char	*name_ascii_12x12;
	char	*name_jis_12x12;
} FontOffset;

static	FontOffset	Offset[] = {
	/* MSX-View */
	{
		0x00024000, 0x00026880, 0x00024000, -1,
		0x00002000, 0x00005cc0, 0x00002000, 0x00014000,
		"ViewKROM",
		"vkh12x8.fnt", "vkz12x8.fnt",
		"vkh12x12.fnt", "vkz12x12.fnt",
	},

	/* FS-A1GT */
	{
		0x001c0000, 0x001c2880, 0x001c0000, -1,
		0x00140000, 0x00143cc0, 0x00140000, -1,
		"ViewGT",
		"vgh12x8.fnt", "vgz12x8.fnt",
		"vgh12x12.fnt", "vgz12x12.fnt",
	},
};

static	FONTX2_CODETABLE	CodeTable_SJIS1[] = {
	{0x8140, 0x817e}, {0x8180, 0x81ac},	/* symbol */
	{0x824f, 0x8258},			/* number */
	{0x8260, 0x8279},			/* alphabet (large) */
			  {0x8281, 0x829a},	/* alphabet (small) */
			  {0x829f, 0x82f1},	/* hiragana */
	{0x8340, 0x837e}, {0x8380, 0x8396},	/* katakana */
			  {0x839f, 0x83b6},	/* greek (large) */
			  {0x83bf, 0x83d6},	/* greek (small) */
	{0x8440, 0x8460},			/* russian (large) */
	{0x8470, 0x847e}, {0x8480, 0x8491},	/* russian (small) */
			  {0x889f, 0x88fc},	/* kanji (JIS1) */
	{0x8940, 0x897e}, {0x8980, 0x89fc},
	{0x8a40, 0x8a7e}, {0x8a80, 0x8afc},
	{0x8b40, 0x8b7e}, {0x8b80, 0x8bfc},
	{0x8c40, 0x8c7e}, {0x8c80, 0x8cfc},
	{0x8d40, 0x8d7e}, {0x8d80, 0x8dfc},
	{0x8e40, 0x8e7e}, {0x8e80, 0x8efc},
	{0x8f40, 0x8f7e}, {0x8f80, 0x8ffc},
	{0x9040, 0x907e}, {0x9080, 0x90fc},
	{0x9140, 0x917e}, {0x9180, 0x91fc},
	{0x9240, 0x927e}, {0x9280, 0x92fc},
	{0x9340, 0x937e}, {0x9380, 0x93fc},
	{0x9440, 0x947e}, {0x9480, 0x94fc},
	{0x9540, 0x957e}, {0x9580, 0x95fc},
	{0x9640, 0x967e}, {0x9680, 0x96fc},
	{0x9740, 0x977e}, {0x9780, 0x97fc},
	{0x9840, 0x9872},
};

static	FONTX2_CODETABLE	CodeTable_SJIS2[] = {
			  {0x989f, 0x98fc},	/* kanji (JIS2) */
	{0x9940, 0x997e}, {0x9980, 0x99fc},
	{0x9a40, 0x9a7e}, {0x9a80, 0x9afc},
	{0x9b40, 0x9b7e}, {0x9b80, 0x9bfc},
	{0x9c40, 0x9c7e}, {0x9c80, 0x9cfc},
	{0x9d40, 0x9d7e}, {0x9d80, 0x9dfc},
	{0x9e40, 0x9e7e}, {0x9e80, 0x9efc},
	{0x9f40, 0x9f7e}, {0x9f80, 0x9ffc},

	{0xe040, 0xe07e}, {0xe080, 0xe0fc},
	{0xe140, 0xe17e}, {0xe180, 0xe1fc},
	{0xe240, 0xe27e}, {0xe280, 0xe2fc},
	{0xe340, 0xe37e}, {0xe380, 0xe3fc},
	{0xe440, 0xe47e}, {0xe480, 0xe4fc},
	{0xe540, 0xe57e}, {0xe580, 0xe5fc},
	{0xe640, 0xe67e}, {0xe680, 0xe6fc},
	{0xe740, 0xe77e}, {0xe780, 0xe7fc},
	{0xe840, 0xe87e}, {0xe880, 0xe8fc},
	{0xe940, 0xe97e}, {0xe980, 0xe9fc},
	{0xea40, 0xea7e}, {0xea80, 0xeaa2},
};

/* ROM data buffer, font data bufer */
#define	BUFFERSIZE	(2048 * 1024)
static	char	RomData[BUFFERSIZE];
static	char	FontData[BUFFERSIZE];

/* misc */
#define	is8dot(x)	((x) == 8)


/* 2line/24bit(12bit-12bit) -> 2line/32bit(12bit-4bit-12bit-4bit) */
static	void	zen12_align(char *in, char *out, int line)
{
	int	i, ix_i, ix_o;

	for (i = ix_i = ix_o = 0; i < line / 2; i++, ix_i += 3, ix_o += 4) {
		out[ix_o + 0] = in[ix_i + 0];
		out[ix_o + 1] = in[ix_i + 1] & 0xf0;
		out[ix_o + 2] = ((in[ix_i + 1] << 4) |
				 ((in[ix_i + 2] >> 4) & 0x0f));
		out[ix_o + 3] = in[ix_i + 2] << 4;
	}

	return;
}

/* 2line/24bit(6bit-6bit-6bit-6bit) -> 2line/16bit(6bit-2bit-6bit-2bit) */
static	void	han12_align(char *in, char *out, int line)
{
	int	i, ix_i, ix_o;

	for (i = ix_i = ix_o = 0; i < line / 2; i++, ix_i += 3, ix_o += 2) {
		out[ix_o + 0] = in[ix_i + 0] & 0xfc;
		out[ix_o + 1] = ((in[ix_i + 1] << 4) |
				 ((in[ix_i + 2] >> 4) & 0x0f));
	}

	return;
}

/* Check MSX-View ROM */
static	int	isViewKROM(void)
{
	static	char	sign[] = "MSXViewKROM";

	return !memcmp(RomData + MSXViewKROM_Offset, sign, sizeof(sign) - 1);
}

#if 0
/* display 16dot/line * n */
static	void	disp16(unsigned char *data, int line)
{
	int	i;
	unsigned short	mask, d;

	for (i = 0; i < line; i++, data += 2) {
		d = (data[0] << 8) | data[1];

		for (mask = 0x8000; mask > 0; mask >>= 1) {
			printf("%s", (mask & d) ? "##" : "  ");
		}
		printf("\n");
	}

	return;
}

/* display 8dot/line * n */
static	void	disp8(unsigned char *data, int line)
{
	int	i;
	unsigned char	mask;

	for (i = 0; i < line; i++, data++) {
		for (mask = 0x80; mask > 0; mask >>= 1) {
			printf("%s", (mask & *data) ? "##" : "  ");
		}
		printf("\n");
	}

	return;
}
#endif

/* SJIS -> JIS */
static	int	_mbcjmstojis(int jms)
{
	int	low, high;

	high = (jms >> 8) & 0xff;
	low  =  jms       & 0xff;

	if (low < 0x9f) {
		if (high < 0xa0) {
			high -= 0x81;
			high *= 2;
			high += 0x21;
		} else {
			high -= 0xe0;
			high *= 2;
			high += 0x5f;
		}
			
		if (low > 0x7f) low--;
		low -= 0x1f;
	} else {
		if (high < 0xa0) {
			high -= 0x81;
			high *= 2;
			high += 0x22;
		} else {
			high -= 0xe0;
			high *= 2;
			high += 0x60;
		}

		low -= 0x7e;
	}

	return (high << 8) | low;
}

/* get kanji font pointer */
static	unsigned char	*get_kanji_pointer(int type, int ysize, int sjis)
{
	int	kuten, ku, ten, idx, step, offset;

	kuten = _mbcjmstojis(sjis) - 0x2020;
	ku  = (kuten >> 8) & 0xff;
	ten =  kuten       & 0xff;

	step = is8dot(ysize) ? 12 : 18;

	if (ku < 48) {
		idx = ku * 96 + ten - (ku >= 16) * 512;

		offset = is8dot(ysize) ? Offset[type].jis_1_12x8 : 
			Offset[type].jis_1_12x12;
	} else {
		idx = (ku - 48) * 96 + ten;

		/* bug fix for ViewKROM */
		switch(ku) {
		case	64:
			if (ten < 7) idx -= 2; 
			break;
		case	66:
			if (ten < 2) idx -= 2;
			break;
		case	84:
			if (ten < 5) idx -= 2;
			break;
		default:
			break;
		}

		offset = is8dot(ysize) ? Offset[type].jis_2_12x8 :
			Offset[type].jis_2_12x12;
	}

	return (offset < 0) ? NULL : (RomData + offset + step * idx);
}

/* copy CodeTable */
static	char	*copy_table(char *dst, FONTX2_CODETABLE *src, int entry)
{
	int	i;

	/* table must be little-endian */
	for (i = 0; i < entry; i++) {
		*dst++ = src[i].start & 0xff;
		*dst++ = (src[i].start >> 8) & 0xff;
		*dst++ = src[i].end & 0xff;
		*dst++ = (src[i].end >> 8) & 0xff;
	}

	return dst;
}

/* convert (kanji) */
static	void	convert_kanji(int type, int ysize)
{
	int	i, tnum1, tnum2, sjis, chr;
	unsigned char	*d, *ptr;
	FILE	*fp;
	FONTX2_KANJI	*f;

	if (ysize != 8 && ysize != 12) goto fin0;

	memset(FontData, 0, sizeof(FontData));
	f = (FONTX2_KANJI *)FontData;

	/* JIS1 available? */
	tnum1 = (get_kanji_pointer(type, ysize, 0x8140) != NULL) ?
		(sizeof(CodeTable_SJIS1) / sizeof(FONTX2_CODETABLE)) : 0;

	/* JIS2 available? */
	tnum2 = (get_kanji_pointer(type, ysize, 0x989f) != NULL) ?
		(sizeof(CodeTable_SJIS2) / sizeof(FONTX2_CODETABLE)) : 0;

	/* create header */
	strncpy(f->h.identifier, "FONTX2", sizeof(f->h.identifier));
	strncpy(f->h.fontname, Offset[type].fontname, sizeof(f->h.fontname));
	for (i = 0; i < sizeof(f->h.fontname); i++) {
		if (f->h.fontname[i] < 0x20) f->h.fontname[i] = 0x20;
	}
	f->h.xsize = 12;
	f->h.ysize = ysize;
	f->h.codetype = CODETYPE_KANJI;
	f->tnum = tnum1 + tnum2;

	/* make CodeTable */
	d = f->d;
	d = copy_table(d, CodeTable_SJIS1, tnum1);
	d = copy_table(d, CodeTable_SJIS2, tnum2);
	chr = 0;

	/* convert font (JIS1) */
	for (i = 0; i < tnum1; i++) {
		for (sjis = CodeTable_SJIS1[i].start;
		     sjis <= CodeTable_SJIS1[i].end; sjis++, chr++) {
			ptr = get_kanji_pointer(type, ysize, sjis);
			if (ptr == NULL) {
				printf("font not found: SJIS #%x\n", sjis);
			} else {
				zen12_align(ptr, d, ysize);
			}
			d += ysize * 2;
		}
	}

	/* convert font (JIS2) */
	for (i = 0; i < tnum2; i++) {
		for (sjis = CodeTable_SJIS2[i].start;
		     sjis <= CodeTable_SJIS2[i].end; sjis++, chr++) {
			ptr = get_kanji_pointer(type, ysize, sjis);
			if (ptr == NULL) {
				printf("font not found: SJIS #%x\n", sjis);
			} else {
				zen12_align(ptr, d, ysize);
			}
			d += ysize * 2;
		}
	}

	/* save result */
	fp = fopen(is8dot(ysize) ? Offset[type].name_jis_12x8 :
		   Offset[type].name_jis_12x12, "wb");
	if (fp != NULL) {
		fwrite(FontData, 1, (sizeof(FONTX2_HEADER) + (tnum1 + tnum2) *
				     sizeof(FONTX2_CODETABLE) + ysize * 2 *
				     chr + 1), fp);
	}
	fclose(fp);

 fin0:
	return;
}

/* convert (ASCII) */
static	void	convert_ascii(int type, int ysize)
{
	int	i, offset, step;
	FILE	*fp;
	FONTX2_ASCII	*f;

	if (ysize != 8 && ysize != 12) goto fin0;

	memset(FontData, 0, sizeof(FontData));
	f = (FONTX2_ASCII *)FontData;

	/* create header */
	strncpy(f->h.identifier, "FONTX2", sizeof(f->h.identifier));
	strncpy(f->h.fontname, Offset[type].fontname, sizeof(f->h.fontname));
	for (i = 0; i < sizeof(f->h.fontname); i++) {
		if (f->h.fontname[i] < 0x20) f->h.fontname[i] = 0x20;
	}
	f->h.xsize = 12 / 2;
	f->h.ysize = ysize;
	f->h.codetype = CODETYPE_ASCII;

	/* convert font (ASCII, 0x20-0x7e) */
	if (is8dot(ysize)) {
		offset = Offset[type].ascii_20_12x8;
		step = 12;
	} else {
		offset = Offset[type].ascii_20_12x12;
		step = 18;
	}

	for (i = 0x20; offset >= 0 && i < 0x80; i++) {
		han12_align(RomData + offset + step * (i - 0x20),
			    &f->d[ysize * i], ysize);
	}

	/* convert font (ASCII, 0xa0-0xdf) */
	if (is8dot(ysize)) {
		offset = Offset[type].ascii_a0_12x8;
	} else {
		offset = Offset[type].ascii_a0_12x12;
	}

	for (i = 0xa0; offset >= 0 && i < 0xe0; i++) {
		han12_align(RomData + offset + step * (i - 0xa0),
			    &f->d[ysize * i], ysize);
	}

	/* save result */
	fp = fopen(is8dot(ysize) ? Offset[type].name_ascii_12x8 :
		   Offset[type].name_ascii_12x12, "wb");
	if (fp != NULL) {
		fwrite(FontData, 1, sizeof(FONTX2_HEADER) + 256 * ysize, fp);
	}
	fclose(fp);

 fin0:
	return;
}

/* convert all characters */
static	void	convert_font(void)
{
	int	type;

	type = isViewKROM() ? 0 : 1;

	/* convert 12x8, ASCII */
	convert_ascii(type, 8);

	/* convert 12x12, ASCII */
	convert_ascii(type, 12);

	/* convert 12x8, Kanji */
	convert_kanji(type, 8);

	/* convert 12x12, Kanji */
	convert_kanji(type, 12);

	return;
}

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

	if (argc < 2) {
		printf("usage: %s [filename]\n", argv[0]);
		result = EXIT_FAILURE;
		goto fin0;
	}

	fp = fopen(argv[1], "rb");
	if (fp == NULL) {
		printf("file open error\n");
		result = EXIT_FAILURE;
		goto fin0;
	}

	memset(RomData, ~0, sizeof(RomData));
	fread(RomData, 1, sizeof(RomData), fp);

	convert_font();

// fin1:
	fclose(fp);
 fin0:
	return result;
}
  

uaa@uaa.org.uk