/************************************************************************
    This file reads a bitmap file (BMP) and creates a series of character
    definition that make possible to display the bitmap in text mode
    using SCL1 ModifyCharSet function.

    Horizontally the bitmap size in bits must a multiple (in pixels) of 8 and
    of 16, 14 or 8 vertically depending of target monitor. The bitmap
    can be displayed using the /D switch. The total number of characters
    must be < than 32 (64 pixels x 64 is a good size). Run with /? switch 
    for more information.

    Bitmaps can be created using Windows's Paintbrush. Use the Options Menu
    Image Attributes to set size in pixels and 16 colors. Use only pure white 
    and black colors.

    Several BMP files are included. Files that start with VGA are for VGA
    and the ones that start with EGA are for EGA. Use the following command
    (after compiling BMPCNV.C) to view:

        VGA monitors:               EGA monitors:

        BMPCNV /D VGABMP1           BMPCNV /D /B:14 EGABMP1
****************************************************************************/

#include <scl1.h>
#include <stdlib.h>

/* structures used by to define BMP file header */

typedef unsigned int  WORD;
typedef unsigned long DWORD;

typedef struct{
        WORD    bfType;
        DWORD   bfSize;
        WORD    bfReserved1;
        WORD    bfReserved2;
        DWORD   bfOffBits;
        }BITMAPFILEHEADER;

typedef struct{
        DWORD      biSize;
        DWORD      biWidth;
        DWORD      biHeight;
        WORD       biPlanes;
        WORD       biBitCount;
        DWORD      biCompression;
        DWORD      biSizeImage;
        DWORD      biXPelsPerMeter;
        DWORD      biYPelsPerMeter;
        DWORD      biClrUsed;
        DWORD      biClrImportant;
        }BITMAPINFOHEADER;

char HelpText[]= "Converts a bitmap file to a character definition table that can\n"
                 "be used to modify EGA/VGA characters set.\n\n"
                 "BMPCNV [/B:bytes] INFILE[.BMP] [OUTFILE[.C]]\n\n"
                 "/B:bytes      specifies the number of bytes per character definition\n"
                 "              values range from 8 to 32 (default = 16)\n"
                 "INFILE[.BMP]  valid bitmap file to be converted\n"
                 "[OUTFILE[.C]] output file created, if not specified BMPCNV\n"
                 "              will use the name of the input file\n"
                 "/D            Displays the bitmap, does not create output file\n";

main(int argc,char **argv)
{
BITMAPFILEHEADER bfh;
BITMAPINFOHEADER bih;
LLData lld;
unsigned int bitbytes;
int tchars,tcols,tlines,line,col,scanline,points=16,display=0,schar;
unsigned char *p;
int i,i2,j,k;
unsigned int mask;
int handle;
char buffer[256];
char infile[80];
char outfile[80];
unsigned char NewChar[16];

printf("\n\nBITMAP conversion utility (C) 1991 by J. R. Alvira\n\n");

    /* process command line arguments */

if(argc < 2)
    {
    printf(HelpText);
    exit(-1);
    }

for(i=1,j=0;i < argc;++i)
    {
    if(*argv[i]=='/' || *argv[i]=='-')
        {
        if(*(argv[i]+1) == 'B' || *(argv[i]+1) == 'b')
            {
            if(*(argv[i]+2) == ':')
                points=atoi(argv[i]+3);
            else
                points=atoi(argv[i]+2);
            }

        else if(*(argv[i]+1) == 'D' || *(argv[i]+1) == 'd')
            display=1;

        else if(*(argv[i]+1) == '?')
            {
            printf(HelpText);
            exit(0);
            }
        else
            printf("Ignoring unknown switch %c\n",*(argv[i]+1));
        }
    else if(j==0)
        {
        strcpy(infile,argv[i]);
        AddExtension(infile,"BMP");
        ++j;
        }
    else
        {
        strcpy(outfile,argv[i]);
        AddExtension(outfile,"C");
        ++j;
        }
    }

if(points < 8 || points > 32)
    {
    printf("Illegal bytes value (8-32)\n");
    exit(-1);
    }

if(j == 1)
    {
    strcpy(outfile,infile);
    ChangeExtension(outfile,"C");
    }

if(OpenFile(infile,&handle,DOS2_RW))
    {
    printf("\nUnable to open %s\n\n",infile);
    exit(-1);
    }

if(ReadFile(handle,(char *)&bfh,sizeof(BITMAPFILEHEADER)))
    {
    CloseFile(handle);
    printf("Error reading file header\n");
    exit(-1);
    }

if(bfh.bfType != 0x4d42)
    {
    CloseFile(handle);
    printf("Not a valid bitmap file\n");
    exit(-1);
    }

if(ReadFile(handle,(char *)&bih,sizeof(BITMAPINFOHEADER)))
    {
    CloseFile(handle);
    printf("Error reading info header\n");
    exit(-1);
    }

if(bih.biCompression)
    {
    CloseFile(handle);
    printf("Bitmat data is compressed, cannot handle compressed format.\n");
    exit(-1);
    }

if((int)(bih.biWidth % 8))
    {
    CloseFile(handle);
    printf("Bitmat width in pixels must be multiple of eight\n");
    exit(-1);
    }

if((int)(bih.biHeight % points))
    {
    CloseFile(handle);
    printf("Bitmat height in pixels must be multiple of the number of bytes per character.\n");
    exit(-1);
    }


    /* calculate number of character columns (8 bits) and lines using
       BMP header information */

tcols=(int)(bih.biWidth / 8);
tlines=(int)(bih.biHeight / points);
tchars=tcols * tlines;

    /* calculate total number of bytes with bitmap data */

bitbytes=(unsigned int)(bfh.bfSize - bfh.bfOffBits);

printf("%i bytes of bitmap data\n",bitbytes);
printf("Creating %i characters, %i bytes each (%i columns-%i lines)\n",tchars,points,tcols,tlines);
printf("Width in pixels: %li\n",bih.biWidth);
printf("Height in pixels: %li\n",bih.biHeight);

p=(unsigned char *)calloc(bitbytes,sizeof(char));   /* allocate buffer */
if(p==0)
    {
    printf("Not enough memory to read bitmap data\n");
    CloseFile(handle);
    exit(-1);
    }

    /* we'll use a linked list to process bitmap data */

LinkedList(LL_INIT,&lld);
lld.DataSize=sizeof(char) * points;     /* size of each character */

    /* create a linked list node for each character */

for(i=0;i < tchars;++i)
    {
    lld.Data=p;
    if(LinkedList(LL_ADD,&lld)==LL_MEM_ERROR)
        {
        printf("Not enough memory to process bitmap data\n");
        CloseFile(handle);
        exit(-1);
        }
    }

    /* move file pointer to the begining of bitmap data */

MoveFilePt2Offset(handle,bfh.bfOffBits);
if(ReadFile(handle,p,bitbytes))             /* read data */
    {
    printf("Unable to read bit data\n");
    CloseFile(handle);
    exit(-1);
    }

    /* conversion begins here */

mask=0x80;
LinkedList(LL_FIRST,&lld);

for(line=0;line < tlines;++line)            /* process each character line */
    {
    LinkedList(LL_SAVE_POSITION,&lld);
    for(scanline=points-1;scanline >= 0;--scanline)  /* each scanline (pixel) */
        {
        for(col=0;col < tcols;++col)        /* all characters in each line */
            {
            for(i=0,mask=0x80;i < 4;++i,++p)
                {
                if(((*p & 0xf0) >> 4))
                    *(lld.Data+scanline) |= mask;
                mask >>= 1;

                if((*p & 0x0f))
                    *(lld.Data+scanline) |= mask;
                mask >>= 1;
                }
            LinkedList(LL_NEXT,&lld);
            }
        LinkedList(LL_RESTORE_POSITION,&lld);
        }
    for(i=0;i < tcols;++i)
        LinkedList(LL_NEXT,&lld);
    }

if(display==0)      /* create file? */
    {

    if(CreateFile(outfile,&handle,F_ARCHIVE))
        {
        printf("Unable to write C source file\n");
        CloseFile(handle);
        exit(-1);
        }

    k=sprintf(buffer,"char CharDef[]={\r\n");
    if(WriteFile(handle,buffer,k))
        {
        printf("Error writing C source file\n");
        CloseFile(handle);
        }
    }

LinkedList(LL_LAST,&lld);
for(i=0;i < tcols-1;++i)
    LinkedList(LL_PREVIOUS,&lld);

        /* set video mode to correctly display bitmap */

VideoConfig();
if(display==1 && (VC_Monitor==VC_VGA || VC_Monitor==VC_EGA))
    {
    printf("Press any key to view bitmap...\n");
    GetKey();
    if(VC_Monitor==VC_VGA)
        {
        switch(points)
            {
            case 16:
                SetVideoMode(3);
                break;
            case 14:
                SetVideo28();
                break;
            case 8:
                SetVideo4350();
                break;
            default:
                display==-1;
                break;
            }
        }
    if(VC_Monitor==VC_EGA)
        {
        switch(points)
            {
            case 14:
                SetVideoMode(3);
                break;
            case 8:
                SetVideo4350();
                break;
            default:
                display==-1;
                break;
            }
        if(tchars > 32)
            {
            printf("Bitmap is too big to display\n");
            display=0;
            }
        }
  }

if(display==1)
    SetCurPos(tlines+2,0);

else if(display==-1)
    {
    printf("Your monitor does not support the characters scanlines\n");
    exit(-1);
    }

for(i=0,schar=192;i < tlines;++i)
    {
    LinkedList(LL_SAVE_POSITION,&lld);
    for(i2=0;i2 < tcols;++i2,++schar)
        {
        for(j=0,k=0;j < points;++j)
            {
            k+=sprintf(buffer+k,"0x%02x,",(unsigned int)(unsigned char)(lld.Data[j]));

            if(display==1)
                NewChar[j]=(unsigned char)(lld.Data[j]);

            if(j==points-1)
                {
                if(i==tlines-1 && i2==tcols-1)
                    {
                    --k;
                    k+=sprintf(buffer+k,"};\r\n\r\n");
                    }
                else
                    k+=sprintf(buffer+k,"\r\n");
                }
            }

        if(display==1)
            {
            ModifyCharSet(points,schar,1,NewChar);
            WriteChar(7,i,i2,1,schar);
            }

        else if(WriteFile(handle,buffer,k))
            {
            printf("Error writing C source\n");
            CloseFile(handle);
            }

        LinkedList(LL_NEXT,&lld);
        }
    LinkedList(LL_RESTORE_POSITION,&lld);
    for(j=0;j < tcols;++j)
        LinkedList(LL_PREVIOUS,&lld);
    }


if(display==1)
    {
    printf("Press any key...\n");
    GetKey();
    InitVideo();
    }
else
    {
    if(CloseFile(handle))
        {
        printf("Error writing C source\n");
        exit(-1);
        }
    else
        printf("File %s has been created\n",outfile);
    }
}




