/*++

Copyright (C) 1994-2001 Microsoft Corporation

Module Name:

    MRCICODE.CPP

Abstract:

    MRCI 1 & MRCI 2 maxcompress and decompress functions

History:

--*/

#include "precomp.h"
#include "mrcicode.h"                   /* prototype verification */

#ifndef NDEBUG
#define NDEBUG                          /* turn off assertions */
#endif
#include <assert.h>                     /* use NDEBUG to inhibit */


#define hash(w)         ((w) & (CHASH - 1))
                                        /* Simply toss the high-order bits */
#define word(p)         ((p)[0] + (((p)[1]) << 8))
                                        /* Return word at location */

#define BITMASK(x)      ((1 << x) - 1)  /* returns lower 'x' bits set */

#define MINDISPSMALL    (0)             /* Minimum small displacement */
#define MINDISPMED      (MAXDISPSMALL + 1)
                                        /* Minimum medium displacement */
#define MINDISPBIG      (MAXDISPMED + 1)/* Minimum big displacement */

#define DISPMAX         (MAXDISPBIG - 1)/* MAXDISPBIG is our end marker */

#define MINMATCH1       (2)             /* Minimum match length for MRCI1 */
#define MINMATCH2       (3)             /* Minimum match length for MRCI2 */
#define MAXMATCH        (512)           /* Maximum match length */

#define EOB             (0)             /* length used to mean end of block */

#define SECTOR          (512)           /* blocking factor */

#define SIG_SIZE        (4)             /* # of block type prefix bytes */


/*
 *  (compress) Reset the hash tables between blocks.
 */

void CBaseMrciCompression::inithash(void)
{
    unsigned *entry;
    int i;

    entry = ahash;
    i = CHASH;

    do
    {
        *entry++ = (unsigned) -1;       /* Mark all entries as empty */
    } while (--i);
}


/*
 *  (compress) Add a character to compressed output buffer.
 */

void CBaseMrciCompression::charbuf(unsigned c)
{
    if (cCompressed-- == 0)             /* make sure there's room */
    {
        throw 1;						/* Data expanding! */
    }

    *pCompressed++ = (unsigned char) c; /* Put character into buffer */
}


/*
 *  (compress) Write n bits to the compressed bitstream.
 */

void CBaseMrciCompression::putbits(unsigned ab,unsigned cbits)
{
    do                                  /* Loop to emit bits */
    {
        if (cbits > cbitsleft)          /* if not enough space */
        {
            cbits -= cbitsleft;         /* doing partial */

            abits |= (ab << (8 - cbitsleft));
                                        /* Put bits in output buffer */

            ab >>= cbitsleft;           /* clip sent bits */

            charbuf(abits);             /* Emit the buffer */
            cbitsleft = 8;              /* Reset buffer count */
            abits = 0;                  /* Reset buffer */
        }
        else                            /* can do all in one pass */
        {
            abits |= ((ab & BITMASK(cbits)) << (8 - cbitsleft));
                                        /* Put bits in output buffer */

            cbitsleft -= cbits;         /* used up some buffer */

            if (cbitsleft == 0)         /* If buffer full */
            {
                charbuf(abits);         /* Emit the buffer */
                cbitsleft = 8;          /* Reset buffer count */
                abits = 0;              /* Reset buffer */
            }

            break;                      /* we've done all cbits */
        }
    } while (cbits);                    /* repeat until done */
}


/*
 *  (compress) Encode a length into the compressed stream.
 */

void CBaseMrciCompression::outlength(unsigned cb)
{
    unsigned alogbits, clogbits;
    unsigned avaluebits, cvaluebits;

    assert(cb >= 2);                    /* Length must be at least two */
    assert(cb <= MAXMATCH);

    if (cb <= 2)
    {
        alogbits = 1;
        clogbits = 1;
        cvaluebits = 0;
    }
    else if (cb <= 4)
    {
        alogbits = 1 << 1;
        clogbits = 2;
        avaluebits = cb - 3;
        cvaluebits = 1;
    }
    else if (cb <= 8)
    {
        alogbits = 1 << 2;
        clogbits = 3;
        avaluebits = cb - 5;
        cvaluebits = 2;
    }
    else if (cb <= 16)
    {
        alogbits = 1 << 3;
        clogbits = 4;
        avaluebits = cb - 9;
        cvaluebits = 3;
    }
    else if (cb <= 32)
    {
        alogbits = 1 << 4;
        clogbits = 5;
        avaluebits = cb - 17;
        cvaluebits = 4;
    }
    else if (cb <= 64)
    {
        alogbits = 1 << 5;
        clogbits = 6;
        avaluebits = cb - 33;
        cvaluebits = 5;
    }
    else if (cb <= 128)
    {
        alogbits = 1 << 6;
        clogbits = 7;
        avaluebits = cb - 65;
        cvaluebits = 6;
    }
    else if (cb <= 256)
    {
        alogbits = 1 << 7;
        clogbits = 8;
        avaluebits = cb - 129;
        cvaluebits = 7;
    }
    else /* (cb <= 512) */
    {
        alogbits = 1 << 8;
        clogbits = 9;
        avaluebits = cb - 257;
        cvaluebits = 8;
    }

    putbits(alogbits,clogbits);

    if (cvaluebits)
    {
        putbits(avaluebits,cvaluebits);
    }
}


/*
 *  (MRCI1 compress) Encode a literal into the compressed stream.
 */

void CBaseMrciCompression::mrci1outsingle(unsigned ch)
{
    ch = (ch << 2) | ((ch & 0x80) ? 1 : 2);

    putbits(ch,9);
}


/*
 *  (MRCI2 compress) Encode a literal into the compressed stream.
 */

void CBaseMrciCompression::mrci2outsingle(unsigned ch)
{
    if (ch & 0x80)
    {
        putbits((ch << 2) | 3,9);
    }
    else
    {
        putbits(ch << 1,8);
    }
}


/*
 *  (MRCI1 compress) Encode a match into the compressed stream.
 */

void CBaseMrciCompression::mrci1outstring(unsigned disp,unsigned cb)
{
    assert(((cb >= MINMATCH1) && (disp != 0) && (disp < MAXDISPBIG)) ||
            ((cb == EOB) && (disp == MAXDISPBIG)));

    if (disp <= MAXDISPSMALL)
    {
        putbits(((disp - MINDISPSMALL) << 2),LOGDISPSMALL + 2);
                                        /* Put small displacement */
    }
    else if (disp <= MAXDISPMED)
    {
        putbits(((disp - MINDISPMED) << 3) | 3,LOGDISPMED + 3);
                                        /* Put medium displacement */
    }
    else
    {
        putbits(((disp - MINDISPBIG) << 3) | 7,LOGDISPBIG + 3);
                                        /* Put big displacement */
    }

    if (cb != EOB)                      /* If not an end marker */
    {
        outlength(cb);                  /* Emit the match length */
    }
}


/*
 *  (MRCI2 compress) Encode a match into the compressed stream.
 */

void CBaseMrciCompression::mrci2outstring(unsigned disp,unsigned cb)
{
    assert(((cb >= MINMATCH2) && (disp != 0) && (disp < MAXDISPBIG)) ||
            ((cb == EOB) && (disp == MAXDISPBIG)));

    if (disp <= MAXDISPSMALL)
    {
        putbits(((disp - MINDISPSMALL) << 3) | 1,LOGDISPSMALL + 3);
                                        /* Put small displacement */
    }
    else if (disp <= MAXDISPMED)
    {
        putbits(((disp - MINDISPMED) << 4) | 5,LOGDISPMED + 4);
                                        /* Put medium displacement */
    }
    else
    {
        putbits(((disp - MINDISPBIG) << 4) | 13,LOGDISPBIG + 4);
                                        /* Put big displacement */
    }

    if (cb != EOB)                      /* If not an end marker */
    {
        outlength(cb - 1);              /* Emit the match length */
    }
}


/*
 *  (MRCI1) MaxCompress
 */

unsigned CBaseMrciCompression::Mrci1MaxCompress(unsigned char *pchbase,unsigned cchunc,
        unsigned char *pchcmpBase,unsigned cchcmpMax)
{
    unsigned cchbest;                   /* Length of best match */
    unsigned cchmatch;                  /* Length of this match */
    unsigned ibest;                     /* Position of best match */
    unsigned icur;                      /* Current position */
    unsigned ihash;                     /* Hash table index */
    unsigned ilink;                     /* Link index */
    unsigned char *pch;             /* Char pointer */
    unsigned char *pch2;            /* Char pointer */
    unsigned char *pchend;          /* End of input (-> last valid) */
    unsigned cch;                       /* per-pass limit */

    cbitsleft = 8;                      /* Buffer is empty */
    abits = 0;
    pCompressed = pchcmpBase;           /* Initialize pointer */

    if (cchunc < cchcmpMax)
    {
        cCompressed = cchunc;           /* limit to source size */
    }
    else
    {
        cCompressed = cchcmpMax;        /* limit to max size offered */
    }

    if (cCompressed < SIG_SIZE)
    {
        return((unsigned) -1);
    }

    *pCompressed++ = 'D';
    *pCompressed++ = 'S';
    *pCompressed++ = '\x00';
    *pCompressed++ = '\x01';

    cCompressed -= SIG_SIZE;

    pch = pchbase;                      /* Initialize */

    if (cchunc-- == 0)
    {
        return(0);                      /* Do nothing to empty buffer */
    }

    inithash();                         /* Initialize tables */

	try
	{
		cchbest = 0;                        /* no match yet */
		icur = 0;                           /* Initialize */

		for (cch = SECTOR - 1; cch <= (cchunc + SECTOR - 1); cch += SECTOR)
		{
			assert(cchbest == 0);           /* must always start with no match */

			if (cch > cchunc)
			{
				cch = cchunc;               /* limit to exact req count */
			}

			pchend = &pchbase[cch];         /* Remember end of buffer */

			while (icur < cch)              /* While at least two chars left */
			{
				/* update hash tables for this character */

				ihash = hash(word(&pchbase[icur]));
											/* Get hash index */
				ilink = ahash[ihash];       /* Get link index */
				ahash[ihash] = icur;        /* Remember position */
				alink[icur % MAXDISPBIG] = ilink;
											/* Chain on rest of list */

				/* walk hash chain looking for matches */

				while (ilink < icur && icur - ilink <= DISPMAX)
				{                           /* While link is valid and in range */
					pch = &pchbase[icur];   /* Point at first byte */
					pch2 = &pchbase[ilink]; /* Point at first byte */

					if (pch[cchbest] == pch2[cchbest] && word(pch) == word(pch2))
					{                       /* If we have a possible best match */
						pch += 2;           /* Skip first pair */
						pch2 += 2;          /* Skip first pair */

						while (pch <= pchend)  /* Loop to find end of match */
						{
							if (*pch != *pch2++)
							{
								break;      /* Break if mismatch */
							}
							pch++;          /* Skip matching character */
						}

						if ((cchmatch = (unsigned)(pch - pchbase) - icur) > cchbest)
						{                   /* If new best match */
							cchbest = cchmatch;  /* Remember length */
							ibest = ilink;  /* Remember position */

							assert((pch-1) <= pchend);

							if (pch > pchend)
							{
								break;      /* Break if we can't do any better */
							}
						}
					}

					assert((alink[ilink % MAXDISPBIG] == (unsigned) -1) ||
							(alink[ilink % MAXDISPBIG] < ilink));

					ilink = alink[ilink % MAXDISPBIG];
											/* Get next link */
				}   /* until end of hash chain reached */

				if (cchbest >= MINMATCH1)   /* If we have a string match */
				{
					mrci1outstring(icur - ibest,cchbest);
											/* Describe matching string */
	#ifdef VXD
					if (icur + cchbest >= cch )  /* If end of sector reached */
	#else
					if (icur + cchbest >= cchunc)  /* If end of buffer reached */
	#endif
					{
						icur += cchbest;    /* Advance the index */
						cchbest = 0;        /* reset for next match */
						break;              /* Done if buffer exhausted */
					}

					icur++;                 /* Skip to first unhashed pair */
	#ifdef VXD
					/* avoid re-seeding all of a big match */

					if (cchbest > MAXDISPSMALL)
					{                       /* If big match */
						icur += cchbest - MAXDISPSMALL - 1;
											/* Skip ahead */
						cchbest = MAXDISPSMALL + 1;
											/* Use shorter length */
					}
	#endif
					/* update hash tables for each add't char in string */

					ibest = icur % MAXDISPBIG;  /* Get current link table index */

					while (--cchbest != 0)  /* Loop to reseed link table */
					{
						ihash = hash(word(&pchbase[icur]));
											/* Get hash index */
						ilink = ahash[ihash];  /* Get link index */
						ahash[ihash] = icur++;  /* Remember position */
						alink[ibest] = ilink;  /* Chain on rest of list */

						if (++ibest < MAXDISPBIG)
						{
							continue;       /* Loop if we haven't wrapped yet */
						}

						ibest = 0;          /* Wrap to zero */
					}

					assert(cchbest == 0);   /* Counter must be 0 */
				}
				else
				{
					mrci1outsingle(pchbase[icur++]);
											/* Else output single character */
					cchbest = 0;            /* Reset counter */
				}
			}

			assert(icur == cch || icur == cch + 1);
											/* Must be at or past last character */
			if (icur == cch)
			{
	#ifndef VXD
				ihash = hash(word(&pchbase[icur]));
											/* Get hash index */
				ilink = ahash[ihash];       /* Get link index */
				ahash[ihash] = icur;        /* Remember position */
				alink[icur % MAXDISPBIG] = ilink;
											/* Chain on rest of list */
	#endif
				mrci1outsingle(pchbase[icur++]);  /* Output last character */
			}

			assert(icur == cch + 1);        /* Must be past last character */

			mrci1outstring(MAXDISPBIG,EOB);  /* Put out an end marker */
		}

		if (cbitsleft != 8)
		{
			charbuf(abits);                 /* Flush bit buffer */
		}

		if ((unsigned) (pCompressed - pchcmpBase) > cchunc)
		{
			return((unsigned) -1);          /* data expanded or not smaller */
		}
	}
	catch(int i)
	{
		return -1;
	}

    return(pCompressed - pchcmpBase);   /* Return compressed size */
}


/*
 *  (MRCI2) MaxCompress
 */

unsigned CBaseMrciCompression::Mrci2MaxCompress(unsigned char *pchbase,unsigned cchunc,
        unsigned char *pchcmpBase,unsigned cchcmpMax)
{
    unsigned cchbest;                   /* Length of best match */
    unsigned cchmatch;                  /* Length of this match */
    unsigned ibest;                     /* Position of best match */
    unsigned icur;                      /* Current position */
    unsigned ihash;                     /* Hash table index */
    unsigned ilink;                     /* Link index */
    unsigned char *pch;             /* Char pointer */
    unsigned char *pch2;            /* Char pointer */
    unsigned char *pchend;          /* End of input (-> last valid) */
    unsigned cch;                       /* per-pass limit */

    cbitsleft = 8;                      /* Buffer is empty */
    abits = 0;
    pCompressed = pchcmpBase;           /* Initialize pointer */

    if (cchunc < cchcmpMax)
    {
        cCompressed = cchunc;           /* limit to source size */
    }
    else
    {
        cCompressed = cchcmpMax;        /* limit to max size offered */
    }

    if (cCompressed < SIG_SIZE)
    {
        return((unsigned) -1);
    }

    *pCompressed++ = 'J';
    *pCompressed++ = 'M';
    *pCompressed++ = '\x00';
    *pCompressed++ = '\x01';

    cCompressed -= SIG_SIZE;

    pch = pchbase;                      /* Initialize */

    if (cchunc-- == 0)
    {
        return(0);                      /* Do nothing to empty buffer */
    }

    inithash();                         /* Initialize tables */

    try
	{
		cchbest = 0;                        /* no match yet */
		icur = 0;                           /* Initialize */

		for (cch = SECTOR - 1; cch <= (cchunc + SECTOR - 1); cch += SECTOR)
		{
			assert(cchbest == 0);           /* must always start with no match */

			if (cch > cchunc)
			{
				cch = cchunc;               /* limit to exact req count */
			}

			pchend = &pchbase[cch];         /* Remember end of buffer */

			while (icur < cch)              /* While at least two chars left */
			{
				/* update hash tables for this character */

				ihash = hash(word(&pchbase[icur]));
											/* Get hash index */
				ilink = ahash[ihash];       /* Get link index */
				ahash[ihash] = icur;        /* Remember position */
				alink[icur % MAXDISPBIG] = ilink;
											/* Chain on rest of list */

				/* walk hash chain looking for matches */

				while (ilink < icur && icur - ilink <= DISPMAX)
				{                           /* While link is valid and in range */
					pch = &pchbase[icur];   /* Point at first byte */
					pch2 = &pchbase[ilink]; /* Point at first byte */

					if (pch[cchbest] == pch2[cchbest] && word(pch) == word(pch2))
					{                       /* If we have a possible best match */
						pch += 2;           /* Skip first pair */
						pch2 += 2;          /* Skip first pair */

						while (pch <= pchend)  /* Loop to find end of match */
						{
							if (*pch != *pch2++)
							{
								break;      /* Break if mismatch */
							}
							pch++;          /* Skip matching character */
						}

						if ((cchmatch = (unsigned)(pch - pchbase) - icur) > cchbest)
						{                   /* If new best match */
							cchbest = cchmatch;  /* Remember length */
							ibest = ilink;  /* Remember position */

							assert((pch-1) <= pchend);

							if (pch > pchend)
							{
								break;      /* Break if we can't do any better */
							}
						}
					}

					assert((alink[ilink % MAXDISPBIG] == (unsigned) -1) ||
							(alink[ilink % MAXDISPBIG] < ilink));

					ilink = alink[ilink % MAXDISPBIG];
											/* Get next link */
				}   /* until end of hash chain reached */

				if (cchbest >= MINMATCH2)   /* If we have a string match */
				{
					mrci2outstring(icur - ibest,cchbest);
											/* Describe matching string */
	#ifdef VXD
					if (icur + cchbest >= cch )  /* If end of sector reached */
	#else
					if (icur + cchbest >= cchunc)  /* If end of buffer reached */
	#endif
					{
						icur += cchbest;    /* Advance the index */
						cchbest = 0;        /* reset for next match */
						break;              /* Done if buffer exhausted */
					}

					icur++;                 /* Skip to first unhashed pair */
	#ifdef VXD
					/* avoid re-seeding all of a big match */

					if (cchbest > MAXDISPSMALL)
					{                       /* If big match */
						icur += cchbest - MAXDISPSMALL - 1;
											/* Skip ahead */
						cchbest = MAXDISPSMALL + 1;
											/* Use shorter length */
					}
	#endif
					/* update hash tables for each add't char in string */

					ibest = icur % MAXDISPBIG;  /* Get current link table index */

					while (--cchbest != 0)  /* Loop to reseed link table */
					{
						ihash = hash(word(&pchbase[icur]));
											/* Get hash index */
						ilink = ahash[ihash];  /* Get link index */
						ahash[ihash] = icur++;  /* Remember position */
						alink[ibest] = ilink;  /* Chain on rest of list */

						if (++ibest < MAXDISPBIG)
						{
							continue;       /* Loop if we haven't wrapped yet */
						}

						ibest = 0;          /* Wrap to zero */
					}

					assert(cchbest == 0);   /* Counter must be 0 */
				}
				else
				{
					mrci2outsingle(pchbase[icur++]);
											/* Else output single character */
					cchbest = 0;            /* Reset counter */
				}
			}

			assert(icur == cch || icur == cch + 1);
											/* Must be at or past last character */
			if (icur == cch)
			{
	#ifndef VXD
				ihash = hash(word(&pchbase[icur]));
											/* Get hash index */
				ilink = ahash[ihash];       /* Get link index */
				ahash[ihash] = icur;        /* Remember position */
				alink[icur % MAXDISPBIG] = ilink;
											/* Chain on rest of list */
	#endif
				mrci2outsingle(pchbase[icur++]);  /* Output last character */
			}

			assert(icur == cch + 1);        /* Must be past last character */

			mrci2outstring(MAXDISPBIG,EOB);  /* Put out an end marker */
		}

		if (cbitsleft != 8)
		{
			charbuf(abits);                 /* Flush bit buffer */
		}

		if ((unsigned) (pCompressed - pchcmpBase) > cchunc)
		{
			return((unsigned) -1);          /* data expanded or not smaller */
		}
	}
	catch(int i)
	{
		return -1;
	}

    return(pCompressed - pchcmpBase);   /* Return compressed size */
}


/*
 *  (decompress) Get a single bit from the compressed input stream.
 */

unsigned CBaseMrciCompression::getbit(void)
{
    unsigned bit;                       /* Bit */

    if (cbitsleft)                      /* If bits available */
    {
        cbitsleft--;                    /* Decrement bit count */

        bit = abits & 1;                /* Get a bit */

        abits >>= 1;                    /* Remove it */
    }
    else                                /* no bits available */
    {
        if (cCompressed-- == 0)         /* If buffer empty */
        {
            throw 1;					/* input overrun */
        }

        cbitsleft = 7;                  /* Reset count */

        abits = *pCompressed++;         /* Get a byte */

        bit = abits & 1;                /* Get a bit */

        abits >>= 1;                    /* Remove it */
    }

    return(bit);                        /* Return the bit */
}


/*
 *  (decompress) Get multiple bits from the compressed input stream.
 */

unsigned CBaseMrciCompression::getbits(unsigned cbits)
{
    unsigned bits;                      /* Bits to return */
    unsigned cbitsdone;                 /* number of bits added so far */
    unsigned cbitsneeded;               /* number of bits still needed */

    if (cbits <= cbitsleft)             /* If we have enough bits */
    {
        bits = abits;                   /* Get the bits */
        cbitsleft -= cbits;             /* Decrement bit count */
        abits >>= cbits;                /* Remove used bits */
    }
    else                                /* If we'll need to read more bits */
    {
        bits = 0;                       /* No bits set yet */
        cbitsdone = 0;                  /* no bits added yet */
        cbitsneeded = cbits;            /* bits needed */

        do
        {
            if (cbitsleft == 0)         /* If no bits ready */
            {
                if (cCompressed-- == 0) /* count down used */
                {
                    throw 1;			/* if input overrun */
                }

                cbitsleft = 8;          /* Reset count */

                abits = *pCompressed++;  /* Get 8 new bits */
            }

            bits |= (abits << cbitsdone);  /* copy bits for output */

            if (cbitsleft >= cbitsneeded)  /* if enough now */
            {
                cbitsleft -= cbitsneeded;  /* reduce bits remaining available */
                abits >>= cbitsneeded;  /* discard used bits */
                break;                  /* got them */
            }
            else                        /* if not enough yet */
            {
                cbitsneeded -= cbitsleft;  /* reduce bits still needed */
                cbitsdone += cbitsleft;  /* increase shift for future bits */
                cbitsleft = 0;          /* reduce bits remaining available */
            }
        } while (cbitsneeded);          /* go back if more bits needed */
    }

    return(bits & BITMASK(cbits));      /* Return the bits */
}


/*
 *  (decompress) Expand a match.
 *
 *  Note: source overwrite is required (so we can't memcpy or memmove)
 */

void CBaseMrciCompression::expandstring(unsigned char **ppchout,unsigned disp,
        unsigned cb)
{
    unsigned char *source;
    unsigned char *target;

    assert(cb != 0);

    target = *ppchout;                  /* where the bytes go */
    source = target - disp;             /* where the bytes come from */

    *ppchout += cb;                     /* Update the output pointer */

    while (cb--)
    {
        *target++ = *source++;
    }
}


/*
 *  (MRCI1) Decompress
 */

unsigned CBaseMrciCompression::Mrci1Decompress(unsigned char *pchin,unsigned cchin,
        unsigned char *pchdecBase,unsigned cchdecMax)
{
    unsigned b;                         /* A byte */
    unsigned length;                    /* Length of match */
    unsigned disp;                      /* Displacement */
    unsigned char *pchout;          /* Output buffer pointer */

    abits = 0;                          /* Bit buffer is empty */
    cbitsleft = 0;                      /* No bits read yet */
    pCompressed = pchin;                /* setup source pointer */
    cCompressed = cchin;                /* setup source counter */

    if ((cCompressed <= SIG_SIZE) ||    /* must have a signature */
            (*pCompressed++ != 'D') || (*pCompressed++ != 'S'))
    {
        return((unsigned) -1);          /* Data corrupted */
    }

    pCompressed += 2;                   /* ignore flags */
    cCompressed -= SIG_SIZE;

    pchout = pchdecBase;                /* Point at output buffer */

    try
	{
		for (;;)
		{
			b = getbits(2);                 /* get two bits */

			if (b == 1)                     /* If single byte 128..255 */
			{                               /* Get the rest of byte */
				*pchout++ = (unsigned char) (getbits(7) | 0x80);
				continue;                   /* Next token */
			}

			if (b == 2)                     /* If single byte 0..127 */
			{                               /* Get the rest of byte */
				*pchout++ = (unsigned char) getbits(7);
				continue;                   /* Next token */
			}

			if (b == 0)
			{
				disp = getbits(6) + MINDISPSMALL;
			}
			else  /* b == 3 */
			{
				if (getbit() == 0)
				{
					disp = getbits(8) + MINDISPMED;
				}
				else
				{
					disp = getbits(12) + MINDISPBIG;
				}
			}

			if (disp == MAXDISPBIG)
			{
				if ((unsigned) (pchout - pchdecBase) >= cchdecMax)
				{
					break;                  /* End marker found */
				}
				else
				{
					continue;               /* End sector found */
				}
			}

			length = 0;                     /* Initialize */

			while (getbit() == 0)
			{
				length++;                   /* Count the leading zeroes */
			}

			assert(b <= 15);                /* Cannot be too big */

			if (length)
			{
				length = getbits(length) + (1 << length) + 1;
			}
			else
			{
				length = 2;
			}

			expandstring(&pchout,disp,length);  /* Copy the match */
		}
	}
	catch(int i)
	{
		return -1;
	}

    return((pchout - pchdecBase));      /* Return decompressed size */
}


/*
 *  (MRCI2) Decompress
 */

unsigned CBaseMrciCompression::Mrci2Decompress(unsigned char *pchin,unsigned cchin,
        unsigned char *pchdecBase,unsigned cchdecMax)
{
    unsigned length;                    /* Length of match */
    unsigned disp;                      /* Displacement */
    unsigned char *pchout;          /* Output buffer pointer */

    abits = 0;                          /* Bit buffer is empty */
    cbitsleft = 0;                      /* No bits read yet */
    pCompressed = pchin;                /* setup source pointer */
    cCompressed = cchin;                /* setup source counter */

    if ((cCompressed <= SIG_SIZE) ||    /* must have a signature */
            (*pCompressed++ != 'J') || (*pCompressed++ != 'M'))
    {
        return((unsigned) -1);          /* Data corrupted */
    }

    pCompressed += 2;                   /* ignore flags */
    cCompressed -= SIG_SIZE;

    pchout = pchdecBase;                /* Point at output buffer */

    try
	{
		for (;;)
		{
			if (getbit() == 0)              /* literal 00..7F */
			{
				*pchout++ = (unsigned char) getbits(7);

				continue;                   /* Next token */
			}

			if (getbit() == 1)              /* literal 80..FF */
			{
				*pchout++ = (unsigned char)(getbits(7) | 0x80);

				continue;                   /* Next token */
			}

			if (getbit() == 0)
			{
				disp = getbits(6) + MINDISPSMALL;
			}
			else
			{
				if (getbit() == 0)
				{
					disp = getbits(8) + MINDISPMED;
				}
				else
				{
					disp = getbits(12) + MINDISPBIG;
				}
			}

			if (disp == MAXDISPBIG)
			{
				if ((unsigned) (pchout - pchdecBase) >= cchdecMax)
				{
					break;                  /* End marker found */
				}
				else
				{
					continue;               /* End sector found */
				}
			}

			length = 0;                     /* Initialize */

			while (getbit() == 0)
			{
				length++;                   /* Count the leading zeroes */
			}

			assert(length <= 15);           /* Cannot be too big */

			if (length)
			{
				length = getbits(length) + (1 << length) + 1;
			}
			else
			{
				length = 2;
			}

			expandstring(&pchout,disp,length + 1);  /* Copy the match */
		}
	}
	catch(int i)
	{
		return -1;
	}

    return((pchout - pchdecBase));      /* Return decompressed size */
}