// XCompress by Deviator//HAZARD
// Modified LZSS family compressor with additional RLE

?define	MinRLE	WinBit+LenBit+1+9/8+1

//-------------------------------------------------------------
dword	fastcall CompressRLE(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 NoAdd2RLE
			EBX += WinLen;
NoAdd2RLE:
			IF (EBX <= LvlLim1){
				StoreBits(0,1);
				StoreBits(EBX,LvlBit1);
			}
			ELSE{
				StoreBits(1,1);
				EAX = EBX-LvlLim1;
				StoreBits(EAX,WinBit);
			}
			n = MatchLen;
			FOR (i = 1; i < n; i++){
				MatchLen = 0;
				cChar++;
				IF (cChar >= WinLen) cChar -= WinLen;
				xPos++;
				DelNode();
				AddNode();
			}
		}
		EBX = xPos;
		if (DSBYTE[EBX] == DSBYTE[EBX+1]){
			ECX = 1;
			EBX++;
			WHILE (DSBYTE[EBX] == DSBYTE[EBX+1]) && (EBX<xLim){
				ECX++;
				EBX++;
			}
			if (ECX-1 > MinRLE){
				$push ECX
				StoreBits(0,1);
				AL = DSBYTE[EBX];
				StoreBits(EAX,8);
				DelNode();
				cChar++;
				IF (cChar >= WinLen) cChar -= WinLen;
				xPos++;
				$pop  ECX
				ECX--;
				i = n = ECX;
				WHILE (i > LenSize){
					StoreBits(3,2);
					StoreBits(LenSize-LenLim,LenBit);
					StoreBits(0,1);
					StoreBits(1,LvlBit1);
					i -= LenSize;
				}
				IF (i > MinRLE){
					StoreBits(1,1);
					IF (i > LenLim){
						StoreBits(1,1);
						StoreBits(i-LenLim,LenBit);
					}
					ELSE{
						StoreBits(0,1);
						StoreBits(i-MinLen,LenLow);
					}
					StoreBits(0,1);
					StoreBits(1,LvlBit1);
				}
				ELSE n -= i;
				LOOPNZ(n){
					DelNode();
					cChar++;
					IF (cChar >= WinLen) cChar -= WinLen;
					xPos++;
				}
			}
		}
		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);
}
//-------------------------------------------------------------
