//************************************************************************
// PACK.C
// Modul zur Datenkomprimierung
// Idee und Implementierung tom ehlert '90
//
// Datenaufzeichnungsformat 
//
//	die Idee die dahintersteckt funktioniert wie folgt :
//  wenn der derzeitige String bereits gesendet worden ist, kodiere
//  ihn als kombination von index,laenge in die bereits empfangenen Daten
//  als 2-Bytekombination.
//
//	Das genaue Format ist wohl am besten durch UNPACK beschrieben, man 
//	macht dann auch keine Fehler in der Dokumentation.
//
//  Das Schema laeuft wie folgt :
//    
//    Das jeweils erste Byte decodiert 8 Gruppen. Bit 7 (0x80) decodiert
//	  die erste Gruppe,Bit 6 (0x40) die zweite usf. . Dabei gilt :
//    	Bit == 0 --> Gruppe besteht aus 1 Byte, dieses ist in den 
// 		   	Zielpuffer zu kopieren.
//    	Bit == 1 --> Gruppe besteht aus komprimierter Information,
//    		lese zwei Byte.
//    
//			index  = *pbuffer & SENDLEN  (0x3ff);
//			length = (*pbuffer >> 12) + MIN_LENGTH;			
//			if (length == 0x0f+MIN_LENGTH)
//				length  = pbuffer[2];			/* code is 3 bytes		  */
//    
//    		memcpy(outbuffer,outbuffer-index,length);
//    
//
// 
//************************************************************************

#include <stdio.h>
#include <setjmp.h>
#include "pack.h"
#include "huffman.h"


typedef unsigned char uchar;

#ifdef MSDOS
	#define ONLY_ON_80x86		/* this is ok	*/
#else
	#define ONLY_ON_80x86 this runs only on 80x86	// BYTE - ORDER !!
#endif
	

#define MIN_LENGTH		3		/* minimum to be compressed */
#define MAXCODELENGTH 0x0f
#define	IBSIZE   	  0xff
#define SENDLEN  	  0xfff		/* MUST BE 00001111111 */

# define CRBLEN  0x800

# define BUFFTAIL (SENDLEN+1)
# define BUFFOVER 0x100

#ifdef PACK

static uchar sendbuff[  CRBLEN+BUFFTAIL+BUFFOVER] = 		{0};
static int   offset1 [1+CRBLEN+BUFFTAIL+BUFFOVER] = {0xffff,0};
#define offset (offset1+1)


#define NVH 0xffff



//
// Die gewaehlte HASH-Function ist beliebig; sie returned einen
// Wert 0..HASH_SIZE. die vorgeschlagene bietet einen Kompromiss
// zwischen sizeof(last(HASH_SIZE)) und Geschwindigkeit, diese
// wird dadurch aber nicht wesentlich verringert
// 
#define HASH_SIZE (0x400+0x200+0x100) 	// (0xff<<2) + (0xff<<1) + 0xff) ?? 
#define HASH_VAL(ptr)	((ptr)[0] + ((ptr)[1]<<1) + ((ptr)[2] << 2))

static int    last[HASH_SIZE] = {0};

#define add_list( c, cptr) offset[cptr] = last[c],last[c] = (cptr)

/**********************************************************************
** output buffer waehrend des packens
*/
static uchar far *packptr = NULL;
static uchar far *packend = NULL;

static uchar far *ibuffer = NULL;
static unsigned short ilen = 0;
static jmp_buf not_packable = {0};

static uchar packmask    = 0;
static uchar packmaskbit = 0;
static uchar far *packmaskptr = NULL;

static int  tosend = 0;
static int 	gesendet = 0;

#define STATIC

STATIC void  new_mask(void);
STATIC void  send1(void);
STATIC void  send(int  len);
STATIC void  move_buffers(void);
STATIC int   xlen(void);
STATIC void  crunch(void);
STATIC int   rdf(unsigned char  *d,unsigned len);
extern int   _fastcall memid(uchar *s1,uchar *s2);

STATIC void new_mask(void)
{
	*packmaskptr = packmask;
	packmaskptr  = packptr++;
	packmaskbit = 0x80;
	packmask    = 0x00;
	if (packptr >= packend)
		longjmp(not_packable,1);
}

STATIC void move_buffers(void)
{
	register int *ip, loop,rd;

	memcpy(sendbuff,sendbuff+CRBLEN,BUFFTAIL+BUFFOVER);
	memcpy((uchar*)offset  ,(uchar*)(offset  +CRBLEN),
							sizeof(offset[0])*(BUFFTAIL+BUFFOVER));
	gesendet -= CRBLEN;
	tosend   -= CRBLEN;
	while ((rd = rdf(sendbuff+tosend,sizeof(sendbuff)-tosend)) > 0)
		tosend += rd;

	for (ip = last,loop = LENGTH(last);loop ;ip++,--loop)
		if (*ip >= 0)
			*ip -= CRBLEN;
	for (ip = offset,loop = BUFFTAIL+BUFFOVER;loop ;ip++,--loop)
		if (*ip >= 0)
			*ip -= CRBLEN;
}


STATIC void crunch(void)
{
	register int len,tbs,find_index;

	while ((tbs = tosend - gesendet) > 0)
		{

//**********************************************************************
// this part is essentially a subroutine, calculating len & find_index
//**********************************************************************
{
	register int sp;
	register int stop_ptr;
	register uchar *sbcheck;
	register short ccheck;
	register uchar *tbs = sendbuff + gesendet;
	register int loop;
	
	len = MIN_LENGTH-1;
	
	stop_ptr = gesendet - SENDLEN;
	if (stop_ptr < 0)
		stop_ptr = 0;

	ccheck = *(int*)(tbs+1);
	sbcheck = sendbuff+1;

	for (sp = last[HASH_VAL(tbs)];sp >= stop_ptr;sp = offset[sp])
		{
		if (*(short *)(sbcheck+sp) == ccheck) 
		    if ((loop = memid(sendbuff+sp,tbs)) > len)
				{
				len = loop;
				ccheck = *(short *)(tbs+len-1);
				sbcheck = sendbuff+len-1;

				find_index = gesendet - sp;
				if (len >= IBSIZE)
					break;
				}
		}
}

		len = min(len,255);
		len = min(len,tbs);



		if (len >= MIN_LENGTH)

//**********************************************************************
// another inlined subroutine :
// write len + index
//**********************************************************************

{
	register int index;register uchar *ibp;

	ONLY_ON_80x86;
		
	if (len < MAXCODELENGTH+MIN_LENGTH)
		{								// remember : findindex <= SENDLEN !!
		*((int far *)packptr)++ = find_index | ((len -MIN_LENGTH) << 12) ;
		}
	else
		{
		*((int far *)packptr)++ = find_index | (MAXCODELENGTH << 12) ;
		*packptr++ = (uchar)len;
		}

	packmask |= packmaskbit;
	if ((packmaskbit >>= 1) == 0)
		new_mask();

	ibp = sendbuff+gesendet-1,index=gesendet-1;
	gesendet += len;
	for ( ; len ;	ibp++,index++, --len)
		{
		register int hval = HASH_VAL(ibp);
		add_list(hval,index);
		}

}
		else 
//**********************************************************************
// another inlined subroutine :
// write literal character
//**********************************************************************

{
	register uchar *s = sendbuff+gesendet;	/* zu sendenden */
	register int hval;

    *packptr++ = *s++;
	if ((packmaskbit >>= 1) == 0)
		new_mask();
	
	hval = HASH_VAL(s-2);
	add_list(hval,gesendet-1);
	gesendet++;

	if (gesendet > CRBLEN +BUFFTAIL )
		move_buffers();
}

	if (gesendet > CRBLEN +BUFFTAIL )
		move_buffers();

		}
}


STATIC int rdf(uchar *d,unsigned len)
{
	len = min(len,ilen);
	memcpy(d,ibuffer,len);
	ilen -= len;
	ibuffer += len;
	return len;
}


int far _loadds pack(pbuffer,pbufferlen,inbuffer,blen)
uchar far *pbuffer;short pbufferlen;
uchar far *inbuffer;short blen;
{
	int huff_length;
	
	if (blen == 0 || setjmp(not_packable))
		return 0xffff;

	ilen = blen;
	ibuffer = inbuffer;
						/* wenn man die naechsten zwei zeilen vertauscht 	*/
						/* und -e eingeschaltet ist verhakelt sich MSC 6.00 */
	packend = pbuffer + pbufferlen - 25;
	packptr = pbuffer;

	if (packptr >= packend)
		longjmp(not_packable,1);

	packmaskptr  = packptr++;
	packmaskbit = 0x80;
	packmask    = 0x00;

	tosend = rdf(sendbuff,CRBLEN+BUFFTAIL+BUFFOVER);
	gesendet = 0;

	memset(last   ,NVH,sizeof(last));
	memset(offset1,NVH,min(sizeof(offset1) , blen));
	gesendet = 0;

	crunch();

	*packmaskptr = packmask;
	if (packmaskbit == 0x80)		// letztes newmask() zuviel
		packptr--;

	return packptr - pbuffer;
}
#endif

#ifdef UNPACKC

//********************************************************************* 
// this is the uncrunching part
// unpackc() is written in C
// unpack()  see UNPACK.ASM
// both routines work identical
//*********************************************************************

int far _loadds unpackc(outbuffer,pbuffer,packlen)
uchar far *outbuffer;
uchar far *pbuffer;register int packlen;
{
	register int uloop;
	register uchar packmask;
	int length;unsigned index;
	uchar far *sbuffer = outbuffer;

	for (uloop = 1;packlen > 0;packmask <<= 1)
		{
		if (--uloop == 0)
			{
			packmask    = *pbuffer++,packlen--;
			uloop = 8;
			}
		if ((packmask & 0x80) == 0)				/* this byte is literally */
			*outbuffer++ = *pbuffer++,packlen--;
		else
			{									/* next 2 bytes coded     */
			ONLY_ON_80x86;
			index = *(int far *)pbuffer;		/* 4 bit length			  */
			length = (index >> 12) + MIN_LENGTH;/* 12 bit offset		  */
			index &= SENDLEN;
			if (length == MAXCODELENGTH+MIN_LENGTH)	/* length > 18			  */
				{								/* use next byte for length*/
				length  = pbuffer[2];			/* code is 3 bytes		  */
				pbuffer += 3;
				packlen -= 3;
				}
			else {
				packlen -= 2;
				pbuffer += 2;
				}
									/* copy BYTEWISE with OVERWRITE */
			if (index == 1)			// memcpy will not work as it copies WORDS
				memset(outbuffer,*(outbuffer-1),length);
			else
				memcpy(outbuffer,outbuffer-index,length);

			outbuffer += length;
			}			
		}
	return outbuffer-sbuffer;
}

#endif

//***********************************************************************
// externe Assmblerroutinen :
//
// memidc(uchar *dest,uchar *src)
//							//   return number of equal bytes
//	{
// 		register uchar *dr = dest;
//	
// 		for (; *src++ == *dr ;dr++)
// 			;
// 		return dr-dest;
// 	}
//***********************************************************************
