#include "stdio.h"

#define	NFILES	2
#define	SRCFILE	0
#define	DSTFILE	1

#define	SF_BPP	1
#define	SF_BPA	2
#define	SF_BPT	3
#define	SF_WBC	4
#define	MAXFMT	4

#define	MAXSP	4	/* maximum number of VRAM planes */
#define	MAXSW	16	/* maximum width of sprite in bytes */
#define MAXSH	0x80	/* maximum height of sprite in bytes */

#define	IP_FMT	1	/* index of format parameter */
#define	IP_PLN	2	/* index of planes parameter */
#define	IP_WDT	3	/* index of width parameter */
#define	IP_HGT	4	/* index of height parameter */
#define	IP_SRC	5	/* index of source file parmeter */
#define	IP_DST	6	/* index of destination parameter */
#define	NP_REQ	7	/* number of required parameters */



char* Ptr2Str(_ptr,_str)
char* _ptr;
char* _str;
{
	return _ptr ? _ptr : (_str ? _str : "");
}


trouble(_text1,_text2)
char *_text1;
char *_text2;
{
 	printf	( "Error: %s %s\n"
		, Ptr2Str(_text1,"unknown")
		, Ptr2Str(_text2,0)
		);

	closeall();
	exit(1);
}


struct	fdesc
{
	FILE*	file;
	char*	name;
};

#define FD struct fdesc


FILE*	creatfd(_fd,_mode)
FD*	_fd;
int	_mode;
{
	unlink(_fd->name);

	if ((_fd->file = creat(_fd->name,_mode)) == ERROR)
		trouble("failed to create", _fd->name);
	return _fd->file;
}


FILE*	openfd(_fd,_mode)
FD*	_fd;
int	_mode;
{
	if ((_fd->file = open(_fd->name,_mode)) == ERROR)
		trouble("failed to open", _fd->name);
	return _fd->file;
}



int closefd(_fd)
FD*	_fd;
{
	int	result;

	result	= SUCCESS;

	if (_fd->file != ERROR)
	{
		if ((result = close(_fd->file)) == ERROR)
			printf("Error closing %s\n",_fd->name);
	}
	return result;
}



int closeall(_fd)
FD*	_fd;
{
	int i;
	int result;

	result = SUCCESS;
	i=0;

	for (i=0; i<NFILES; ++i) result &= closefd(_fd++);

	return result;
}


unsigned sizefd(_fd, _size)
FD*		_fd;
unsigned	_size;
{
	unsigned	result;

	seek(_fd->file,0,LAST);

	if ( (result = tell(_fd->file)) < _size)
			trouble("too short file",_fd->name);
	else
		if (seek(_fd->file,0,START) == ERROR)
		{
			result = 0;
			trouble("seek error in",_fd->name);
		}
		else result = _size;

	return result;
}


struct	sptdesc
{
	int (*handler)();
	char *text;
};

static struct sptdesc sptFmt[MAXFMT];


usage()
{
 register int i;

 printf("Perlin Production Co. Ltd. April 2012 [on-the-knee]\n");
 printf("Generates file with 7 extra sprites shifted right from\n");
 printf("sprite bitmap file.\n");
 printf("-- GENERATED OUTPUT SPRITES WILL BE ONE BYTE WIDER --\n");
 printf("\nUsage: SPTx8 format planes width height infile outfile\n\n");
 printf("NOTES:\n");
 printf("1. format - sprite format 1..%d\n",MAXFMT);
 for (i=0; i < MAXFMT; ++i) printf("\t%d - %s\n", i+1, sptFmt[i].text);
 printf("2. planes - number of planes in sprite 1..%d\n", MAXSP);
 printf("3. width  - width  of sprite in bytes  1..%d\n", MAXSW);
 printf("4. height - height of sprite in pixels 1..%d\n", MAXSH);
}



int	IPGet(_argv, _index, _max, _text)
char**	_argv;
int	_index;
int	_max;
char*	_text;
{
	int result;
	result = atoi(_argv[_index]);

	if (result < 1 || result > _max) trouble (_text,0);
	return result;
}


static	int		width,height,planes;
static	unsigned	sizCol, sizSrc, sizBuf;
static	char		*buf;


int	FmtBPP	(_step)
int	_step;
{
 int		line,plane,col,pos;
 char		mask, tmp;
 register char	c;

 for (line  = 0; line  < height; ++line)
    for (plane = 0; plane < planes; ++plane)
    {
	pos = planes * line + plane;
	mask = 0;

	for (col = 0; col <= width; ++col)
	{
		if (pos >= sizBuf)
		{
		 printf("line:%d, plane:%d, pos:%d\n",line,plane,pos);
		 trouble("internal, pos is out of sizBuf",0);
	 	}
	 	c = buf[pos];
	 	tmp = (c & 1) ? 0x80 : 0;
	 	buf[pos] = (c >> 1) | mask;
	 	mask = tmp;
	 	pos += sizCol;
	}
    }
    return 0;
}



main(argc,argv)
int	argc;
char	**argv;
{
	FD	fd[NFILES];
	FILE	*srcfile, *dstfile;
	int	i,idFmt;
	struct	sptdesc* sf;

	for (i=0; i < MAXFMT; ++i) sptFmt[i].handler = 0;

	sptFmt[SF_BPT-1].handler = FmtBPP;

	sptFmt[SF_BPP-1].text = "plane-by-plane b-t-l-r";
	sptFmt[SF_BPA-1].text = "pixel-by-pixel l-r-b-t";
	sptFmt[SF_BPT-1].text = "byte-through-planes b-t-l-r";
	sptFmt[SF_WBC-1].text = "bi-directional columns";

	for (i=0; i<NFILES; ++i) fd[i].file = ERROR;
	
	if ( argc != NP_REQ )
	{
		usage();
		trouble	( "Invalid number of parameters",0);
	}

	idFmt	= IPGet(argv, IP_FMT, MAXFMT,"invalid sprite format") - 1;
	planes	= IPGet(argv, IP_PLN, MAXSP, "invalid number of planes");
	width	= IPGet(argv, IP_WDT, MAXSW, "invalid sprite width");
	height	= IPGet(argv, IP_HGT, MAXSH, "invalid sprite height");

	sf = &sptFmt[idFmt];
	if (!sf->handler)
		trouble(sf->text,"sprite format is not currently supported");

	sizCol	= planes * height;	/* size of 1 column in bytes */
	sizSrc	= sizCol * width;
	sizBuf	= sizSrc + sizCol;	/* extra column for shifted sprite */

	printf("sizCol:%d, sizSrc:%d, sizBuf:%d\n", sizCol,sizSrc,sizBuf);

	if (!(buf = malloc(sizBuf))) trouble("out of memory");

	setmem(buf,sizBuf,0);

	fd[SRCFILE].name = argv[IP_SRC];
	fd[DSTFILE].name = argv[IP_DST];

	if (!strcmp(fd[SRCFILE].name,fd[DSTFILE].name))
		trouble("file names must differ",0);

	openfd(&fd[SRCFILE],FCB_READ);

	sizefd(&fd[SRCFILE],sizSrc);

	if ((read(fd[SRCFILE].file,buf,sizSrc)) != sizSrc)
		trouble("read error in",fd[SRCFILE].name);

	creatfd(&fd[DSTFILE],FCB_WRITE);

	for (i = 0; i < 8; (*(sf->handler))(++i) )
	{
		putchar('.');
		if ((write(fd[DSTFILE].file,buf,sizBuf)) != sizBuf)
			trouble("write error in",fd[DSTFILE].name);
	}
	free(buf);
	closeall(fd);
}
