/*	
 *	cliplzh.h
 *
 *	P/ECE LZH
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2004 Naoyuki Sawa
 *
 *	* Wed Apr 14 12:30:00 JST 2004 Naoyuki Sawa
 *	- 作成開始。
 *	* Wed Nov 18 21:37:53 JST 2015 Naoyuki Sawa
 *	- 'extern "C" {'〜'}'で囲みました。.cppを含むWin32プロジェクトからもインクルード出来るようにするためです。
 */
#ifndef __CLIP_LZH_H__
#define __CLIP_LZH_H__

#ifdef  __cplusplus
extern "C" {
#endif//__cplusplus

/****************************************************************************
 *	ファイルヘッダ
 ****************************************************************************/

#define LZHBYTE(p)	((unsigned char )(((unsigned char*)(p))[0]))
#define LZHHALF(p)	((unsigned short)(((unsigned char*)(p))[0] | ((unsigned char*)(p))[1] << 8))
#define LZHWORD(p)	((unsigned int  )(((unsigned char*)(p))[0] | ((unsigned char*)(p))[1] << 8 | ((unsigned char*)(p))[2] << 16 | ((unsigned char*)(p))[3] << 24))

/*
 *	レベル0ヘッダ
 */
typedef struct _LZHLEVEL0HEADER {
	unsigned char header_size	[1];		// ヘッダの大きさ(method_id以降)
	unsigned char header_sum	[1];		// ヘッダのチェックサム(method_id以降)
							//-----------------------------------------------
	         char method_id		[5];		// 圧縮法の種類					|
	unsigned char packed_size	[4];		// 圧縮後のファイルサイズ			|
	unsigned char original_size	[4];		// 元のファイルサイズ				|
	unsigned char time		[2];		// ファイルの最終更新時間			|
	unsigned char date		[2];		// ファイルの最終更新日				|
	unsigned char attribute		[1];		// ファイルの属性				| header_size
	unsigned char level		[1];		// ヘッダのレベル(0x00固定)			|
	unsigned char name_length	[1];		// ファイル名の長さ				|
//	         char pathname		[name_length];	// ファイル名(フルパス名)			|
//	unsigned char file_crc		[2];		// ファイルのCRC-16				|
//	unsigned char ext_header	[...];		// 拡張可能部分					|
} LZHLEVEL0HEADER;					//-----------------------------------------------
//	unsigned char file_data		[packed_size];	// 圧縮データ					| packed_size
							//-----------------------------------------------

/*
 *	レベル1ヘッダ
 */
typedef struct _LZHLEVEL1HEADER {
	unsigned char header_size	[1];		// 基本ヘッダの大きさ(method_id以降)
	unsigned char header_sum	[1];		// 基本ヘッダのチェックサム(method_id以降)
							//-----------------------------------------------
	         char method_id		[5];		// 圧縮法の種類					|
	unsigned char skip_size		[4];		// スキップサイズ				|
	unsigned char original_size	[4];		// 元のファイルサイズ				|
	unsigned char time		[2];		// ファイルの最終更新時間			|
	unsigned char date		[2];		// ファイルの最終更新日				|
	unsigned char reserved		[1];		// 予約フィールド(0x20固定)			|
	unsigned char level		[1];		// ヘッダのレベル(0x01固定)			| header_size
	unsigned char name_length	[1];		// ファイル名の長さ(0ならば拡張ヘッダを参照)	|
//	         char filename		[name_length];	// ファイル名(ファイル名のみ)			|
//	unsigned char file_crc		[2];		// ファイルのCRC-16				|
//	unsigned char os_id		[1];		// 書庫を作成したOSの識別子			|
//	unsigned char ext_header	[...];		// 拡張可能部分(レベル0ヘッダ互換)		|
//	unsigned char next_header_size	[2];		// 最初の拡張ヘッダサイズ			|
} LZHLEVEL1HEADER;					//-----------------------------------------------
//	LZHEXTHEADER ext-header		[...];		// 拡張ヘッダ					|
//	LZHEXTHEADER ext-header		[...];		//						|
//			.		  .		//						|
//			.		  .		//						| skip_size
//			.		  .		//						|
//	LZHEXTHEADER ext-header		[...];		//						|
//	unsigned char file_data		[...];		// 圧縮データ					|
							//-----------------------------------------------

/*
 *	レベル2ヘッダ
 */
typedef struct _LZHLEVEL2HEADER {			//-----------------------------------------------
	unsigned char total_header_size	[2];		// 全ヘッダの大きさ(total_header_sizeも含む)	|
	         char method_id		[5];		// 圧縮法の種類					|
	unsigned char packed_size	[4];		// 圧縮後のファイルサイズ			|
	unsigned char original_size	[4];		// 元のファイルサイズ				|
	unsigned char time		[4];		// ファイルの最終更新時間			|
	unsigned char reserved		[1];		// 予約フィールド(0x20固定)			|
	unsigned char level		[1];		// ヘッダのレベル(0x02固定)			|
	unsigned char file_crc		[2];		// ファイルのCRC-16				|
	unsigned char os_id		[1];		// 書庫を作成したOSの識別子			| total_header_size
	unsigned char next_header_size	[2];		// 最初の拡張ヘッダサイズ			|
} LZHLEVEL2HEADER;					//						|
//	LZHEXTHEADER ext-header		[...];		// 拡張ヘッダ					|
//	LZHEXTHEADER ext-header		[...];		//						|
//			.		  .		//						|
//			.		  .		//						|
//			.		  .		//						|
//	LZHEXTHEADER ext-header		[...];		//						|
//	unsigned char padding		[0 or 1];	// パディング(無い場合もあります)		|
//							//-----------------------------------------------
//	unsigned char file_data		[packed_size];	// 圧縮データ					| packed_size
							//-----------------------------------------------

/*
 *	拡張ヘッダ(レベル1、レベル2共通)
 */
typedef struct _LZHEXTHEADER {
	unsigned char ext_type		[1];		/* 識別子 */
//	unsigned char data		[...];		/* データ部分 */
//	unsigned char next_header_size	[2];		/* 最初の拡張ヘッダサイズ */
} LZHEXTHEADER;

/* LZHEXTHEADER.ext_type */
#define LZH_EXTTYPE_HEADER_CRC		0		/* ヘッダのCRC-16 */
#define LZH_EXTTYPE_FILENAME		1		/* ファイル名 */
#define LZH_EXTTYPE_DIRECTORY		2		/* ディレクトリ名 */

/*
 *	レベル0〜2ヘッダから、必要な部分だけを取り出しました。
 */
typedef struct _LZHDIR {
	char pathname[128];		/* フルパス名 */
	char method_id[5];		/* 圧縮法の種類 */
	int packed_size;		/* 圧縮後のファイルサイズ */
	int original_size;		/* 元のファイルサイズ */
	int file_crc;			/* CRC-16 */
	const void* file_data;		/* 圧縮データアドレス */
} LZHDIR;

/* チェックサムを計算します。
 * [in]
 *	data		データ先頭アドレス。
 *	len		データサイズ。(バイト単位)
 * [out]
 *	戻り値		チェックサムを返します。
 * [note]
 *	* lzh_gethdr0()、lzh_gethdr1()から利用します。
 */
int lzh_chksum(const void* data, int len);

/* LZH仕様のCRC-16を計算します。
 * [in]
 *	data		データ先頭アドレス。
 *	len		データサイズ。(バイト単位)
 * [out]
 *	戻り値		CRC-16を返します。
 * [note]
 *	* LZHのCRC-16は、一般的なCRC-16とは多項式が異なります。
 *	  LZH以外の用途には使えないようです。
 */
int lzh_crc16(const void* data, int len);

/* ファイル名を比較します。
 * [in]
 *	s1,s2		比較するファイル名。
 * [out]
 *	戻り値		一致したら、0を返します。
 *			一致しなければ、0以外の値を返します。
 * [note]
 *	* ファイル名の大文字と小文字は区別しません。
 *	  また、パス名の"/"と"\"は区別しません。
 *	* lzh_find()から利用します。
 */
int lzh_strcmp(const char* s1, const char* s2);

/* 拡張ヘッダを読み込みます。
 * [in]
 *	dir			ファイル情報を格納するLZHDIR構造体。
 *	exthdr			最初の拡張ヘッダの先頭アドレス。
 *	next_header_size	最初の拡張ヘッダサイズ。
 *	filename		レベル1ヘッダから利用する場合、基本ヘッダ内のファイル名へのポインタ。
 *				レベル2ヘッダから利用する場合、NULL。
 *	filename_size		レベル1ヘッダから利用する場合、基本ヘッダ内のファイル名の長さ
 *				レベル2ヘッダから利用する場合、0。
 * [out]
 *	戻り値			成功ならば、拡張ヘッダの合計サイズを返します。
 *				失敗ならば、負の値を返します。
 *	dir->pathname		ファイル名(フルパス名)を格納します。
 * [note]
 *	* lzh_gethdr1()、lzh_gethdr2()から利用します。
 */
int lzh_getexthdr(LZHDIR* dir, const void* exthdr, int next_header_size, const char* filename, int filename_size);

/* レベル0ヘッダを読み込み、ファイル情報を作成します。
 * [in]
 *	dir		ファイル情報を格納するLZHDIR構造体。
 *	header		レベル0ヘッダへのポインタ。
 * [out]
 *	戻り値		成功ならば、現在のヘッダから次のヘッダへのオフセットを返します。
 *			失敗ならば、負の値を返します。
 * [note]
 *	* lzh_dir()から利用します。
 */
int lzh_gethdr0(LZHDIR* dir, const LZHLEVEL0HEADER* header);

/* レベル1ヘッダを読み込み、ファイル情報を作成します。
 * [in]
 *	dir		ファイル情報を格納するLZHDIR構造体。
 *	header		レベル1ヘッダへのポインタ。
 * [out]
 *	戻り値		成功ならば、ヘッダサイズを返します。
 *			失敗ならば、負の値を返します。
 * [note]
 *	* lzh_dir()から利用します。
 */
int lzh_gethdr1(LZHDIR* dir, const LZHLEVEL1HEADER* header);

/* レベル2ヘッダを読み込み、ファイル情報を作成します。
 * [in]
 *	dir		ファイル情報を格納するLZHDIR構造体。
 *	header		レベル2ヘッダへのポインタ。
 * [out]
 *	戻り値		成功ならば、ヘッダサイズを返します。
 *			失敗ならば、負の値を返します。
 * [note]
 *	* lzh_dir()から利用します。
 */
int lzh_gethdr2(LZHDIR* dir, const LZHLEVEL2HEADER* header);

/****************************************************************************
 *	出力バイトストリーム
 ****************************************************************************/

typedef struct _LZHBYTESTREAM {
	unsigned char* buffer;	/* バッファ先頭アドレス */
	int len;		/* バッファサイズ (byte単位) */
	int pos;		/* 書き込み位置   (byte単位) */
} LZHBYTESTREAM;

/* 出力バイトストリームを初期化します。
 * [in]
 *	bytestream	LZHBYTESTREAM構造体。
 *	buffer		出力バッファの先頭アドレス。
 *	len		出力バッファサイズ。(バイト単位)
 * [out]
 *	戻り値		成功ならば、0を返します。
 *			失敗ならば、負の値を返します。
 */
int lzh_bytestream_init(LZHBYTESTREAM* out, void* buffer, int len);

/* 1バイト書き込みます。
 * [in]
 *	bytestream	LZHBYTESTREAM構造体。
 *	value		書き込むバイト値。[7:0]
 *			[31:8]は捨てられます。
 * [out]
 *	戻り値		成功ならば、書き込んだバイト数(=1)を返します。
 *			失敗ならば、負の値を返します。
 *	*out		読み込んだバイト列。
 */
int lzh_bytestream_put(LZHBYTESTREAM* out, int value);

/****************************************************************************
 *	入力ビットストリーム
 ****************************************************************************/

typedef struct _LZHBITSTREAM {
	const unsigned char* data;	/* データ先頭アドレス */
	int len;			/* データサイズ (bit単位) */
	int pos;			/* 読み込み位置 (bit単位) */
} LZHBITSTREAM;

/* 入力ビットストリームを初期化します。
 * [in]
 *	in		LZHBITSTREAM構造体。
 *	data		入力データの先頭アドレス。
 *	len		入力データサイズ。(バイト単位)
 * [out]
 *	戻り値		成功ならば、0を返します。
 *			失敗ならば、負の値を返します。
 */
int lzh_bitstream_init(LZHBITSTREAM* in, const void* data, int len);

/* ビット列を読み込みます。
 * [in]
 *	in		LZHBITSTREAM構造体。
 *	out		読み込んだビット列を格納する変数へのポインタ。
 *	cnt		読み込むビット数。(0〜32)
 * [out]
 *	戻り値		成功ならば、読み込んだビット数(=bits)を返します。
 *			失敗ならば、負の値を返します。
 *	*out		読み込んだビット列。
 */
int lzh_bitstream_get(LZHBITSTREAM* in, int* out, int cnt);

/****************************************************************************
 *	二分木 (binary tree)
 ****************************************************************************/

#define LZHBINARYTREE_MAXBITS	16		/* 符号の最大ビット長(仕様) */
#define LZHBINARYTREE_MAXVALUE	(256 + 256 - 3)	/* 符号を割り当てる値の最大値(仕様) */

typedef struct _LZHBINARYTREE {
	unsigned short next[2];	/* [0]:bit=0時の探査方向 / [1]:bit=1時の探査方向 */
} LZHBINARYTREE;

/* LZHBINARYTREE.next[bit] */
#define LZHBINARYTREE_BLANK	((1 << 16) - 1)	/* LZHBINARYTREE.next[bit]が空きであることを示します。 */
#define LZHBINARYTREE_LEAF	(1 << (16 - 1))	/* LZHBINARYTREE.next[bit]の最上位ビットが1ならば、それ以外のビットが値を示します。 */
						/* LZHBINARYTREE.next[bit]の最上位ビットが0ならば、次のノードインデクスを示します。 */

/* 0〜values-1の値に符号を割り当て、二分木に{符号、値}のペアを追加します。
 * [in]
 *	tree		LZHBINARYTREE配列。
 *	len		LZHBINARYTREE配列の要素数。
 *	code_bits	0〜values-1の値に対応する符号のビット数を指定する配列。(0〜LZHBINARYTREE_MAXBITS)
 *	values		code_bits配列の要素数。(0〜LZHBINARYTREE_MAXVALUE+1)
 * [out]
 *	戻り値		使用したLZHBINARYTREE配列の要素数を返します。
 *			失敗ならば、負の値を返します。
 * [note]
 *	* 呼び出し側にて、code_bits配列の内容を設定しておいてください。
 */
int lzh_binarytree_init(LZHBINARYTREE tree[/*len*/], int len, const unsigned char code_bits[/*values*/], int values);

/* 二分木に{符号、値}のペアを追加します。
 * [in]
 *	tree		LZHBINARYTREE配列。
 *	len		LZHBINARYTREE配列の要素数。
 *	code		符号。
 *	code_bits	符号ビット数。(1〜LZHBINARYTREE_MAXBITS)
 *	value		値。(0〜LZHBINARYTREE_MAXVALUE)
 * [out]
 *	戻り値		追加成功ならば、値を格納したノードのインデクスを返します。(0〜len-1)
 *			追加失敗ならば、負の値を返します。
 * [note]
 *	* lzh_binarytree_init()から利用します。
 *	  アプリケーションプログラムから直接呼び出すことはありません。
 */
int lzh_binarytree_add(LZHBINARYTREE tree[/*len*/], int len, int code, int code_bits, int value);

/* 二分木から符号に対応する値を検索します。
 * [in]
 *	tree		LZHBINARYTREE配列。
 *	len		LZHBINARYTREE配列の要素数。
 *	in		符号を読み込むビットストリーム。
 *	out		値を格納するための変数へのポインタ。
 * [out]
 *	戻り値		見つかったならば、0を返します。
 *			見つからなかったならば、負の値を返します。
 *	*out		見つかった値。
 */
int lzh_binarytree_lookup(LZHBINARYTREE tree[/*len*/], int len, LZHBITSTREAM* in, int* out);

/****************************************************************************
 *	-lh5-タイプの展開サブルーチン
 ****************************************************************************/

/* -lh5-タイプの展開サブルーチンです。
 * [in]
 *	dir		ファイル情報。
 *	out		出力バイトストリーム。
 *	in		入力ビットストリーム。
 * [out]
 *	戻り値		成功ならば、出力バイト数を返します。
 *			失敗ならば、負の値を返します。
 * [note]
 *	* lzh_uncompress()から利用します。
 *	  アプリケーションプログラムから直接呼び出すことはありません。
 */
int lzh_uncompress_lh5(LZHDIR* dir, LZHBYTESTREAM* out, LZHBITSTREAM* in);
int _lzh_uncompress_lh5(LZHDIR* dir, LZHBYTESTREAM* out, LZHBITSTREAM* in,
	unsigned char code_bits[/*LZHBINARYTREE_MAXVALUE + 1*/], LZHBINARYTREE nodes[/*nodes_len*/], int nodes_len);

/* 単進符号によるビット長表現を復号します。
 * [in]
 *	in		入力ビットストリーム。
 *	out		ビット長を格納するための変数へのポインタ。
 * [out]
 *	戻り値		成功ならば、0を返します。
 *			失敗ならば、負の値を返します。
 *	*out		ビット長を格納します。
 * [note]
 *	* lzh_uncompress_lh5()から利用します。
 *	  アプリケーションプログラムから直接呼び出すことはありません。
 */
int lzh_bitlength_decode_lh5(LZHBITSTREAM* in, int* out);

/* 8KBスライド辞書の距離表現を復号します。
 * [in]
 *	distance_code_tree	Distance code ツリーの先頭ノード。
 *	distance_code_tree_len	Distance code ツリーのノード数。
 *	in			入力ビットストリーム。
 *	out			ビット長を格納するための変数へのポインタ。
 * [out]
 *	戻り値			成功ならば、0を返します。
 *				失敗ならば、負の値を返します。
 *	*out			ビット長を格納します。
 * [note]
 *	* lzh_uncompress_lh5()から利用します。
 *	  アプリケーションプログラムから直接呼び出すことはありません。
 */
int lzh_distance_decode_lh5(LZHBINARYTREE* distance_code_tree, int distance_code_tree_len, LZHBITSTREAM* in, int* out);

/****************************************************************************
 *	LZHデコーダ
 ****************************************************************************/

typedef struct _LZH {
	const unsigned char* data;	/* LZHデータ先頭アドレス */
	int len;			/* LZHデータサイズ */
	/* lzh_dir() */
	int dir_pos;			/* ファイル情報走査位置 */
} LZH;

/* LZHデータを開きます。
 * [in]
 *	lzh		LZH構造体。
 *	data		LZHデータ先頭アドレス。
 *	len		LZHデータサイズ。(バイト単位)
 * [out]
 *	戻り値		成功ならば、0を返します。
 *			失敗ならば、負の値を返します。
 */
int lzh_init(LZH* lzh, const void* data, int len);

/* ファイル情報を読み込みます。
 * [in]
 *	lzh		LZH構造体。
 *	dir		NULLを指定すると、ファイル情報走査位置を先頭へ戻します。
 *			NULL以外を指定すると、ファイル情報を読み込んで格納し、走査位置を次へ進めます。
 * [out]
 *	戻り値		dirにNULLを指定した場合、常に0を返します。
 *			dirにNULL以外を指定した場合、ファイル情報読み込み成功ならば、0を返します。
 *			                             ファイル情報読み込み失敗ならば、負の値を返します。
 * [note]
 *	* ファイル名一覧を表示するプログラム例を示します。
 *		lzh_init(&lzh, data, len);
 *		lzh_dir(&lzh, NULL);
 *		while(lzh_dir(&lzh, &dir) == 0) {
 *			puts(dir.pathname);
 *		}
 */
int lzh_dir(LZH* lzh, LZHDIR* dir);

/* 指定ファイル名に一致するファイル情報を読み込みます。
 * [in]
 *	lzh		LZH構造体。
 *	pathname	ファイル名。
 *	dir		ファイル情報を格納するための変数へのポインタ。
 * [out]
 *	戻り値		指定ファイル名に一致するファイル情報が見つかったら、0を返します。
 *			指定ファイル名に一致するファイル情報が見つからなければ、負の値を返します。
 * [note]
 *	* ファイル名の大文字と小文字は区別しません。
 *	  また、パス名の"/"と"\"は区別しません。
 */
int lzh_find(LZH* lzh, const char* pathname, LZHDIR* dir);

/* 指定ファイル名に一致するファイルを展開します。
 * [in]
 *	lzh		LZH構造体。
 *	pathname	ファイル名。
 *	outbuf		出力バッファ。
 *	outlen		出力バッファサイズ。(バイト単位)
 * [out]
 *	戻り値		成功ならば、出力バイト数を返します。
 *			失敗ならば、負の値を返します。
 * [note]
 *	* 失敗の要因は、次のようなものがあります。
 *	  ・指定ファイル名に一致するファイルが見つからない。
 *	  ・出力バッファサイズが不足している。
 *	  ・展開データのCRCが一致しない。(LZHデータが破損している)
 */
int lzh_uncompress(LZH* lzh, const char* pathname, void* outbuf, int outlen);

#ifdef  __cplusplus
}//extern "C"
#endif//__cplusplus

#endif /*__CLIP_LZH_H__*/
