/* 2FASTNC Converts compressed IFF file to fast loading MCGA format */
/* This version does not save the palette, only width and height  */
/* use C O M P A C T MODE!!!  */

#include <stdio.h>
#include <dos.h>
#include <string.h>

FILE* f;
union REGS regs;

#define FORM 0x464f524dL
#define ILBM 0x494c424dL
#define BMHD 0x424d4844L
#define CMAP 0x434d4150L
#define BODY 0x424f4459L

unsigned char far *screen;

unsigned char palbuf[768];

unsigned char planes,compress;
int width,height;

unsigned char red[256],green[256],blue[256];

long read32bit();
long to32bit();

unsigned char fname[80];
unsigned char fastfname[80];

main(int argc, char **argv)
{
       if(! (f = fopen(argv[1],"rb")))
       {
               printf("Couldn't find %s!\n",argv[1]);
               exit(1);
       }
       strcpy(fname,argv[1]);


       setpal();

       if(compress == 1)
               load_comp();
       else
               load_uncompx();

       getch();

       if(argc > 2)
          strcpy(fastfname,argv[2]);
       else
          strcpy(fastfname,"");
       save_fastformat(fastfname);
       set_text();
}

load_comp()
{
unsigned int i,plane,scrstart;
unsigned int addr,scan,nextline;
unsigned char byte,b;

       for(scan=0;scan < height;scan++)
       {
               for(plane=0;plane < planes; plane++)              /* 8 bitplanes */
               {
                       addr = 320*scan;
                       nextline = addr + width;
                       do
                       {
                               byte = (unsigned char)getc(f);

                               if(byte < 128)
                               {
                                       for(i=0;i < byte+1; i++)
                                       {
                                               b = (unsigned char)getc(f);

                                               screen[addr++] |= (b >> 7 & 1) << plane;
                                               screen[addr++] |= (b >> 6 & 1) << plane;
                                               screen[addr++] |= (b >> 5 & 1) << plane;
                                               screen[addr++] |= (b >> 4 & 1) << plane;
                                               screen[addr++] |= (b >> 3 & 1) << plane;
                                               screen[addr++] |= (b >> 2 & 1) << plane;
                                               screen[addr++] |= (b >> 1 & 1) << plane;
                                               screen[addr++] |= (b & 1) << plane;
                                       }
                               }
                               else if(byte > 128)
                               {
                                       b = (unsigned char)getc(f);

                                       for(i=0;i < (256-byte)+1; i++)
                                       {
                                               screen[addr++] |= (b >> 7 & 1) << plane;
                                               screen[addr++] |= (b >> 6 & 1) << plane;
                                               screen[addr++] |= (b >> 5 & 1) << plane;
                                               screen[addr++] |= (b >> 4 & 1) << plane;
                                               screen[addr++] |= (b >> 3 & 1) << plane;
                                               screen[addr++] |= (b >> 2 & 1) << plane;
                                               screen[addr++] |= (b >> 1 & 1) << plane;
                                               screen[addr++] |= (b & 1) << plane;
                                       }
                               }
                       } while(addr < nextline);
               }
       }
       fclose(f);
}


load_uncompx()
{
int scan;

       for(scan = 0;scan<height;scan++)
         fread(&screen[320*scan],width,1,f);
       fclose(f);
}

load_uncomp()
{
unsigned int i,plane;
unsigned int addr,scan,nextline;
unsigned char b;


       for(scan=0;scan < height;scan++)
       {
               addr = 320*scan;
               nextline = addr+width;

               do
               {
                       for(plane=0;plane<planes;plane++)
                       {
                               addr = 320*scan;

                               for (i=0;i<(width/8);i++)
                               {
                                       b = (unsigned char)getc(f);

                                       screen[addr++] |= (b >> 7 & 1) << plane;
                                       screen[addr++] |= (b >> 6 & 1) << plane;
                                       screen[addr++] |= (b >> 5 & 1) << plane;
                                       screen[addr++] |= (b >> 4 & 1) << plane;
                                       screen[addr++] |= (b >> 3 & 1) << plane;
                                       screen[addr++] |= (b >> 2 & 1) << plane;
                                       screen[addr++] |= (b >> 1 & 1) << plane;
                                       screen[addr++] |= (b & 1) << plane;
                               }
                       }
               } while(addr < nextline);
       }
       fclose(f);
}



setpal()
{
int i;
unsigned char trash[10];
unsigned long id,length;

       id = read32bit();
       if(id != FORM)
          error("Not an IFF/LBM file!");

       length = read32bit();

       id = read32bit();
       if(id != ILBM)
         error("Not an IFF/LBM file!");


       while(1)
       {
               id = read32bit();
               length = read32bit();
               if(id == BMHD) break;
               fseek(f,length,SEEK_CUR);
       }

       width = read16bit();
       height = read16bit();

       fseek(f,4L,SEEK_CUR);

       planes = (unsigned char)getc(f);        /* no of bitplanes */
       getc(f);
       compress = (unsigned char)getc(f);      /* compress flag */

       fseek(f,9L,SEEK_CUR);

       while(1)
       {
               id = read32bit();
               length = read32bit();
               if(id == CMAP) break;
               fseek(f,length,SEEK_CUR);
       }

       screen = MK_FP(0xA000,0);
       set_mcga();

       fread(palbuf,768,1,f);
       for (i=0;i<768;i++)
         palbuf[i] >>= 2;

       regs.h.ah = 0x10;
       regs.h.al = 0x12;
       regs.x.bx = 0;
       regs.x.cx = 256;
       regs.x.dx = (unsigned)palbuf;
       int86(0x10,&regs,&regs);

       while(1)
       {
               id = read32bit();
               length = read32bit();
               if(id == BODY) break;
               fseek(f,length,SEEK_CUR);
       }
}


save_fastformat(unsigned char *fastfname)
{
FILE* f;
int i,scan;

       if(strlen(fastfname) == 0)
       {
               strcpy(fastfname,fname);
               fastfname[strchr(fastfname,'.')-fastfname] = 0;
               strcat(fastfname,".FST");
       }
       f = fopen(fastfname,"wb");
       fwrite(&width,2,1,f);
       fwrite(&height,2,1,f);

       scan = 0;
       do
       {
               fwrite(&screen[scan*320],width,1,f);
               scan++;
       } while(scan < height);

       fclose(f);
}


error(char *errmsg)
{
       printf("Error: %s\n",errmsg);
       fclose(f);
       exit(1);
}

int read16bit()
{
       int c1, c2;
       c1 = getc(f);
       c2 = getc(f);
       return to16bit(c1,c2);
}

int to16bit(c1,c2)
int c1, c2;
{
       return ((c1 & 0xff ) << 8) + (c2 & 0xff);
}

long read32bit()
{
       int c1, c2, c3, c4;

       c1 = getc(f);
       c2 = getc(f);
       c3 = getc(f);
       c4 = getc(f);
       return to32bit(c1,c2,c3,c4);
}

long to32bit(c1,c2,c3,c4)
{
       long value = 0L;

       value = (c1 & 0xff);
       value = (value<<8) + (c2 & 0xff);
       value = (value<<8) + (c3 & 0xff);
       value = (value<<8) + (c4 & 0xff);
       return (value);
}

set_mcga()
{
       regs.h.ah = 0;
       regs.h.al = 0x13;
       int86(0x10,&regs,&regs);
}


set_text()
{
       regs.h.ah = 0;
       regs.h.al = 3;
       int86(0x10,&regs,&regs);
}


/*

<IFF Format>    *.IFF

4 bytes         "FORM" (FORM chunk ID)
1 long          length of file that follows
4 bytes         "ILBM" (InterLeaved BitMap file ID)

4 bytes         "BMHD" (BitMap HeaDer chunk ID)
1 long          length of chunk [20]
20 bytes        1 word = image width in pixels
                               1 word = image height in lines
                               1 word = image x-offset [usually 0]
                               1 word = image y-offset [usually 0]
                               1 byte = # bitplanes
                               1 byte = mask (0=no, 1=impl., 2=transparent, 3=lasso)
                               1 byte = compressed [1] or uncompressed [0]
                               1 byte = unused [0]
                               1 word = transparent color (for mask=2)
                               1 byte = x-aspect [5=640x200, 10=320x200/640x400, 20=320x400]
                               1 byte = y-aspect [11]
                               1 word = page width (usually the same as image width)
                               1 word = page height (usually the same as image height)

4 bytes         "CMAP" (ColorMAP chunk ID)
1 long          length of chunk [3*n where n is the # colors]
3n bytes        3 bytes per RGB color.  Each color value is a byte
                               and the actual color value is left-justified in the
                               byte such that the most significant bit of the value
                               is the MSB of the byte.  (ie. a color value of 15 ($0F)
                               is stored as $F0)  The bytes are stored in R,G,B order.

4 bytes         "CRNG" (Color RaNGe chunk ID)
1 long          length of chunk [8]
8 bytes         1 word = reserved [0]
                               1 word = animation speed (16384 = 60 steps per second)
                               1 word = active [1] or inactive [0]
                               1 byte = left/lower color animation limit
                               1 byte = right/upper color animation limit

4 bytes         "CAMG" (Commodore AMiGa viewport mode chunk ID)
1 long          length of chunk [4]
1 long          viewport mode bits (bit 11 = HAM, bit 3 = interlaced)

4 bytes         "BODY" (BODY chunk ID)
1 long          length of chunk [# bytes of image data that follow]
? bytes         actual image data

NOTES: Some of these chunks may not be present in every IFF file, and may
not be in this order.  You should always look for the ID bytes to find a
certain chunk.  All chunk IDs are followed by a long value that tells the
size of the chunk (note that "ILBM" is not a chunk ID).  This is the number of
bytes that FOLLOW the 4 ID bytes and size longword.  The exception to this is
the FORM chunk.  The size longword that follows the FORM ID is the size of the
remainder of the file.  The FORM chunk must always be the first chunk in an
IFF file.

*/