#include <stdio.h>
#include <iff/ilbm.h>

#define ID_DLTA MakeID('D','L','T','A')
#define ID_ANSQ MakeID('A','N','S','Q')
#define ID_FORM MakeID('F','O','R','M')

char even[120000];
char odd[120000];

char packed[120000];

struct {long name;
	long len; } chunk;

long	next_form_pos = 0x0C;
long	next_chunk_pos = 0x34;

BitMapHeader ilbmbmhd;

char	cmap[32*sizeofColorRegister];
int	cmap_len;

char	camg[4];
int	camg_len;

FILE *in, *out;

int frame = 1;

struct {
	short uni_flag;
	short y_size;
	short num_blocks; } wall;

struct {
	short uni_flag;
	short y_size;
	short x_size;
	short num_blocks; } pile;

long	raster_size, plane_size, image_size, insize;


void
next_form()
{
	int	q;

	q=fseek(in, next_form_pos, SEEK_SET);
	if (q!=0) {
		printf("FSEEK fails in 'next_form': dest was %ld\n", next_chunk_pos);
		exit(0);
	}
	fread(&chunk, (size_t)sizeof(chunk), (size_t)1, in);
	next_form_pos = ftell(in) + chunk.len;
	next_chunk_pos = ftell(in)+0x04;
}

void
next_chunk()
{
	int	q;

	q=fseek(in, next_chunk_pos, SEEK_SET);
	if (q!=0) {
		printf("FSEEK fails in 'next_chunk': dest was %ld\n", next_chunk_pos);
		exit(0);
	}
	fread(&chunk, (size_t)sizeof(chunk), (size_t)1, in);
	next_chunk_pos = ftell(in) + chunk.len;
}

delta_proc(buffer)
char *buffer;
{
    short	change_type;
    long	seek_val;
    int	b;
    short	offset;
    long	x, y, z;
    char	charval;
    long	real_offset;
    char	*dest;

    while(!feof(in)) {
	fread(&change_type, (size_t)sizeof(short), (size_t)1, in);

	switch(change_type) {
	case 0: /* End of DELTA */
	    return;

	case 1: /* Wall */
	    fread(&wall, (size_t)sizeof(wall), (size_t)1, in);
	    for (b=0; b<wall.num_blocks; b++) {
		fread(&offset, (size_t)2, (size_t)1, in);

		real_offset = ((long)offset/raster_size) * ilbmbmhd.nPlanes;
		real_offset *= raster_size;
		real_offset += offset % raster_size;

		for (z=0; z<ilbmbmhd.nPlanes; z++) {
		    for (y=0; y<wall.y_size; y++) {
			fread(&charval, (size_t)1, (size_t)1, in);
			dest = buffer;
			dest += z * raster_size*wall.y_size;
			dest += y * raster_size;
			dest += real_offset;
			if (wall.uni_flag == 1)
			    *dest ^= charval;
			else
			    *dest = charval;
		    }
		}

		/*
		 * If we've stopped on an odd boundary, read and throw away
		 * another byte.
		 */
		seek_val = ftell(in);
		if (seek_val & 0x01L)
		    fread(&charval, (size_t)1, (size_t)1, in);
	    }
	    break;

	case 2: /* Pile */
	    fread(&pile, (size_t)sizeof(pile), (size_t)1, in);
	    for (b=0; b<pile.num_blocks; b++) {
		fread(&offset, (size_t)2, (size_t)1, in);
		real_offset = ((long)offset/raster_size) * ilbmbmhd.nPlanes;
		real_offset *= raster_size;
		real_offset += offset % raster_size;

		for (z=0; z<ilbmbmhd.nPlanes; z++) {
		    for (y=0; y<pile.y_size; y++) {
			for (x=0; x<pile.x_size; x++) {
			    fread(&charval, (size_t)1, (size_t)1, in);
			    dest = buffer;
			    dest += z * raster_size*pile.y_size;
			    dest += y * raster_size;
			    dest += real_offset + x;
			    if (pile.uni_flag == 1)
				*dest ^= charval;
			    else
				*dest = charval;
			}
		    }
		}

		/*
		 * If we've stopped on an odd boundary, read and throw away
		 * another byte.
		 */
		seek_val = ftell(in);
		if (seek_val & 0x01L)
		    fread(&charval, (size_t)1, (size_t)1, in);
	    }
	    break;

	default:
	    printf("I seem to have found change_type %d\n", change_type);
	    printf("I didn't think there was such a thing!\n");
	    exit(1);
	    break;
	}
    }
}

/*----------------------------------------------------------------------*
 * unpacker.c Convert data from "cmpByteRun1" run compression. 11/15/85
 *
 * By Jerry Morrison and Steve Shaw, Electronic Arts.
 * This software is in the public domain.
 *
 *	control bytes:
 *	 [0..127]   : followed by n+1 bytes of data.
 *	 [-1..-127] : followed by byte to be repeated (-n)+1 times.
 *	 -128       : NOOP.
 *
 * This version for the Commodore-Amiga computer.
 * Manxified and simplified slightly by SDB...
 *----------------------------------------------------------------------*/

/* This macro computes the worst case packed size of a "row" of bytes. */
#define MaxPackedSize(rowSize)  ( (rowSize) + ( ((rowSize)+127) >> 7 ) )


/*----------- UnPackRow ------------------------------------------------*/

#define UGetByte()	(*source++)
#define UPutByte(c)	(*dest++ = (c))

/* Given POINTERS to POINTER variables, unpacks one row, updating the source
 * and destination pointers until it produces dstBytes bytes. */
int UnPackRow(BYTE **pSource, BYTE **pDest, int srcBytes0, int dstBytes0)
/*	BYTE **pSource, **pDest;  int srcBytes0, dstBytes0; */
{
    register BYTE *source = *pSource;
    register BYTE *dest   = *pDest;
    register int n;
    register BYTE c;
    register int srcBytes = srcBytes0, dstBytes = dstBytes0;
    int error = TRUE;	/* assume error until we make it through the loop */
    WORD minus128 = -128;  /* get the compiler to generate a CMP.W */

    while( dstBytes > 0 )  {
	if ( (srcBytes -= 1) < 0 )  goto ErrorExit;
    	n = UGetByte();

    	if (n >= 0) {
	    n += 1;
	    if ( (srcBytes -= n) < 0 )  goto ErrorExit;
	    if ( (dstBytes -= n) < 0 )  goto ErrorExit;
	    do {  UPutByte(UGetByte());  } while (--n > 0);
	    }

    	else if (n != minus128) {
	    n = -n + 1;
	    if ( (srcBytes -= 1) < 0 )  goto ErrorExit;
	    if ( (dstBytes -= n) < 0 )  goto ErrorExit;
	    c = UGetByte();
	    do {  UPutByte(c);  } while (--n > 0);
	    }
	}
    error = FALSE;	/* success! */

  ErrorExit:
    *pSource = source;  *pDest = dest;
    return(error);
}


save_block(filename, block)
char	*filename, *block;
{
	FILE *out;

	out = fopen(filename, "w");
	chunk.name = ID_FORM;
	chunk.len = image_size + sizeof(ilbmbmhd) + cmap_len + 28;
	if (camg_len)
		chunk.len += camg_len+8;
	fwrite(&chunk, (size_t)sizeof(chunk), (size_t)1, out);

	chunk.name = ID_ILBM;
	fwrite(&chunk, (size_t)sizeof(chunk.name), (size_t)1, out);

	chunk.name = ID_BMHD;
	chunk.len = sizeof(ilbmbmhd);
	fwrite(&chunk, (size_t)sizeof(chunk), (size_t)1, out);

	fwrite(&ilbmbmhd, (size_t)sizeof(ilbmbmhd), (size_t)1, out);

	chunk.name = ID_CMAP;
	chunk.len = cmap_len;
	fwrite(&chunk, (size_t)sizeof(chunk), (size_t)1, out);

	fwrite(&cmap, (size_t)cmap_len, (size_t)1, out);

	if (camg_len) {
		chunk.name = ID_CAMG;
		chunk.len = camg_len;
		fwrite(&chunk, (size_t)sizeof(chunk), (size_t)1, out);
		fwrite(&camg, (size_t)camg_len, (size_t)1, out);
	}

	chunk.name = ID_BODY;
	chunk.len = image_size;
	fwrite(&chunk, (size_t)sizeof(chunk), (size_t)1, out);

	fwrite(block, (size_t)image_size, (size_t)1, out);

	fclose(out);
}




/***************************************************************/

main(argc, argv)
int argc;
char *argv[];
{
	int	oddframe;
	int	read_size;
	char	*inptr, *outptr;
	long	i, j;
	char	filename[20];


	if (argc != 2) {
		printf("Usage: unmovie file.movie\n");
		printf("Generates files named f??? representing successive frames\n");
		printf("of the movie, in the same directory.\n");
		printf("For looping movies, the last two frames will be the same as\n");
		printf("the first two.\n");
		printf("For ping-pong movies, the last frame will equal the third\n");
		printf("from last frame.\n");
		exit(0);
	}

	in = fopen(argv[1], "r");
	if (in == NULL) {
		printf("Can't seem to open %s for input!\n", argv[1]);
		exit(1);
	}

	/*
	 * First read in the BMHD
	 */
	fseek(in, 0x20, SEEK_SET);
	fread(&ilbmbmhd, (size_t)sizeof(ilbmbmhd), (size_t)1, in);

	raster_size = (ilbmbmhd.w+7)/8;
	plane_size = raster_size * ilbmbmhd.h;
	image_size = plane_size * ilbmbmhd.nPlanes;

	/*
	 * Scan for the BODY. If we encounter a CAMG or a CMAP, store it
	 * for later.
	 */
	cmap_len = 0;
	camg_len = 0;
	for (;;) {
		next_chunk();
		if (chunk.name == ID_BODY)
			break;
		else if (chunk.name == ID_CMAP) {
			cmap_len = chunk.len;
			fread(&cmap, (size_t)cmap_len, (size_t)1, in);
		}
		else if (chunk.name == ID_CAMG) {
			camg_len = chunk.len;
			fread(&camg, (size_t)camg_len, (size_t)1, in);
		}
	}

	/*
	 * Read it in.
	 */

	read_size = fread(packed, (size_t)chunk.len, (size_t)1, in);
	if (read_size != 1) {
		printf("Couldn't read the first image - corrupt file?\n");
		exit(1);
	}
	inptr = packed;
	outptr = even;
	for (outptr = even; outptr<&even[image_size]; ) {
		insize = &packed[chunk.len] - inptr;
		if (ilbmbmhd.compression == cmpByteRun1) {
			if (UnPackRow(&inptr, &outptr, (int)insize,
			    (int)raster_size)) {
				printf("Some sort of unpacker error!\n");
				exit(1);
			}
		} else {
			/*
			 * Uncompressed input - just copy it
			 */
			memcpy(outptr, inptr, raster_size);
			outptr += raster_size;
			inptr += raster_size;
		}
	}


	/*
	 * Store the body out as f001
	 */
	printf("Storing frame f001\n");
	ilbmbmhd.compression = cmpNone;

	save_block("f001", even);

	/*
	 * Copy to the "odd" buffer
	 */
	memcpy(odd, even, sizeof(even));

	next_form();

	for (oddframe=FALSE;;oddframe=!oddframe) {

		/*
		 * We're through if the next chunk is ANSQ
		 */
		next_chunk();
		if (chunk.name == ID_ANSQ)
			break;

		/*
		 * Find the next DLTA
		 */
		next_form();
		while (chunk.name != ID_DLTA) {
			next_chunk();
		}
		delta_proc(oddframe ? odd : even);

		frame += 1;
		sprintf(filename, "f%03d", frame);
		printf("Storing frame %s\n", filename);
		save_block(filename, oddframe ? odd : even);
	}
	exit(0);
}
