/*
 * Original Source http://azuco.sumomo.ne.jp/column/ag_ps2cmp/ps2lzss.cpp
 */
#include "stdafx.h"

unsigned long ps2_lzss(
	unsigned char *sbuf,
	unsigned char *dbuf,
	unsigned long dlen,
	CompressFlagType compress_flag,
	unsigned long enconding_bit
	)
{	
	unsigned long fv = 0;
	unsigned long fc = 0;
	unsigned long sa = 0;
	unsigned long sl = 0;
	unsigned long sc = 0;
	unsigned long el = 0;
	unsigned long dd = 0;
	unsigned long sp = 0;
	unsigned long dp = 0;
	unsigned long bitscnt = getBitCount(enconding_bit);
	unsigned long mask = 1;
	mask <<= 16 - bitscnt;
	mask -= 1;
	unsigned long value1 = 0;
	unsigned long value2 = 0;
	memset(dbuf, 0x00, dlen);

	if(enconding_bit == 0x0f) {
		value1 = enconding_bit + 3;
		value2 = enconding_bit - 3;
	}
	else if(enconding_bit == 0x0f00) {
		value1 = (enconding_bit >> 8) + 3;
		value2 = (enconding_bit >> 8) - 3;
	}

	for(;;) {
		fv = sbuf[sp++];
		// 8bit分ループ
		for(fc = 0; fc < 8; fc++) {
			// 最右bitが 0 なら 圧縮、そうでなければ未圧縮
			if((fv & 1) == (unsigned long)compress_flag) {
				// 圧縮データは2byte分(例：圧縮データが 0xEBF0, enconding_bit が 0x0f)
				if(enconding_bit == 0x0f) {
					// 長さ情報(例：0xEBF0 の F0 と AND して 0 を得て +3 する)
					sl = 3 + (unsigned long)((sbuf[sp+1] & 0x0f));
					// 位置情報(例：0xEBF0 の F0 を 上位にずらして、下位を0クリア。その後 0xEBF0 の EB と OR => 0x0FEBとなる)
					sa = (unsigned long)((sbuf[sp+1] << (8 - bitscnt) & 0xf00) | sbuf[sp]);
				}
				else if(enconding_bit == 0x0f00) {
					sl = 3 + (unsigned long)((sbuf[sp] & 0x0f));
					sa = (unsigned long)((sbuf[sp] << 4 & enconding_bit) | sbuf[sp+1]);
				}

				sc = (dp - sa - value2) & ~mask;
				if(sc & 0x80000000) {
					sc = 0;
				}

				if(dp > (sc + sa + value1)) {
					if((dp - (sc + sa + value1)) > sl) {
						el = sl;
					}
					else {
						el = dp - (sc + sa + value1);
					}
				}
				else {
					el = 1;
				}

				for(dd = 0 ; dd < sl; dd += el) {
					if(dp + dd > dlen - el) {
						memcpy(dbuf + dlen - el, dbuf + sc + sa + value1, el);
					}
					else if(sc + sa + value1 > dlen - el) {
						memcpy(dbuf + dp + dd, dbuf + dlen - el, el);
					}
					else {
						memcpy(dbuf + dp + dd, dbuf + sc + sa + value1, el);
					}
				}
				sp += 2;
				dp += sl;
			}
			else {
				if(dp >= dlen) {
					return dp;
				}
				dbuf[dp++] = sbuf[sp++];
			}
			// 圧縮フラグをlittleからbigにかけて判定するために1bitずつシフト
			fv >>= 1;
			if(dp >= dlen) {
				return dp;
			}
		}
	}
}

unsigned long ps2_lzss2(
	unsigned char *sbuf,
	unsigned char *dbuf,
	unsigned long dlen,
	CompressFlagType compress_flag,
	unsigned long enconding_bit
	)
{
	unsigned long fv = 0;
	unsigned long fc = 0;
	unsigned long sa = 0;
	unsigned long sl = 0;
	unsigned long sc = 0;
	unsigned long el = 0;
	unsigned long dd = 0;
	unsigned long sp = 0;
	unsigned long dp = 0;
	unsigned long bitscnt = getBitCount(enconding_bit);
	unsigned long mask = 1;
	mask <<= 8 - bitscnt;
	mask -= 1;
	unsigned long value1 = 0;
	unsigned long value2 = 0;
	memset(dbuf, 0x00, dlen);

	if(enconding_bit == 0xc0) {
		value1 = enconding_bit + 2;
		value2 = enconding_bit - 2;
	}

	for(;;) {
		fv = sbuf[sp++];
		if(fv == 0xff) {
			memset(dbuf + dp, 0, 3);
			dp += 3;
			continue;
		}
		// 8bit分ループ
		for(fc = 0; fc < 7; fc++) {
			if((fv & 1) == (unsigned long)compress_flag) {
				if(enconding_bit == 0xc0) {
					sl = 2 + (unsigned long)((sbuf[sp] >> 6 & 0x03));
					sa = (unsigned long)((sbuf[sp] & 0x3f));
				}

				sc = (dp - sa - value2) & ~mask;
				if(sc & 0x80000000) {
					sc = 0;
				}

				if(dp > (sc + sa + value1)) {
					if((dp - (sc + sa + value1)) > sl) {
						el = sl;
					}
					else {
						el = dp - (sc + sa + value1);
					}
				}
				else {
					el = 1;
				}

				for(dd = 0 ; dd < sl; dd += el) {
					if(dp + dd > dlen - el) {
						memcpy(dbuf + dlen - el, dbuf + sc + sa + value1, el);
					}
					else if(sc + sa + value1 > dlen - el) {
						memcpy(dbuf + dp + dd, dbuf + dlen - el, el);
					}
					else {
						memcpy(dbuf + dp + dd, dbuf + sc + sa + value1, el);
					}
				}
				sp++;
				dp += sl;
			}
			else {
				if(dp >= dlen) {
					return dp;
				}
				dbuf[dp++] = sbuf[sp++];
			}
			// 圧縮フラグをlittleからbigにかけて判定するために1bitずつシフト
			fv >>= 1;
			if(dp >= dlen) {
				return dp;
			}
		}
	}
}
