File:  [Isaki's NoNo m68k/m88k emulator] / nono / util / mkcgrom / mkcgrom.cpp
Revision 1.1.1.5 (vendor branch): download - view: text, annotated - select for diffs
Wed Apr 29 17:05:58 2026 UTC (2 months, 3 weeks ago) by root
Branches: MAIN, Isaki
CVS tags: v027, HEAD
nono 1.7.0

//
// CGROM.DAT 生成
//
// Copyright (C) 2012-2022 Tetsuya Isaki
//

#include "header.h"
#include <limits.h>
#include <fcntl.h>
#include <iconv.h>
#include <array>
#include <vector>

#define HOWMANY(x, y)	(((x) + (y - 1)) / (y))

#define warning(...)	do {	\
	printf("Warning: ");	\
	printf(__VA_ARGS__);	\
} while (0)

// フォント種別
enum fonttype_t {
	FONT_16x16,
	FONT_8x8,
	FONT_8x16,
	FONT_12x12,
	FONT_12x24,
	FONT_24x24,
	FONT_6x12,
	FONT_MAX,
};

// 範囲
#define R_CTRL		(0x0001)		// 制御文字
#define R_ASCII		(0x0002)		// ASCII(20-7F)
#define R_KANA		(0x0004)		// カナ(A0-DF)
#define R_HIRA		(0x0008)		// ひら(86-BF,E0-FF)
#define R_80		(0x0010)		// \(0x80)
#define R_81		(0x0020)		// 〜(0x81)
#define R_82		(0x0040)		// |(0x82)
#define R_SWITCH	(R_80 | R_81 | R_82)
#define R_ALL		(0x007f)		// 全部

// BDF 形式で使われるバウンディングボックス
struct BBX {
	int w;
	int h;
	int x;
	int y;
};

// 1文字分のフォントデータ
struct font_t {
	int code;
	std::array<uint8, 3 * 24> pattern;
};

// エラーが起きたら即終了する
class File
{
 public:
	bool Create(const std::string& filename_) {
		fd = open(filename_.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0644);
		if (fd < 0) {
			err(1, "open: %s", filename_.c_str());
		}
		return true;
	}

	void Close() {
		close(fd);
	}

	ssize_t Write(const void *buf, size_t len) {
		auto r = write(fd, buf, len);
		if (r < 0) {
			err(1, "File.Write(%d)", fd);
		}
		return r;
	}

	off_t Seek(off_t off) {
		auto r = lseek(fd, off, SEEK_SET);
		if (r < 0) {
			err(1, "File.Seek(%d, %jd)", fd, (intmax_t)off);
		}
		return r;
	}

 private:
	int fd {};
};

// グローバル変数
static std::string fontdir;
static File cgrom;

// クラス構造
//
// Font : すべてのフォントの基底
//  |
//  +- BDF : BDF形式ファイルを読み込むすべての基底クラス
//     |
//     +- BDF_1byte : 文字コードマッピングが不要な1バイトフォントを出力
//     |  |
//     |  +- BDF_1byte_unicode : Unicode から1バイトフォントを出力
//     |     |
//     |     +- BDF_1byte_jis0208 : JIS X 0208 から1バイトフォントを出力
//     |
//     +- BDF_kanji_jis0208 : JIS X 0208 から漢字フォントを出力する

//
// フォント基底クラス
//
class Font
{
 public:
	Font(const std::string& filename_, int, int, int);
	virtual ~Font();
	void SetRange(int flag) { range_flag = flag; }

	bool debug {};

 protected:
	bool Open();
	void Close();

	static bool IsCtrl(int c) { return (c < 0x20); }
	static bool IsAscii(int c) { return ((0x20 <= c) && (c < 0x7f)); }
	static bool IsKana(int c) { return ((0xa0 <= c) && (c < 0xe0)); }
	static bool IsHira(int c) {
		if ((0x86 <= c) && (c < 0xa0))
			return true;
		if ((0xe0 <= c) && (c <= 0xff))
			return true;
		return false;
	}
	bool IsRange(int code) const;

	int pattern_len {};		// 1文字分のバイト数
	int offset {};
	std::string filename {};
	FILE *fp {};
	int range_flag {};		// 出力範囲フラグ
};

// コンストラクタ
Font::Font(const std::string& filename_, int font_x, int font_y, int out_offset)
{
	range_flag = R_ALL;

	// 引数
	filename = filename_;
	pattern_len = HOWMANY(font_x, 8) * font_y;
	offset = out_offset;

	printf("generate %dx%d from %s\n", font_x, font_y, filename.c_str());

	// ここでもうオープンする
	Open();
}

// デストラクタ
Font::~Font()
{
	Close();
}

// オープン
bool
Font::Open()
{
	std::string pathname = ::fontdir + "/" + filename;

	fp = fopen(pathname.c_str(), "r");
	if (fp == NULL) {
		printf("Cannot open: %s\n", pathname.c_str());
		throw 0;
	}

	return true;
}

// クローズ
void
Font::Close()
{
	if (fp) {
		fclose(fp);
	}
	fp = NULL;
}

// 文字コードが今回出力を許可された範囲内か
bool
Font::IsRange(int code) const
{
	if ((range_flag & R_CTRL) && IsCtrl(code)) {
		return true;
	}
	if ((range_flag & R_ASCII) && IsAscii(code)) {
		return true;
	}
	if ((range_flag & R_KANA) && IsKana(code)) {
		return true;
	}
	if ((range_flag & R_HIRA) && IsHira(code)) {
		return true;
	}
	if ((range_flag & R_80) && (code == 0x80)) {
		return true;
	}
	if ((range_flag & R_81) && (code == 0x81)) {
		return true;
	}
	if ((range_flag & R_82) && (code == 0x82)) {
		return true;
	}
	return false;
}


//
// BDF 基底クラス
//
class BDF : public Font
{
	using inherited = Font;
 public:
	BDF(const std::string& filename_, int, int, int);
	virtual ~BDF() override;

	void Convert();

 protected:
	bool ReadProperties();
	bool ReadNextChar(font_t& data);
	virtual int GetAddr(int code) const;	// CGROM 収録アドレスを求める
	static int Unicode2JIS(int unicode);
	static int JIS2Addr(int code);

	BBX fontbbx {};		// FONTBOUNDINGBOX。.w = 横サイズ、.h = 縦サイズ
	int font_descent {};
};

// コンストラクタ
BDF::BDF(const std::string& filename_, int font_width, int font_height,
		int out_offset)
	: inherited(filename_, font_width, font_height, out_offset)
{
	// ここでプロパティを読んでおく
	ReadProperties();
}

// デストラクタ
BDF::~BDF()
{
}

// BDF のプロパティ (ヘッダみたいなもの) を読み込む
bool
BDF::ReadProperties()
{
	char buf[100];
	char *p;
	int pixel_size;		// 縦サイズ
	int quad_width;		// 横サイズ

	pixel_size = -1;
	quad_width = -1;

	while (fgets(buf, sizeof(buf), fp)) {
		if (strncmp(buf, "FONTBOUNDINGBOX ", 15) == 0) {
			// フォントの縦横サイズ
			fontbbx.w = strtol(buf + 15, &p, 10);
			fontbbx.h = strtol(p, NULL, 10);

		} else if (strncmp(buf, "PIXEL_SIZE ", 11) == 0) {
			// フォントの縦サイズ
			pixel_size = strtol(buf + 11, NULL, 10);

		} else if (strncmp(buf, "QUAD_WIDTH ", 11) == 0) {
			// フォントの横サイズ
			quad_width = strtol(buf + 11, NULL, 10);

		} else if (strncmp(buf, "FONT_DESCENT ", 13) == 0) {
			font_descent = strtol(buf + 13, NULL, 10);

		} else if (strncmp(buf, "ENDPROPERTIES", 13) == 0) {
			break;
		}
	}

	// PIXEL_SIZE, QUAD_WIDTH のほうを優先。
	// TTF→BDF では FONTBOUNDINGBOX がうまく適切な値にならないため。
	// 元から BDF なフォントでは大抵同じ値になってるはず。
	if (pixel_size != -1) {
		fontbbx.h = pixel_size;
	}
	if (quad_width != -1) {
		fontbbx.w = quad_width;
	}

	return true;
}

// BDF から次の1文字を読み込んで data に格納する。
// 取り出せなければ false を返す。
bool
BDF::ReadNextChar(font_t& data)
{
	char buf[100];
	BBX bbx = {0};
	bool in_bitmap;
	int y = 0;
	int dwidth = 0;

	in_bitmap = false;
	while (fgets(buf, sizeof(buf), fp)) {
		if (strncmp(buf, "STARTCHAR ", 10) == 0) {
			data.code = strtol(buf + 10, NULL, 16);
			dwidth = 0;
			memset(&data.pattern[0], 0, data.pattern.size());

		} else if (strncmp(buf, "ENCODING ", 9) == 0) {
			data.code = strtol(buf + 9, NULL, 10);

		} else if (strncmp(buf, "DWIDTH ", 7) == 0) {
			dwidth = strtol(buf + 7, NULL, 10);
			if (dwidth > 24) {
				printf("unsupported DWIDTH: %d\n", dwidth);
				throw 0;
			}

		} else if (strncmp(buf, "BBX ", 4) == 0) {
			char *p = buf + 4;
			bbx.w = strtol(p, &p, 10);
			bbx.h = strtol(p, &p, 10);
			bbx.x = strtol(p, &p, 10);
			bbx.y = strtol(p, &p, 10);

			if (bbx.w > dwidth) {
				warning("char %x: BBX.w(%d) > DWIDTH(%d)\n",
					data.code, bbx.w, dwidth);
			}
			if (bbx.h > fontbbx.h) {
				warning("char %x: BBX.h(%d) > FONTBBX.h(%d)\n",
					data.code, bbx.h, fontbbx.h);
			}

		} else if (strncmp(buf, "BITMAP", 6) == 0) {
			if (dwidth == 0) {
				printf("BITMAP without DWIDTH!\n");
				throw 0;
			}
			in_bitmap = true;

			// BBX による縦方向の開始行を調整
			y = fontbbx.h - font_descent;	// 上から原点までの行数
			y -= bbx.y;						// 上から BBX 底辺までの行数
			y -= bbx.h;						// 上から BBX 上辺までの行数
			y *= (dwidth + 7) / 8;			// バイト数に

		} else if (strncmp(buf, "ENDCHAR", 7) == 0) {
			in_bitmap = false;
			return true;

		} else if (in_bitmap) {
			if (y > pattern_len) {
				printf("char %x: pattern %d > %d bytes\n",
					data.code, y, pattern_len);
				throw 0;
			}
			int len = strlen(buf) - 1;	// '\n'分を引く
			uint32 val = strtol(buf, NULL, 16);
			// val を左詰めしてから
			val <<= (4 - (len / 2)) * 8;
			// BBX による横方向の位置調整
			val >>= bbx.x;

			// 1行を構成するバイト列を出力。等幅フォントを作っているので
			// グリフごとの幅(DWIDTH)ではなく出力したいフォントの幅。
			data.pattern[y++] = (val >> 24) & 0xff;
			if (fontbbx.w > 8) {
				data.pattern[y++] = (val >> 16) & 0xff;
			}
			if (fontbbx.w > 16) {
				data.pattern[y++] = (val >> 8) & 0xff;
			}
		}
	}

	// EOF なので取り出す文字がない
	return false;
}

// 変換メイン
void
BDF::Convert()
{
	font_t data;

	// フォントを1文字ずつ読み込んで
	while (ReadNextChar(data)) {
		// CGROM 収録アドレスを求めて書き出す
		int addr = GetAddr(data.code);
		if (addr >= 0) {
			cgrom.Seek(offset + addr * pattern_len);
			cgrom.Write(&data.pattern[0], pattern_len);
		}
	}
}

// CGROM 収録アドレスを得る
int
BDF::GetAddr(int code) const
{
	// デフォルトはそのまま返す
	return code;
}

// Unicode を JIS に変換する
/*static*/ int
BDF::Unicode2JIS(int unicode)
{
	auto cd = iconv_open("iso-2022-jp", "unicode");
	if (cd == (iconv_t)-1) {
		printf("iconv_open failed\n");
		return 0;
	}

	// ホストバイトオーダー4バイトのコードポイント
	const char *src = (const char *)&unicode;
	size_t srcleft = sizeof(unicode);
	// JIS は前後のエスケープも含む
	std::array<char, 16> dstbuf;
	char *dst = dstbuf.data();
	size_t dstlen = dstbuf.size();

	ICONV(cd, &src, &srcleft, &dst, &dstlen);
	iconv_close(cd);

	int jiscode = ((unsigned int)dstbuf[3] << 8) + (unsigned int)dstbuf[4];
	return jiscode;
}

// JIS コードから CGROM 収録コードを求めて返す
/*static*/ int
BDF::JIS2Addr(int code)
{
	int jh, jl, addr;

	// JIS コード 7425、7426 (区点84-05、84-06) の2文字は
	// JIS X 0208:1990 で追加されたものなので、CGROM にはない。
	if (code > 0x7424) {
		return -1;
	}

	jh = (code >> 8);
	jl = (code & 0xff);

	// (Unicodeから変換した)コードが JIS の範囲外なら無視
	if (jl < 0x21 || jl >= 0x7f) {
		return -1;
	}

	if (jh < 0x30) {
		addr = (jh - 0x21) * 94 + (jl - 0x21);
	} else {
		addr = (jh - 0x28) * 94 + (jl - 0x21);
	}
	return addr;
}


//
// BDF フォントから1バイトフォントを作るクラス。
// 文字コードは ASCII/ISO-8859 か JIS X 0201 など
// そのまま1バイトコードにマップできるもの。
//
class BDF_1byte : public BDF
{
	using inherited = BDF;
 public:
	BDF_1byte(const std::string& filename_, int, int, int);
	virtual ~BDF_1byte() override;

	int GetAddr(int code) const override;
};

// コンストラクタ
BDF_1byte::BDF_1byte(const std::string& filename_, int font_x, int font_y,
		int out_offset)
	: inherited(filename_, font_x, font_y, out_offset)
{
}

// デストラクタ
BDF_1byte::~BDF_1byte()
{
}

// CGROM 収録アドレスを返す
int
BDF_1byte::GetAddr(int code) const
{
	// 指定範囲に入っているかどうかだけ
	if (IsRange(code)) {
		return code;
	}
	return -1;
}


//
// BDF 日本語フォントから1バイトフォントを作るクラス。
// 文字コードは Unicode。
//
class BDF_1byte_unicode : public BDF_1byte
{
	using inherited = BDF_1byte;
 public:
	BDF_1byte_unicode(const std::string& filename_, int, int, int);
	virtual ~BDF_1byte_unicode() override;

	int GetAddr(int code) const override;

 protected:
	static const wchar_t table_uni[];
};

// コンストラクタ
BDF_1byte_unicode::BDF_1byte_unicode(const std::string& filename_,
		int font_x, int font_y, int out_offset)
	: inherited(filename_, font_x, font_y, out_offset)
{
}

// デストラクタ
BDF_1byte_unicode::~BDF_1byte_unicode()
{
}

// CGROM 収録アドレスを返す
int
BDF_1byte_unicode::GetAddr(int code) const
{
	for (int i = 0; i < 256; i++) {
		if (code == table_uni[i]) {
			return inherited::GetAddr(i);
		}
	}

	return -1;
}

// Unicode を1バイト文字コードに変換するテーブル。
// 制御記号(0x01〜0x1b)、矢印(0x1c〜0x1f)、チルダ(0x81)、パイプ(0x82)は
// ここでは扱わない。
const wchar_t BDF_1byte_unicode::table_uni[] =
	L"                "		// +00
	L"            →←↑↓"		// +10
	L" !”#$%&’()*+,−./"		// +20
	L"0123456789:;<=>?"		// +30
	L"@ABCDEFGHIJKLMNO"		// +40
	L"PQRSTUVWXYZ[¥]^_"		// +50
	L"`abcdefghijklmno"		// +60
	L"pqrstuvwxyz{|} ̄ "		// +70
	L"\     をぁぃぅぇぉゃゅょっ"		// +80
	L" あいうえおかきくけこさしすせそ"		// +90
	L" 。「」、・ヲァィゥェォャュョッ"		// +a0
	L"ーアイウエオカキクケコサシスセソ"		// +b0
	L"タチツテトナニヌネノハヒフヘホマ"		// +c0
	L"ミムメモヤユヨラリルレロワン゛゜"		// +d0
	L"たちつてとなにぬねのはひふへほま"		// +e0
	L"みむめもやゆよらりるれろわん  ";	// +f0


//
// BDF 日本語フォントから1バイトフォントを作るクラス。
// 文字コードは JIS X 0208 (の非漢字部分)。
//
class BDF_1byte_jis0208 : public BDF_1byte_unicode
{
	using inherited = BDF_1byte_unicode;
 public:
	BDF_1byte_jis0208(const std::string& filename_, int, int, int);
	virtual ~BDF_1byte_jis0208() override;

	int GetAddr(int code) const override;

 protected:
	static std::array<int, 256> table_jis;
	static bool table_jis_prepared;
};

// JIS コードを1バイト文字コードに変換するテーブル
std::array<int, 256> BDF_1byte_jis0208::table_jis;

// テーブルが用意できているか
bool BDF_1byte_jis0208::table_jis_prepared;

// コンストラクタ
BDF_1byte_jis0208::BDF_1byte_jis0208(const std::string& filename_,
		int font_x, int font_y, int out_offset)
	: inherited(filename_, font_x, font_y, out_offset)
{
	// 最初に一度だけ table_uni から table_jis を用意しておく
	if (!table_jis_prepared) {
		for (int i = 0, end = table_jis.size(); i < end; i++) {
			int unicode = table_uni[i];
			table_jis[i] = Unicode2JIS(unicode);
		}
		table_jis_prepared = true;
	}
}

// デストラクタ
BDF_1byte_jis0208::~BDF_1byte_jis0208()
{
}

// CGROM 収録アドレスを求める
int
BDF_1byte_jis0208::GetAddr(int code) const
{
	for (int i = 0, end = table_jis.size(); i < end; i++) {
		if (code == table_jis[i]) {
			return i;
		}
	}

	return -1;
}


//
// BDF フォントから漢字フォントを作るクラス。
// 文字コードは JIS X 0208。
//
class BDF_kanji_jis0208 : public BDF
{
	using inherited = BDF;
 public:
	BDF_kanji_jis0208(const std::string& filename_, int, int, int);
	virtual ~BDF_kanji_jis0208() override;

	int GetAddr(int code) const override;
};

// コンストラクタ
BDF_kanji_jis0208::BDF_kanji_jis0208(const std::string& filename_,
		int font_x, int font_y, int out_offset)
	: inherited(filename_, font_x, font_y, out_offset)
{
}

// デストラクタ
BDF_kanji_jis0208::~BDF_kanji_jis0208()
{
}

// CGROM 収録コードを求めて返す
int
BDF_kanji_jis0208::GetAddr(int code) const
{
	// JIS コードを変換するだけ
	return JIS2Addr(code);
}


//
// アプリケーション
//

static void MakeCGROM(const std::string& outfile);
static void MakeMon(const std::string& outfile);

int
main(int ac, char *av[])
{
	int c;
	std::string outfile;
	bool opt_m;

	::fontdir = "./fonts";
	outfile = "./cgrom.dat";
	opt_m = false;

	while ((c = getopt(ac, av, "f:ho:m")) != -1) {
		switch (c) {
		 case 'f':
			::fontdir = optarg;
			break;
		 case 'o':
			outfile = optarg;
			break;
		 case 'm':
			opt_m = true;
			break;

		 case 'h':
		 default:
			errx(1, "usage: [-f ./font] [-o ./cgrom.dat] [-m]");
		}
	}

	if (opt_m) {
		MakeMon(outfile);
	} else {
		MakeCGROM(outfile);
	}
	return 0;
}
// X680x0 CGROM を作成する
void
MakeCGROM(const std::string& outfile)
{
	// 出力ファイルを空で作成
	printf("output file: %s\n", outfile.c_str());
	cgrom.Create(outfile);
	cgrom.Seek(768 * 1024 - 1);
	cgrom.Write("", 1);

	try {
		BDF_1byte_jis0208 r8("misaki_gothic-hikanji.bdf", 8, 8, 0x3a000);
		r8.Convert();

		BDF_1byte o8("nono-8x8.bdf", 8, 8, 0x3a000);
		o8.Convert();

		BDF_1byte_jis0208 r12("shnmk12min-hikanji.bdf", 12, 12, 0x3b800);
		r12.Convert();

		BDF_1byte o12x12("nono-12x12.bdf", 12, 12, 0x3b800);
		o12x12.Convert();

		BDF_1byte h8("milkjf_8x16r.bdf", 8, 16, 0x3a800);
		h8.Convert();

		BDF_1byte o8x16("nono-8x16.bdf", 8, 16, 0x3a800);
		o8x16.Convert();

		BDF_1byte h6("shnm6x12r.bdf", 6, 12, 0xbf400);
		h6.SetRange(R_ASCII | R_KANA);
		h6.Convert();

		BDF_1byte o6("nono-6x12.bdf", 6, 12, 0xbf400);
		o6.Convert();

		BDF_1byte_unicode t6("ume-tmo3-6x12-hira.bdf", 6, 12, 0xbf400);
		t6.SetRange(R_HIRA);
		t6.Convert();

		BDF_kanji_jis0208 k16("shnmk16min.bdf", 16, 16, 0);
		k16.Convert();

		BDF_1byte h12("hibiya-12x24.bdf", 12, 24, 0x3d000);
		h12.SetRange(R_ASCII);
		h12.Convert();

		BDF_1byte o24("nono-12x24.bdf", 12, 24, 0x3d000);
		o24.Convert();

		BDF_kanji_jis0208 k24("hibiya-24x24.bdf", 24, 24, 0x40000);
		k24.Convert();

		BDF_1byte_unicode t12("ume-tmo3-12x24-kana.bdf", 12, 24, 0x3d000);
		t12.SetRange(R_HIRA | R_KANA);
		t12.Convert();
	} catch (...) {
	}

	cgrom.Close();
}

// モニタウィンドウ用 12dot フォントデータ
void
MakeMon(const std::string& outfile)
{
	// 出力ファイルを空で作成
	printf("output file: %s\n", outfile.c_str());
	cgrom.Create(outfile);
	try {
		BDF_kanji_jis0208 k12("knmzn12x.bdf", 12, 12, 0);
		k12.Convert();
	} catch (...) {
	}

	cgrom.Close();
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.