// PDicReader.cpp: CPDicReader クラスのインプリメンテーション
//
//////////////////////////////////////////////////////////////////////
//	2007/03/21 hishida

#include "stdafx.h"
#include "PDicReader.h"

#ifdef _WIN32_WCE
#include "FileIO.h"
#endif

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

#define	_KANJI_CP	932
#define	_DEF_RELEASECONTROL	1


//////////////////////////////////////////////////////////////////////
//	覚え書き
//////////////////////////////////////////////////////////////////////
/*
	Unicode版のコンパイル指定
	------------------------------------------------------------
								MBCS		Unicode
	------------------------------------------------------------
	エントリポイントシンボル	無し		wWinMainCRTStartup
	プリプロセッサの定義		_MBCS		_UNICODE,UNICODE
	------------------------------------------------------------
*/
/*
PDICのデータ構造

	ヘッダー部	(256)
	拡張ヘッダー部(可変長:現在0)
	インデックス部(可変長:256単位)
	データ部	(可変長:256単位)

	インデックスの開始位置	=	ヘッダ長(256)


	NEWDIC2(3.0)
	(ﾃﾞｨｽｸ上の位置)=(ﾍｯﾀﾞｰｻｲｽﾞ)
					+(ｲﾝﾃﾞｯｸｽｻｲｽﾞ)
					+(ﾃﾞｰﾀﾌﾞﾛｯｸ長)*(物理ﾌﾞﾛｯｸ番号)

	NEWDIC3(4.0)
	(ﾃﾞｨｽｸ上の位置)=(ﾍｯﾀﾞｰｻｲｽﾞ)
					+(拡張ﾍｯﾀﾞｰｻｲｽﾞ)
					+(ｲﾝﾃﾞｯｸｽブロック数)*(ブロックサイズ)
					+(ブロックサイズ)*(物理ﾌﾞﾛｯｸ番号)

  	(ディスクの位置)=(header_size) 
					+(extheader)
					+(index_block)*(block_size)
					+(block_size)×(物理ブロック番号)

					※ブロックサイズは256

  dictype & 0x08 ならUnicode


*/

//	BOCU-1からUCS2への変換
int BOCU1_decode( byte*p,		// in:BOCU-1
				 int length,	// in:入力文字列のバイト数
				 WCHAR*dest,	// out:UCS2への変換先
				 int maxdest )	// in:destの最大長
{
	ASSERT( p );
	ASSERT( dest );

    Bocu1Rx rx={ 0, 0, 0 };
    int32_t c, i, sLength;

	WCHAR*	destorg	= dest;
    i = sLength = 0;
    while( i<length && dest<destorg+maxdest-1 ) {
        c=decodeBocu1(&rx, p[i++]);
        if(c<-1) {
			break;
        }
        if(c>=0) {
			*dest++	= c;
        }
    }
	*dest	= '\0';
    return sLength;
}

#if	defined(UNICODE)
// 単純サーチ(UTF-16)
int SearchString( WCHAR* textstr, int textlen, WCHAR* keyword, int keywordlen )
{
	WCHAR *wbp;
	for ( wbp = textstr; wbp<=textstr+textlen-keywordlen; ++wbp ) {
		if ( _tcsncmp( wbp, keyword, keywordlen ) == 0 ) {
			return wbp - textstr;
		}
	}
	return -1;
}
#endif

// 単純サーチ(バイト列)
int SearchString( byte* textstr, int textlen, byte* keyword, int keywordlen )
{
	byte *bp;
	for ( bp = textstr; bp<=textstr+textlen-keywordlen; ++bp ) {
		if ( strncmp( (char*)bp, (char*)keyword, keywordlen ) == 0 ) {
			return bp - textstr;
		}
	}
	return -1;
}

#define	_BM_SEARCH	1	// 全文検索はBM法で実行

#if	defined(_BM_SEARCH)

#define	CSIZE	256		// byte 単位で比較を行う

// 移動量テーブルの作成
//	byte bm_table[CSIZE];
void make_bm_table( byte *pattern,	// 検索文字列
					int size ,		// 検索文字列長
					byte *bm_table	// 移動量テーブル(配列長はCSIZE)
					)
{
	int i;
	for( i=0; i<CSIZE; i++ )
		bm_table[i] = size;
	size -= 1;
	for ( i=0; i<size; ++i ) {
		bm_table[ pattern[i] ] = size - i;
	}
}

// BM法による検索
int bm_search(	byte*buffer,	// 検索対象文字列
				int bufLen,		// 同、長さ
				byte*pattern,	// 検索文字列
				int patlen,		// 同、長さ
				byte *bm_table	// 移動量テーブル make_bm_table() で作成したもの
				)
{
	int	row = 1;	// 物理行数：改行個数+1
	long rowPos = 0;	// 行数カウント用インデックス
	long BOLPos = 0;		// 一致行の行頭
	int nMatch = 0;	// 一致件数

//	if ( bufLen<=0 )
//		return -1;
	ASSERT( bufLen>0 );
	//検索エンジン
	int i;
	unsigned char c, tail;

	if ( pattern == NULL )
		return -1;
	tail = pattern[ patlen -1 ];					// 最後の文字
	int Index = 0;

	if( patlen == 1 ) {			// 検索文字列の長さが1の場合
		while(1) {
			if( Index > bufLen ) {
				return -1;
			}
			if( (buffer[Index] ) == tail ) {
				return Index;
			}
			Index++;
		}
	}
	else {						// 検索文字列の長さが2以上の場合
		Index += (patlen-1);	// 検索開始ポインタを検索文字列の最後の文字にセット

		while(1) {								// 検索開始
			if( Index > bufLen ) {			//見つからなかった
				return -1;
			}
			c = buffer[Index];
			if( c == tail ) {
				for( i=1; i<patlen; i++ ) {
					if( buffer[ Index-i ] != pattern[ (patlen-1)-i ] ) {
						Index++;
						goto NO_MATCH;
					}
				}
				return Index;					//発見!!
			}
			else	Index += bm_table[ (int)c ];
NO_MATCH:;
		}
	}
	return -1;
}

#endif



//////////////////////////////////////////////////////////////////////
// 構築/消滅
//////////////////////////////////////////////////////////////////////

CPDicReader::CPDicReader()
{
	m_fp	= NULL;
	m_pdic_ver	= 0;
	m_pIndex	= NULL;
	m_pData	= NULL;

	// 検索構造
	m_hitent		= NULL;	// 検索一致結果
	m_numhit		= 0;	// ヒット数
	m_title_pool	= NULL;	// 検索に一致した見出し語のプール
	m_max_title_pool= 0;	// m_title_poolの割り当てサイズ
	m_title_len		= 0;	// m_title_poolの最終書込位置
	m_bOpened		= 0;
}

CPDicReader::~CPDicReader()
{
	Close();
}

static char PDIC_MAGIC_STR[]	= "=============== Dictionary for PDIC ===============";

// PDIC 辞書を開く
//	0	正常
//	-1	失敗
int	CPDicReader::Open( LPCTSTR pszFilePath )
{
	ASSERT( pszFilePath );

	byte buffer[PDIC_HEADER_256];	// ヘッダ先頭256

	if ( m_bOpened ) 
		return -1;

	// PDIC辞書ファイルをopenする
	m_fp	= _tfopen( pszFilePath, _T("rb") );
	if ( !m_fp ) {
		return -1;
	}

	// ヘッダ部を読む
	if ( fread( &buffer, 1,PDIC_HEADER_256, m_fp ) != PDIC_HEADER_256 ) {
		goto header_error;
	}
	ASSERT(sizeof(m_header) == PDIC_HEADER_256);
	memcpy( &m_header, buffer, PDIC_HEADER_256 );
	// error check
	m_pdic_ver = m_header.version >> 8;
	if ( m_pdic_ver < 4
		|| m_header.header_size	== 0
		|| m_header.header_size	> PDIC_MAX_HEADER_LEN
		|| m_header.block_size == 0 
		|| m_header.block_size > PDIC_MAX_BLKLEN
		) {
		goto header_error;
	}
	if ( memcmp( (char*)PDIC_MAGIC_STR, (char*)m_header.headername+0x0D, sizeof(PDIC_MAGIC_STR)-1 )==0	// PDIC/Unicode
	  || memcmp( (char*)PDIC_MAGIC_STR, (char*)m_header.headername+0x18, sizeof(PDIC_MAGIC_STR)-1 )==0	// PDIC for win32
	  ) {
		;
	}
	else {
		goto header_error;
	}

	//	バージョンによって項目のオフセットが異なるので、調整を行う。
	//	ver.5 を仮定しているので、ver.5の場合は調整なし
	//	なお、nindex,nblockは使用せず、nindex2,nblock2を使う
	switch ( m_pdic_ver ) {
	case 2:
		m_header.nindex2	=	m_header.nindex;
		m_header.nblock2	=	m_header.nblock;
		m_header.extheader	=	0;
		m_header.index_blkbit	= 0;	// 16 bit
		m_header.empty_block2	= m_header.empty_block;
		break;
	case 3:
		m_header.nindex2	=	m_header.nindex;
		m_header.nblock2	=	m_header.nblock;
		m_header.extheader	=	0;
		m_header.index_blkbit	= 0;	// 16 bit
		m_header.block_size	*=16;	// NEWDIC2の場合、16で割った数が入っている
		m_header.empty_block2	= m_header.empty_block;
		break;
	case 4:
		m_header.olenumber	=	LFOURBYTEUINT(buffer+167);
		m_header.os			=	buffer[171];
		m_header.extheader	=	LFOURBYTEUINT(buffer+182);	
		m_header.empty_block2	= LFOURBYTEUINT(buffer+186);
		m_header.nindex2	=	LFOURBYTEUINT(buffer+190);
		m_header.nblock2	=	LFOURBYTEUINT(buffer+194);
		m_header.index_blkbit	= buffer[198];
		m_header.update_count	= LFOURBYTEUINT(buffer+208);
		break;
	case 5:
		break;
	}
	m_pIndex	= new CPDicIndex( m_fp, &m_header );
	m_pData		= new CPDicData( m_fp, &m_header );

	m_bOpened	= 1;
	return 0;

header_error:
	Close();
	return -1;
}

// PDIC 辞書のファイルハンドルのみ再open
//	0	正常
//	-1	失敗
int	CPDicReader::ReOpen( LPCTSTR pszFilePath )
{
	ASSERT( pszFilePath );

	if ( !m_bOpened || !m_fp ) 
		return -1;

	fclose( m_fp );

	// PDIC辞書ファイルをopenする
	m_fp	= _tfopen( pszFilePath, _T("rb") );
	if ( !m_fp ) {
		return -1;
	}
	if ( m_pData )	m_pData->m_fp	= m_fp;
	if ( m_pIndex )	m_pIndex->m_fp	= m_fp;

	return 0;

}

// PDIC 辞書の使用を終了する
void CPDicReader::Close()
{
	if ( m_fp ) {
		fclose( m_fp );
		m_fp	= NULL;
	}

	if ( m_pIndex ) {
		delete m_pIndex;
		m_pIndex	= NULL;
	}

	if ( m_pData ) {
		delete m_pData;
		m_pData	= NULL;
	}

	// 検索構造
	ClearHitEnt();

	m_bOpened	= 0;
}

void CPDicReader::ClearHitEnt()
{
	//	検索結果をクリア
	if ( m_hitent ) {
		free( m_hitent );
		m_hitent	= NULL;
	}
	if ( m_title_pool ) {
		free( m_title_pool );
		m_title_pool	= NULL;
	}
	m_numhit		= 0;	// ヒット数
	m_max_hitent	= 0;	// m_hitent[]の割当サイズ

	m_max_title_pool	= 0;	// m_title_poolの割当サイズ
	m_title_len		= 0;	// m_title_poolの最終書込位置

}

// PDIC Unicode検索語の正規化
//	カタカナのみ清音化している

WCHAR fromPDICTbl[]	= {
	//	hira dakuon
	/*	0x304C,	0x304E,	0x3050,	0x3052,	0x3054,	//	ga	gi	gu	ge	go
	 0x3056,	0x3058,	0x305A,	0x305C,	0x305E,	//	za	zi	zu	ze	zo
	 0x3060,	0x3062,	0x3065,	0x3067,	0x3069,	//	da	di	du	de	do
	 0x3070,	0x3073,	0x3076,	0x3079,	0x307C,	//	ba	bi	bu	be	bo
	 0x3071,	0x3074,	0x3077,	0x307A,	0x307D,	//	pa	pi	pu	pe	po	
	 
	 0x3041,	0x3043,	0x3045,	0x3047,	0x3049,	//	xa	xi	xu	xe	xo
	 0x3063,									//	xtu
	 0x3083,	0x3085,	0x3087,					//	xya	xyu	xyo
	 0x308E,	0x3095,	0x3096,					//	xwa	xka	xke
	 */
	//	kata dakuon
	0x30AC,	0x30AE,	0x30B0,	0x30B2,	0x30B4,	//	ga	gi	gu	ge	go
	0x30B6,	0x30B8,	0x30BA,	0x30BC,	0x30BE,	//	za	zi	zu	ze	zo
	0x30C0,	0x30C2,	0x30C5,	0x30C7,	0x30C9,	//	da	di	du	de	do
	0x30D0,	0x30D3,	0x30D6,	0x30D9,	0x30DC,	//	ba	bi	bu	be	bo
	0x30D1,	0x30D4,	0x30D7,	0x30DA,	0x30DD,	//	pa	pi	pu	pe	po	
	
	0x30A1,	0x30A3,	0x30A5,	0x30A7,	0x30A9,	//	xa	xi	xu	xe	xo
	0x30C3,									//	xtu
	0x30E3,	0x30E5,	0x30E7,					//	xya	xyu	xyo
	0x30EE,	0x30F5,	0x30F6,					//	xwa	xka	xke
	
};

WCHAR toPDICTbl[]	= {
	//	hira seion
	/*	0x304B,	0x304D,	0x304F,	0x3051,	0x3053,	//	ka	ki	ku	ke	ko
	 0x3055,	0x3057,	0x3059,	0x305B,	0x305D,	//	sa	si	su	se	so
	 0x305F,	0x3061,	0x3064,	0x3066,	0x3068,	//	ta	ti	tu	te	to
	 0x306F,	0x3072,	0x3075,	0x3078,	0x307B,	//	ha	hi	hu	he	ho
	 0x306F,	0x3072,	0x3075,	0x3078,	0x307B,	//	ha	hi	hu	he	ho
	 
	 0x3042,	0x3044,	0x3046,	0x3048,	0x304A,	//	a	i	u	e	o
	 0x3064,									//	tu
	 0x3084,	0x3086,	0x3088,					//	ya	yu	yo
	 0x308F,	0x304B,	0x3051,					//	wa	ka	ke
	 */
	//	kata seion
	0x30AB,	0x30AD,	0x30AF,	0x30B1,	0x30B3,	//	ka	ki	ku	ke	ko
	0x30B5,	0x30B7,	0x30B9,	0x30BB,	0x30BD,	//	sa	si	su	se	so
	0x30BF,	0x30C1,	0x30C4,	0x30C6,	0x30C8,	//	ta	ti	tu	te	to
	0x30CF,	0x30D2,	0x30D5,	0x30D8,	0x30DB,	//	ha	hi	hu	he	ho
	0x30CF,	0x30D2,	0x30D5,	0x30D8,	0x30DB,	//	ha	hi	hu	he	ho
	
	0x30A2,	0x30A4,	0x30A6,	0x30A8,	0x30AA,	//	a	i	u	e	o
	0x30C4,									//	tu
	0x30E4,	0x30E6,	0x30E8,					//	ya	yu	yo
	0x30EF,	0x30AB,	0x30B1,					//	wa	ka	ke
	
};


WCHAR normalizePDIC( WCHAR uch ) {
	WCHAR xch = 0;
	int ix;
	int imax = sizeof(fromPDICTbl) / sizeof(WCHAR);
	for ( ix=0;ix<imax; ++ix ) {
		if ( fromPDICTbl[ix] == uch ) {
			xch = toPDICTbl[ix];
			break;
		}
	}
	return xch;
}


// キーワード検索を行う
//	戻り値：一致件数	0なら不一致
int CPDicReader::SearchWord( LPCTSTR keyword,	// 検索キーワード
							int bExact,		// T:完全一致
							int maxHit,		// 検索一致数上限; 0 は無制限
							int *over		// 上限を超えた場合、1を設定
							)
{
	ASSERT( keyword );

#if	defined(UNICODE)

	WCHAR wcNrmSearchWord[PDIC_MAX_TITLE+1];	// PDIC Unicode用 正規化検索文字列

	if ( GetPDICVer() >= 6 ) {
		// Unicode版の場合、検索語の正規化
		//	(1)英字の小文字化
		//	(2)カタカナ半濁音、濁音、拗音の清音化
		int ix;
		int klen	= _tcslen(keyword);
		for ( ix=0;ix<klen && ix<PDIC_MAX_TITLE ; ++ix ) {
			WCHAR	xch;
			if ( keyword[ix]>='A' && keyword[ix]<='Z' ) {
				wcNrmSearchWord[ix] = keyword[ix]+('a'-'A');
			}
			else if ( keyword[ix] == '-' ) {
				wcNrmSearchWord[ix]	= ' ';
			}
			else if ( xch = normalizePDIC( keyword[ix] ) ) {
				wcNrmSearchWord[ix]	= xch;
			}
			else {
				wcNrmSearchWord[ix] = keyword[ix];
			}
		}
		wcNrmSearchWord[ix] = 0;
		
		keyword	= wcNrmSearchWord;
	}
#endif

	unsigned long	_datablk;	// データブロックの物理ブロック番号
	byte _titlestr[PDIC_MAX_TITLE+1];	// インデックスに記録された見出し語
#if	defined(UNICODE)
	TCHAR w_titlestr[PDIC_MAX_TITLE+1];
	byte mb_keyword[PDIC_MAX_TITLE+1];
	int nChar;
#endif
	int	keywordlen;

	int _titlelen,_hoffs,_honbunlen,_yoffs,_yoreilen,_poffs,_phoneticlen,_level;
	m_numhit	= 0;
	*over	= false;
//	if ( maxHit == 0 ) {
//		maxHit	= PDIC_MAX_HIT;
//	}

	//	検索結果をクリア
	ClearHitEnt();

	//	(1)先頭のインデックス番号を見つける
	long 	_idxno	= m_pIndex->BSearchIndex( keyword );

	//	(2)インデックスブロックをもとにデータブロックを順に読む
	int	_over	= 0;
	int	status;

#if	defined(UNICODE)
	if ( m_header.dictype & 0x08 ) {	// BOCU1
		keywordlen	= _tcslen(keyword);
	}
	else {
		nChar = ::WideCharToMultiByte( _KANJI_CP, NULL, keyword, _tcslen(keyword), 
					(LPSTR)mb_keyword, PDIC_MAX_TITLE, NULL, NULL);	// 変換(→ShiftJIS)
		mb_keyword[nChar]	= '\0';
		keywordlen	= nChar;
	}
#else
	keywordlen	= _tcslen(keyword);
#endif

	// 次のインデックスを読む
	while ( !_over 
		&& _idxno<m_header.nindex2 
		&& m_pIndex->ReadRec( _idxno, &_datablk, (byte*)_titlestr, &_titlelen ) == 0 ) {

		////////////////////////////////////////////////////
		// 以下はdicorder == 0 (コード順)の場合用
		////////////////////////////////////////////////////
#if	defined(UNICODE)
		//	Unicode辞書の時、BOCU->UCS2に変換後比較する
		if ( m_header.dictype & 0x08 ) {	// BOCU1
			BOCU1_decode( _titlestr, _titlelen, w_titlestr, PDIC_MAX_TITLE );
			status = _tcsncmp( w_titlestr, keyword, keywordlen ) ;
		}
		else {
			//	非Unicode辞書の場合、ShiftJISで比較する
			status = strncmp( (char*)_titlestr, (char*)mb_keyword, keywordlen ) ;
		}
#else
		status = strncmp( (char*)_titlestr, keyword, keywordlen ) ;
#endif
		if ( status > 0 ) {	// キーワードを超えたので探索終了
			_over	= 1;
			break;
		}
		// _datablkから始まるデータブロック(連続ブロック)を読む
		if ( m_pData->ReadBlks( _datablk ) == -1 ) {
			break;
		}
		int	_num	= 0;
		// 連続ブロック中のデータレコードを読む
		while ( !_over ) {
			int	_recOffs	= m_pData->GetOffs();
			if ( m_pData->ReadRec( (byte*)_titlestr, &_titlelen,
								&_hoffs, &_honbunlen,
								&_yoffs, &_yoreilen,
								&_poffs, &_phoneticlen, &_level ) != 0 ) {
				break;
			}
			int	_strlen;
	#if	defined(UNICODE)
			//	Unicode辞書の時、BOCU->UCS2に変換後比較する
			if ( m_header.dictype & 0x08 ) {	// BOCU1
				BOCU1_decode( _titlestr, _titlelen, w_titlestr, PDIC_MAX_TITLE );
				// PDIC/Unicode 対応 ; 検索文字列\t表示文字列
				if ( bExact && m_pdic_ver >= 6 ) {
					int ix;
					for ( ix=0;ix<PDIC_MAX_TITLE;++ix ) {
						if ( w_titlestr[ix] == 0x09 ) {
							w_titlestr[ix]	= 0;
							break;
						}
					}
				}
				_strlen	= _tcslen( w_titlestr );
				status = _tcsncmp( w_titlestr, keyword, keywordlen ) ;
			}
			else {
				//	非Unicode辞書の場合、ShiftJISで比較する
				_strlen	= _titlelen;
				status = strncmp( (char*)_titlestr, (char*)mb_keyword, keywordlen ) ;
			}
	#else
			_strlen	= _titlelen;
			status = strncmp( (char*)_titlestr, keyword, keywordlen ) ;
	#endif
			if ( status == 0 ) {	// 
				//	!bExact の時は一致(前方一致)
				//	bExactの時は_titlestrの長さがkeywordlenに等しいとき一致
				if ( !bExact || bExact && _strlen == keywordlen ) {
					//	条件に一致するデータは検索結果テーブルに登録

					//	検索結果テーブルが未割当なら、ここで割り当て
					//	サイズが不足する場合、拡張
					if ( !m_hitent ) {
						m_max_hitent	= MAX_PDIC_HITENT;
						m_hitent	= (PDIC_HITENT*)malloc( m_max_hitent * sizeof(PDIC_HITENT) );
					}
					else if ( m_max_hitent <= m_numhit ) {
						m_max_hitent	+= MAX_PDIC_HITENT;
						m_hitent	= (PDIC_HITENT*)realloc( m_hitent, m_max_hitent * sizeof(PDIC_HITENT) );
					}
					//	見出し語は、見出し語プールに保存する
					if ( !m_title_pool ) {
						m_max_title_pool	= MAX_PDIC_POOL;
						m_title_pool	= (byte*)malloc( m_max_title_pool * sizeof(byte) );
					}
					else if ( m_max_title_pool <= m_title_len+_titlelen+1 ) {
						m_max_title_pool	+= MAX_PDIC_POOL;
						m_title_pool	= (byte*)realloc( m_title_pool, m_max_title_pool * sizeof(byte) );
					}

					//	検索結果テーブルに登録する
					memset( &m_hitent[m_numhit] ,0, sizeof(PDIC_HITENT) );
					m_hitent[m_numhit].indexno		= _idxno;
					m_hitent[m_numhit].blkno		= _datablk;
					m_hitent[m_numhit].titleoffs	= m_title_len;
					m_hitent[m_numhit].titlelen		= _titlelen;
					m_hitent[m_numhit].hoffs		= _hoffs;
					m_hitent[m_numhit].honbunlen	= _honbunlen;
					m_hitent[m_numhit].yoffs		= _yoffs;
					m_hitent[m_numhit].yoreilen		= _yoreilen;
					m_hitent[m_numhit].poffs		= _poffs;
					m_hitent[m_numhit].phoneticlen	= _phoneticlen;
					m_hitent[m_numhit].level		= _level;
					m_hitent[m_numhit].recOffs		= _recOffs;
//					m_hitent[m_numhit].num			= _num;

					memcpy( m_title_pool+m_title_len,_titlestr,_titlelen );
					m_title_len	+= _titlelen;
					m_title_pool[m_title_len++]	= '\0';

					++m_numhit;
				}
				if ( bExact && _strlen > keywordlen ) {
					_over	= 1;
				}
			}
			else if ( status < 0 ) {	//
				//	検索語よりも手前なので、スキップ
			}
			else if ( status > 0 ) {
				// キーワードを超えたので探索終了
				_over	= 1;
				break;
			}
			++_num;

			//	検索上限が設定されていて件数が超える場合は、検索中止
			if ( maxHit>0 && m_numhit>=(unsigned long)maxHit ) {
				_over	= 1;
				*over	= true;
			}
		}
		++_idxno;

	}
	return m_numhit;
}

// 指定したインデックス番号の示すデータブロックを全て検索
int CPDicReader::SearchAbs( unsigned long idxno )
{
	unsigned long	_datablk;	// データブロックの物理ブロック番号
	byte _titlestr[PDIC_MAX_TITLE+1];	// インデックスに記録された見出し語

	int _titlelen,_hoffs,_honbunlen,_yoffs,_yoreilen,_poffs,_phoneticlen,_level;

	//	検索結果をクリア
	ClearHitEnt();

	//	インデックスブロックをもとにデータブロックを順に読む
	int	_over	= 0;

	// 次のインデックスを読む
	if ( idxno<(unsigned long)m_header.nindex2 
		&& m_pIndex->ReadRec( idxno, &_datablk, (byte*)_titlestr, &_titlelen ) == 0
		&& m_pData->ReadBlks( _datablk ) == 0 ) {

		int	_num	= 0;
		// 連続ブロック中のデータレコードを読む
		while ( !_over ) {
			int	_recOffs	= m_pData->GetOffs();
			if ( m_pData->ReadRec( (byte*)_titlestr, &_titlelen,
								&_hoffs, &_honbunlen,
								&_yoffs, &_yoreilen,
								&_poffs, &_phoneticlen, &_level ) != 0 ) {
				break;
			}


			//	検索結果テーブルが未割当なら、ここで割り当て
			//	サイズが不足する場合、拡張
			if ( !m_hitent ) {
				m_max_hitent	= MAX_PDIC_HITENT;
				m_hitent	= (PDIC_HITENT*)malloc( m_max_hitent * sizeof(PDIC_HITENT) );
			}
			else if ( m_max_hitent <= m_numhit ) {
				m_max_hitent	+= MAX_PDIC_HITENT;
				m_hitent	= (PDIC_HITENT*)realloc( m_hitent, m_max_hitent * sizeof(PDIC_HITENT) );
			}
			//	見出し語は、見出し語プールに保存する
			if ( !m_title_pool ) {
				m_max_title_pool	= MAX_PDIC_POOL;
				m_title_pool	= (byte*)malloc( m_max_title_pool * sizeof(byte) );
			}
			else if ( m_max_title_pool <= m_title_len+_titlelen+1 ) {
				m_max_title_pool	+= MAX_PDIC_POOL;
				m_title_pool	= (byte*)realloc( m_title_pool, m_max_title_pool * sizeof(byte) );
			}

			//	検索結果テーブルに登録する
			memset( &m_hitent[m_numhit] ,0, sizeof(PDIC_HITENT) );
			m_hitent[m_numhit].indexno		= idxno;
			m_hitent[m_numhit].blkno		= _datablk;
			m_hitent[m_numhit].titleoffs	= m_title_len;
			m_hitent[m_numhit].titlelen		= _titlelen;
			m_hitent[m_numhit].hoffs		= _hoffs;
			m_hitent[m_numhit].honbunlen	= _honbunlen;
			m_hitent[m_numhit].yoffs		= _yoffs;
			m_hitent[m_numhit].yoreilen		= _yoreilen;
			m_hitent[m_numhit].poffs		= _poffs;
			m_hitent[m_numhit].phoneticlen	= _phoneticlen;
			m_hitent[m_numhit].level		= _level;
			m_hitent[m_numhit].recOffs		= _recOffs;
//			m_hitent[m_numhit].num			= _num;

			memcpy( m_title_pool+m_title_len,_titlestr,_titlelen );
			m_title_len	+= _titlelen;
			m_title_pool[m_title_len++]	= '\0';

			++m_numhit;

			++_num;
		}
	}
	return m_numhit;
}

#if defined(_DEF_RELEASECONTROL)
void ReleaseControl();
#else
// 制御をシステムに返す
void ReleaseControl()
{
		MSG message;
		if ( ::PeekMessage(&message,NULL,0,0,PM_REMOVE)) {
			::TranslateMessage(&message);
			::DispatchMessage(&message);
		}
		Sleep( 0 );	// 同じ優先順位のスレッドがあれば処理を譲る
}
#endif

// 全文検索
int CPDicReader::SearchFullText(
				LPCTSTR keyword,	// 検索キーワード
				int maxHit,		// 検索一致数上限; 0 は無制限
				int *over,		// 上限を超えた場合、1を設定
				short *bAbort,	// 中断フラグ
				void ( *EchoCallback )(LPVOID pParam,int count, int blks, int nRead ),	// 表示コールバック
				LPVOID pParam,	// コールバック関数に引き渡すcallerのオブジェクト
				int flag	// 検索対象フラグ	PDIC_S_TITLE | PDIC_S_HONBUN | PDIC_S_YOREI
		)	
{
	ASSERT( keyword );

	byte _titlestr[PDIC_MAX_TITLE+1];	// インデックスに記録された見出し語
#if	defined(UNICODE)
	TCHAR w_honbun[PDIC_MAX_HONBUN+1];
	TCHAR w_titlestr[PDIC_MAX_TITLE+1];
	byte mb_keyword[PDIC_MAX_TITLE+1];
	int nChar;
	int	_strlen;
#endif
	int	keywordlen;

	int _titlelen,_hoffs,_honbunlen,_yoffs,_yoreilen,_poffs,_phoneticlen,_level;
	m_numhit	= 0;
	*over	= false;
//	if ( maxHit == 0 ) {
//		maxHit	= PDIC_MAX_HIT;
//	}

	//	検索結果をクリア
	ClearHitEnt();

	//	(1)先頭に位置づける
	m_seq_idxno	= 0;

	//	(2)インデックスブロックをもとにデータブロックを順に読む
	int	_over	= 0;
	int	status;

#if	defined(UNICODE)
	if ( m_header.dictype & 0x08 ) {	// BOCU1
		keywordlen	= _tcslen(keyword);
	}
	else {
		nChar = ::WideCharToMultiByte( _KANJI_CP, NULL, keyword, _tcslen(keyword), 
					(LPSTR)mb_keyword, PDIC_MAX_TITLE, NULL, NULL);	// 変換(→ShiftJIS)
		mb_keyword[nChar]	= '\0';
		keywordlen	= nChar;
	}
#else
	keywordlen	= _tcslen(keyword);
#endif

	// 次のインデックスを読む
	while ( !_over 
		&& m_seq_idxno<(unsigned long)m_header.nindex2 
		&& m_pIndex->ReadRec( m_seq_idxno, &m_seq_datablk, (byte*)_titlestr, &_titlelen ) == 0 ) {

		// _datablkから始まるデータブロック(連続ブロック)を読む
		if ( m_pData->ReadBlks( m_seq_datablk ) == -1 ) {
			break;
		}
		int	_num	= 0;
		// 連続ブロック中のデータレコードを読む
		while ( !_over ) {
			int	_recOffs	= m_pData->GetOffs();
			// 次のレコードを読む
			if ( m_pData->ReadRec( (byte*)_titlestr, &_titlelen, 
									&_hoffs, &_honbunlen,
									&_yoffs, &_yoreilen,
									&_poffs, &_phoneticlen, &_level)!= 0 ) {
				//	データブロック内の最後のレコードの場合、次のデータブロックを読む

				// インデックスから、次のデータブロックを得る
				++m_seq_idxno;
				if ( m_pIndex->ReadRec( m_seq_idxno, &m_seq_datablk, (byte*)_titlestr,&_titlelen ) != 0 ) {
					break;
				}
				// 次のデータブロックを読む
				if ( m_pData->ReadBlks( m_seq_datablk ) != 0 ) {
					break;
				}
				// 次のレコードを読む
				_recOffs	= m_pData->GetOffs();
				if ( m_pData->ReadRec( (byte*)_titlestr, &_titlelen,
									&_hoffs, &_honbunlen,
									&_yoffs, &_yoreilen,
									&_poffs, &_phoneticlen, &_level )!= 0 ) {
					break;
				}
			}
			byte* p = m_pData->GetBuffer();
			if ( !p ) 
				break;

			if ( EchoCallback ) EchoCallback( pParam, m_numhit, m_header.nindex2,m_seq_idxno );
			ReleaseControl();
			if ( *bAbort ) {
				_over	= 1;
				break;
			}

#if	defined(_BM_SEARCH)
			byte bm_table[CSIZE];
	#if	defined(UNICODE)
			if ( m_header.dictype & 0x08 ) {	// BOCU1
				make_bm_table( (byte*)keyword, keywordlen*sizeof(WCHAR) ,bm_table);
			}
			else {
				make_bm_table( mb_keyword, keywordlen ,bm_table);
			}
	#else
			make_bm_table( (byte*)keyword, keywordlen , bm_table);
	#endif
#endif
			status	= -1;	// 一致すれば0

			// (1)見出しを全文検索
			if ( flag & PDIC_S_TITLE ) {
#if	defined(UNICODE)
				//	Unicode辞書の時、BOCU->UCS2に変換後比較する
				if ( m_header.dictype & 0x08 ) {	// BOCU1
					BOCU1_decode( _titlestr, _titlelen, w_titlestr, PDIC_MAX_TITLE );
					_strlen	= _tcslen( w_titlestr );
	#if	defined(_BM_SEARCH)
					if ( bm_search( (byte*)w_titlestr,_strlen*sizeof(WCHAR), (byte*)keyword, keywordlen*sizeof(WCHAR),bm_table ) != -1 ) {
	#else
					if ( SearchString( w_titlestr,_strlen, (WCHAR*)keyword, keywordlen ) != -1 ) {
	#endif
						status	= 0;
					}
				}
				else {
					//	非Unicode辞書の場合、ShiftJISで比較する
	#if	defined(_BM_SEARCH)
					if ( bm_search( _titlestr,_titlelen, mb_keyword, keywordlen,bm_table ) != -1 ) {
	#else
					if ( SearchString( _titlestr,_titlelen, mb_keyword, keywordlen ) != -1 ) {
	#endif
						status	= 0;
					}
				}
#else
	#if	defined(_BM_SEARCH)
				if ( bm_search( _titlestr,_titlelen, (byte*)keyword, keywordlen,bm_table ) != -1 ) {
	#else
				if ( SearchString( _titlestr,_titlelen, keyword, keywordlen ) != -1 ) {
	#endif
					status	= 0;
				}
#endif
			}
			// (2)本文を全文検索
			if ( status != 0 
				&& ( flag & PDIC_S_HONBUN )
				&& _honbunlen>0 ) {	// 
		#if	defined(UNICODE)
				//	Unicode辞書の時、BOCU->UCS2に変換後比較する
				if ( m_header.dictype & 0x08 ) {	// BOCU1
					BOCU1_decode( p+_hoffs, _honbunlen, w_honbun, PDIC_MAX_HONBUN );
					_strlen	= _tcslen( w_honbun );
					ASSERT(_strlen<PDIC_MAX_HONBUN);
#if	defined(_BM_SEARCH)
					if ( bm_search( (byte*)w_honbun,_strlen*sizeof(WCHAR), (byte*)keyword, keywordlen*sizeof(WCHAR),bm_table ) != -1 ) {
#else
					if ( SearchString( w_honbun,_strlen, (WCHAR*)keyword, keywordlen ) != -1 ) {
#endif
						status	= 0;
					}
				}
				else {
					//	非Unicode辞書の場合、ShiftJISで比較する
#if	defined(_BM_SEARCH)
					if ( bm_search( p+_hoffs,_honbunlen, mb_keyword, keywordlen,bm_table ) != -1 ) {
#else
					if ( SearchString( p+_hoffs,_honbunlen, mb_keyword, keywordlen ) != -1 ) {
#endif
						status	= 0;
					}
				}
		#else
#if	defined(_BM_SEARCH)
				if ( bm_search( p+_hoffs,_honbunlen, (byte*)keyword, keywordlen,bm_table ) != -1 ) {
#else
				if ( SearchString( p+_hoffs,_honbunlen, keyword, keywordlen ) != -1 ) {
#endif
					status	= 0;
				}
		#endif
			}
			// (3)用例を全文検索
			if ( status != 0
				&& ( flag & PDIC_S_YOREI )
				&& _yoreilen>0 ) {	// 
		#if	defined(UNICODE)
				//	Unicode辞書の時、BOCU->UCS2に変換後比較する
				if ( m_header.dictype & 0x08 ) {	// BOCU1
					BOCU1_decode( p+_yoffs, _yoreilen, w_honbun, PDIC_MAX_HONBUN );
					_strlen	= _tcslen( w_honbun );
					ASSERT(_strlen<PDIC_MAX_HONBUN);
#if	defined(_BM_SEARCH)
					if ( bm_search( (byte*)w_honbun,_strlen*sizeof(WCHAR), (byte*)keyword, keywordlen*sizeof(WCHAR),bm_table ) != -1 ) {
#else
					if ( SearchString( w_honbun,_strlen, (WCHAR*)keyword, keywordlen ) != -1 ) {
#endif
						status	= 0;
					}
				}
				else {
					//	非Unicode辞書の場合、ShiftJISで比較する
#if	defined(_BM_SEARCH)
					if ( bm_search( p+_yoffs,_yoreilen, mb_keyword, keywordlen,bm_table ) != -1 ) {
#else
					if ( SearchString( p+_yoffs,_yoreilen, mb_keyword, keywordlen ) != -1 ) {
#endif
						status	= 0;
					}
				}
		#else
#if	defined(_BM_SEARCH)
				if ( bm_search( p+_yoffs,_yoreilen, (byte*)keyword, keywordlen,bm_table ) != -1 ) {
#else
				if ( SearchString( p+_yoffs,_yoreilen, keyword, keywordlen ) != -1 ) {
#endif
					status	= 0;
				}
		#endif
			}

			//	条件に一致するデータは検索結果テーブルに登録
			if ( status == 0 ) {	// 

				//	検索結果テーブルが未割当なら、ここで割り当て
				//	サイズが不足する場合、拡張
				if ( !m_hitent ) {
					m_max_hitent	= MAX_PDIC_HITENT;
					m_hitent	= (PDIC_HITENT*)malloc( m_max_hitent * sizeof(PDIC_HITENT) );
				}
				else if ( m_max_hitent <= m_numhit ) {
					m_max_hitent	+= MAX_PDIC_HITENT;
					m_hitent	= (PDIC_HITENT*)realloc( m_hitent, m_max_hitent * sizeof(PDIC_HITENT) );
				}
				//	見出し語は、見出し語プールに保存する
				if ( !m_title_pool ) {
					m_max_title_pool	= MAX_PDIC_POOL;
					m_title_pool	= (byte*)malloc( m_max_title_pool * sizeof(byte) );
				}
				else if ( m_max_title_pool <= m_title_len+_titlelen+1 ) {
					m_max_title_pool	+= MAX_PDIC_POOL;
					m_title_pool	= (byte*)realloc( m_title_pool, m_max_title_pool * sizeof(byte) );
				}

				//	検索結果テーブルに登録する
				memset( &m_hitent[m_numhit] ,0, sizeof(PDIC_HITENT) );
				m_hitent[m_numhit].indexno		= m_seq_idxno;
				m_hitent[m_numhit].blkno		= m_seq_datablk;
				m_hitent[m_numhit].titleoffs	= m_title_len;
				m_hitent[m_numhit].titlelen		= _titlelen;
				m_hitent[m_numhit].hoffs		= _hoffs;
				m_hitent[m_numhit].honbunlen	= _honbunlen;
				m_hitent[m_numhit].yoffs		= _yoffs;
				m_hitent[m_numhit].yoreilen		= _yoreilen;
				m_hitent[m_numhit].poffs		= _poffs;
				m_hitent[m_numhit].phoneticlen	= _phoneticlen;
				m_hitent[m_numhit].level		= _level;
				m_hitent[m_numhit].recOffs		= _recOffs;

				memcpy( m_title_pool+m_title_len,_titlestr,_titlelen );
				m_title_len	+= _titlelen;
				m_title_pool[m_title_len++]	= '\0';

				++m_numhit;
			}
			++_num;

			//	検索上限が設定されていて件数が超える場合は、検索中止
			if ( maxHit>0 && m_numhit>=(unsigned long)maxHit ) {
				_over	= 1;
				*over	= true;
			}
		}
		++m_seq_idxno;

	}
	return m_numhit;

}



//	0	正常
//	-1	失敗
int CPDicReader::GetHitEnt( PDIC_HITENT **hit_list, int *numhit )
{
	if ( !m_hitent ) {
		*numhit	= 0;
		return -1;
	}
	*hit_list	= m_hitent;
	*numhit		= m_numhit;

	return 0;
}

// 検索一致個数を返す
int CPDicReader::GetHitCount()
{
	return m_numhit;
}

byte* CPDicReader::GetTextPool()
{
	return m_title_pool;
}

int CPDicReader::GetMaxText()
{
	return m_title_len;
}

unsigned long CPDicReader::GetOffs()
{
	return m_pData->GetOffs();
}

unsigned long CPDicReader::GetBlkNo()	// 現在の連続ブロック先頭番号
{
	return m_pData->GetBlkNo();
}

unsigned long	CPDicReader::GetIndexNo()			// 現在のインデックス番号
{
	return m_seq_idxno;
}

NEWDIC3_5 *CPDicReader::GetHeader()	// PDIC辞書のヘッダ情報(Ver5形式)
{
	return &m_header;
}

// PDIC辞書のバージョン番号
int CPDicReader::GetPDICVer()
{
	return m_pdic_ver;
}

// num番目の検索結果を取得する。
//	読出領域は、呼び出し元で確保する。
//		見出し語	  248字
//		訳語		 3000字
//		用例		20000字
//	0	正常
//	-1	失敗
int CPDicReader::ReadResult( int num,		//	指定した番号の検索結果を取得
							LPTSTR title,	//	見出し語(unicodeの場合、UCS-2)
							int maxtitle,	//	見出し語の領域の最大長(max:1024)
							LPTSTR honbun,	//	訳語(unicodeの場合、UCS-2)
							int maxhonbun, 	//	訳語の領域の最大長(max:3000)
							LPTSTR yorei,	//	用例(unicodeの場合、UCS-2)
							int maxyorei, 	//	用例の領域の最大長(max:20000)
							LPTSTR phonetic,	//	発音(unicodeの場合、UCS-2)
							int maxphonetic,	//	発音の領域の最大長(max:1000)
							int*level,			// 単語レベル
							unsigned short (*phoneticUniMap)[256][2] 
							)
{
	ASSERT ( num>=0 && num<(int)m_numhit ) ;
	if ( num<0 || num>=(int)m_numhit ) 
		return -1;
	if ( !m_hitent ) 
		return -1;
	if ( !m_title_pool ) 
		return -1;
	return ReadResultEnt( &m_hitent[num], m_title_pool,
							 title,	//	見出し語(unicodeの場合、UCS-2)
							 maxtitle,
							 honbun,	//	訳語(unicodeの場合、UCS-2)
							 maxhonbun, 	//	訳語の領域の最大長(max:3000)
							 yorei,	//	用例(unicodeの場合、UCS-2)
							 maxyorei, 	//	用例の領域の最大長(max:20000)
							 phonetic,	//	発音(unicodeの場合、UCS-2)
							 maxphonetic,	//	発音の領域の最大長(max:1000)
							level,			// 単語レベル
							phoneticUniMap
							);
}

//	読出領域は、呼び出し元で確保する。
//		見出し語	  248字
//		訳語		 3000字
//		用例		20000字
//	0	正常
//	-1	失敗
int CPDicReader::ReadResultEnt( PDIC_HITENT*pHitEnt,
							byte*pTitlePool,
							LPTSTR title,	//	見出し語(unicodeの場合、UCS-2)
							int maxtitle,	//	見出し語の領域の最大長(max:1024)
							LPTSTR honbun,	//	訳語(unicodeの場合、UCS-2)
							int maxhonbun, 	//	訳語の領域の最大長(max:3000)
							LPTSTR yorei,	//	用例(unicodeの場合、UCS-2)
							int maxyorei, 	//	用例の領域の最大長(max:20000)
							LPTSTR phonetic,	//	発音(unicodeの場合、UCS-2)
							int maxphonetic,	//	発音の領域の最大長(max:1000)
							int*level,			// 単語レベル
							unsigned short (*phoneticUniMap)[256][2] 
							)
{
	ASSERT( title );
	ASSERT( honbun );
	ASSERT( yorei );

	title[0]	= '\0';
	honbun[0]	= '\0';
	yorei[0]	= '\0';
	phonetic[0]	= '\0';

	if ( m_pData->ReadBlks( pHitEnt->blkno ) != 0 ) 
		return -1;
	byte* p = m_pData->GetBuffer();
	if ( !p ) 
		return -1;

#if	defined(UNICODE)
	//	Unicode辞書の場合、BOCU1->UCS2
	if ( m_header.dictype & 0x08 ) {	// BOCU1
		BOCU1_decode( pTitlePool+pHitEnt->titleoffs,pHitEnt->titlelen, title, maxtitle );
		BOCU1_decode( p+pHitEnt->hoffs,pHitEnt->honbunlen, honbun, maxhonbun );
		if ( pHitEnt->yoreilen>0 ) {
			BOCU1_decode( p+pHitEnt->yoffs,pHitEnt->yoreilen, yorei, maxyorei );
		}
		if ( pHitEnt->phoneticlen>0 ) {
			if ( phoneticUniMap  /*&& m_pdic_ver < 6*/ ) {
				TCHAR tmp_phonetic[PDIC_MAX_PHONETIC+1];
				BOCU1_decode( p+pHitEnt->poffs,pHitEnt->phoneticlen, tmp_phonetic, maxphonetic );
				MapPhoneticUni( tmp_phonetic,_tcslen(tmp_phonetic), phonetic, maxphonetic,phoneticUniMap );
			}
			else {
				BOCU1_decode( p+pHitEnt->poffs,pHitEnt->phoneticlen, phonetic, maxphonetic );
			}
		}
	}
	else {
		//	非Unicode辞書の場合、ShiftJIS->UCS2
		int nChar = ::MultiByteToWideChar(
			  _KANJI_CP,	// コードページ	
			  0,			// 文字の種類を指定するフラグ
			  (char*)pTitlePool+pHitEnt->titleoffs,	// マップ元文字列のアドレス
			  pHitEnt->titlelen,	// マップ元文字列のバイト数
			  title,		// マップ先ワイド文字列を入れるバッファのアドレス
			  maxtitle	// バッファのサイズ
		);
		title[nChar]	= 0;

		nChar = ::MultiByteToWideChar(
			  _KANJI_CP,	// コードページ	
			  0,			// 文字の種類を指定するフラグ
			  (char*)p+pHitEnt->hoffs,	// マップ元文字列のアドレス
			  pHitEnt->honbunlen,	// マップ元文字列のバイト数
			  honbun,		// マップ先ワイド文字列を入れるバッファのアドレス
			  maxhonbun	// バッファのサイズ
		);
		honbun[nChar]	= 0;

		if ( pHitEnt->yoreilen>0 ) {
			nChar = ::MultiByteToWideChar(
				  _KANJI_CP,	// コードページ	
				  0,			// 文字の種類を指定するフラグ
				  (char*)p+pHitEnt->yoffs,	// マップ元文字列のアドレス
				  pHitEnt->yoreilen,	// マップ元文字列のバイト数
				  yorei,		// マップ先ワイド文字列を入れるバッファのアドレス
				  maxyorei	// バッファのサイズ
			);
			yorei[nChar]	= 0;

		}
		if ( pHitEnt->phoneticlen>0 ) {

			if ( phoneticUniMap ) {
				MapPhoneticUni( p+pHitEnt->poffs,pHitEnt->phoneticlen, phonetic, maxphonetic,phoneticUniMap );
			}
			else {
				nChar = ::MultiByteToWideChar(
					  _KANJI_CP,	// コードページ	
					  0,			// 文字の種類を指定するフラグ
					  (char*)p+pHitEnt->poffs,	// マップ元文字列のアドレス
					  pHitEnt->phoneticlen,	// マップ元文字列のバイト数
					  phonetic,		// マップ先ワイド文字列を入れるバッファのアドレス
					  maxphonetic	// バッファのサイズ
				);
				phonetic[nChar]	= 0;
			}
		}

	}
#else
	strcpy(  title, (LPTSTR)pTitlePool+pHitEnt->titleoffs );
	int	datasize	= min( maxhonbun, pHitEnt->honbunlen );
	memcpy( honbun, p+pHitEnt->hoffs, datasize );
	honbun[ datasize ]	=  '\0';

	if ( pHitEnt->yoreilen>0 ) {
		datasize	= min( maxyorei, pHitEnt->yoreilen );
		memcpy( yorei, p+pHitEnt->yoffs, datasize );
		yorei[ datasize ]	=  '\0';
	}

	if ( pHitEnt->phoneticlen>0 ) {
		datasize	= min( maxphonetic, pHitEnt->phoneticlen );
		memcpy( phonetic, p+pHitEnt->poffs, datasize );
		phonetic[ datasize ]	=  '\0';
	}
#endif
	*level	= pHitEnt->level;

	return 0;
}


//	順次検索で先頭に位置づける
//	0	正常
//	-1	失敗
int CPDicReader::FindFirst()
{
	return SeekIndex( 0 );
}

//	指定したインデックス番号の示すデータブロックに位置づける
//	0	正常
//	-1	失敗
int CPDicReader::SeekIndex( unsigned long idxno )
{
	byte _titlestr[PDIC_MAX_TITLE+1];	// インデックスに記録された見出し語
	int _titlelen;

	m_seq_idxno	= idxno;
	m_seq_datablk	= 0;

	if ( m_pIndex->ReadRec( m_seq_idxno, &m_seq_datablk, (byte*)_titlestr,&_titlelen ) != 0 ) {
		return -1;
	}
	if ( m_pData->ReadBlks( m_seq_datablk ) != 0 ) {
		return -1;
	}

	return 0;
}

#if	defined(UNICODE)
int CPDicReader::MapPhoneticUni( LPCTSTR src,int slen, LPTSTR dest, int maxdest,
							   unsigned short (*CMap)[256][2] )
{
	int i,dlen;
	dlen	= 0;
	for ( i=0; i<slen; ++i ) {
		if ( src[i] >= ' ' && src[i]<256 && (*CMap)[src[i]][0] ) {
			*dest++	= (*CMap)[src[i]][0];
			++dlen;
			if ( (*CMap)[src[i]][1] ) {
				*dest++	= (*CMap)[src[i]][1];
				++dlen;
			}
		}
		else {
			*dest++	= src[i];
			++dlen;
		}
		if ( dlen>=maxdest-1 )
			break;
	}
	*dest	= '\0';
	return dlen;
}

int CPDicReader::MapPhoneticUni( byte* src,int slen, LPTSTR dest, int maxdest,
							   unsigned short (*CMap)[256][2]  )
{
	int i,dlen;
	dlen	= 0;
	for ( i=0; i<slen; ++i ) {
		if ( src[i] >= ' ' && src[i]<256 && (*CMap)[src[i]][0] ) {
			*dest++	= (*CMap)[src[i]][0];
			++dlen;
			if ( (*CMap)[src[i]][1] ) {
				*dest++	= (*CMap)[src[i]][1];
				++dlen;
			}
		}
		else {
			*dest++	= src[i];
			++dlen;
		}
		if ( dlen>=maxdest-1 )
			break;
	}
	*dest	= '\0';
	return dlen;
}

#endif

//	順次検索で次のレコードを読む
//	0	正常
//	-1	これ以上のレコードはない
int CPDicReader::ReadNext(	LPTSTR title,	//	見出し語(unicodeの場合、UCS-2)
							int maxtitle,	//	見出し語の領域の最大長(max:1024) 
							LPTSTR honbun,	//	訳語(unicodeの場合、UCS-2)
							int maxhonbun, 	//	訳語の領域の最大長(max:3000)
							LPTSTR yorei,	//	用例(unicodeの場合、UCS-2)
							int maxyorei,	//	用例の領域の最大長(max:20000)
							LPTSTR phonetic,	//	発音(unicodeの場合、UCS-2)
							int maxphonetic,	//	発音の領域の最大長(max:1000)
							int*level,			// 単語レベル
							unsigned short (*phoneticUniMap)[256][2] ,
							PDIC_ITEMINFO *pInfo
							)
{
	ASSERT( title );
	ASSERT( honbun );
	ASSERT( yorei );
	ASSERT( phonetic );

	title[0]	= '\0';
	honbun[0]	= '\0';
	yorei[0]	= '\0';
	phonetic[0]	= '\0';
	*level	= 0;

	byte _titlestr[PDIC_MAX_TITLE+1];	// インデックスに記録された見出し語
	int _titlelen,_hoffs,_honbunlen,_yoffs,_yoreilen,_poffs,_phoneticlen,_level;

	// 次のレコードを読む
	if ( m_pData->ReadRec( (byte*)_titlestr, &_titlelen, 
							&_hoffs, &_honbunlen,
							&_yoffs, &_yoreilen,
							&_poffs, &_phoneticlen, &_level, pInfo )!= 0 ) {
		//	データブロック内の最後のレコードの場合、次のデータブロックを読む

		// インデックスから、次のデータブロックを得る
		++m_seq_idxno;
		if ( m_pIndex->ReadRec( m_seq_idxno, &m_seq_datablk, (byte*)_titlestr,&_titlelen ) != 0 ) {
			return -1;
		}
		// 次のデータブロックを読む
		if ( m_pData->ReadBlks( m_seq_datablk ) != 0 ) {
			return -1;
		}
		// 次のレコードを読む
		if ( m_pData->ReadRec( (byte*)_titlestr, &_titlelen,
							&_hoffs, &_honbunlen,
							&_yoffs, &_yoreilen,
							&_poffs, &_phoneticlen, &_level, pInfo )!= 0 ) {
			return -1;
		}
	}
	if ( pInfo ) {
		pInfo->indexno	= m_seq_idxno;
	}

	byte* p = m_pData->GetBuffer();
	byte *_honbun	= p+_hoffs;
	byte *_yorei	= p+_yoffs;
	byte *_phonetic	= p+_poffs;


#if	defined(UNICODE)
	//	Unicode辞書の場合、BOCU1->UCS2
	if ( m_header.dictype & 0x08 ) {	// BOCU1
		if ( _titlelen>0 ) {
			BOCU1_decode( _titlestr,_titlelen, title, maxtitle );
		}
		if ( _honbunlen>0 ) {
			BOCU1_decode( _honbun,_honbunlen, honbun, maxhonbun );
		}
		if ( _yoreilen>0 ) {
			BOCU1_decode( _yorei,_yoreilen, yorei, maxyorei );
		}
		if ( _phoneticlen>0 ) {
			if ( phoneticUniMap /*&& m_pdic_ver < 6*/ ) {
				TCHAR tmp_phonetic[PDIC_MAX_PHONETIC+1];
				BOCU1_decode( _phonetic,_phoneticlen, tmp_phonetic, maxphonetic );
				MapPhoneticUni( tmp_phonetic,_tcslen(tmp_phonetic), phonetic, maxphonetic, phoneticUniMap );
			}
			else {
				BOCU1_decode( _phonetic,_phoneticlen, phonetic, maxphonetic );
			}
		}
	}
	else {
		//	非Unicode辞書の場合、ShiftJIS->UCS2
		int nChar;
		if ( _titlelen>0 ) {
			nChar = ::MultiByteToWideChar(
				  _KANJI_CP,	// コードページ	
				  0,			// 文字の種類を指定するフラグ
				  (char*)_titlestr,	// マップ元文字列のアドレス
				  _titlelen,	// マップ元文字列のバイト数
				  title,		// マップ先ワイド文字列を入れるバッファのアドレス
				  maxtitle	// バッファのサイズ
			);
			title[nChar]	= 0;
		}
		if ( _honbunlen>0 ) {
			nChar = ::MultiByteToWideChar(
				  _KANJI_CP,	// コードページ	
				  0,			// 文字の種類を指定するフラグ
				  (char*)_honbun,	// マップ元文字列のアドレス
				  _honbunlen,	// マップ元文字列のバイト数
				  honbun,		// マップ先ワイド文字列を入れるバッファのアドレス
				  maxhonbun	// バッファのサイズ
			);
			honbun[nChar]	= 0;
		}
		if ( _yoreilen>0 ) {
			nChar = ::MultiByteToWideChar(
				  _KANJI_CP,	// コードページ	
				  0,			// 文字の種類を指定するフラグ
				  (char*)_yorei,	// マップ元文字列のアドレス
				  _yoreilen,	// マップ元文字列のバイト数
				  yorei,		// マップ先ワイド文字列を入れるバッファのアドレス
				  maxyorei	// バッファのサイズ
			);
			yorei[nChar]	= 0;
		}
		if ( _phoneticlen>0 ) {
			if ( phoneticUniMap ) {
				MapPhoneticUni( _phonetic,_phoneticlen, phonetic, maxphonetic, phoneticUniMap );
			}
			else {
				int ix;
				for ( ix=0;ix<_phoneticlen;++ix ) {
					phonetic[ix]	= _phonetic[ix];
				}
				phonetic[_phoneticlen]	= 0;
			}
		}
	}
#else
	if ( _titlelen>0 ) {
		ASSERT(_titlelen<=maxtitle);
		memcpy( title, _titlestr, _titlelen );
		title[_titlelen]	=  '\0';
	}
	if ( _honbunlen>0 ) {
		ASSERT(_honbunlen<=maxhonbun);
		memcpy( honbun, _honbun, _honbunlen );
		honbun[_honbunlen]	=  '\0';
	}
	if ( _yoreilen>0 ) {
		ASSERT(_yoreilen<=maxyorei);
		memcpy( yorei, _yorei, _yoreilen );
		yorei[_yoreilen]	=  '\0';
	}
	if ( _phoneticlen>0 ) {
		ASSERT(_phoneticlen<=maxphonetic);
		memcpy( phonetic, _phonetic, _phoneticlen );
		phonetic[_phoneticlen]	=  '\0';
	}
#endif
	*level	= _level;

	return 0;
}

// PDICの辞書情報をテキストで表示
void CPDicReader::PDicDumpInfo(  char*pBuf,int maxbuf, int bCRLF)
{
	NEWDIC3_5	*pHeader	= GetHeader();
	if ( !pHeader )
		return;
	int slen	= 0;
	sprintf( pBuf+slen,"[Personal Dictionary]\r\n");
	slen	+=	strlen(pBuf+slen);	if ( slen>=maxbuf-255 ) return;

	// 辞書名(dictitle)
//	sprintf( pBuf+slen,"dictitle = %s\r\n", pHeader->dictitle );
//	slen	+=	strlen(pBuf+slen);	if ( slen>=maxbuf-255 ) return;

	// 辞書のバージョン(version)
	sprintf( pBuf+slen,"version = %d.%02d\r\n", pHeader->version>>8,
											pHeader->version & 0xff  );
	slen	+=	strlen(pBuf+slen);	if ( slen>=maxbuf-255 ) return;

	// 見出語の最大長(lword)	
	sprintf( pBuf+slen,"lword = %d\r\n", pHeader->lword );
	slen	+=	strlen(pBuf+slen);	if ( slen>=maxbuf-255 ) return;

	// 訳語の最大長(ljapa)
	sprintf( pBuf+slen,"ljapa = %d\r\n", pHeader->ljapa );
	slen	+=	strlen(pBuf+slen);	if ( slen>=maxbuf-255 ) return;

	// ブロックのバイト数(block_size)	
	sprintf( pBuf+slen,"block_size = %d\r\n", pHeader->block_size );
	slen	+=	strlen(pBuf+slen);	if ( slen>=maxbuf-255 ) return;

	// インデックスブロック数(index_block)
	sprintf( pBuf+slen,"index_block = %d\r\n", pHeader->index_block );
	slen	+=	strlen(pBuf+slen);	if ( slen>=maxbuf-255 ) return;

	// ヘッダ長(header_size)
	sprintf( pBuf+slen,"header_size = %d\r\n", pHeader->header_size );
	slen	+=	strlen(pBuf+slen);	if ( slen>=maxbuf-255 ) return;

	// 登録単語数(nword)
	sprintf( pBuf+slen,"nword   = %d\r\n", pHeader->nword );
	slen	+=	strlen(pBuf+slen);	if ( slen>=maxbuf-255 ) return;

	// 辞書の順番(dicorder)
	sprintf( pBuf+slen,"dicorder= [%02x] %s\r\n", 
		pHeader->dicorder,
		(pHeader->dicorder == 0x00 )?"code order ":
		(pHeader->dicorder == 0x01 )?"ignore case ":
		(pHeader->dicorder == 0x02 )?"lexicographic order ":
		(pHeader->dicorder == 0x03 )?"descending order ":
				""
		);
	slen	+=	strlen(pBuf+slen);	if ( slen>=maxbuf-255 ) return;

	// 辞書の種別(dictype)
	sprintf( pBuf+slen,"dictype = [%02x] %s\r\n", 
		pHeader->dictype,
		(pHeader->dictype == 0x01)?"AR compressed":
		(pHeader->dictype == 0x08)?"BOCU-1 compressed":
		(pHeader->dictype == 0x09)?"AR compressed / BOCU-1 compressed":
									""
		);
	slen	+=	strlen(pBuf+slen);	if ( slen>=maxbuf-255 ) return;

	// 単語属性の長さ(attrlen)
	sprintf( pBuf+slen,"attrlen = %d\r\n", pHeader->attrlen );
	slen	+=	strlen(pBuf+slen);	if ( slen>=maxbuf-255 ) return;

	// os type
	sprintf( pBuf+slen,"os      = [%02x] %s\r\n", 
		pHeader->os,
		(pHeader->os==0x00)?"DOS,Windows,OS/2":
		(pHeader->os==0x01)?"Mac":
		(pHeader->os==0x02)?"UNIX - SJIS":
		(pHeader->os==0x03)?"UNIX - EUC":
		(pHeader->os==0x04)?"UNIX - JIS":
		(pHeader->os==0x20)?"BOCU encoding":
		""
		);
	slen	+=	strlen(pBuf+slen);	if ( slen>=maxbuf-255 ) return;

	// index_blkbit; // 0:16bit, 1:32bit
	sprintf( pBuf+slen,"index_blkbit = [%02x] %s\r\n", 
		pHeader->index_blkbit,
		(pHeader->index_blkbit==0x00)?"16bit":
		(pHeader->index_blkbit==0x01)?"32bit":
		""
		);
	slen	+=	strlen(pBuf+slen);	if ( slen>=maxbuf-255 ) return;

	// インデックス要素の数(nindex2)
	sprintf( pBuf+slen,"nindex2 = %d\r\n", pHeader->nindex2 );
	slen	+=	strlen(pBuf+slen);	if ( slen>=maxbuf-255 ) return;

	// 使用データブロック数
	sprintf( pBuf+slen,"nblock2 = %d\r\n", pHeader->nblock2 );
	slen	+=	strlen(pBuf+slen);	if ( slen>=maxbuf-255 ) return;


}


//////////////////////////////////////////////////////////////////////
// インデックス部
//////////////////////////////////////////////////////////////////////

CPDicIndex::CPDicIndex( FILE*fp,
					   NEWDIC3_5 *header
					   )
{
	ASSERT( fp );
	ASSERT( header );

	m_fp		= fp;
	m_header	= header;
	m_pdic_ver = (m_header->version) >> 8;

	m_indexTbl	= NULL;
	m_maxIndex	= 0;

}

CPDicIndex::~CPDicIndex()
{
	if ( m_indexTbl ) {
		free( m_indexTbl );
		m_indexTbl	= NULL;
	}
}

#ifdef _WIN32_WCE
// バッファ付read(WindowsCEのReadFile()がバッファリングされず効率が悪いため)
int buffered_read( byte*_iobuf,			// I/Oバッファ
				  unsigned int bufsize,	// I/Oバッファのサイズ
				  byte**_bufp,			// I/Oバッファのポインタ(初期値は、_iobuf+bufsizeとすること)
				  int size,				// 入力データのサイズ
				  byte*outbuf,			// 入力データを格納する領域
				  FILE*m_fp				// file pointer
				  )
{
	int _written = 0;
	while ( _written < size ) {
		if ( (*_bufp) < _iobuf+bufsize ) {
			*outbuf++	= **_bufp;
			(*_bufp)++;
			++_written;
		}
		else {
			if ( fread( _iobuf, 1, bufsize, m_fp ) != bufsize )	
				return _written;
			*_bufp	= _iobuf;
			*outbuf++	= **_bufp;
			(*_bufp)++;
			++_written;
		}
	}
	return _written;
}
#endif

//	インデックス部を読み取り、配列に格納
//	0	正常
//	-1	失敗
int	CPDicIndex::LoadIndex()
{
	unsigned long	_curIdxNo;
	unsigned long _curoffs	= 0;	// インデックス先頭からのオフセット

	m_maxIndex	= m_header->nindex2;
	m_indexTbl	= (PDIC_INDEX*)malloc( (m_maxIndex+1)*sizeof(PDIC_INDEX) );
	memset( m_indexTbl, 0, (m_maxIndex+1)*sizeof(PDIC_INDEX) );

//	if ( fseek( m_fp, PDIC_HEADER_256, SEEK_SET ) ) {
	if ( fseek( m_fp, m_header->header_size, SEEK_SET ) ) {
		return -1;
	}
#ifdef _WIN32_WCE
	byte _iobuf[PDIC_MAX_BLKLEN];
	byte *_bufp = _iobuf+m_header->block_size;
#endif
	byte _buffer[4];

	unsigned long _blk;
	for ( _curIdxNo = 0; _curIdxNo<m_maxIndex; ++_curIdxNo ) {
		if ( _curoffs >=m_maxIndex*m_header->block_size )
			break;

		_blk	=	0;
		if ( m_header->index_blkbit == 1 ) {
#ifdef _WIN32_WCE
			if ( buffered_read( _iobuf,m_header->block_size, &_bufp, 4, _buffer, m_fp )!= 4 )	break;
#else
			if ( fread( _buffer, 1, 4, m_fp ) != 4 )	break;
#endif
			_blk	= (_buffer[0])
					+ (_buffer[1]<<8)
					+ (_buffer[2]<<16)
					+ (_buffer[3]<<24);
			_curoffs	+= 4;
		}
		else {
#ifdef _WIN32_WCE
			if ( buffered_read( _iobuf,m_header->block_size, &_bufp, 2, _buffer, m_fp )!= 2 )	break;
#else
			if ( fread( _buffer, 1, 2, m_fp ) != 2 )	break;
#endif
			_blk	= (_buffer[0]) + (_buffer[1]<<8);
			_curoffs	+= 2;
		}
		m_indexTbl[_curIdxNo].offs		= _curoffs;
		m_indexTbl[_curIdxNo].blkno	= _blk;
		int _titlelen	= 0;
#ifdef _WIN32_WCE
		while ( buffered_read( _iobuf,m_header->block_size, &_bufp, 1, _buffer, m_fp )>0 ) {
#else
		while ( fread( _buffer, 1, 1, m_fp )>0 ) {
#endif
			++_titlelen;
			if ( !_buffer[0] )
				break;
		}
		m_indexTbl[_curIdxNo].titlelen	= _titlelen - 1;
		_curoffs	+= _titlelen;

		if ( m_indexTbl[_curIdxNo].blkno == 0 && m_indexTbl[_curIdxNo].titlelen == 0 ) {
			// 終了条件
			break;
		}
	}

	return 0;
}

//	インデックスブロック内での順次エントリread
//	0	正常
//	-1	これ以上のレコードはない
int	CPDicIndex::ReadRec( unsigned long idxno,	// インデックス番号(0..nindex2)
						unsigned long *datablk,
						byte*title,
						int *titlelen )
{
	if ( idxno>=(unsigned long)m_header->nindex2 ) 
		return -1;
	ASSERT( idxno>=0 && (long)idxno<m_header->nindex2 );

	if ( !m_indexTbl ) {
		LoadIndex();	// インデックスを読み込み
	}

//	if ( fseek( m_fp, PDIC_HEADER_256 + m_indexTbl[idxno].offs, SEEK_SET ) ) {
	if ( fseek( m_fp, m_header->header_size + m_indexTbl[idxno].offs, SEEK_SET ) ) {
#if  defined(_WIN32_WCE)
		RaiseException(	EXCEPTION_CODE,	/*DWORD dwExceptionCode*/
						0,		/*DWORD dwExceptionFlags*/
						0,		/*DWORD nNumberOfArguments*/
						NULL);	/*ULONG_PTR *lpArguments*/	
#else
		throw;
#endif
//		return -1;
	}
	ASSERT( m_indexTbl[idxno].titlelen<=PDIC_MAX_TITLE );
	if ( fread( title, m_indexTbl[idxno].titlelen+1,1, m_fp ) == 0 ) {
		return -1;
	}
	*datablk	= m_indexTbl[idxno].blkno;
	*titlelen	= m_indexTbl[idxno].titlelen;

	return 0;
}


//	インデックス表からバイナリサーチで最も近傍のインデックスを見つける
// 「C言語による最新アルゴリズム辞典」奥村晴彦著より
int CPDicIndex::BSearchIndex( LPCTSTR keyword )
{
	unsigned long	datablk;
	int	_titlelen;
	byte _title[PDIC_MAX_TITLE+1];
#if	defined(UNICODE)
	int	nChar;
	TCHAR	w_idxkey[PDIC_MAX_TITLE+1];
	byte	mb_keyword[PDIC_MAX_TITLE+1];
#endif
	int	keywordlen;

	if ( !m_indexTbl ) {
		LoadIndex();	// インデックスを読み込み
	}

#if	defined(UNICODE)
	if ( m_header->dictype & 0x08 ) {	// BOCU1
		keywordlen	= _tcslen(keyword);
	}
	else {
		nChar = ::WideCharToMultiByte( _KANJI_CP, NULL, keyword, _tcslen(keyword), 
					(LPSTR)mb_keyword, PDIC_MAX_TITLE, NULL, NULL);	// 変換(→ShiftJIS)
		mb_keyword[nChar]	= '\0';
		keywordlen	= nChar;
	}
#else
	keywordlen	= _tcslen(keyword);
#endif

	int left	= 0;	// インデックス部のブロック番号
	int right	= m_header->nindex2;
	int mid;
	while ( left < right ) {
		mid = ( left + right ) / 2;
		if ( ReadRec( mid, &datablk,_title,&_titlelen ) ) 
			return 0;
		int	comp;
		////////////////////////////////////////////////////
		// 以下はdicorder == 0 (コード順)の場合
		////////////////////////////////////////////////////
#if	defined(UNICODE)
		//	Unicode辞書の場合、BOCU1->UCS2に変換して比較
		if ( m_header->dictype & 0x08 ) {	// BOCU1
			BOCU1_decode( _title,_titlelen, w_idxkey,PDIC_MAX_TITLE );
			comp	= _tcsncmp( w_idxkey , keyword, keywordlen );
		}
		else {
			//	非Unicode辞書の場合、ShiftJISで比較する（文字コード順）
			//	 _mbsncmp()はうまく動かない
			comp = strncmp( (char*)_title, (char*)mb_keyword, keywordlen ) ;
		}
#else
		//	 _mbsncmp()はうまく動かない
		comp	= strncmp( (char*)_title , keyword, keywordlen );
#endif
		if ( comp < 0 ) 
			left = mid + 1; 
		else 
			right = mid;
	}
	return max(0,left-1);
}

//////////////////////////////////////////////////////////////////////
// データ部
//////////////////////////////////////////////////////////////////////

CPDicData::CPDicData(FILE*fp,NEWDIC3_5 *header)
{
	ASSERT( fp );
	ASSERT( header );

	m_fp	= fp;
	m_header	= header;
	m_pdic_ver = (m_header->version) >> 8;
	m_curBlk	= 0xFFFFFFFF;
	m_recOffs	= 0;
	m_blocks	= 0;	// 使用ブロック数
	m_maxblocks	= 0;
	m_blkbuf	= NULL;
}

CPDicData::~CPDicData()
{
	if ( m_blkbuf ) {
		free( m_blkbuf );
		m_blkbuf	= NULL;
	}
}

// 連続データブロックの先頭アドレスを返す
byte*CPDicData::GetBuffer()
{
	return m_blkbuf;
}

unsigned long CPDicData::GetOffs()
{
	return m_recOffs;
}

unsigned long CPDicData::GetBlkNo()	// 現在の連続ブロック先頭番号
{
	return m_curBlk;
}

// blknoから始まる連続データブロックを読む
//	ただし複数ブロックの連結の機能はNEWDIC3以降
//	0	正常
//	-1	失敗
int	CPDicData::ReadBlks( unsigned long blkno	/*データ部の物理ブロック番号*/ )
{
	/*
		先頭ブロックのMSB	0:	フィールド長2バイト
							1:	フィールド長4バイト
		先頭ブロックの先頭2バイトは使用ブロック数;0なら空きブロック
		ブロック長は256バイト
		使用ブロック数>1なら連結ブロック
	*/
	if ( m_curBlk == blkno ) {
		// 読み込み済
		m_recOffs	= 2;	// 使用ブロック数を読み飛ばす
		return 0;
	}
	m_curBlk	= blkno;
	m_recOffs	= 0;

	// 先頭ブロックを読む
	if ( ReadBlk( blkno ) ) 
		return -1;

	// フィールド長の決定と使用ブロック数を得る
	m_blocks	= LTWOBYTEUINT(m_buffer);
	if ( m_blocks & 0x8000 ) {
		m_fieldSize	= 4;
	}
	else {
		m_fieldSize	= 2;
	}
	m_blocks	&= 0x7FFF;
	// 使用ブロック数0なら空きブロックである
	if ( m_blocks == 0 ) 
		return -1;	// 空きブロックだった

	//	使用ブロック数が入るだけの領域を動的に割り当てる
	if ( m_maxblocks == 0 ) {
		m_blkbuf	= (byte*)malloc( m_blocks*m_header->block_size );
		m_maxblocks	= m_blocks;
	}
	else if ( m_maxblocks < m_blocks ) {
		m_blkbuf	= (byte*)realloc( m_blkbuf, m_blocks*m_header->block_size );
		m_maxblocks	= m_blocks;
	}
	memset( m_blkbuf, 0, m_blocks*m_header->block_size );
	memcpy( m_blkbuf+0, m_buffer, m_header->block_size );

	// 連続するブロックをメモリに読み込む
	int	_blks;
	for ( _blks = 1;_blks<m_blocks ; ++_blks ) {
		if ( ReadBlk( blkno+_blks ) ) 
			break;
		memcpy( m_blkbuf+_blks*m_header->block_size, m_buffer, m_header->block_size );
	}
	//	先頭レコード位置に設定
	m_recOffs	= 2;	// 使用ブロック数を読み飛ばす
	return 0;
}

// 読込済の連続データブロックから、順にレコードを読む
//	各引数はNULLにしてもよい。その場合、callerに複写しない
//	0	レコードあり
//	-1	これ以上のレコードはない
int	CPDicData::ReadRec( byte* title,	// 見出しの読出領域(callerに複写する)
										//	※NULLにすると複写しない
						int	*titlelen,	// titleの最大長(文字数)
						int	*hoffs,		// 訳語の開始位置オフセット
						int	*honbunlen,	// 訳語の長さ(文字数)
						int	*yoffs,		// 用例の開始位置オフセット
						int	*yoreilen,	// 用例の長さ
						int *poffs,		// 発音の開始位置オフセット
						int *phoneticlen,	// 発音の長さ
						int *level,		// 単語レベル
						PDIC_ITEMINFO *pInfo
						)
{

	/*
	データ部：
	非Unicode辞書
	================================
	名称		サイズ	備考
	ﾌｨｰﾙﾄﾞ長	2 ﾊﾞｲﾄ	見出語から訳語の最後までのﾊﾞｲﾄ単位の長さ(ﾌｨｰﾙﾄﾞ長、圧縮長は含めない)
	圧縮長		1 ﾊﾞｲﾄ	見出語部の圧縮している長さを示す
	見出語		可変長	NULL 終端 圧縮してある
	見出語属性	1 ﾊﾞｲﾄ	
					0x80 必ずつける
					下位4bit 単語レベル値(0-15)
					0x10 拡張構成フラグ
					0x20 暗記必須単語フラグ
					0x40 修正単語フラグ
	訳語		可変長	NULL 終端無し

	Unicode辞書
	================================
	名称		サイズ	備考
	ﾌｨｰﾙﾄﾞ長	2 ﾊﾞｲﾄ	見出語から訳語の最後までのﾊﾞｲﾄ単位の長さ(ﾌｨｰﾙﾄﾞ長、見出語属性、圧縮長は含めない)
						ここのサイズはデータブロックの先頭の最上位ビットによって決まる。
	圧縮長		1 ﾊﾞｲﾄ	見出語部の圧縮している長さ(バイト単位)を示す
	見出語属性	1 ﾊﾞｲﾄ	※出現位置が違う
	見出語		可変長NULL 終端	圧縮長を除いた残りの見出語部分	BOCU1 encoding
	訳語		可変長NULL 終端無し	BOCU1 encoding
	*/

	ASSERT( m_recOffs>=2 && m_recOffs< m_blocks*m_header->block_size);
	ASSERT( m_blkbuf );

	int	_fieldLen,_complen,_titlelen,_honbunlen;
	int	_ext_attr,_ext_len;
	int	nextoffs;
	byte	_title_attr;
	PDIC_ITEMINFO info;

/*
	if ( titlelen )		*titlelen	= 0;
	if ( honbunlen )	*honbunlen	= 0;
	if ( yoreilen )	*yoreilen	= 0;
	if ( phoneticlen )	*phoneticlen	= 0;
	if ( hoffs )	*hoffs	= 0;
	if ( yoffs )	*yoffs	= 0;
	if ( poffs )	*poffs	= 0;
	if ( level )	*level	= 0;
*/
	memset( &info,0,sizeof(PDIC_ITEMINFO) );
	info.blkno		= GetBlkNo();
	info.recOffs	= m_recOffs;
		
	// (1)フィールド長
	if ( m_fieldSize == 4 ) {
		_fieldLen	= LFOURBYTEUINT(m_blkbuf+m_recOffs);
		m_recOffs	+=	4;
	}
	else {
		_fieldLen	= LTWOBYTEUINT(m_blkbuf+m_recOffs);
		m_recOffs	+=	2;
	}
	if ( _fieldLen == 0 ) {
		return -1;	// 終了
	}

	// (2)圧縮長
	_complen	= m_blkbuf[m_recOffs];
	++m_recOffs;

	if ( m_header->dictype & 0x08 ) {	// BOCU1
		// (3)見出語属性
		_title_attr	= m_blkbuf[m_recOffs];
		++m_recOffs;

		//	フィールド長は、見出語から訳語の最後までのバイト単位の長さ
		nextoffs	= m_recOffs+_fieldLen;
		// (4)見出語 NULL終端
		_titlelen	= strlen( (char*)m_blkbuf+m_recOffs);
		int _full_titlelen	= _complen+_titlelen;
		ASSERT( _full_titlelen <= PDIC_MAX_TITLE );
		strncpy( (char*)m_lastTitle+_complen, (char*)m_blkbuf+m_recOffs,_titlelen );
		m_lastTitle[ _full_titlelen ]	= 0;
		if ( title ) {  
			strncpy( (char*)title,(char*)m_lastTitle,_full_titlelen );
			title[ _full_titlelen ]	= 0;
		}
		info.titlelen	= _full_titlelen;

		m_recOffs	+= _titlelen + 1;

		// (5)訳語 NULL終端
		info.hoffs	= m_recOffs;
		if ( _title_attr & 0x10 ) {	// 拡張属性
			_honbunlen	= strlen( (char*)m_blkbuf+m_recOffs);
			info.hoffs	= m_recOffs;
			info.honbunlen	= _honbunlen;

			m_recOffs	+=	_honbunlen + 1;
			//	この後ろに、拡張属性が続く
			while( m_recOffs<=nextoffs ) {
				//	拡張属性
				_ext_attr	= m_blkbuf[m_recOffs];
				++m_recOffs;
				if ( _ext_attr == 0x80 ) {
					break;
				}
				else if ( _ext_attr & 0x10 ) {	// binary
					if ( m_fieldSize == 4 ) {
						_ext_len	= LFOURBYTEUINT(m_blkbuf+m_recOffs);
						m_recOffs	+=	4;
					}
					else {
						_ext_len	= LTWOBYTEUINT(m_blkbuf+m_recOffs);
						m_recOffs	+=	2;
					}
					// 読み飛ばす
					m_recOffs	+=	_ext_len;
				}
				else {
					_ext_len	= strlen( (char*)m_blkbuf+m_recOffs);
					if ( _ext_attr == 0x01 ) {	// 用例
						info.yoffs		= m_recOffs;
						info.yoreilen	= _ext_len;
					}
					else if ( _ext_attr == 0x02 ) {	// 発音記号
						info.poffs	= m_recOffs;
						info.phoneticlen	= _ext_len;
					}
					m_recOffs	+=	_ext_len+1;
				}
			}
			ASSERT( m_recOffs == nextoffs );
			m_recOffs	=	nextoffs;
		}
		else {	// 拡張属性無
			_honbunlen	= nextoffs - m_recOffs;
			info.hoffs	= m_recOffs;
			info.honbunlen	= _honbunlen;
			m_recOffs	= nextoffs;
		}
		info.level	= _title_attr & 0x0f;
	}
	else {
		// (3)見出語 NULL終端
		_titlelen	= strlen( (char*)m_blkbuf+m_recOffs);
		int _full_titlelen	= _complen+_titlelen;
		ASSERT( _full_titlelen <= PDIC_MAX_TITLE );
		strncpy( (char*)m_lastTitle+_complen, (char*)m_blkbuf+m_recOffs,_titlelen );
		m_lastTitle[ _full_titlelen ]	= 0;
		if ( title ) {  
			strncpy( (char*)title,(char*)m_lastTitle,_full_titlelen );
			title[ _full_titlelen ]	= 0;
		}
		info.titlelen	= _full_titlelen;

		m_recOffs	+= _titlelen + 1;
		// (4)見出語属性
		_title_attr	= m_blkbuf[m_recOffs];
		++m_recOffs;

		// (5)訳語 拡張属性の場合はNULL終端
		if ( _title_attr & 0x10 ) {	// 拡張属性
			_honbunlen	= strlen( (char*)m_blkbuf+m_recOffs);
			info.hoffs	= m_recOffs;
			info.honbunlen	= _honbunlen;

			nextoffs	= m_recOffs	+ _fieldLen - _titlelen - 2;
			m_recOffs	+=	_honbunlen + 1;
			//	この後ろに、拡張属性が続く
			while( m_recOffs<=nextoffs ) {
				//	拡張属性
				_ext_attr	= m_blkbuf[m_recOffs];
				++m_recOffs;
				if ( _ext_attr == 0x80 ) {
					break;
				}
				else if ( _ext_attr & 0x10 ) {	// binary
					if ( m_fieldSize == 4 ) {
						_ext_len	= LFOURBYTEUINT(m_blkbuf+m_recOffs);
						m_recOffs	+=	4;
					}
					else {
						_ext_len	= LTWOBYTEUINT(m_blkbuf+m_recOffs);
						m_recOffs	+=	2;
					}
					// 読み飛ばす
					m_recOffs	+=	_ext_len;
				}
				else {
					_ext_len	= strlen( (char*)m_blkbuf+m_recOffs);
					if ( _ext_attr == 0x01 ) {	// 用例
						info.yoffs	= m_recOffs;
						info.yoreilen	= _ext_len;
					}
					else if ( _ext_attr == 0x02 ) {	// 発音記号
						info.poffs	= m_recOffs;
						info.phoneticlen	= _ext_len;
					}
					m_recOffs	+=	_ext_len+1;
				}
			}
			ASSERT( m_recOffs == nextoffs );
			m_recOffs	=	nextoffs;
		}
		else {	// 拡張属性無
			_honbunlen	= _fieldLen - _titlelen - 2;
			info.hoffs	= m_recOffs;
			info.honbunlen	= _honbunlen;
			m_recOffs	+= _honbunlen;
		}
		info.level	= _title_attr & 0x0f;
	}

	if ( titlelen )		*titlelen	= info.titlelen;
	if ( hoffs )		*hoffs		= info.hoffs;
	if ( honbunlen )	*honbunlen	= info.honbunlen;
	if ( yoffs )		*yoffs		= info.yoffs;
	if ( yoreilen )		*yoreilen	= info.yoreilen;
	if ( poffs )		*poffs		= info.poffs;
	if ( phoneticlen )	*phoneticlen	= info.phoneticlen;
	if ( level )		*level		= info.level;

	if ( pInfo )	{
		info.blocks		= m_blocks;
		info.fieldSize	= m_fieldSize;
		*pInfo			= info;
	}

	return 0;
}

// 下位I/F	物理データブロックを読む
//	0	正常
//	-1	失敗
int	CPDicData::ReadBlk( unsigned long blkno )
{
	unsigned long seekoffs;
	//	Seek
	if ( (m_header->version>>8) >= 4 ) {
		seekoffs	= m_header->header_size
					+ m_header->extheader
					+ m_header->index_block * m_header->block_size
					+ m_header->block_size * blkno;
	}
	else {
		seekoffs	= m_header->header_size
					+ m_header->index_size
					+ m_header->block_size * blkno;
	}
	if ( fseek( m_fp, seekoffs, SEEK_SET ) ) {
//		return -1;
#if  defined(_WIN32_WCE)
		RaiseException(	EXCEPTION_CODE,	/*DWORD dwExceptionCode*/
						0,		/*DWORD dwExceptionFlags*/
						0,		/*DWORD nNumberOfArguments*/
						NULL);	/*ULONG_PTR *lpArguments*/	
#else
		throw;
#endif
	}

	//	データブロックを読む
	if ( fread( m_buffer, 1, m_header->block_size, m_fp )
					!= (unsigned int)m_header->block_size ) {
		return -1;
	}

	return 0;
}

