/**********************************************************************
				CHOPIFF.C	Analyse and chop an IFF file
**********************************************************************/

#include <portab.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dos.h>
#include <errno.h>

#define FORM 0x464f524d
#define VHDR 0x56484452
#define BMHD 0x424d4844
#define CMAP 0x434d4150
#define CAMG 0x43414d47
#define BODY 0x424f4459
#define CRNG 0x43524e47

#define	END_OF_FILE				-2
#define NOERROR 				-1
#define MEMORY					0
#define NOT_ILBM				1
#define NOT_8SVX				2
#define NOT_ENOUGH_DATA			3
#define DISK_ACCESS_FAILED		4
#define CANT_OPEN_GRAPHICS_LIB	5
#define BAD_CHOP_ARGS			6
#define BAD_SPRITE_ARGS			7
#define	CANT_OPEN_FILE			8
#define CANT_SAVE_COLS			9

char *errormessage[] = {"\nCAN'T ALLOCATE MEMORY !!\n",
						"\nTHIS IS NOT AN ILBM OR PBM FORM !!\n",
						"\nTHIS IS NOT AN 8SVX FORM !!\n",
						"\nCANNOT CHOP RASTER !!   Not enough data in form\n",
						"\nDISK ACCESS FAILED\n",
						"\nCAN'T OPEN GRAPHICS LIBRARY !!\n",
						"\nBAD CHOP ARGS !!\n",
						"\nBAD SPRITE ARGS !!\n",
						"\nCAN'T OPEN FILE\n",
						"\nCAN'T SAVE ENOUGH COLOURS\n"};

typedef LONG FIXED;		/* A fixed point value, [16bits . 16bits] */

struct {
	ULONG oneshothisamples;
	ULONG repeathisamples;
	ULONG samplesperhicycle;
	UWORD samplespersec;
	UBYTE ctoctave;
	UBYTE scompression;
	FIXED volume;
	} vhdr;

struct {
	UWORD rwidth,rheight;
	WORD xorigin,yorigin;
	UBYTE planes,mask,compression,pad;
	UWORD transparency;
	UBYTE xaspect,yaspect;
	WORD pagewidth,pageheight;
	} bmhd;
/**/
ULONG	viewmode;
ULONG	bodylength;
BYTE	*body;

FILE *fp;

int CHUNKS=0,COLS=0,SAVECOLS=0,CHOP=0,ILCHOP=0,SPRITE=0,SPRITEHEADER=0,SPRITETAIL=0,SAMPLE=0;
int formILBMflag,formPBMflag,form8SVXflag;
int VHDRflag,BMHDflag,BODYflag;
int startrow,height,startbyte,width,startcol,numberofcols,spritex,spritey;
char bpl_mask[50];
char filename[200];

void unpackraster(int,int,int,BYTE,BYTE *,UBYTE *[]);
void strsfn(char *,char *,char *,char *,char *);
void strmfn(char *,char *,char *,char *,char *);

main(argc,argv)
int argc;
char *argv[];
	{
	union	{
			ULONG numeric;
			char text[5];
			} chunk;
	ULONG chunklength;
	struct find_t fileinfo;
	char *cp,drive[3],path[75];
	unsigned filefound;
	int i,error;

	chunk.text[4] = 0;

	printf("\nChopIFF v1.2 (c)1990 Maverick Developments\n\n");

	if (argc < 2)
		{
		printf("Commandline: ChopIFF [options] [filename] <filename>...\n\n");
		printf("Options:\n");
		printf("  -chunks\n");
		printf("  -cols <save [start col] [number]>\n");
		printf("  -chop [start row] [height] [start byte] [byte width] [bpl mask]\n");
		printf("  -ilchop [start row] [height] [start byte] [byte width] [bpl mask]\n");
		printf("  -sprite [start row] [height] [start byte] [bpl mask]\n");
		printf("  -spriteheader [x] [y]\n");
		printf("  -spritetail\n");
		printf("  -sample\n");
		return (0);
		}
/**/
	for (i=1;i<argc-1;i++)
		{
		if (argv[i][0] != '-') break;
		if (strcmp(argv[i],"-cols") == 0)
			{
			COLS = 1;
			if ((i+1 < argc) && strcmp(argv[i+1],"save") == 0)
				{
				i+=2;
				if (sscanf(argv[i],"%d",&startcol) != 1) break;
				if (sscanf(argv[++i],"%d",&numberofcols) != 1) break;
				SAVECOLS = 1;
				}
			continue;
			}
		if (strcmp(argv[i],"-chunks") == 0)
			{
			CHUNKS = 1;
			continue;
			}
		if (strcmp(argv[i],"-chop") == 0)
			{
			CHOP = 1;
			if (i+5 >= argc) break;
			if (sscanf(argv[++i],"%d",&startrow) != 1
				|| sscanf(argv[++i],"%d",&height) != 1
				|| sscanf(argv[++i],"%d",&startbyte) != 1
				|| sscanf(argv[++i],"%d",&width) != 1
				|| sscanf(argv[++i],"%s",bpl_mask) != 1) break;
			strcat(bpl_mask,"nnnnnnnn");
			continue;
			}
		if (strcmp(argv[i],"-ilchop") == 0)
			{
			ILCHOP = 1;
			if (i+5 >= argc) break;
			if (sscanf(argv[++i],"%d",&startrow) != 1
				|| sscanf(argv[++i],"%d",&height) != 1
				|| sscanf(argv[++i],"%d",&startbyte) != 1
				|| sscanf(argv[++i],"%d",&width) != 1
				|| sscanf(argv[++i],"%s",bpl_mask) != 1) break;
			strcat(bpl_mask,"nnnnnnnn");
			continue;
			}
		if (strcmp(argv[i],"-sprite") == 0)
			{
			SPRITE = 1;
			if (i+4 >= argc) break;
			if (sscanf(argv[++i],"%d",&startrow) != 1
				|| sscanf(argv[++i],"%d",&height) != 1
				|| sscanf(argv[++i],"%d",&startbyte) != 1
				|| sscanf(argv[++i],"%s",bpl_mask) != 1) break;
			strcat(bpl_mask,"nnnnnnnn");
			continue;
			}
		if (strcmp(argv[i],"-spriteheader") == 0)
			{
			SPRITEHEADER = 1;
			if (i+2 >= argc) break;
			if (sscanf(argv[++i],"%d",&spritex) != 1
				|| sscanf(argv[++i],"%d",&spritey) != 1) break;
			continue;
			}
		if (strcmp(argv[i],"-spritetail") == 0)
			{
			SPRITETAIL = 1;
			continue;
			}
		if (strcmp(argv[i],"-sample") == 0)
			{
			SAMPLE = 1;
			continue;
			}
		printf("Bad args\n");
		return (-1);
		}
/**/
	for (;i<argc;i++)
		{
		formILBMflag=formPBMflag=form8SVXflag=0;
		VHDRflag=BMHDflag=BODYflag=0;

		if (filefound =_dos_findfirst(argv[i],_A_NORMAL,&fileinfo) != 0)	/* find first file that fits template */
			{
			printf("Can't match file %s\n",argv[i]);
			return (-1);
			}
		strsfn(argv[i],drive,path,0,0);

		while (filefound == 0)
			{
			strmfn(filename,drive,path,fileinfo.name,0);
			if ((fp = fopen(filename,"rb")) == NULL)
				{
				printf("Can't find file %s\n",filename);
				return (-1);
				}
		
			if (CHUNKS == 1) printf("Analysis of IFF file %s\n\n",filename);

			cp = strchr(filename,'.');
			if (cp != 0) *cp = 0;														/* remove extension */
	
			error=NOERROR;
			while (error == NOERROR)
				{
				if ((fread(chunk.text,4,1,fp) != 1) ||
					(fread((char *) &chunklength,4,1,fp) != 1))
					{
					error = END_OF_FILE;
					break;
					}
				if (CHUNKS == 1) printf("%4.4s	  %-8u",chunk.text,(unsigned) chunklength);
				switch (chunk.numeric)
					{
					case FORM:
						error = chunk_form();
						break;
					case VHDR:
						error = chunk_vhdr(chunklength);
						break;
					case BMHD:
						error = chunk_bmhd(chunklength);
						break;
					case CMAP:
						error = chunk_cmap(chunklength);
						break;
					case CAMG:
						error = chunk_camg(chunklength);
						break;
					case BODY:
						error = chunk_body(chunklength);
						break;
					case CRNG:
						error = chunk_crng(chunklength);
						break;
					default:
						if (CHUNKS == 1) printf("	 Unkown chunk type !!\n");
						error = nextchunk(chunklength);
						break;
					}
				}
/**/
			fclose(fp);
			if ((CHOP == 1 || ILCHOP == 1) && error <= NOERROR) error = chopraster();
			if (SPRITE == 1 && error <= NOERROR) error = getsprite();
			if (SAMPLE == 1 && error <= NOERROR) error = sample();
			if (error >= 0)
				{
				printf("%s\n",errormessage[error]);
				return (-1);
				}

			filefound =_dos_findnext(&fileinfo);		/* find next file that fits template */
			}
		}
	return (0);
	}

chunk_form()
	{
	char formtype[5];

	if (fread(formtype,4,1,fp) != 1) return (DISK_ACCESS_FAILED);
	formtype[4]=0;
	if (CHUNKS == 1) printf("\n 	   Form type:	   %s\n",formtype);
	if (strcmp(formtype,"ILBM") == 0) formILBMflag = 1;
	if (strcmp(formtype,"PBM ") == 0) formPBMflag = 1;
	if (strcmp(formtype,"8SVX") == 0) form8SVXflag = 1;
	return (NOERROR);
	}

chunk_vhdr(chunklength)
ULONG chunklength;
	{
	if (CHUNKS == 1) printf("    Voice 8 Header\n");
	
	if (chunklength != sizeof(vhdr))
		return (unexpected_chunk_length(chunklength));
	
	if (fread((char *) &vhdr,sizeof(vhdr),1,fp) != 1)
		return (DISK_ACCESS_FAILED);

	if (CHUNKS == 1)
		{
		printf("        One shot hi samples:    %4u\n",vhdr.oneshothisamples);
		printf("        Repeat hi samples:      %4u\n",vhdr.repeathisamples);
		printf("        Samples per hi cycle:   %4u\n",vhdr.samplesperhicycle);
		printf("        Samples per sec:        %4u\n",vhdr.samplespersec);
		printf("        Number of octaves:      %4u\n",vhdr.ctoctave);
		printf("        Compression algorithm:  %4u\n",vhdr.scompression);
		printf("        Volume:                 %12d\n",vhdr.volume);
		}
	VHDRflag = 1;
	return (NOERROR);
	}
/**/
chunk_bmhd(chunklength)
ULONG chunklength;
	{
	if (CHUNKS == 1) printf("	 Bitmap Header\n");
	if (chunklength != sizeof(bmhd))
		return (unexpected_chunk_length(chunklength));

	if (fread((char *) &bmhd,sizeof(bmhd),1,fp) != 1)
		return (DISK_ACCESS_FAILED);

	if (CHUNKS == 1)
		{
		printf("        Raster width:       %4u    Raster height:      %4u\n",
				bmhd.rwidth,bmhd.rheight);
		printf("        X origin:           %4d    Y origin:           %4d\n",
				bmhd.xorigin,bmhd.yorigin);
		printf("        No of bitplanes:    %4u    Mask:               %4u\n",
				bmhd.planes,bmhd.mask);
		printf("        Compression:        %4u    Transparency:       %4u\n",
				bmhd.compression,bmhd.transparency);
		printf("        X aspect ratio:     %4u    Y aspect ratio:     %4u\n",
				bmhd.xaspect,bmhd.yaspect);
		printf("        Page width:         %4d    Page height:        %4d\n",
				bmhd.pagewidth,bmhd.pageheight);
		}
	BMHDflag = 1;
	return (NOERROR);
	}
/**/
chunk_cmap(chunklength)
ULONG chunklength;
	{
	FILE *pal_fp;
	int i,no_colors;
	UBYTE rgb[256][3];
	UWORD palette[256];
	char palname[3+200+15];

	if (CHUNKS == 1 || COLS == 1) printf("	  Colour map\n");

	if (chunklength > 256*3) return(unexpected_chunk_length(chunklength));

	no_colors = chunklength/3;
	if (CHUNKS == 1 || COLS == 1)
		printf("        Number of colours :%d\n",no_colors);

	if (COLS == 1)
		{
		if (fread((char *) rgb,1,(int) chunklength,fp) != chunklength)
			return (DISK_ACCESS_FAILED);
		for (i=0;i<no_colors;i+=2)
			{
			palette[i] = ((rgb[i][0]&0xf0)<<4)+(rgb[i][1]&0xf0)+((rgb[i][2]&0xf0)>>4);
			palette[i+1] = ((rgb[i+1][0]&0xf0)<<4)+(rgb[i+1][1]&0xf0)+((rgb[i+1][2]&0xf0)>>4);
			printf("        %2u  RGB:    %3X             %2u  RGB:    %3X\n",
					i,palette[i],i+1,palette[i+1]);
			}
		if (SAVECOLS == 1)
			{
			if (startcol+numberofcols > no_colors) return (CANT_SAVE_COLS);
			strcpy(palname,filename);
			strcat(palname,".pal");
			printf("Palette output filename : %s\n",palname);
			if ((pal_fp = fopen(palname,"wb")) == NULL) return (CANT_OPEN_FILE);
			fwrite((char *) &palette[startcol],numberofcols,2,pal_fp);
			fclose(pal_fp);
			}
		return (NOERROR);
		}
	else
		return (nextchunk(chunklength));
	}

chunk_camg(chunklength)
ULONG chunklength;
	{
	if (CHUNKS == 1) printf("    Amiga viewmodes chunk\n");

	if (chunklength != 4) return (unexpected_chunk_length(chunklength));

	if (fread((char *) &viewmode,4,1,fp) != 1) return (DISK_ACCESS_FAILED);

	if (CHUNKS == 1) printf("        Viewmode:   0x%X\n",viewmode);
	return (NOERROR);
	}
/**/
chunk_body(chunklength)
ULONG chunklength;
	{
	if (CHUNKS == 1) printf("    Data\n");

	if (CHOP == 1 || ILCHOP == 1 || SPRITE == 1 || SAMPLE == 1)
		{
		if ((body = (BYTE *) malloc((unsigned) chunklength+1)) == NULL) return (MEMORY);
		if (fread(body,(int) chunklength,1,fp) != 1) return (DISK_ACCESS_FAILED);
		BODYflag = 1;
		bodylength = chunklength;
		return (NOERROR);
		}
	else
		return (nextchunk(chunklength));
	}

chunk_crng(chunklength)
ULONG chunklength;
	{
	if (CHUNKS == 1) printf("    Colour register range (Deluxe Paint)\n");
	return (nextchunk(chunklength));
	}

unexpected_chunk_length(chunklength)
ULONG	chunklength;
	{
	if (CHUNKS == 1) printf("        UNEXPECTED CHUNK LENGTH\n");
	return(nextchunk(chunklength));
	}

nextchunk(chunklength)
ULONG chunklength;
	{
	int fseek_result;

	if ((chunklength % 2) == 0)
		fseek_result = fseek(fp,(long) chunklength,SEEK_CUR);
	else
		fseek_result = fseek(fp,(long) (chunklength+1),SEEK_CUR);
	return ((fseek_result == 0) ? NOERROR : END_OF_FILE);
	}
/**/
chopraster()
	{
	BYTE *planes[8];
	FILE *fp;
	int p,r;
	char chopname[200];

	if (formILBMflag != 1 && formPBMflag != 1) return (NOT_ILBM);
	if (BMHDflag+BODYflag != 2) return (NOT_ENOUGH_DATA);

	if ((startrow<0 || startrow>bmhd.rheight-1)
		|| (height<0 || startrow+height>bmhd.rheight)
		|| (startbyte<0 || startbyte>bmhd.rwidth/8-1)
		|| (width<0 || startbyte+width>bmhd.rwidth/8))
		return (BAD_CHOP_ARGS);

	strcpy(chopname,filename);
	strcat(chopname,".chp");
	printf("Chop output filename : %s\n",chopname);
	if ((fp = fopen(chopname,"wb")) == NULL) return (CANT_OPEN_FILE);

	for (p=0;p<bmhd.planes;p++)
		if ((planes[p] = malloc((size_t) (bmhd.rwidth/8L*bmhd.rheight))) == NULL) return (MEMORY);

	unpackraster(bmhd.planes,bmhd.rwidth,bmhd.rheight,bmhd.compression,body,planes);
	printf("\n\n");
	free(body);
	
	if (CHOP == 1)
		for (p=0;p<bmhd.planes;p++)
			{
			if (bpl_mask[p] == 'y' || bpl_mask[p] == 'Y')
				for (r=startrow;r<startrow+height;r++)
					fwrite(planes[p]+r*(bmhd.rwidth/8)+startbyte,width,1,fp);
			}
	else
		for (r=startrow;r<startrow+height;r++)
				for (p=0;p<bmhd.planes;p++)
					if (bpl_mask[p] == 'y' || bpl_mask[p] == 'Y') fwrite(planes[p]+r*(bmhd.rwidth/8)+startbyte,width,1,fp);
	
	fclose(fp);
	for (p=0;p<bmhd.planes;p++) free(planes[p]);
	return (NOERROR);
	}
/**/	
getsprite()
	{
	BYTE *planes[8];
	UBYTE sh[4];
	ULONG st=0;
	FILE *fp;
	int p,r;
	char spritename[3+200+15];

	if (formILBMflag != 1 && formPBMflag != 1) return (NOT_ILBM);
	if (BMHDflag+BODYflag != 2) return (NOT_ENOUGH_DATA);

	if ((startrow<0 || startrow>bmhd.rheight-1)
		|| (height<0 || startrow+height>bmhd.rheight)
		|| (startbyte<0 || startbyte>bmhd.rwidth/8-1)
		|| (startbyte+2>bmhd.rwidth/8))
		return (BAD_SPRITE_ARGS);

	strcpy(spritename,filename);
	strcat(spritename,".spr");
	printf("Sprite output filename : %s\n",spritename);
	if ((fp = fopen(spritename,"wb")) == NULL) return (CANT_OPEN_FILE);

	if (SPRITEHEADER == 1)
		{
		sh[0] = (0x3e+spritey) & 0xff;
		sh[1] = ((0x80+spritex)>>1) & 0xff;
		sh[2] = ((0x3e+spritey)+height) & 0xff;
		sh[3] = (((0x3e+spritey) & 0x100)>>6)+(((0x3e+spritey+height) & 0x100)>>7)+((0x80+spritex) & 1);
		fwrite(sh,4,1,fp);
		}

	for (p=0;p<bmhd.planes;p++)
		if ((planes[p] = malloc((size_t) (bmhd.rwidth/8L*bmhd.rheight))) == NULL) return (MEMORY);

	unpackraster(bmhd.planes,bmhd.rwidth,bmhd.rheight,bmhd.compression,body,planes);
	printf("\n\n");
	free(body);
	
	for (r=startrow;r<startrow+height;r++)
			for (p=0;p<bmhd.planes;p++)
				if (bpl_mask[p] == 'y' || bpl_mask[p] == 'Y') fwrite(planes[p]+r*(bmhd.rwidth/8)+startbyte,2,1,fp);

	if (SPRITETAIL == 1) fwrite((char *) &st,4,1,fp);

	fclose(fp);

	for (p=0;p<bmhd.planes;p++) free(planes[p]);

	return (NOERROR);
	}
/**/
sample()
	{
	FILE *fp;
	char samplename[3+200+15],padbyte=0;
	UBYTE volume;
	UWORD length;

	if (form8SVXflag != 1) return (NOT_8SVX);
	if (VHDRflag+BODYflag != 2) return (NOT_ENOUGH_DATA);

	strcpy(samplename,filename);
	strcat(samplename,".sa");
	printf("SA format sample filename : %s\n",samplename);
	if ((fp = fopen(samplename,"wb")) == NULL) return (CANT_OPEN_FILE);

	length = (UWORD) (vhdr.oneshothisamples + vhdr.repeathisamples);
	fwrite((char *) &length,2,1,fp);
	fwrite((char *) &vhdr.samplespersec,2,1,fp);
	volume = (vhdr.volume >> 10);
	fwrite(&volume,1,1,fp);
	fwrite(&vhdr.scompression,1,1,fp);
	fwrite(body,(int) bodylength,1,fp);
	if ((bodylength & 1) == 1) fwrite(&padbyte,1,1,fp);

	printf("\n\n");
	free(body);
	fclose(fp);
	return (NOERROR);
	}
/**/
void unpackraster(depth,width,rows,compression,data,planes)
int depth;
int width;
int rows;
BYTE compression;
BYTE *data;
UBYTE *planes[];
	{
	UBYTE *plane[8];
	int bytesperrow;
	int p,r,count;
	BYTE c;

	for (p=0;p<depth;p++)
		{
		plane[p] = planes[p];
		memset(plane[p],0,width/8*rows);
		}

	if (formILBMflag == 1)
		{
		bytesperrow = width/8;
		switch (compression)
			{
			case 0:
				for (r=0;r<rows;r++)
					for (p=0;p<depth;p++)
						{
						memcpy(plane[p],data,bytesperrow);
						data += bytesperrow;
						plane[p] += bytesperrow;
						}
				break;
			case 1:
				for (r=0;r<rows;r++)
					for (p=0,count=0;p<depth;p++,count=0)
						while (count < bytesperrow)
							{
							c = *data++;
							if ((0 <= c) && (c <= 127))
								{
								memcpy(plane[p],data,c+1);
								data += c+1;
								count += c+1;
								plane[p] += c+1;
								continue;
								}
							if ((-128 < c) && (c < 0))
								{
								memset(plane[p],(int) *data++,1-c);
								count += 1-c;
								plane[p] += 1-c;
								continue;
								}
							}
				break;
			}
		}
	}

/**/
void strsfn(filename,drive,path,node,ext)
char *filename;
char *drive;
char *path;
char *node;
char *ext;
	{
	char *s,*p;

	p=strchr(filename,':');			/* Drive string = "d" */
	if (p != 0)
		{
		if (drive != 0)
			{
			strncpy(drive,filename,1);
			drive[1] = 0;
			}
		s=p+1;
		}
	else
		{
		if (drive !=0) drive[0]=0;
		s=filename;
		}

	p=strrchr(s,'\\');				/* Path string = "\path\" */
	if (p!=0)
		{
		if (path !=0)
			{
			strncpy(path,s,p-s+1);
			path[p-s+1]=0;
			}
		s=p+1;
		}
	else if (path != 0) path[0]=0;
	
	p=strrchr(s,'.');				/* Node string = "node" */
	if (p!=0)						/* Ext string = "ext" */
		{
		if (node != 0)
			{
			strncpy(node,s,p-s);
			node[p-s]=0;
			}
		if (ext !=0) strcpy(ext,p+1);
		}
	else
		{
		if (node != 0) strcpy(node,s);
		if (ext != 0) ext[0]=0;
		}
	}

void strmfn(filename,drive,path,node,ext)
char *filename;
char *drive;
char *path;
char *node;
char *ext;
	{
	filename[0]=0;
	if (drive != 0 && drive[0] != 0)
		{
		strcpy(filename,drive);
		strcat(filename,":");
		}
	if (path != 0)
		{
		strcat(filename,path);
		if (path[0] != 0 && path[strlen(path)-1] != '\\') strcat(filename,"\\");
		}
	strcat(filename,node);
	if (ext != 0)
		{
		strcat(filename,".");
		strcat(filename,ext);
		}
	}

