/* IFF ILBM routines */
/* NOTE: This only works if you link with libnix. It fails with ixemul		*
because ixemul.library is opened by the maintask and iffpage is called		*
by the subtask. The problem are the file-commands which should be replaced	*
by Amigados-commands (Open(), Write()...) or you link with libnix 			*/

#include "PostPre.h"
#include "Global.h"

extern	int	page_counter;
extern	BPTR	outfh;

int				iffseq = 0;
static int		newpage=TRUE, counter, end, ifferr, xa, ya, i,
					j, k, ch, fslen;
static long		addr1, addr2;
static UWORD	w;
static char		fname[110], fsnum[10];
static FILE		*ifffptr;

/* Find the greatest common divisor of two positive integers.  If one is
 * zero the result is the other */

int igcd(int n, int m)
{   unsigned int n1, m1, r;
    if      (n > m)
    {   n1 = n;
        m1 = m;
    }
    else if (m > n)
    {   n1 = m;
        m1 = n;
    }
    else
        return n;
    while (m1 != 0)
    {   r = n1 % m1;
        n1 = m1;
        m1 = r;
    }
    return (int) n1;
}

/* Put a byte */

static void iffputb(int b)
{   if (ifferr) return;
    if (putc((int) b, ifffptr) == EOF)
    {   ifferr = 1;
        return;
    }
}

/* Put a word */

static void iffputw(int w)
{   iffputb(w>>8);
    iffputb(w);
}

/* Put a long */

static void iffputl(int l)
{   iffputb(l>>24);
    iffputb(l>>16);
    iffputb(l>>8);
    iffputb(l);
}

/* Put a string */

static void iffputs(char *str)
{   while (*str) iffputb(*str++);
}

/* Pack a bitmap row */

static void iffpack(char *buf, int len)
{   int b, c, l;
    if (ifferr) return;
    l = 0;
    while (len--)
    {   b = *buf++;                  /* Pick up a byte */
        c = 1;
        while (len && *buf == b && c < 128)
        {   c++;
            buf++;
            len--;                   /* See if it begins a run */
        }
        if (c == 2 &&                /* If a two byte run */
            l > 0 &&                 /*    and preceeded by literals */
            l <= 125 &&              /*    and not more than 125 of them */
            (len <= 2 ||             /*    and no more than 2 bytes left */
             *buf != *(buf + 1)))    /*        or not followed by a run */
        {   c = 1;                   /* Then make it a literal */
            buf--;
            len++;
        }
        if (c == 1)                  /* If not a run */
        {   l++;                     /* Then it must be a literal */
            c = 0;
        }
        if (l > 0 &&                 /* If we have some literals */
            (c > 1 ||                /*    and beginning a run */
             l == 127 ||             /*    or reached 127 */
             len == 0))              /*    or no more bytes left */
        {   if (putc((unsigned char) (l - 1), ifffptr) == EOF)
            {   ifferr = 1;
                return;
            }
            while (l)              /* Then write out  the literals */
            {   if (putc((unsigned char) *(buf - c - l), ifffptr) == EOF)
                {   ifferr = 1;
                    return;
                }
                l--;
            }
        }
        if (c > 1)                   /* If we have a run, write it */
        {   if (putc((unsigned char) (1-c), ifffptr) == EOF)
            {   ifferr = 1;
                return;
            }
            if (putc((unsigned char) b, ifffptr) == EOF)
            {   ifferr = 1;
                return;
            }
        }
    }
}

/* Determine the current address */

static long ifftell(void)
{   long addr;
    if (ifferr) return 0;
    if ((addr = ftell(ifffptr)) == -1)
    {   ifferr = 1;
        return 0;
    }
    return addr;
}

/* Fix up the length of a chunk */

static void ifffixup(long addr)
{   long size;
    if (ifferr) return;
    if ((size = ftell(ifffptr)) == -1)
    {   ifferr = 1;
        return;
    }
    if (size & 1) iffputb(0);
    size = size - addr - 8;
    if (fseek(ifffptr, addr + 4, 0) != 0)
    {   ifferr = 1;
        return;
    }
    iffputl(size);
    if (fseek(ifffptr, 0, 2) != 0)
    {   ifferr = 1;
        return;
    }
}

/* Write the page to the iff output file */

void iffpage(struct BitMap *my_iff_bm)
{

/* Compute the aspect ratio.  Make sure the values fit into a byte */

	xa = parm.page.yden;
	ya = parm.page.xden;
	i = igcd(xa, ya);
	xa /= i;
	ya /= i;
	while (xa > 255 && ya > 255)
	{
		xa /= 2;
		ya /= 2;
	}

/* Construct the file name.  Copy it, replacing "*" by the sequence
 * number.  The scan it backwards replacing "?" by digits */

	if(newpage)
	{
		i = iffseq;
		iffseq++;
		fslen = 0;
		while (i)
		{
			fsnum[fslen++] = i % 10 + '0';
			i /= 10;
		}
		i = j = 0;
		for (;;)
		{
			if (j > 100)
			{
				ioerror = errioerror;
				return;
			}
			ch = Options.PostOpts.iffname[i++];
			if (ch == '*')
			{
				k = fslen;
				while (k--) fname[j++] = fsnum[k];
			}
			else fname[j++] = ch;
			if (ch == 0) break;
		}
		k = 0;
		while (--j)
		{
			if (fname[j] == '?') fname[j] = (k < fslen) ? fsnum[k++] : '0';
		}
	}

	if(argverbose && !argwindow)
	{
		FPrintf(outfh, " to iff-file '%s' ... ", (ULONG) fname);
		Flush(outfh);
	}

/* Open the file, write a FORM ILBM, and close it again */

	if(newpage)
	{
		ifferr = 0;
		ifffptr = fopen(fname, "wb");
		if (ifffptr == NULL) ifferr = 1;

		addr1 = ifftell();
		iffputs("FORM");          /* FORM ILBM */
		iffputl(0);
		iffputs("ILBM");

		iffputs("BMHD");          /* BMHD */
		iffputl(20);
		iffputw(parm.page.xsize); /* Width */
		iffputw(parm.page.yheight); /* Height */
		iffputw(0);               /* X position */
		iffputw(0);               /* Y position */
		iffputb(parm.page.depth); /* Number of bit planes */
		iffputb(0);               /* Masking:     None */
		iffputb(1);               /* Compression: ByteRun */
		iffputb(0);               /* Pad1 */
		iffputw(0);               /* Transparent colour */
		iffputb(xa);              /* X aspect */
		iffputb(ya);              /* Y aspect */
		iffputw(parm.page.xsize); /* Source width */
		iffputw(parm.page.yheight); /* Source height */

		addr2 = ifftell();
		iffputs("CMAP");          /* CMAP */
		iffputl(0);
		for (i = 0; i < colormap.Count; i++)
		{
			w = ((UWORD *) colormap.ColorTable)[i];
			iffputb(((w >> 8) & 15) << 4);
			iffputb(((w >> 4) & 15) << 4);
			iffputb(( w       & 15) << 4);
		}
		ifffixup(addr2);

		iffputs("CAMG");      /* CAMG */
		iffputl(4);
		iffputl(Options.Screen.ScrDisplayID);

		addr2 = ifftell();
		iffputs("BODY");          /* BODY */
		iffputl(0);

		counter = 0;
	}

	end = my_iff_bm->Rows;
	counter += my_iff_bm->Rows;
	if(counter < parm.page.yheight)
	{
		newpage = FALSE;
	}
	else
	{
		newpage = TRUE;
		end -= counter-parm.page.yheight;
	}

	for (j = 0; j < end; j++)
		for (i = 0; i < my_iff_bm->Depth; i++)
			iffpack((char *) my_iff_bm->Planes[i] + j * my_iff_bm->BytesPerRow,
						my_iff_bm->BytesPerRow);

	fflush(ifffptr);
	if(newpage)
	{
		ifffixup(addr2);
		ifffixup(addr1);
		if (fclose(ifffptr) == EOF) ifferr = 1;
	}
	if (ifferr) ioerror = errioerror;
}
