/* depack.c */

/* Copyright (c) Marc Espie, 1995
 * See accompanying file README for distribution information
 *
 * Modified by Claudio Matsuoka for use in xmp.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdio.h>
#include <stdlib.h>
#ifdef __EMX__
#include <sys/types.h>
#endif
#include <sys/stat.h>
#include <unistd.h>
#include "xmpi.h"

#define val(p) ((p)[0]<<16 | (p)[1] << 8 | (p)[2])

static uint32 shift_in;
static uint32 counter;
static uint8 *source;


static uint32 get_bits (uint32 n)
{
    uint32 result = 0;
    int i;

    for (i = 0; i < n; i++) {
	if (counter == 0) {
	    counter = 8;
	    shift_in = *--source;
	}
	result = (result << 1) | (shift_in & 1);
	shift_in >>= 1;
	counter--;
    }
    return result;
}


static void
ppdepack (uint8 *packed, uint8 *depacked, uint32 plen, uint32 unplen)
{
    uint8 *dest;
    int n_bits;
    int idx;
    uint32 bytes;
    int to_add;
    uint32 offset;
    uint8 offset_sizes[4];
    int i;

    offset_sizes[0] = packed[4];	/* skip signature */
    offset_sizes[1] = packed[5];
    offset_sizes[2] = packed[6];
    offset_sizes[3] = packed[7];

    /* initialize source of bits */
    source = packed + plen - 4;

    dest = depacked + unplen;

    /* skip bits */
    get_bits (source[3]);

    /* do it forever, i.e., while the whole file isn't unpacked */
    while (1) {
	/* copy some bytes from the source anyway */
	if (get_bits (1) == 0) {
	    bytes = 0;
	    do {
		to_add = get_bits (2);
		bytes += to_add;
	    } while (to_add == 3);
	    for (i = 0; i <= bytes; i++)
		*--dest = get_bits (8);
	    if (dest <= depacked)
		return;
	}
	/* decode what to copy from the destination file */
	idx = get_bits (2);
	n_bits = offset_sizes[idx];
	/* bytes to copy */
	bytes = idx + 1;
	if (bytes == 4) {	/* 4 means >=4 */
	    /* and maybe a bigger offset */
	    if (get_bits (1) == 0)
		offset = get_bits (7);
	    else
		offset = get_bits (n_bits);

	    do {
		to_add = get_bits (3);
		bytes += to_add;
	    } while (to_add == 7);
	} else
	    offset = get_bits (n_bits);
	for (i = 0; i <= bytes; i++) {
	    dest[-1] = dest[offset];
	    dest--;
	}
	if (dest <= depacked)
	    return;
    }
}


int xmpi_decrunch_pp (FILE *f, FILE *fo)
{
    uint8 *packed, *unpacked;
    int plen, unplen;
    struct stat st;

    if (fo == NULL)
	return -1;

    fstat (fileno (f), &st);
    plen = st.st_size;
    counter = 0;
    packed = malloc (plen);
    fread (packed, plen, 1, f);
    unpacked = malloc (unplen = val (packed + plen - 4));
    ppdepack (packed, unpacked, plen, unplen);

    fwrite (unpacked, unplen, 1, fo);
    free (unpacked);
    free (packed);

    return 0;
}
