#include <stdio.h>
#include <zlib.h>

#define MAXUCOMPSIZE	32768
#define MAXCOMPSIZE		33096		// 32768 + 1%

typedef unsigned int uint32;
typedef unsigned char uint8;

static int bigendian = 0;
static uint8 ubuf[MAXUCOMPSIZE];
static uint8 cbuf[MAXCOMPSIZE];

static int readui32(FILE *in, uint32 *v)
{
    if (fread(v, sizeof (uint32), 1, in) != 1)
        return(0);

    if (bigendian)
    {
	    uint32 x = *v;
        *v = (((x)>>24) + (((x)>>8)&0xff00) + (((x)<<8)&0xff0000) + ((x)<<24));
    } /* if */

    return(1);
}

static int isBigendian(void)
{
    uint32 x = 0x01000000;
    return(*((uint8 *) &x));
} /* isBigendian */


int main(int argc, char **argv)
{
    const char *err = "Read failure";
    FILE *in;
    FILE *out;
    uint32 usize;
    uint32 csize;
    uLongf x;

    if (argc != 3)
    {
        fprintf(stderr, "USAGE: %s <infile.uz2> <outfile>\n", argv[0]);
        return(1);
    } /* if */

    if ((sizeof (uint32) != 4) || (sizeof (uint8) != 1))
    {
        fprintf(stderr, "ERROR: Program is miscompiled!\n");
        return(2);
    } /* if */

    bigendian = isBigendian();

    remove(argv[2]);
    out = fopen(argv[2], "wb");
    in = fopen(argv[1], "rb");

    if ((!in) || (!out))
    {
        fprintf(stderr, "Failed to open %s.\n", argv[in ? 1 : 2]);
        return(3);
    } /* if */

    while (1)
    {
        if (!readui32(in, &csize))
        {
            if (feof(in))
                err = NULL;  /* we're done! */
            break;
        } /* if */

        if (!readui32(in, &usize))
            break;

        if ( (usize > sizeof (ubuf)) || (csize > sizeof (cbuf)) )
        {
            err = "Bogus .uz2 file.";
            break;
        } /* if */

        if (fread(cbuf, csize, 1, in) != 1)
            break;

        x = usize;
        if ((uncompress(ubuf, &x, cbuf, csize) != Z_OK) || (x != usize))
        {
            err = "Decompression error.";
            break;
        } /* if */

        if (fwrite(ubuf, usize, 1, out) != 1)
        {
            err = "Write error.";
            break;
        } /* if */
    } /* while */

    fclose(in);
    fclose(out);

    if (err != NULL)
    {
        remove(argv[2]);
        fprintf(stderr, "ERROR: %s\n", err);
        return(4);
    } /* if */

    return(0);
} /* main */

/* end of unpackuz2.c ... */
