/*-------------------------------------------------------------------------*\
|* File name:	SLC_UNCO.C						Revision date:	1996.09.23 *|
|* Revised by:	Ulf Ronald Andersson			Revision start:	1996.08.02 *|
|* Original by:	Steve Adam & Dan Ackerman								   *|
\*-------------------------------------------------------------------------*/

/*-------------------------------------------------------------------------*\
|* URAn revision history:												   *|
|* 1996.09.02:	Macros DECODEL, DECODEU & DECODES made to calc unsigned	   *|
|* 1996.09.02:	Blockmoves for decoded packets corrected				   *|
|* 1996.09.14:	Corrected some packing variables for signed calculations   *|
|* 1996.09.23:	Added some tcp header initialization                       *|
\*-------------------------------------------------------------------------*/

#include <stdio.h>      /* Included for NULL.  Might change that later...   */
#include <tos.h>
#include <string.h>
#include <stdlib.h>

#include "globdefs.h"
#include "cslip_hd.h"

extern uint16 calc_sum(char *, uint16, uint16);

/*-------------------------------------------------------------------------*\
|*			DECODEx macros for VJ decoding								   *|
\*-------------------------------------------------------------------------*/

#define DECODEL(f) \
{	if (*cp) \
		(f) += (uint32)*cp++; \
	else \
	{	(f) += (int32)((int16)(((uint8)cp[1] << 8) | (uint8)cp[2])); \
		cp += 3; \
	} \
}

#define DECODES(f) \
{	if (*cp) \
		(f) += (uint16)*cp++; \
	else \
	{	(f) += (uint16)(((uint8)cp[1] << 8) | (uint8)cp[2]); \
		cp += 3; \
	} \
}

#define DECODEU(f) \
{	if (*cp == 0) \
		(f) = (uint16)*cp++; \
	else \
	{	(f) = (uint16)(((uint8)cp[1] << 8) | (uint8)cp[2]); \
		cp += 3; \
	} \
}



/*-------------------------------------------------------------------------*\
|*			Function "slc_unco"											   *|
\*-------------------------------------------------------------------------*/

long
slc_uncompress (b, type, comp)
	GPKT *b;
	octet type;
	struct slcompress *comp;
{
	register	char	*cp;
	register	uint32	hlen;
	register	long	len;
	register	int		changes;
	TCP_HDR	*th;
	struct	cstate	*cs;
	IP_HDR	*ip;
	uint16	tmp_hlen;	/* used by the tcp header init patch */

/* URAn NB:	char *newbuf; this declaration is redundant with new block copy method */

	len = b->ip_len;

	switch (type) {

	case TYPE_UNCOMPRESSED_TCP:
		/*
		 *  Here we grab all the header info for later decompression
		 *  rstate stands for recieve state.
		 */

	    ip = (IP_HDR *)b->pip;

		if (len < 40 || ip->ptcl >= MAX_STATES)
			{
				goto bad;
			}
		/*
		 * clear out TYPE_UNCOMPRESSED_TCP
		 */

		ip->ver &= 0x4;
		comp->last_recv = ip->ptcl;
/* URAn NB:	cs = &comp->rstate[comp->last_recv];  BUG
 *		Pure_C complained about pointer conversion for the above,
 *		and it would result in an address of a pointer inside the
 * 		rstate array, rather than in one of the pointer values.
 */
		cs = comp->rstate[comp->last_recv];  /* URAn: corrected */
		comp->flags &= ~SLF_TOSS;
		ip->ptcl = IPPROTO_TCP;
		hlen = ip->ihl;
		hlen += ((TCP_HDR *)&((long *)ip)[hlen << 2])->ofst;
		hlen <<= 2;
		BCOPY(ip, &cs->cs_ip, hlen);
		cs->cs_ip.sum = 0;
		cs->cs_hlen = hlen;
		
		return (len);

	case TYPE_COMPRESSED_TCP:
		break;

	default:
		goto bad;
	}

	/* We've got a compressed packet. */
	/* INCR (sls_compressedin); */

	cp = b->fp;				/* dstart */ /* was fp experimenting now */
	changes = *cp++;

	if (changes & NEW_C) {
		/* Make sure the state index is in range, then grab the state.
		 * If we have a good state index, clear the 'discard' flag. */
		if (*cp >= MAX_STATES)
			{
				goto bad;
			}
		comp->flags &= ~SLF_TOSS;
		comp->last_recv = *cp++;
	} else {
		/* this packet has an implicit state index.  If we've
		 * had a line error since the last time we got an
		 * explicit state index, we have to toss the packet. */

		if (comp->flags & SLF_TOSS) {
			/*INCR (sls_tossed);*/
			return (0);
		}
	}

/* URAn NB:	cs = &comp->rstate[comp->last_recv];  BUG
 *		Pure_C complained about pointer conversion for the above,
 *		and it would result in an address of a pointer inside the
 * 		rstate array, rather than in one of the pointer values.
 */
	cs = comp->rstate[comp->last_recv];  /* URAn: corrected */
	hlen = cs->cs_ip.ihl << 2;
	th = (TCP_HDR *)&((unsigned char *)&cs->cs_ip)[hlen];
	th->sum = (*cp << 8) | cp[1];
	cp += 2;

	if (changes & TCP_PUSH_BIT)
		th->f_psh = 1;
	else
		th->f_psh = 0;

	switch (changes & SPECIALS_MASK) {

	case SPECIAL_I:
		{
			uint32 i = cs->cs_ip.len - cs->cs_hlen;
			th->ack = th->ack + i;
			th->seq = th->seq + i;
		}
		break;

	case SPECIAL_D:
		th->seq = th->seq + cs->cs_ip.len
				   - cs->cs_hlen;
		break;

	default:
		if (changes & NEW_U)
		{	th->f_urg = 1;
			DECODEU(th->urgent); /*DECODEU*/
		} else
			th->f_urg = 0;
		if (changes & NEW_W)
			DECODES (th->window);
		if (changes & NEW_A)
			DECODEL (th->ack);
		if (changes & NEW_S)
			DECODEL (th->seq);
		break;
	}
	if (changes & NEW_I)
	{	DECODES (cs->cs_ip.id);
	}
	else
		cs->cs_ip.id = cs->cs_ip.id++;


	/*
	 * At this point, cp points to the first byte of data in the
	 * packet.  If we're not aligned on a 4-byte boundary, copy the
	 * data down so the ip & tcp headers will be aligned.  Then back up
	 * cp by the tcp/ip header length to make room for the reconstructed
	 * header (we assume the packet we were handed has enough space to
	 * prepend 128 bytes of header).  Adjust the length to account for
	 * the new header & fill in the IP total length.
	 */

	len -= (cp - (char *)b->fp); 	/* was fp */

	if (len < 0)
		{
		/* we must have dropped some characters (crc should detect
		 * this but the old slip framing won't) */
		goto bad;
		}


	/* URAn addition 1996.09.23
	 * Here the packet is almost ready for unpacking the header,
	 * so this is a good place to add the tcp header initialization.
	 */

    tmp_hlen = ((IP_HDR *)b->pip)->ihl * 4;  /* Number of octets in ip header    */
    b->mp = &b->pip[tmp_hlen];
    b->mlen = b->ip_len - tmp_hlen;
    tmp_hlen = th->ofst * 4;         /* Calc header len  */
    b->data = &b->mp[tmp_hlen];     /* set generic data pointer to first char   */
    b->dlen = b->mlen - tmp_hlen;   /* after TCP header, and set the data size  */


	/* OK it doesn't like adjusting the beginning of the packet 
		which is what we seem to be doing.  So lets try making space
		building it there and moving it back - worth a try */

/* URAn NB: I will use 'in-situ' transfers instead (memcpy supports overlap handling) */
/*          the extra buffer is thus not used any more, and I added some more tests   */
/*          for 'impossible' cases just to be safe (same as in asm version).          */

	if (cs->cs_hlen <= 0)  goto bad;
	if (cs->cs_hlen > 128) goto bad;

	len += cs->cs_hlen;
	cs->cs_ip.len = (int16)len;

/*	We must now move the packet data up in 'b' RAM to allow for the new header size
 *	so this move must start at the end and work downwards in RAM to handle overlap
 *	(memcpy supports this).  Then we must move the new cstate header into the room made.
 *	The data needed for both moves will be read before moving any, for overlap safety.
 *  (This may actually be unnecessary, but certainly can't do any harm.)
 */
	{	uint32	new_hlen = cs->cs_hlen;

		BCOPY((char *)(b->data), ((char *) b)+new_hlen, b->dlen);
		BCOPY((char *)&cs->cs_ip, (char *) b, new_hlen);
    }

	/* recompute the ip header checksum */
	((IP_HDR *)b)->sum = 0; 		/* Zero sum before new calculation  */

    ((IP_HDR *)b)->sum = calc_sum(b->pip, 0, (int16)hlen>>1);
    /*((IP_HDR *)cp)->sum = calc_sum(b->pip, 0, hlen);*/

	return (len);

bad:
	comp->flags |= SLF_TOSS;
	/*INCR (sls_errorin);*/
	return (0);
}

/*-------------------------------------------------------------------------*\
|*			Function "VJ_deco"											   *|
\*-------------------------------------------------------------------------*/

uint16
VJ_deco(cpp)
	void	**cpp;
{	register	octet	*cp = *cpp;
    register	uint16	retval;
	DECODEU(retval);
	*cpp = cp;
	return(retval);
}
/*-------------------------------------------------------------------------*\
|* End of file:	SLC_UNCO.C												   *|
\*-------------------------------------------------------------------------*/
