/*
 * CONSOLE : Consoleプログラム：TTY関連ルーチン
 * Copyright(C) 1997-1999 Eiichiro Ito, Yasuhiro Mizukoshi, Fjw
 *
 * 1997/07/04:Eiichiro Ito
 *  (1) 作成開始
 * 修正履歴
 * 1998/02/15:Yasuhiro Mizukoshi
 *  (1) ESCシーケンス対応
 * 1998/02/19:Eiichiro Ito
 *  (1) 画面更新ロジック修正
 * 1998/02/20:Eiichiro Ito
 *  (1) 反転表示対応
 * 1998/03/31:Yasuhiro Mizukoshi
 *  (1) ESCシーケンス修正
 * 1998/04/11:Fjw
 *  (1) 特殊グラフィックキャラクタの処理の追加
 *
 */
#define	STRICT
#include	<windows.h>
#include	<string.h>
#include	<tchar.h>
#include	"tty.h"
#include	"client.h"
#include	"kctrl.h"

LPTSTR		ConsoleText = 0 ;	/* 画面内容 */
LPSTR		ConsoleAttr = 0 ;	/* 画面属性 */
LPTSTR		ConsoleOText = 0 ;	/* 直前の画面内容 */
LPSTR		ConsoleOAttr = 0 ;	/* 直前の画面属性 */
DWORD		ConsoleCharW = 6 ;
DWORD		ConsoleCharH = 12 ;
DWORD		ConsoleCW = 80 ;	/* 画面の横文字数 */
DWORD		ConsoleCH = 12 ;	/* 画面の縦文字数 */
DWORD		ConsoleCol = 0 ;	/* カーソル横位置 */
DWORD		ConsoleLine = 0 ;	/* カーソル縦位置 */
DWORD		ConsoleTopX = 0 ;
DWORD		ConsoleTopY = 0 ;
int			ConsoleBack = WHITE_BRUSH ;

static	DWORD		ConsoleW = 480 ;
static	DWORD		ConsoleH = 190 ;
static	TCHAR		LastChar = 0 ;

extern	struct ClientList_t	ClientList[MAX_CLIENT] ;
extern	DWORD				Cur_client ;

extern	int	atoi( const char * ) ;

void	KDrawTextAttr( HDC hDC, LPCTSTR text, LPCSTR attr, const RECT *rect ) ;
void	parse_attr( LPSTR str ) ;

#define		is_kanji(x)		(((x)>=0x81 && (x)<=0x9F)||((x)>=0xE0 && (x)<=0xFC))
#define		GetTextTop()	(&ConsoleText[ (ConsoleCW + 1) * ConsoleLine ])
#define		GetAttrTop()	(&ConsoleAttr[ (ConsoleCW + 1) * ConsoleLine ])
#define		WidthOfText()	((ConsoleCW + 1) * sizeof (TCHAR))
#define		WidthOfAttr()	((ConsoleCW + 1) * sizeof (CHAR))
#define		AttrBase		((CHAR)0x80)

void
tty_setwindow( DWORD x, DWORD y, DWORD w, DWORD h, DWORD charw, DWORD charh )
{
	ConsoleTopX = x ;
	ConsoleTopY = y ;
	ConsoleCharW = charw ;
	ConsoleCharH = charh ;
	ConsoleCW = w / charw ;
	ConsoleCH = h / charh ;
}

BOOL
tty_init( void )
{
	DWORD	size ;

	tty_release() ;
	ConsoleW = ConsoleCW * ConsoleCharW ;
	ConsoleH = ConsoleCH * ConsoleCharH ;
	/* 内容 */
	size = WidthOfText() * ConsoleCH ;
	ConsoleText = LocalAlloc( LPTR, size ) ;
	if ( ConsoleText == NULL ) {
		tty_release() ;
		return FALSE ;
	}
	ConsoleOText = LocalAlloc( LPTR, size ) ;
	if ( ConsoleOText == NULL ) {
		tty_release() ;
		return FALSE ;
	}
	/* 属性 */
	size = WidthOfAttr() * ConsoleCH ;
	ConsoleAttr = LocalAlloc( LPTR, size ) ;
	if ( ConsoleAttr == NULL ) {
		tty_release() ;
		return FALSE ;
	}
	ConsoleOAttr = LocalAlloc( LPTR, size ) ;
	if ( ConsoleOAttr == NULL ) {
		tty_release() ;
		return FALSE ;
	}
	tty_cls() ;
	return TRUE ;
}

void
tty_release( void )
{
	if ( ConsoleText ) {
		LocalFree( (HLOCAL) ConsoleText ) ;
		ConsoleText = 0 ;
	}
	if ( ConsoleOText ) {
		LocalFree( (HLOCAL) ConsoleOText ) ;
		ConsoleOText = 0 ;
	}
	if ( ConsoleAttr ) {
		LocalFree( (HLOCAL) ConsoleAttr ) ;
		ConsoleAttr = 0 ;
	}
	if ( ConsoleOAttr ) {
		LocalFree( (HLOCAL) ConsoleOAttr ) ;
		ConsoleOAttr = 0 ;
	}
}

void
tty_cls( void )
{
	memset( ConsoleText,  0, WidthOfText() * ConsoleCH ) ;
	memset( ConsoleOText, 1, WidthOfText() * ConsoleCH ) ;
	memset( ConsoleAttr,  0, WidthOfAttr() * ConsoleCH ) ;
	memset( ConsoleOAttr, 1, WidthOfAttr() * ConsoleCH ) ;
	ConsoleCol = ConsoleLine = 0 ;
}

/*
 * 画面の一部領域を強制的に更新するよう設定する
 */
void
tty_invalidate( const RECT *pRect )
{
	if ( !ConsoleOText ) {
		return ;
	}
	if ( pRect ) {
		/* 一部を更新する場合 */
		DWORD	i, y1, y2 ;

		y1 = (pRect->top    - ConsoleTopY) / ConsoleCharH ;
		y2 = (pRect->bottom - ConsoleTopY) / ConsoleCharH ;
		for ( i = y1 ; i <= y2 ; i ++ ) {
			ConsoleOText[ i * (ConsoleCW + 1) ] = 1 ;
		}
	} else {
		/* すべて更新する場合 */
		memset( ConsoleOText, 1, WidthOfText() * ConsoleCH ) ;
	}
}

void
tty_draw( HWND hWnd, HDC hDC )
{
	RECT		rect ;
	LPTSTR		text, otext ;
	LPSTR		attr, oattr ;
	DWORD		i, w = ConsoleCW * ConsoleCharW ;
	DWORD		x = ConsoleTopX, y = ConsoleTopY ;

	if ( !ConsoleText || !ConsoleOText || !ConsoleOAttr ) {
		return ;
	}
	/* 全ての行を描く */
	rect.left = x, rect.right  = x + w - 1 ;
	rect.top  = y, rect.bottom = y + ConsoleCharH - 1 ;
	text  = ConsoleText ;
	otext = ConsoleOText ;
	attr  = ConsoleAttr ;
	oattr = ConsoleOAttr ;
	for ( i = 0 ; i < ConsoleCH ; i ++ ) {
		if ( _tcscmp( otext, text ) || strcmp( oattr, attr ) ) {
			/* 内容が異なる場合だけ描画 */
			KDrawTextAttr( hDC, text, attr, &rect ) ;
		}
		_tcscpy( otext, text ) ;
		strcpy( oattr, attr ) ;
		rect.top += ConsoleCharH, rect.bottom += ConsoleCharH ;
		text  += ConsoleCW + 1 ;
		otext += ConsoleCW + 1 ;
		attr  += ConsoleCW + 1 ;
		oattr += ConsoleCW + 1 ;
	}
}

DWORD
get_charwidth( DWORD col, TCHAR c )
{
	static	DWORD	tab = 0 ;

	if ( !tab ) {
		tab = GetTabWidth() ;
	}
	if ( c == '\t' ) {
		return tab - (col % tab) ;
	}
	return is_hankaku( c ) ? 1 : 2 ;
}

void
overwrite( LPTSTR ptr, DWORD pos, TCHAR nc, LPSTR attr, CHAR na )
{
	TCHAR	c, buffer[ 128 ] ;
	DWORD	col = 0, clen1, clen2, count ;

	while ( c = *ptr ) {
		clen1 = get_charwidth( col, c ) ;
		if ( col == pos ) {
			clen2 = get_charwidth( col, nc ) ;
			*ptr++  = nc ;
			*attr++ = na ;
			if ( !(c = *ptr) ) {
				return ;
			}
			if ( clen1 == clen2 ) {
			} else if ( clen1 > clen2 ) {
				clen1 -= clen2 ;
				/* テキスト */
				_tcscpy( buffer, ptr ) ;
				count = clen1 ;
				do {
					*ptr++ = TEXT(' ') ;
				} while ( -- count ) ;
				_tcscpy( ptr, buffer ) ;
				/* 属性 */
				strcpy( (LPSTR) buffer, attr ) ;
				count = clen1 ;
				do {
					*attr++ = AttrBase ;
				} while ( -- count ) ;
				strcpy( attr, (LPSTR) buffer ) ;
			} else if ( clen1 < clen2 ) {
				clen2 -= clen1 ;
				clen1 = get_charwidth( col, c ) ;
				if ( clen1 == clen2 ) {
					_tcscpy( ptr, ptr + 1 ) ;
					strcpy( attr, attr + 1 ) ;
				} else {
					*ptr = TEXT(' ') ;
					*attr = AttrBase ;
				}
			}
			return ;
		} else if ( col + clen1 > pos ) {
			clen1 = col + clen1 - pos ;
			col = pos ;
			/* テキスト */
			_tcscpy( buffer, ptr + 1 ) ;
			count = clen1 ;
			do {
				*ptr++ = TEXT(' ') ;
			} while ( -- count ) ;
			_tcscpy( ptr, buffer ) ;
			/* 属性 */
			strcpy( (LPSTR) buffer, attr + 1 ) ;
			count = clen1 ;
			do {
				*attr++ = AttrBase ;
			} while ( -- count ) ;
			strcpy( attr, (LPSTR) buffer ) ;
			continue ;
		}
		ptr ++, attr ++, col += clen1 ;
	}
	for ( ; col < pos ; col ++ ) {
		*ptr++ = TEXT(' ') ;
		*attr++ = AttrBase ;
	}
	*ptr++ = nc ;
	*ptr   = 0 ;
	*attr++ = na ;
	*attr   = 0 ;
}

void
backspace( LPTSTR ptr, LPDWORD orgcol, LPSTR attr )
{
	TCHAR	c ;
	DWORD	col = *orgcol, newcol = 0, clen ;

	if ( !col ) {
		return ;
	}
	while ( c = *ptr ) {
		if ( (col == 1) || (!is_hankaku(c) && col == 2) ) {
			while ( c = ptr[1] ) {
				*ptr++ = c ;
				attr[0] = attr[1] ;
				attr ++ ;
			}
			*ptr = 0 ;
			*attr = 0 ;
			(*orgcol) = newcol ;
			return ;
		}
		clen = is_hankaku(c) ? 1 : 2 ;
		ptr ++, attr ++, col -= clen ;
		newcol += clen ;
	}
}

BOOL
get_str_pos( LPTSTR ptr, DWORD pos, LPDWORD result )
{
	TCHAR	c ;
	DWORD	col = 0, clen1, index = 0 ;

	while ( c = *ptr ) {
		clen1 = get_charwidth( col, c ) ;
		if ( col == pos || col + clen1 > pos ) {
			*result = index ;
			return TRUE ;
		}
		ptr ++, index ++, col += clen1 ;
	}
	return FALSE ;
}

int
tty_getwidth( LPCTSTR str )
{
	TCHAR	c ;
	int		width = 0 ;

	while ( c = *str++ ) {
		width += get_charwidth( width, c ) ;
	}
	width *= GetFontHW() ;
	return width ;
}

BOOL
tty_addchar_sub( HWND hWnd, HDC hDC, TCHAR c, LPBOOL f_scroll, LPBOOL f_redraw )
{
	DWORD		i, clen ;
	LPSTR		attrtop, attrdst ;
	LPTSTR		linetop, linedst ;
	CHAR		attr = (CHAR) (CurTerm.bTermAttr) | AttrBase ;

	*f_scroll = *f_redraw = FALSE ;
	if ( c == '\r' ) {
		return FALSE ;
	} else if ( c == 0x0C ) {
		tty_draw( hWnd, hDC ) ;
		*f_redraw = TRUE ;
		return FALSE ;
	} else if ( c == 8 ) {
		if ( ConsoleCol ) {
			linetop = GetTextTop() ;
			attrtop = GetAttrTop() ;
			backspace( linetop, &ConsoleCol, attrtop ) ;
		}
		return TRUE ;
	}
	if ( LastChar ) {
		c |= LastChar << 8 ;
		LastChar = 0 ;
		c = sjis2unicode_char( c ) ;
	} else if ( is_kanji( c ) ) {
		LastChar = c ;
		return FALSE ;
//「×」を半角かなと認識しないように以下の変換をカットした
//	} else if ( c < 0x100 ) {
//		c = sjis2unicode_char( c ) ;
	}
	clen = get_charwidth( ConsoleCol, c ) ;
	if ( ConsoleCol + clen > ConsoleCW || c == '\n' ) {
		ConsoleCol = 0 ;
		ConsoleLine ++ ;
		if ( ConsoleLine >= ConsoleCH ) {
			ConsoleLine = ConsoleCH - 1 ;
			/* スクロールが必要 */
			*f_scroll = TRUE ;
			/* テキストのスクロール */
			linedst = ConsoleText ;
			linetop = &ConsoleText[ ConsoleCW + 1 ] ;
			for ( i = 0 ; i < ConsoleCH - 1 ; i ++ ) {
				memcpy( linedst, linetop, WidthOfText() ) ;
				linedst += ConsoleCW + 1 ;
				linetop += ConsoleCW + 1 ;
			}
			memset( linedst, 0, WidthOfText() ) ;
			/* 属性のスクロール */
			attrdst = ConsoleAttr ;
			attrtop = &ConsoleAttr[ ConsoleCW + 1 ] ;
			for ( i = 0 ; i < ConsoleCH - 1 ; i ++ ) {
				memcpy( attrdst, attrtop, WidthOfAttr() ) ;
				attrdst += ConsoleCW + 1 ;
				attrtop += ConsoleCW + 1 ;
			}
			memset( attrdst, 0, WidthOfAttr() ) ;
		}
		if ( c == '\n' ) {
			goto end ;
		}
	}
	linetop = GetTextTop() ;
	attrtop = GetAttrTop() ;
	if ( IsSetMode( INSERT ) ) {
		/* 挿入モード */
		LPTSTR	linepos, lineend = linetop + ConsoleCW ;
		LPSTR	attrpos, attrend = attrtop + ConsoleCW ;

		*(lineend - 1) = TEXT('\0') ;
		*(attrend - 1) = '\0' ;
		for ( linepos = lineend, attrpos = attrend ; linepos > (linetop+ConsoleCol) ; linepos --, attrpos -- ) {
			*(linepos) = *(linepos - 1) ;
			*(attrpos) = *(attrpos - 1) ;
		}
		*(linetop + ConsoleCol) = c ;
		*(attrtop + ConsoleCol) = attr ;
	} else {
		overwrite( linetop, ConsoleCol, c, attrtop, attr ) ;
	}
	/* カーソルを進める */
	ConsoleCol += clen ;
end:
	return TRUE ;
}

void
tty_addchar( HWND hWnd, HDC hDC, TCHAR c )
{
	LPSTR		attrtop ;
	LPTSTR		linetop ;
	RECT		rect, rect1, rectErase ;
	BOOL		f_scroll, f_redraw ;
	DWORD		x = ConsoleTopX, y = ConsoleTopY ;

	if ( tty_addchar_sub( hWnd, hDC, c, &f_scroll, &f_redraw ) && !f_redraw ) {
		HideCaret( hWnd ) ;
		if ( f_scroll ) {
			/* 実際に画面をスクロールする */
			rect.left = x, rect.right  = x + ConsoleW - 1 ;
			rect.top = y + ConsoleCharH ;
			rect.bottom = y + ConsoleH - 1 ;
			rect1.left = x, rect1.right  = x + ConsoleW - 1 ;
			rect1.top = y,  rect1.bottom = y + ConsoleH - 1 ;
			ScrollDC( hDC, 0, - (int) ConsoleCharH, &rect, &rect1, NULL, NULL ) ;
			/* スクロールした行をクリアする */
			rect.left = x, rect.right = x + ConsoleW ;
			rect.top = y + ConsoleH - ConsoleCharH ;
			rect.bottom = rect.top + ConsoleCharH ;
			FillRect( hDC, &rect, GetStockObject( ConsoleBack ) ) ;
		} else {
			linetop = GetTextTop() ;
			attrtop = GetAttrTop() ;
			/* 行を描く */
			rect.left = x, rect.right  = x + ConsoleW - 1 ;
			rect.top  = y + ConsoleLine * ConsoleCharH ;
			rect.bottom = rect.top + ConsoleCharH - 1 ;
			KDrawTextAttr( hDC, linetop, attrtop, &rect ) ;
			rectErase.left = x + tty_getwidth( linetop ) ;
			rectErase.right = rect.right + 1 ;
			rectErase.top = rect.top ;
			rectErase.bottom = rect.bottom + 1 ;
			if ( rectErase.left < rectErase.right ) {
				FillRect( hDC, &rectErase, GetStockObject( ConsoleBack ) ) ;
			}
		}
		/* カーソルを表示する */
		ShowCaret( hWnd ) ;
		tty_setcaret() ;
	}
}

void
tty_addstr( HWND hWnd, HDC hDC, LPCTSTR str )
{
	TCHAR		c ;
	int			dist ;
	LPCTSTR		linetop ;
	LPCSTR		attrtop ;
	RECT		rect, rect1, rectErase ;
	BOOL		f_draw = FALSE, f_scroll, f_redraw ;
	DWORD		i, x = ConsoleTopX, y = ConsoleTopY ;
	DWORD		num_scroll = 0, top_line = ConsoleLine, num_line = 1 ;

	while ( c = *str++ ) {
		f_draw |= tty_addchar_sub( hWnd, hDC, c, &f_scroll, &f_redraw ) ;
		if ( f_redraw ) {
			f_draw = FALSE ;
			num_scroll = 0 ;
			top_line = 0 ;
			continue ;
		}
		if ( f_scroll ) {
			num_scroll ++ ;
		}
	}
	if ( !f_draw ) {
		return ;
	}
	if ( ConsoleLine > top_line ) {
		num_line = ConsoleLine - top_line + 1 ;
	}
	HideCaret( hWnd ) ;
	if ( num_scroll >= ConsoleCH ) {
		top_line = 0 ;
		num_line = ConsoleCH ;
	} else if ( num_scroll ) {
		dist = ConsoleCharH * num_scroll ;
		rect.left = x, rect.right  = x + ConsoleW - 1 ;
		rect.top = y + dist ;
		rect.bottom = y + ConsoleH - 1 ;
		rect1.left = x, rect1.right  = x + ConsoleW - 1 ;
		rect1.top = y,  rect1.bottom = y + ConsoleH - 1 ;
		ScrollDC( hDC, 0, - (int) dist, &rect, &rect1, NULL, NULL ) ;
		top_line = top_line > num_scroll ? top_line - num_scroll : 0 ;
		num_line = ConsoleCH - top_line ;
	}
	y = ConsoleTopY + top_line * ConsoleCharH ;
	rect.left = x, rect.right  = x + ConsoleW - 1 ;
	rect.top  = y, rect.bottom = y + ConsoleCharH - 1 ;
	linetop = &ConsoleText[ (ConsoleCW + 1) * top_line ] ;
	attrtop = &ConsoleAttr[ (ConsoleCW + 1) * top_line ] ;
	for ( i = 0 ; i < num_line ; i ++ ) {
		KDrawTextAttr( hDC, linetop, attrtop, &rect ) ;
		rectErase.left = x + tty_getwidth( linetop ) ;
		rectErase.right = rect.right + 1 ;
		rectErase.top = rect.top ;
		rectErase.bottom = rect.bottom + 1 ;
		if ( rectErase.left < rectErase.right ) {
			FillRect( hDC, &rectErase, GetStockObject( ConsoleBack ) ) ;
		}
		rect.top += ConsoleCharH, rect.bottom += ConsoleCharH ;
		linetop += ConsoleCW + 1 ;
		attrtop += ConsoleCW + 1 ;
	}
	ShowCaret( hWnd ) ;
	tty_setcaret() ;
}

void tty_set(HWND hWnd, HDC hDC, TCHAR c)
{
	DWORD f_scroll, f_redraw;
	tty_addchar_sub(hWnd, hDC, c, &f_scroll, &f_redraw) ;
	CurTerm.bConsDraw = TRUE;
}

void EscGk0J(HWND hWnd, HDC hDC)
{
	DWORD col  = ConsoleCol;
	DWORD line = ConsoleLine;
	ConsoleCol  = 0;
	for (; ConsoleLine < ConsoleCH; ConsoleLine++){
		tty_cleareol(hWnd, hDC);
	}
	ConsoleCol  = col;
	ConsoleLine = line;
}

void EscGk1J(HWND hWnd, HDC hDC)
{
	DWORD col  = ConsoleCol;
	DWORD line = ConsoleLine;
	ConsoleCol  = 0;
	ConsoleLine = 0;
	for (; ConsoleLine <= line; ConsoleLine++){
		tty_cleareol(hWnd, hDC);
	}
	ConsoleCol  = col;
	ConsoleLine = line;
}

void EscGk2J(HWND hWnd, HDC hDC)
{
	tty_cls();
}

void EscGk0K(HWND hWnd, HDC hDC)
{
	tty_cleareol(hWnd, hDC);
}

void
EscGk1K( HWND hWnd, HDC hDC )
{
	LPTSTR	text = GetTextTop() ;
	LPSTR	attr = GetAttrTop() ;
	int		num = ConsoleCol + 1 ;

	while ( num && text ) {
		*text++ = TEXT(' ') ;
		*attr++ = AttrBase ;
		num -- ;
	}
}

void EscGk2K(HWND hWnd, HDC hDC)
{
	ConsoleCol = 0;
	tty_cleareol(hWnd, hDC);
}

void SetMode(BOOL bSet, DWORD flag)
{
	if (bSet)
		CurTerm.TermSetup |= flag;
	else
		CurTerm.TermSetup &= ~flag;
}

BOOL IsSetMode(DWORD flag)
{
	if (CurTerm.TermSetup & flag)
		return TRUE;
	else
		return FALSE;
}


extern TCHAR SpecialGraphTable[32];	// Fjw

void vt100_addchar(HWND hWnd, HDC hDC, TCHAR c)
{
	switch(CurTerm.term_state){
	case TERM_DATA:
		if (c == 0x7f){
			/* 無視される */
		} else if (c >= 0x20){
			if( CurTerm.char_set == CS_GRAPH && 0x5f<=c && c<=0x7e){	// Fjw
				// 特殊グラフィックキャラクタの処理 
				if( SpecialGraphTable[c-0x5f] != 0)
					tty_set( hWnd, hDC, SpecialGraphTable[c-0x5f] );
			} else {
				tty_set(hWnd, hDC, c);
			}
		} else {
			switch(c){
			case 0x1b:
				/* ESCAPEシーケンスへ */
				CurTerm.term_tmp[CurTerm.term_tmp_pos] = ESC;
				CurTerm.term_tmp_pos++;
				CurTerm.term_state = TERM_ESC;
				break;
			case 0x08:
				/* カーソルを左へ1カラム移動.
				   カーソルがレフトマージンに
				   あるときは無視される */
				if (ConsoleCol != 0)
					ConsoleCol--;
				break;
			case 0x0a:
				/* ラインフィードまたはニュー
				   ライン動作を行う */
				if (ConsoleLine >= CurTerm.scroll_bottom){
					tty_scroll_up(hWnd, hDC, CurTerm.scroll_top, CurTerm.scroll_bottom);
					CurTerm.bConsDraw = TRUE;
				} else {
					ConsoleLine++;
				}
				break;
			case 0x0d:
				/* カーソルを現在の行のレフト
				   マージンまで移動 */
				ConsoleCol = 0;
				break;
			case 0x09:
				/* カーソルを次のタブ位置へ移動.
				   タブ位置がセットされていない
				   時はライトマージンへ移動 */
				ConsoleCol = (ConsoleCol/CurTerm.tab_size + 1) * CurTerm.tab_size;
				if (ConsoleCol >= ConsoleCW)
					ConsoleCol = ConsoleCW - 1;
				break;
			case 0x07:
				/* ベル音を発生する */
				MessageBeep(0xFFFFFFFF);
				break;
			case 0x0e:
				/* G1セットを選択する(NYI) */
				CurTerm.char_set = CurTerm.G1_char_set ;	// Fjw
				break;
			case 0x0f:
				/* G0セットを選択する(NYI) */
				CurTerm.char_set = CurTerm.G0_char_set ;	// Fjw
				break;
			case 0:
				/* 無視される */
				break;
			default:
				tty_set(hWnd, hDC, c);
			}
		}
		break;

	case TERM_ESC:
		switch(c){
		case '[':
			/* ESC[の処理へ */
			CurTerm.term_tmp[CurTerm.term_tmp_pos] = '[';
			CurTerm.term_tmp_pos++;
			CurTerm.term_state = TERM_CSI;
			break;
		case '(':	// Fjw
			/* ESC(の処理へ */
			CurTerm.term_tmp[CurTerm.term_tmp_pos] = '(';
			CurTerm.term_tmp_pos++;
			CurTerm.term_state = TERM_G0;
			break;
		case ')':	// Fjw
			/* ESC)の処理へ */
			CurTerm.term_tmp[CurTerm.term_tmp_pos] = ')';
			CurTerm.term_tmp_pos++;
			CurTerm.term_state = TERM_G1;
			break;
		case '7':
			/* カーソル位置と属性を保存 */
			CurTerm.col_tmp  = ConsoleCol;
			CurTerm.line_tmp = ConsoleLine;
			CurTerm.term_tmp_pos = 0;
			CurTerm.term_state   = TERM_DATA;
			break;
		case '8':
			/* カーソル位置と属性を復帰 */
			ConsoleCol  = CurTerm.col_tmp;
			ConsoleLine = CurTerm.line_tmp;
			CurTerm.term_tmp_pos = 0;
			CurTerm.term_state   = TERM_DATA;
			break;
		case 'c':
			/* ターミナルの状態をリセット */
			tty_cls() ;
			CurTerm.bConsDraw = TRUE;
			CurTerm.term_tmp_pos = 0;
			CurTerm.term_state   = TERM_DATA;
			break;
		case 'E':
			/* ニューライン: 次行のカラム1へ移動.
			   最下行ならスクロールアップ */
			if (ConsoleLine >= CurTerm.scroll_bottom){
				tty_scroll_up(hWnd, hDC, CurTerm.scroll_top, CurTerm.scroll_bottom);
				ConsoleLine = CurTerm.scroll_bottom;
				CurTerm.bConsDraw = TRUE;
			} else {
				ConsoleLine++;
			}
			ConsoleCol   = 0;
			CurTerm.term_tmp_pos = 0;
			CurTerm.term_state   = TERM_DATA;
			break;
		case 'D':
			/* インデックス: 次行へ移動.
			   最下行ならスクロールアップ */
			if (ConsoleLine >= CurTerm.scroll_bottom){
				tty_scroll_up(hWnd, hDC, CurTerm.scroll_top, CurTerm.scroll_bottom);
				CurTerm.bConsDraw = TRUE;
			} else {
				ConsoleLine++;
			}
			CurTerm.term_tmp_pos = 0;
			CurTerm.term_state   = TERM_DATA;
			break;
		case 'M':
			/* 逆インデックス: 前行へ移動.
			   最上行ならスクロールダウン */
			if (ConsoleLine <= CurTerm.scroll_top){
				tty_scroll_down(hWnd, hDC, CurTerm.scroll_top, CurTerm.scroll_bottom);
				CurTerm.bConsDraw = TRUE;
			} else {
				ConsoleLine--;
			}
			CurTerm.term_tmp_pos = 0;
			CurTerm.term_state   = TERM_DATA;
			break;
		case '=':
		case '>':
		case 'H':
		case 'Z':
			/* NYI */
			CurTerm.term_tmp_pos = 0;
			CurTerm.term_state   = TERM_DATA;
			break;
		default:
			CurTerm.term_tmp_pos = 0;
			CurTerm.term_state   = TERM_DATA;
			break;
		}
		break;

	case TERM_CSI:
		switch (c){
		case '0': case '1': case '2': case '3': case '4':
		case '5': case '6': case '7': case '8': case '9':
		case ';': case '?':
			CurTerm.term_tmp[CurTerm.term_tmp_pos] = (char)c;
			CurTerm.term_tmp_pos++;
			break;
		case 'A':
			/* カーソルを上に移動 */
			if (CurTerm.term_tmp_pos > 2){
				CurTerm.term_tmp[CurTerm.term_tmp_pos] = '\0';
				ConsoleLine -= atoi(CurTerm.term_tmp + 2);
			} else {
				ConsoleLine--;
			}
			if (ConsoleLine < 0)
				ConsoleLine = 0;
			CurTerm.term_tmp_pos = 0;
			CurTerm.term_state   = TERM_DATA;
			break;
		case 'B':
			/* カーソルを下に移動 */
			if (CurTerm.term_tmp_pos > 2){
				CurTerm.term_tmp[CurTerm.term_tmp_pos] = '\0';
				ConsoleLine += atoi(CurTerm.term_tmp + 2);
			} else {
				ConsoleLine++;
			}
			if (ConsoleLine >= ConsoleCH)
				ConsoleLine = (ConsoleCH - 1);
			CurTerm.term_tmp_pos = 0;
			CurTerm.term_state   = TERM_DATA;
			break;
		case 'C':
			/* カーソルを右に移動 */
			if (CurTerm.term_tmp_pos > 2){
				CurTerm.term_tmp[CurTerm.term_tmp_pos] = '\0';
				ConsoleCol += atoi(CurTerm.term_tmp + 2);
			} else {
				ConsoleCol++;
			}
			if (ConsoleCol >= ConsoleCW)
				ConsoleCol = (ConsoleCW - 1);
			CurTerm.term_tmp_pos = 0;
			CurTerm.term_state   = TERM_DATA;
			break;
		case 'D':
			/* カーソルを左に移動 */
			if (CurTerm.term_tmp_pos > 2){
				CurTerm.term_tmp[CurTerm.term_tmp_pos] = '\0';
				ConsoleCol -= atoi(CurTerm.term_tmp + 2);
			} else {
				ConsoleCol--;
			}
			if (ConsoleCol < 0)
				ConsoleCol = 0;
			CurTerm.term_tmp_pos = 0;
			CurTerm.term_state   = TERM_DATA;
			break;
		case 'H':
		case 'f':
			/* カーソルの移動 */
			if (CurTerm.term_tmp_pos > 2){
				char *p = strchr(CurTerm.term_tmp, ';');
				if (p != NULL){
					*p = '\0';
					ConsoleLine = (atoi(CurTerm.term_tmp + 2) - 1);
					if (ConsoleLine >= ConsoleCH)
						ConsoleLine = ConsoleCH -1;
					CurTerm.term_tmp[CurTerm.term_tmp_pos] = '\0';
					ConsoleCol = (atoi(p + 1) - 1);
					if (ConsoleCol >= ConsoleCW)
						ConsoleCol = ConsoleCW - 1;
				}
			} else {
				ConsoleCol  = 0;
				ConsoleLine = 0;
			}
			CurTerm.term_tmp_pos = 0;
			CurTerm.term_state   = TERM_DATA;
			break;
		case 'J':
			if (CurTerm.term_tmp_pos == 3){
				switch(CurTerm.term_tmp[2]){
				case '0':
					EscGk0J(hWnd, hDC);
					break;
				case '1':
					EscGk1J(hWnd, hDC);
					break;
				case '2':
					EscGk2J(hWnd, hDC);
					break;
				default:
					break;
				}
			} else {
				EscGk0J(hWnd, hDC);
			}
			CurTerm.bConsDraw = TRUE;
			CurTerm.term_tmp_pos = 0;
			CurTerm.term_state   = TERM_DATA;
			break;
		case 'K':
			if (CurTerm.term_tmp_pos == 3){
				switch(CurTerm.term_tmp[2]){
				case '0':
					EscGk0K(hWnd, hDC);
					break;
				case '1':
					EscGk1K(hWnd, hDC);
					break;
				case '2':
					EscGk2K(hWnd, hDC);
					break;
				default:
					break;
				}
			} else {
				EscGk0K(hWnd, hDC);
			}
			CurTerm.bConsDraw = TRUE;
			CurTerm.term_tmp_pos = 0;
			CurTerm.term_state = TERM_DATA;
			break;
		case 'L':
		case 'M':
			/* 行の挿入と削除 */
		{
			int count;
			if (CurTerm.term_tmp_pos > 2){
				CurTerm.term_tmp[CurTerm.term_tmp_pos] = '\0';
				count = atoi(CurTerm.term_tmp + 2);
			} else {
				count = 1;
			}
			if ((ConsoleLine+count-1) > CurTerm.scroll_bottom)
				count = CurTerm.scroll_bottom-ConsoleLine+1;
			while (count){
				if (c == 'L')
					tty_scroll_down(hWnd, hDC, ConsoleLine, CurTerm.scroll_bottom);
				else
					tty_scroll_up(hWnd, hDC, ConsoleLine, CurTerm.scroll_bottom);
				count--;
			}
		}
			CurTerm.bConsDraw = TRUE;
			CurTerm.term_tmp_pos = 0;
			CurTerm.term_state = TERM_DATA;
			break;
		case 'P':
			/* 文字の削除 */
		{
			int		count ;
			LPTSTR	linetop = GetTextTop() ;
			LPSTR	attrtop = GetAttrTop() ;
			if (CurTerm.term_tmp_pos > 2){
				CurTerm.term_tmp[CurTerm.term_tmp_pos] = '\0';
				count = atoi(CurTerm.term_tmp + 2);
			} else {
				count = 1;
			}
			wcscpy(linetop+ConsoleCol, linetop+ConsoleCol+count);
			strcpy(attrtop+ConsoleCol, attrtop+ConsoleCol+count);
		}
			CurTerm.bConsDraw = TRUE;
			CurTerm.term_tmp_pos = 0;
			CurTerm.term_state = TERM_DATA;
			break;
		case 'h':
		case 'l':
		{
			int num;
			BOOL bSet = (c == 'h') ? TRUE : FALSE;
			if (CurTerm.term_tmp_pos > 2){
				CurTerm.term_tmp[CurTerm.term_tmp_pos] = '\0';
				if (CurTerm.term_tmp[2] != '?'){
					num = atoi(CurTerm.term_tmp + 2);
					switch(num){
					case 4:
						/* 挿入/置換 */
						SetMode(bSet, INSERT);
						break;
					case 20:
						/* New line/Line feed */
						SetMode(bSet, NEWL_LF);
						break;
					}
				} else {
					num = atoi(CurTerm.term_tmp + 3);
					switch(num){
					case 1:
						/* カーソルキーモード */
						SetMode(bSet, CURSORKEY);
						break;
					case 2:
						/* ANSI/VT52 */
						SetMode(bSet, ANSI_VT52);
						break;
					case 3:
						/* カラム */
						if ((IsSetMode(COLUMN_NUM) && !bSet) ||
							(!IsSetMode(COLUMN_NUM) && bSet) ){
							tty_cls();
						}
						SetMode(bSet, COLUMN_NUM);
						break;
					case 4:
						/* スクロール */
						SetMode(bSet, SCROLLING);
						break;
					case 5:
						/* スクリーン */
						SetMode(bSet, SCREEN);
						break;
					case 6:
						/* 原点 */
						SetMode(bSet, ORIGIN);
						break;
					case 7:
						/* 折り返し */
						SetMode(bSet, AUTOWRAP);
						break;
					case 8:
						/* リピート */
						SetMode(bSet, AUTOREPEAT);
						break;
					case 25:
						/* カーソル表示 */
						SetMode(bSet, SHOWCURSOR);
						break;
					}
				}
			}
			CurTerm.term_tmp_pos = 0;
			CurTerm.term_state = TERM_DATA;
		}
			break;
		case 'n':
			if (CurTerm.term_tmp_pos == 3){
				TCHAR tBuf[KEYBUFF_SIZE];
				switch(CurTerm.term_tmp[2]){
				case '6':
					/* カーソル位置のレポート */
					wsprintf(tBuf, TEXT("\033[%d;%dR"), ConsoleLine+1, ConsoleCol+1);
					str_client_send(tBuf);
					break;
				default:
					break;
				}
			}
			CurTerm.term_tmp_pos = 0;
			CurTerm.term_state = TERM_DATA;
			break;
		case 'g':
			/* NYI */
			CurTerm.term_tmp_pos = 0;
			CurTerm.term_state = TERM_DATA;
			break;
		case 'm':
			/* 文字属性の設定 */
			if ( CurTerm.term_tmp_pos > 2 ) {
				CurTerm.term_tmp[CurTerm.term_tmp_pos] = '\0' ;
				parse_attr( CurTerm.term_tmp + 2 ) ;
			} else {
				tty_setattr( 0 ) ;
			}
			CurTerm.term_tmp_pos = 0;
			CurTerm.term_state = TERM_DATA;
			break;
		case 'r':
			/* スクロール領域の設定 */
			if (CurTerm.term_tmp_pos > 2){
				char *p = strchr(CurTerm.term_tmp, ';');
				if (p != NULL){
					*p = '\0';
					CurTerm.scroll_top    = (atoi(CurTerm.term_tmp + 2) - 1);
					if (CurTerm.scroll_top >= ConsoleCH)
						CurTerm.scroll_top = (ConsoleCH - 1);
					CurTerm.term_tmp[CurTerm.term_tmp_pos] = '\0';
					CurTerm.scroll_bottom = (atoi(p + 1) - 1);
					if (CurTerm.scroll_bottom >= ConsoleCH)
						CurTerm.scroll_bottom = (ConsoleCH - 1);
				}
			} else {
				/* NYI */
			}
			CurTerm.term_tmp_pos = 0;
			CurTerm.term_state = TERM_DATA;
			break;
		default:
			CurTerm.term_tmp_pos = 0;
			CurTerm.term_state = TERM_DATA;
			break;
		}
		break;
	case TERM_G0:	// G0のキャラクタセットを設定する ESC(  Fjw
		switch (c){
		case 'B':	CurTerm.G0_char_set = CS_USASCII;	break;
		case '0':	CurTerm.G0_char_set = CS_GRAPH;		break;
		case '1':	CurTerm.G0_char_set = CS_ALTROM;	break;
		case '2':	CurTerm.G0_char_set = CS_ALTROM2;	break;
		case 'J':	CurTerm.G0_char_set = CS_JIS;		break;
		case 'I':	CurTerm.G0_char_set = CS_KANA;		break;
		}
		CurTerm.term_tmp_pos = 0;
		CurTerm.term_state   = TERM_DATA;
		break;
	case TERM_G1:	// G1のキャラクタセットを設定する ESC)  Fjw
		switch (c){
		case 'B':	CurTerm.G1_char_set = CS_USASCII;	break;
		case '0':	CurTerm.G1_char_set = CS_GRAPH;		break;
		case '1':	CurTerm.G1_char_set = CS_ALTROM;	break;
		case '2':	CurTerm.G1_char_set = CS_ALTROM2;	break;
		case 'J':	CurTerm.G1_char_set = CS_JIS;		break;
		case 'I':	CurTerm.G1_char_set = CS_KANA;		break;
		}
		CurTerm.term_tmp_pos = 0;
		CurTerm.term_state   = TERM_DATA;
		break;
	}
	return;
}

void vt100_addstr( HWND hWnd, HDC hDC, LPCTSTR str )
{
	TCHAR	c ;
	HideCaret(hWnd);
	CurTerm.bConsDraw = FALSE;
	while (c = *str++){
		vt100_addchar(hWnd, hDC, c);
	}
	if (CurTerm.bConsDraw) tty_draw( hWnd, hDC ) ;
	ShowCaret(hWnd);
	tty_setcaret();
}

void
tty_setcaret( void )
{
	SetCaretPos( ConsoleTopX + ConsoleCol * ConsoleCharW, ConsoleTopY + ConsoleLine * ConsoleCharH ) ;
}

DWORD
tty_getwh( void )
{
	return ConsoleCH * 256 + ConsoleCW ;
}

DWORD
tty_getcur( void )
{
	return ConsoleLine * 256 + ConsoleCol ;
}

void
tty_setcur( DWORD value )
{
	ConsoleLine = value / 256 ;
	ConsoleCol = value % 256 ;
	tty_setcaret() ;
}

void
tty_cleareol( HWND hWnd, HDC hDC )
{
	RECT	rect ;
	DWORD	index ;
	LPTSTR	linetop = GetTextTop() ;
	LPSTR	attrtop = GetAttrTop() ;

	if ( get_str_pos( linetop, ConsoleCol, &index ) ) {
		linetop[ index ] = TEXT('\0') ;
		attrtop[ index ] = '\0' ;
		if ( CurTerm.ttymode != TTY_VT100 ) {
			HideCaret( hWnd ) ;
			rect.left = ConsoleTopX + tty_getwidth( linetop ) ;
			rect.right = ConsoleTopX + ConsoleW ;
			rect.top = ConsoleTopY + ConsoleLine * ConsoleCharH ;
			rect.bottom = rect.top + ConsoleCharH ;
			if ( rect.left < rect.right ) {
				FillRect( hDC, &rect, GetStockObject( ConsoleBack ) ) ;
			}
			ShowCaret( hWnd ) ;
		}
	}
}

void
tty_scroll_up( HWND hWnd, HDC hDC, DWORD from, DWORD to )
{
	LPTSTR		linetop, linenext ;
	LPSTR		attrtop, attrnext ;
	RECT		rcScroll, rcClip, rect ;
	DWORD		lines, count, x = ConsoleTopX, y = ConsoleTopY ;

	if ( from >= to ) {
		return ;
	}
	lines = to - from + 1 ;
	if ( CurTerm.ttymode != TTY_VT100 ) {
		/* カーソルを消す */
		HideCaret( hWnd ) ;
		/* スクロールする */
		rcClip.left = x ;
		rcClip.right = x + ConsoleW - 1 ;
		rcClip.top = y + from * ConsoleCharH ;
		rcClip.bottom = y + (to + 1) * ConsoleCharH ;
		memcpy( &rcScroll, &rcClip, sizeof rcScroll ) ;
		rcScroll.top = y + (from + 1) * ConsoleCharH ;
		ScrollDC( hDC, 0, - (int) ConsoleCharH, &rcScroll, &rcClip, NULL, NULL ) ;
		/* 残った部分をクリアする */
		rect.left = x, rect.right = x + ConsoleW ;
		rect.top = y + to * ConsoleCharH ;
		rect.bottom = rect.top + ConsoleCharH ;
		FillRect( hDC, &rect, GetStockObject( ConsoleBack ) ) ;
		/* カーソルを戻す */
		ShowCaret( hWnd ) ;
	}
	/* テキストのスクロール */
	linetop  = &ConsoleText[ (ConsoleCW + 1) * from ] ;
	linenext = &linetop[ ConsoleCW + 1 ] ;
	count = lines - 1 ;
	do {
		memcpy( linetop, linenext, WidthOfText() ) ;
		linetop  += ConsoleCW + 1 ;
		linenext += ConsoleCW + 1 ;
	} while ( -- count ) ;
	memset( linetop, 0, WidthOfText() ) ;
	/* 属性のスクロール */
	attrtop  = &ConsoleAttr[ (ConsoleCW + 1) * from ] ;
	attrnext = &attrtop[ ConsoleCW + 1 ] ;
	count = lines - 1 ;
	do {
		memcpy( attrtop, attrnext, WidthOfAttr() ) ;
		attrtop  += ConsoleCW + 1 ;
		attrnext += ConsoleCW + 1 ;
	} while ( -- count ) ;
	memset( attrtop, 0, WidthOfAttr() ) ;
}

void
tty_scroll_down( HWND hWnd, HDC hDC, DWORD from, DWORD to )
{
	LPTSTR		linetop, linenext ;
	LPSTR		attrtop, attrnext ;
	RECT		rcScroll, rcClip, rect ;
	DWORD		lines, count, x = ConsoleTopX, y = ConsoleTopY ;

	if ( from >= to ) {
		return ;
	}
	lines = to - from + 1 ;
	if ( CurTerm.ttymode != TTY_VT100 ) {
		/* カーソルを消す */
		HideCaret( hWnd ) ;
		/* スクロールする */
		rcClip.left = x ;
		rcClip.right = x + ConsoleW - 1 ;
		rcClip.top = y + from * ConsoleCharH ;
		rcClip.bottom = y + (to + 1) * ConsoleCharH ;
		memcpy( &rcScroll, &rcClip, sizeof rcScroll ) ;
		rcScroll.bottom = y + to * ConsoleCharH ;
		ScrollDC( hDC, 0, (int) ConsoleCharH, &rcScroll, &rcClip, NULL, NULL ) ;
		/* 残った部分をクリアする */
		rect.left = x, rect.right = x + ConsoleW ;
		rect.top = y + from * ConsoleCharH ;
		rect.bottom = rect.top + ConsoleCharH ;
		FillRect( hDC, &rect, GetStockObject( ConsoleBack ) ) ;
		/* カーソルを戻す */
		ShowCaret( hWnd ) ;
	}
	/* テキストのスクロール */
	linetop  = &ConsoleText[ (ConsoleCW + 1) * (to - 1) ] ;
	linenext = &linetop[ ConsoleCW + 1 ] ;
	count = lines - 1 ;
	do {
		memcpy( linenext, linetop, WidthOfText() ) ;
		linetop  -= ConsoleCW + 1 ;
		linenext -= ConsoleCW + 1 ;
	} while ( -- count ) ;
	memset( linenext, 0, WidthOfText() ) ;
	/* 属性のスクロール */
	attrtop  = &ConsoleAttr[ (ConsoleCW + 1) * (to - 1) ] ;
	attrnext = &attrtop[ ConsoleCW + 1 ] ;
	count = lines - 1 ;
	do {
		memcpy( attrnext, attrtop, WidthOfAttr() ) ;
		attrtop  -= ConsoleCW + 1 ;
		attrnext -= ConsoleCW + 1 ;
	} while ( -- count ) ;
	memset( attrnext, 0, WidthOfAttr() ) ;
}

/*
 * 属性文字を設定する
 */
BYTE
tty_getattr( void )
{
	return CurTerm.bTermAttr ;
}

/*
 * 属性文字を設定する
 */
void
tty_setattr( BYTE c )
{
	CurTerm.bTermAttr = c ;
}

/*
 * 指定された属性の解析と設定を行う
 */
void
parse_attr( LPSTR str )
{
	int		val ;
	BYTE	attr ;
	CHAR	c, *start = str ;

	attr = tty_getattr() ;
	while ( c = *str ) {
		if ( c == ';' ) {
			*str = 0 ;
			val = atoi( start ) ;
			switch ( val ) {
			case 0:	attr = 0 ;			break ;
			case 7:	attr |= REVERSE ;	break ;
			}
			start = ++ str ;
		} else {
			str ++ ;
		}
	}
	if ( *start ) {
		val = atoi( start ) ;
		switch ( val ) {
		case 0:	attr = 0 ;			break ;
		case 7:	attr |= REVERSE ;	break ;
		}
	}
	tty_setattr( attr ) ;
}

/*
 * 属性描画を行う
 */
void
KDrawTextAttr( HDC hDC, LPCTSTR text, LPCSTR attr, const RECT *rect )
{
	TCHAR	c ;
	CHAR	a, lastattr = AttrBase ;
	DWORD	col = 0, cw, lastcol = 0 ;

	KDrawText( hDC, text, -1, (LPRECT) rect, DT_LEFT ) ;
	while ( c = *text++ ) {
		cw = get_charwidth( col, c ) ;
		a = *attr++ ;
		if ( a != lastattr ) {
			/* 属性が変化した場合 */
			if ( lastattr != AttrBase ) {
				/* 直前までの属性を描く */
				PatBlt( hDC,
						rect->left + lastcol * ConsoleCharW, rect->top,
						(col - lastcol) * ConsoleCharW, rect->bottom - rect->top - 1,
						PATINVERT ) ;
			}
			lastattr = a ;
			if ( a != AttrBase ) {
				lastcol = col ;
			}
		}
		col += cw ;
	}
	if ( lastattr != AttrBase ) {
		/* 直前までの属性を描く */
		PatBlt( hDC,
				rect->left + lastcol * ConsoleCharW, rect->top,
				(col - lastcol) * ConsoleCharW, rect->bottom - rect->top - 1,
				PATINVERT ) ;
	}
}


// 特殊グラフィックキャラクタを似たような文字に置き換えるためのテーブル Fjw
TCHAR SpecialGraphTable[32] ={
	TEXT(' '),	// Blank
	TEXT(' '),	// ◆ Diamond
	TEXT(' '),	// Check Board(error indicator)
	0x0009,		// HT(horizontal tab)
	0x000c,		// FF(form feed)
	0x000d,		// CR(carriage return)
	0x000a,		// LF(line feed)
	TEXT(' '),	// Degree symbol
	TEXT(' '),	// ± Plus/Minus
	0x0000,		// NL(new line)
	0x0000,		// VT(vertical tab)
	TEXT('+'),	// ┘ Lower-right corner
	TEXT('+'),	// ┐ Upper-right corner
	TEXT('+'),	// ┌ Upper-left corner
	TEXT('+'),	// └ Lower-left corner
	TEXT('+'),	// Crossing lines
	TEXT('-'),	// Horizontal line-Scan 1
	TEXT('-'),	// Horizontal line-Scan 3
	TEXT('-'),	// Horizontal line-Scan 5
	TEXT('-'),	// Horizontal line-Scan 7
	TEXT('-'),	// Horizontal line-Scan 9
	TEXT('+'),	// ├ Left T
	TEXT('+'),	// ┤ Right T
	TEXT('+'),	// ┴ Bottom T
	TEXT('+'),	// ┬ Top T
	TEXT('|'),	// Vertical Bar
	TEXT(' '),	// ≦ Less than or equal to
	TEXT(' '),	// ≧ Less than or equal to
	TEXT(' '),	// π PI(mathematical)
	TEXT(' '),	// ≠ Not equal to
	TEXT(' '),	// £ UK pound sign
	TEXT('･'),	// Center dot
};

