/* depack.c */

/* Powerpacker derunching routine */

/* Copyright (c) Marc Espie, 1995
 * See accompanying file README for distribution information
 *
 * Modified by Claudio Matsuoka for use in xmp.
 *
 * Again modified by Ulrich Sibiller (<uli42@gmx.de>) for use 
 * without xmp. Nearly without error-control, but it works 
 * (didn't find the README-file, so this is lacking). 
 * 
 * compile with 
 *  gcc unpp.c -o unpp
 */

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>

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

typedef signed char int8;
typedef signed short int int16;
typedef signed int int32;
typedef unsigned char uint8;
typedef unsigned short int uint16;
typedef unsigned int uint32;

static uint32 shift_in;
static uint32 counter = 0;
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, char *temp)
{
	uint8 *packed, *unpacked;
	int plen, unplen;
	struct stat st;
	FILE *fo;
	
	fstat (fileno (f), &st);
	plen = st.st_size;
	
	packed = malloc (plen);
	fread (packed, plen, 1, f);
	unpacked = malloc (unplen = val (packed + plen - 4));
	ppdepack (packed, unpacked, plen, unplen);
	
	fo = fopen (temp, "w");
	if (fo == NULL) 
	{
		free (unpacked);
		free (packed);
		return -1;
	}
	
	fwrite (unpacked, unplen, 1, fo);
	free (unpacked);
	free (packed);
	fclose (fo);
	
	return 0;
}


int main(int argc, char *argv[])
{
	FILE *infile;
	int rc = 0;

	if (argc != 3)
	{
		fprintf(stderr, "USAGE: %s <infile> <outfile>\n",argv[0]);
		exit(1);
	}
	
	infile = fopen(argv[1], "r");

	if (infile == NULL)
	{
		fprintf(stderr, "Unable to open input file %s\n", argv[1]);
		exit(1);
	}
	
	rc = xmpi_decrunch_pp(infile, argv[2]);

	if (rc != 0)
	  fprintf(stderr, "Error %d while decrunching!\n"); 

	fclose (infile);
	return (rc);
}



