// XCompress by Deviator//HAZARD
// Modified LZSS family compressor

?define	WinBit	13;		// Длина окна ! Попробуйте сделать больше. (14)
?define	LvlBit1	8
?define	LvlLim1	1 << LvlBit1 - 1
?define	LenBit	8
?define	MinLen	WinBit+LenBit+1/8+1
?define	LenLow	3
?define	LenLim	1 << LenLow - 1 + MinLen
?define	NODENIL	WinLen
?define	LenSize	1 << LenBit + LenLim - 1
?define	WinLen	1 << WinBit + LvlLim1
#define SIZEINITWIN WinLen+1*2

word	Rson[WinLen+256+16];
word	Lson[WinLen];
word	Dad[WinLen];
dword	MatchPos;
dword	MatchLen;
dword	cChar;
dword cCode,cLen;
dword	xLim,xPos,xBst;

?include "guixc.h--";

void InitTree()
{
	$push EDI
	EDI=#Rson+SIZEINITWIN;
	ECX=256;
	AX=NODENIL;
	$rep $stosw
	FOR (EDI=0;EDI<WinLen*2;EDI+=2){
		Rson[EDI]=Lson[EDI]=Dad[EDI]=NODENIL;
	}
	$pop EDI
}

void	AddNode()
byte	dir;
{
	EBX = xPos;
	EBX = DSBYTE[EBX];
	EDI = WinLen+1+EBX;
	dir = TRUE;
	Rson[cChar] = Lson[cChar] = NODENIL;
	while (MatchLen < LenSize){
		IF (dir == TRUE){
			IF (Rson[EDI*2] == NODENIL){
				Rson[EDI*2] = cChar;
				Dad[cChar]  = DI;
				return();
	  	}
			ELSE EDI = Rson[EDI*2];
		}
		ELSE{
			IF (Lson[EDI*2] == NODENIL){
				Lson[EDI*2] = cChar;
				Dad[cChar]  = DI;
				return();
	  	}
			ELSE EDI = Lson[EDI*2];
		}
		ECX = cChar-EDI;
		$or  ECX,ECX
		$jge NoAdd1
		ECX += WinLen;
NoAdd1:
		EBX = xPos-ECX;
		ESI = xPos;
		ECX = 1;
		EDX = xLim-ESI;
		WHILE(DSBYTE[ESI+ECX]==DSBYTE[EBX+ECX])&&(ECX < LenSize)&&(ECX<EDX)ECX++;
		IF (DSBYTE[ESI+ECX] > DSBYTE[EBX+ECX]) AL = TRUE;
		ELSE AL = FALSE;
		dir=AL;
		IF(ECX > MatchLen){
			MatchPos = EDI;
			MatchLen = ECX;
		}
		EDX = cChar-MatchPos+xPos;
		IF (cChar-EDI+xPos < EDX) && (ECX >= MatchLen){
			MatchPos = EDI;
			MatchLen = ECX;
		}
	}
	EBX = cChar;
	Dad[EBX*2] = Dad[EDI*2];
	ECX = Lson[EDI*2];
	Lson[EBX*2]= CX;
	Dad[ECX*2] = BX;
	ECX = Rson[EDI*2];
	Rson[EBX*2]= CX;
	Dad[ECX*2] = BX;
	ECX = Dad[EDI*2];
	EDX = cChar;
	IF (Rson[ECX*2] == DI) Rson[ECX*2] = DX;
	ELSE Lson[ECX*2] = DX;
	Dad[EDI*2] = NODENIL;
}

void	DelNode()
{
	EDX = cChar << 1;
	EBX = Dad[EDX];
	IF(EBX == NODENIL) return();
	EBX <<= 1;
	IF (Lson[EDX] == NODENIL) ECX = Rson[EDX];
	ELSE IF (Rson[EDX] == NODENIL) ECX = Lson[EDX];
	ELSE{
		ECX = Rson[EDX];
		WHILE (Lson[ECX*2] != NODENIL) ECX = Lson[ECX*2];
		EDI = Lson[EDX];
		Lson[ECX*2] = EDI;
		Dad[EDI*2]  = ECX;
		ECX = Rson[EDX];
	}
	IF (ECX != NODENIL){
		EAX = EBX>>1;
		Dad[ECX*2] = EAX;
	}
	IF (Rson[EBX] == cChar) Rson[EBX] = ECX;
	ELSE Lson[EBX] = ECX;
	Dad[EDX]  = Lson[EDX] = Rson[EDX] = NODENIL;
}

void StoreBits(dword Code,Cnt)
{
	ECX = 32-Cnt;
	Code <<= CL;
	loop(Cnt){
		cCode <<= 1;
		Code  <<= 1;
		IF(CARRYFLAG)cCode |= 1;
		cLen--;
		IF(ZEROFLAG){
			EDI >< xBst;
			EAX >< cCode;
			$stosd
			EAX >< cCode;
			EDI >< xBst;
			cLen = 32;
		}
	}
}

//-------------------------------------------------------------
dword	fastcall Compress(ESI,EDI,ECX)
dword	i,n;
dword xDst;
{
	xDst=EDI;
	EAX=ECX;
	$stosd
	xBst=EDI;
	xLim=ESI+ECX;
	cLen = 32;
	InitTree();
	xPos = ESI;
	cChar = 0;
	AddNode();
	MatchLen = MatchPos = 0;
	XcInit();
	do{
		IF (MatchLen > LenSize) MatchLen = LenSize;
		IF (MatchLen < MinLen){
			StoreBits(0,1);
			EBX = xPos;
			AL = DSBYTE[EBX];
			StoreBits(EAX,8);
		}
		else{
			StoreBits(1,1);
			IF (MatchLen <= LenLim){
				StoreBits(0,1);
				StoreBits(MatchLen-MinLen,LenLow);
			}
			ELSE{
				StoreBits(1,1);
				StoreBits(MatchLen-LenLim,LenBit);
			}
			EBX = cChar-MatchPos;
			$or  EBX,EBX
			$jge NoAdd2
			EBX += WinLen;
NoAdd2:
			IF(EBX <= LvlLim1){
				StoreBits(0,1);
				StoreBits(EBX,LvlBit1);
			}
			ELSE{
				StoreBits(1,1);
				StoreBits(EBX-LvlLim1,WinBit);
			}
			n = MatchLen;
			FOR (i = 1; i < n; i++){
				MatchLen = 0;
				cChar++;
				IF (cChar >= WinLen) cChar -= WinLen;
				xPos++;
				DelNode();
				AddNode();
			}
		}
		MatchLen = 0;
		cChar++;
		IF (cChar >= WinLen) cChar -= WinLen;
		xPos++;
		DelNode();
		AddNode();
		XcCheck();
	} while (xPos<xLim);
	ECX = cLen;
	EAX = cCode;
	$shl EAX,CL
	EDI = xBst;
	$stosd
	return(EDI-xDst);
}

//-------------------------------------------------------------
:dword GetBits(dword Cnt)
{
	EBX=0;
	loop(Cnt){
		EBX<<=1;
		cCode<<=1;
		IF(CARRYFLAG)BL|=1;
		cLen--;
		IF(cLen==0){
			cLen=32;
			ESI><xBst;
			$lodsd
			cCode=EAX;
			xBst><ESI;
		}
  }
  return(EBX);
}

:dword	Uncompress(dword xSrc,xDst)		// просто так... потому как
{						// данный модуль может работать
	ESI=xSrc;					// отдельно от EPack'a,
	$lodsd					// только нужно закомментарить
	$push EAX					// все, что касается guixc.h--
	xLim=EAX+xDst;
	$lodsd
	cCode=EAX;
	xBst=ESI;
	cLen=32;
	EDI=xDst;
	do{
		IF(GetBits(1)==0){
			GetBits(8);
			$stosb
		}
		ELSE{
			IF(GetBits(1)==0)EAX=GetBits(LenLow)+MinLen;
			ELSE EAX=GetBits(LenBit)+LenLim;
			ECX><EAX;
			IF(GetBits(1)==0)GetBits(LvlBit1);
			ELSE GetBits(WinBit)+LvlLim1;
			EBX><EAX;
			ESI=EDI-EBX;
			$rep $movsb
		}
	}while(EDI<xLim);
	$pop EAX
	return(EAX);
}
