/*
 *		Language Independent Library
 *  Copyright 2001-2011 Green Hills Software,Inc.
 *
 *  This program is the property of Green Hills Software, Inc,
 *  its contents are proprietary information and no part of it
 *  is to be disclosed to anyone except employees of Green Hills
 *  Software, Inc., or as agreed in writing signed by the President
 *  of Green Hills Software, Inc.
*/

/*----------------------------------------------------------------------*/
/* ind_uzip.c: ROM decompression code.					*/
/*									*/
/* CALLED FROM:  __ghs_ind_crt0		  				*/
/* ENTRY POINT:  __ghs_decompress 					*/
/*									*/
/* Decompression code based upon LZSS by Haruhiko Okumura		*/
/*                                                                      */
/* This code was modified by GHS to not require a temporary ring buffer */
/*----------------------------------------------------------------------*/

#include "ind_startup.h"

/* Data for the decompression algorithm used for CROM */
#define DECODE_N 	  4096     /* size of ring buffer */
#define DECODE_F 	  18       /* upper limit for match_length */
#define DECODE_THRESHOLD  2        /* encode string into position and length
				    if match_length is greater than this */

/*----------------------------------------------------------------------*/
/*									*/
/*	Decompress and Copy from CROM to RAM				*/
/*									*/
/*----------------------------------------------------------------------*/
syze_t __ghs_decompress(void *dst, const void *src, syze_t n)
{
    int r,c;
    unsigned int flags;

    const unsigned char *s = (const unsigned char *)src;
    unsigned char *t = (unsigned char *)dst;

    const unsigned char *s_stop = s + n;
    unsigned char *t_start = t;

    /* Decompress and copy */
    r = DECODE_N - DECODE_F;
    flags = 0;
    while (s < s_stop) {
	if (((flags >>= 1) & 0x100) == 0) {
	    /* Flag byte describing the next sequence of 8 literals or
	       dictionary references. */
	    c = *s++;

	    /* Use the higher byte in flags to count to 8.*/
	    flags = c | 0xff00;
	}
	if (flags & 1) {
	    /* Literal (byte): copy byte directly. */
	    c = *s++;
	    *t++ = (char)c;
	    r = ((r + 1) & (DECODE_N - 1));
	} else {
	    /* Pattern Reference: 2 bytes consisting of:
	       12 bits of offset (0..4095)
	       4 bits of length, representing lengths 3..18 */
	    unsigned char* pattern;	    
	    int patoff, patlen, i;

	    patoff = *s++;
	    patlen = *s++;
	    patoff |= ((patlen & 0xf0) << 4);
	    patlen = (patlen & 0x0f) + DECODE_THRESHOLD + 1;

	    pattern = t - (r - patoff);
	    if (r <= patoff)
		pattern -= DECODE_N;

	    /* The pattern >= t check is only necessary for the case where
	     * we are unpacking within DECODE_F bytes of 0 and where addresses
	     * are considered unsigned.  In this case, pattern could be
	     * a large positive value.
	     */
	    for (i=0; (pattern < t_start || pattern >= t) && (i < patlen);
			i++) {
		*t++ = 0;
		pattern++;
	    }

	    /* Copy the pattern. */
	    for ( ; i < patlen; i++) {
		*t++ = *pattern++;
	    }
	    r = ((r + patlen) & (DECODE_N - 1));	    
	}
    }
    /* Return the number of bytes written. */
    return t - (unsigned char *)dst;
}
