/*
 *  font.c -- fontx2形式フォントファイルからフォントを得る
 *
 *  revision history:
 *	0.0: Oct. 29, 1995 by Dai Ishijima
 */

#include <stdio.h>
#include <string.h>
#ifndef NO_STDLIB_H
#include <stdlib.h>
#endif

#define exch(type,a,b) {type exch_tmp; exch_tmp = a; a = b; b = exch_tmp;}
#define READBINARY "rb"

extern char *prog;

/*
 * fontx2フォントファイルに関する定義	!! 機種依存に注意 !!
 */
#define TBLMAX 100	/* DBCSフォントファイルのテーブルの大きさ */
#define ID_LEN 6	/* フォントIDの長さ (sizeof("FONTX2")) */
#define NAM_LEN 8	/* フォント名の長さ */

#define TYPE_IS_DBCS 1
#define TYPE_IS_SBCS 0

/* fontxフォントファイルのヘッダ部 */
typedef struct {
    char id[ID_LEN];
    char name[NAM_LEN];
    unsigned char width;
    unsigned char height;
    unsigned char type;		/* DBCS or SBCS */
} fontheader;

/* DBCSフォントファイルのテーブル */
typedef struct {
    unsigned short start;
    unsigned short end;
} fonttable;

/* DBCSフォントファイルのヘッダ部 */
typedef struct {
    fontheader header;
    unsigned char tabsiz;
    fonttable table[TBLMAX];
} dbcsheader;

static FILE *fdbcs, *fsbcs;
static dbcsheader dbcshdr;
static fontheader sbcshdr;

/*
 *  フォントキャッシュに関する定義
 */
#define DBCSFSIZE 32	/* DBCSフォントの一文字あたりのバイト数 */
#define DBCSCHARS 512	/* DBCSキャッシュの大きさ (文字数) */
#define DCACHESIZ (DBCSCHARS*DBCSFSIZE) /* DBCSフォントキャッシュの大きさ */
#define SBCSFSIZE 16	/* SBCSフォント... */
#define SBCSCHARS 256
#define SCACHESIZ (SBCSCHARS*SBCSFSIZE) 

typedef struct {
    char *p;
    unsigned short code;
} cachetab;

static unsigned char dfontbuf[DCACHESIZ];	/* DOSでは64kの壁に注意 */
static unsigned char sfontbuf[SCACHESIZ];	/* SBCSは全部読んでしまう */
static cachetab dcachetab[DBCSCHARS];

static int dcharsiz;	/* 1文字が何バイトか */
static int scharsiz;
static int dcachesiz;	/* dbcsキャッシュに何文字蓄えられるか */
static int dcacheofs;	/* リングバッファのオフセット */
static unsigned long dffofs;


/* fontx2ファイルのヘッダを読む */
int readheader(FILE *fp, fontheader *header, int type)
{
    fread(header, 1, sizeof(fontheader), fp);
    return((strncmp(header->id, "FONTX2", ID_LEN) != 0)
	   || (header->type != type));
}


/* pos番目に格納されているフォントの文字コードを返す */
unsigned int pos2code(unsigned int pos)
{
    unsigned int i, p;

    i = 0;
    p = 0;
    while ((p < pos) && (i < dbcshdr.tabsiz)) {
	p += dbcshdr.table[i].end - dbcshdr.table[i].start + 1;
	++i;
    }
    return(dbcshdr.table[i - 1].end - p + pos + 1);
}


/* 文字コードがcodeのフォントが何番目に格納されているかを返す */
unsigned int code2pos(unsigned int code)
{
    unsigned int i, p;

    i = -1;
    p = 0;
    do {
	++i;
	p += dbcshdr.table[i].end - dbcshdr.table[i].start + 1;
    } while ((dbcshdr.table[i].end < code) && (i < dbcshdr.tabsiz));
    return(p - dbcshdr.table[i].end + code - 1);
}


void readtbl(FILE *fp, fonttable *table, int tabsiz)
{
    int i;
    unsigned int ch1, ch2;
    
    for (i = 0; i < tabsiz; i++) {
	ch1 = getc(fp);
	ch2 = getc(fp);
	table[i].start = (ch2 << 8) | ch1;
	ch1 = getc(fp);
	ch2 = getc(fp);
	table[i].end = (ch2 << 8) | ch1;
    }
}


/* dbcsフォントキャッシュの初期化 */
void initdbcs(char *filename)
{
    long len;
    int i;

    if ((fdbcs = fopen(filename, READBINARY)) == NULL) {
	fprintf(stderr, "%s: can't open dbcs font: %s\n", prog, filename);
	exit(1);
    }
    /* ヘッダ読み込み */
    if (readheader(fdbcs, &dbcshdr.header, TYPE_IS_DBCS) != 0) {
	fprintf(stderr, "%s: %s is not a dbcs font file\n", prog, filename);
	exit(1);
    }
    /* 1文字が何バイトで表されるか */
    dcharsiz = ((dbcshdr.header.width + 7) / 8) * dbcshdr.header.height;
    /* 領域テーブルのサイズ取得 */
    dbcshdr.tabsiz = getc(fdbcs);
    if (dbcshdr.tabsiz > TBLMAX) {
	fprintf(stderr, "%s: table of %s too large\n", prog, filename);
	exit(1);
    }
    /* 領域テーブル読み込み */
    readtbl(fdbcs, dbcshdr.table, dbcshdr.tabsiz);
    /* テーブルの次からフォントデータが格納される */
    dffofs = ftell(fdbcs); /* その位置を覚えておく */
    /* とりあえずキャッシュを埋めてしまう */
    len = fread(dfontbuf, 1, DCACHESIZ, fdbcs);
    if ((dcachesiz = len / dcharsiz) > DBCSCHARS) {
	dcachesiz = DBCSCHARS;
    }
    dcacheofs = 0;
    for (i = 0; i < dcachesiz; i++) {
	dcachetab[i].p = &dfontbuf[i * dcharsiz];
	dcachetab[i].code = pos2code(i);
    }
}


void seekdbcs(unsigned int code)
{
    fseek(fdbcs, (unsigned long)code2pos(code) * dcharsiz + dffofs, SEEK_SET);
}


unsigned char *dbcsfont(unsigned int code)
{
    int i;

    if (++dcacheofs >= dcachesiz) {
	dcacheofs -= dcachesiz;
    }
    for (i = dcacheofs; i < dcachesiz; i++) {
	if (code == dcachetab[i].code) {
	    break;
	}
    }
    if (i >= dcachesiz) {
	for (i = 0; i < dcacheofs; i++) {
	    if (code == dcachetab[i].code) {
		break;
	    }
	}
    }
    if (code == dcachetab[i].code) { /* フォントがキャッシュ中にあった */
	exch(int, dcachetab[dcacheofs].code, dcachetab[i].code);
	exch(char *, dcachetab[dcacheofs].p, dcachetab[i].p);
	return(dcachetab[dcacheofs].p);
    }
    else { /* キャッシュになかったらファイルから読む */
	dcachetab[dcacheofs].code = code;
	seekdbcs(code);
	fread(dcachetab[dcacheofs].p, dcharsiz, 1, fdbcs);
	return(dcachetab[dcacheofs].p);
    }
}


/* JIS -> シフトJIS 変換 */
unsigned int jtos(unsigned int code)
{
    unsigned int x, y;
    unsigned char hi, lo;

    hi = code / 256;
    lo = code % 256;
    x = (hi - 0x21) / 2 + 0x81;
    if (x > 0x9f) {
	x += (0xe0 - 0xa0);
    }
    if (hi % 2 == 1) {
	y = lo - 0x21 + 0x40;
	if (y > 0x7e) {
	    ++y;
	}
    }
    else {
	y = lo - 0x21 + 0x9f;
    }
    return(x * 256 + y);
}


void initsbcs(char *filename)
{
    if ((fsbcs = fopen(filename, READBINARY)) == NULL) {
	fprintf(stderr, "%s: can't open sbcs font: %s\n", prog, filename);
	exit(1);
    }
    if (readheader(fsbcs, &sbcshdr, TYPE_IS_SBCS) != 0) {
	fprintf(stderr, "%s: %s is not an sbcs font file\n", prog, filename);
	exit(1);
    }
    /* 1文字が何バイトで表されるか */
    scharsiz = ((sbcshdr.width + 7) / 8) * sbcshdr.height;
    if (scharsiz > SBCSFSIZE) {
	fprintf(stderr, "%s: %s too large\n", prog, filename);
	exit(1);
    }
    fread(sfontbuf, 1, SCACHESIZ, fsbcs);
}


unsigned char *sbcsfont(unsigned int code)
{
    return(&sfontbuf[code * scharsiz]);
}


/* 文字コードが code の文字のフォントを返す */
unsigned char *font(unsigned int code)
{
    if (code > 0x100) {
	return(dbcsfont(jtos(code)));
    }
    else {
	return(sbcsfont(code));
    }
}
