/*	
 *	cliplzss.c
 *
 *	LZSS展開ルーチン
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2013 Naoyuki Sawa
 *
 *	* Thu Sep 10 00:42:56 JST 2009 Naoyuki Sawa
 *	- 新規作成。
 *	* Sun Jul 07 18:34:29 JST 2013 Naoyuki Sawa
 *	- LZSS_1、LZSS_1_init()、LZSS_1_get()を追加しました。
 *	- LZSS_2、LZSS_2_init()、LZSS_2_get()を追加しました。
 */
#include "clip.h"

/****************************************************************************
 *	
 ****************************************************************************/

int
lzss_uncompress1(void* _dst_data/*[dst_lim]*/, int dst_lim, const void* _src_data/*[src_len]*/, int src_len)
{
	      unsigned char* dst_data = _dst_data;
	const unsigned char* src_data = _src_data;

	/*{{この範囲は、lzss_uncompress1とlzss_uncompress2とで、ほぼ共通の処理*/
	int flag_mask = 0;
	int flag = 0;
	int dst_len = 0;
	int src_pos = 0;
	int hdr;
	int len;
	int dist;
	int pos;
	while(src_pos < src_len) {
		/* フラグを取得する */
		if(!flag_mask) {
			flag_mask = 1 << 7;	/*{{lzss_uncompress1とlzss_uncompress2とで、違うのはここだけ}}*/
			flag = src_data[src_pos++];
		}
		/* 圧縮コードの場合 */
		if(!(flag & flag_mask)) {
			/* 圧縮コード {(match_dist-1),(match_len-2)} を取得する */
			hdr = src_data[src_pos++];
			len = (hdr & 15) + 2;
			dist = (hdr >> 4) + 1;
			/* 圧縮コードを展開する */
			pos = dst_len - dist;
			do {
				if(dst_len < dst_lim) {
					dst_data[dst_len] = dst_data[pos];
				}
				dst_len++;
				pos++;
			} while(--len);
		/* 無圧縮コードの場合 */
		} else {
			/* 無圧縮コードを出力する */
			if(dst_len < dst_lim) {
				dst_data[dst_len] = src_data[src_pos];
			}
			dst_len++;
			src_pos++;
		}
		/* フラグビット位置を進める */
		flag_mask >>= 1;
	}
	/*}}この範囲は、lzss_uncompress1とlzss_uncompress2とで、ほぼ共通の処理*/

	return dst_len;
}

/*--------------------------------------------------------------------------*/

int
lzss_uncompress2(void* _dst_data/*[dst_lim]*/, int _dst_lim, const void* _src_data/*[src_len]*/, int _src_len)
{
	      unsigned short* dst_data = _dst_data;
	const unsigned short* src_data = _src_data;
	int dst_lim = _dst_lim >> 1; /* ハーフワード単位に変換。奇数だった場合は、一時的に端数を切り捨てておく */
	int src_len = _src_len >> 1; /* ハーフワード単位に変換。奇数だった場合は、一時的に端数を切り捨てておく */

	/*{{この範囲は、lzss_uncompress1とlzss_uncompress2とで、ほぼ共通の処理*/
	int flag_mask = 0;
	int flag = 0;
	int dst_len = 0;
	int src_pos = 0;
	int hdr;
	int len;
	int dist;
	int pos;
	while(src_pos < src_len) {
		/* フラグを取得する */
		if(!flag_mask) {
			flag_mask = 1 << 15;	/*{{lzss_uncompress1とlzss_uncompress2とで、違うのはここだけ}}*/
			flag = src_data[src_pos++];
		}
		/* 圧縮コードの場合 */
		if(!(flag & flag_mask)) {
			/* 圧縮コード {(match_dist-1),(match_len-2)} を取得する */
			hdr = src_data[src_pos++];
			len = (hdr & 15) + 2;
			dist = (hdr >> 4) + 1;
			/* 圧縮コードを展開する */
			pos = dst_len - dist;
			do {
				if(dst_len < dst_lim) {
					dst_data[dst_len] = dst_data[pos];
				}
				dst_len++;
				pos++;
			} while(--len);
		/* 無圧縮コードの場合 */
		} else {
			/* 無圧縮コードを出力する */
			if(dst_len < dst_lim) {
				dst_data[dst_len] = src_data[src_pos];
			}
			dst_len++;
			src_pos++;
		}
		/* フラグビット位置を進める */
		flag_mask >>= 1;
	}
	/*}}この範囲は、lzss_uncompress1とlzss_uncompress2とで、ほぼ共通の処理*/

	/* 入力データバイト数が奇数だった場合は、入力データの端数バイトを、そのまま出力データの末尾に追加する */
	dst_len <<= 1; /* バイト単位に変換 */
	if(_src_len/*バイト単位*/ & 1) {
		if(dst_len/*バイト単位*/ < _dst_lim/*バイト単位*/) {
			((unsigned char*)_dst_data)[dst_len/*バイト単位*/] = ((const unsigned char*)_src_data)[_src_len/*バイト単位*/ - 1];
		}
		dst_len++; /* バイト単位 */
	}
	return dst_len; /* バイト単位 */
}

/****************************************************************************
 *	
 ****************************************************************************/

void LZSS_1_init(LZSS_1* self, const void* data) {
	self->data      = data;		/* 初期化必須 */
	self->flag_mask = 0;		/* クリア必須 */
	self->flag      = 0;		/* クリア必須ではないが、判り易さのためにクリアしておく。初回に必ず取得される。 */
	self->len       = 0;		/* クリア必須 */
	self->dist      = 0;		/* クリア必須ではないが、判り易さのためにクリアしておく。圧縮コードの開始時に必ず取得される。 */
	self->pos       = 0;		/* クリア必須ではないが、判り易さのためにクリアしておく。実際にはどこから記録開始しても良い。 */
//	memset(self->dic, 0, sizeof self->dic);	/* クリア不要。正しい圧縮データならば、まだ値を格納していない箇所は参照されないはず。初期化時間を短縮するために、クリアしないことにした。 */
}

unsigned char LZSS_1_get(LZSS_1* self) {
	unsigned char val;
	unsigned char hdr;
	/* フラグを未取得ならば… */
	if(!self->flag_mask) {
		/* フラグを取得する */
		self->flag_mask = 1 << 7;
		self->flag = *self->data++;
	}
	/* 圧縮コードの場合 */
	if(!(self->flag & self->flag_mask)) {
		/* 圧縮コードを未取得ならば… */
		if(!self->len) {
			/* 圧縮コード {(match_dist-1),(match_len-2)} を取得する */
			hdr = *self->data++;
			self->len  = (hdr & 15) + 2;
			self->dist = (hdr >> 4) + 1;
		}
		/* 圧縮コードを展開する */
		val = self->dic[(self->pos - self->dist) & 15];
		/* 圧縮コードの展開が完了したら… */
		if(!--self->len) {
			/* フラグ位置を次へ進める */
			self->flag_mask >>= 1;
		}
	} else {
		/* 無圧縮コードを取得する */
		val = *self->data++;
		/* フラグ位置を次へ進める */
		self->flag_mask >>= 1;
	}
	/* 値を辞書に記録する */
	self->dic[self->pos++ & 15] = val;
	return val;
}

/*--------------------------------------------------------------------------*/

void LZSS_2_init(LZSS_2* self, const void* data) {
	self->data      = data;		/* 初期化必須 */
	self->flag_mask = 0;		/* クリア必須 */
	self->flag      = 0;		/* クリア必須ではないが、判り易さのためにクリアしておく。初回に必ず取得される。 */
	self->len       = 0;		/* クリア必須 */
	self->dist      = 0;		/* クリア必須ではないが、判り易さのためにクリアしておく。圧縮コードの開始時に必ず取得される。 */
	self->pos       = 0;		/* クリア必須ではないが、判り易さのためにクリアしておく。実際にはどこから記録開始しても良い。 */
//	memset(self->dic, 0, sizeof self->dic);	/* クリア不要。正しい圧縮データならば、まだ値を格納していない箇所は参照されないはず。初期化時間を短縮するために、クリアしないことにした。 */
}

unsigned short LZSS_2_get(LZSS_2* self) {
	unsigned short val;
	unsigned short hdr;
	/* フラグを未取得ならば… */
	if(!self->flag_mask) {
		/* フラグを取得する */
		self->flag_mask = 1 << 15;
		self->flag = *self->data++;
	}
	/* 圧縮コードの場合 */
	if(!(self->flag & self->flag_mask)) {
		/* 圧縮コードを未取得ならば… */
		if(!self->len) {
			/* 圧縮コード {(match_dist-1),(match_len-2)} を取得する */
			hdr = *self->data++;
			self->len  = (hdr & 15) + 2;
			self->dist = (hdr >> 4) + 1;
		}
		/* 圧縮コードを展開する */
		val = self->dic[(self->pos - self->dist) & 4095];
		/* 圧縮コードの展開が完了したら… */
		if(!--self->len) {
			/* フラグ位置を次へ進める */
			self->flag_mask >>= 1;
		}
	} else {
		/* 無圧縮コードを取得する */
		val = *self->data++;
		/* フラグ位置を次へ進める */
		self->flag_mask >>= 1;
	}
	/* 値を辞書に記録する */
	self->dic[self->pos++ & 4095] = val;
	return val;
}

