/*
   F_GRAPH.C

   GIF, BMP, PPM, Filters adapted from DeuTex by Olivier Montanuy
   Integrated into DEU by Mark Mathews
   PCX Filter by Mark Mathews

*/

#include "deu.h"
#include "d_misc.h"
#include "d_wads.h"
#include "f_color.h"
#include "f_graph.h"
#include "f_lzw.h"



/* ------------------ START GIF module ----------------------------*/

/*
struct PIXEL
  {
    UInt8 R;
    UInt8 G;
    UInt8 B;
  };
*/
static struct
  {
    UInt16 Transparent;
    UInt16 DelayTime;
    UInt16 InputFlag;
    UInt16 Disposal;
  } Gif89 = { -1, -1, -1, 0 };
static struct
{
  Int16 Width;
  Int16 Height;
  Int16 BitPixel;
  Int16 ColorRes;
  Int16 Backgnd;
  Int16 AspRatio;
} GifScreen;
static char  GifIdent[6];
const GIFHEADsz = 2+2+1+1+1;
static struct GIFHEAD   /*size =7*/
{
  Int16 xsize;
  Int16 ysize;
  UInt8 info;     /*b7=colmap b6-4=colresol-1 b2-1=bitperpix-1*/
  UInt8 backgnd;  /*Backg color*/
  UInt8 aspratio; /*Aspect ratio*/
} GifHead;
struct PIXEL GifColor[256];   /*color map*/
const GIFIMAGEsz = 2+2+2+2+1;
static struct GIFIMAGE        /*size =9*/
{
  Int16 ofsx;  /*0,1 left offset*/
  Int16 ofsy;  /*2,3 top offset*/
  Int16 xsize; /*4,5 width*/
  Int16 ysize; /*6,7 heigth*/
  char info;   /*8   b7=colmap b6=interlace b2-1=bitperpix-1*/
} GifImage;


UInt8 Idx2Doom[256];


static UInt8 huge *Raw;
static Int32 CountTop=0;
static Int32 CountCur=0;



#define INTERLACE  0x40
#define COLORMAP   0x80

/*
** process the GIF extensions
*/
Int16 GIFreadBlock(FILE *fp, char buff[256])
{
  Int16	data,count,c;
  if((data=fgetc(fp))==EOF)
    return -1;/*no data block*/
  count = data&0xFF;
  for(c=0;c<count;c++)
  {
    if((data=fgetc(fp))==EOF)
      return -1;
    buff[c]= data&0xFF;
  }
  return count;
}


static void GIFextens(FILE *fp)
{
  char	Buf[256];
  Int16 label;
  if((label=fgetc(fp))==EOF)
    return;
  switch (label&0xFF)
  {
    case 0x01:		/* Plain Text Extension */
      GIFreadBlock(fp, Buf);
      /*Int16 lpos; Int16 tpos;Int16 width;Int16 heigth;char cellw;char cellh;char foregr;char backgr*/
      break;
    case 0xff:		/* Application Extension */
      break;
    case 0xfe:		/* Comment Extension */
      break;
    case 0xf9:		/* Graphic Control Extension */
      GIFreadBlock(fp, Buf);
      Gif89.Disposal    = (Buf[0] >> 2) & 0x7;
      Gif89.InputFlag   = (Buf[0] >> 1) & 0x1;
      Gif89.DelayTime   = ((Buf[2]<<8)&0xFF00) + Buf[1];
      if(Buf[0] & 0x1)Gif89.Transparent = Buf[3];
      break;
    default: 		/*Unknown GIF extension*/
      break;
  }
  while (GIFreadBlock(fp, Buf)>0);
}


/*
** Read Gif Indexes
*/
static UInt8 far *GIFreadPix(FILE *fp,Int16 Xsz,Int16 Ysz)
{
   UInt8 far *raw=NULL;
   UInt16 rawSz;
#if NEWGIFD
#else
   Int16 v;
   UInt16 rawpos;
   unsigned char c=0;
#endif

   /*
   ** get some space
   */
   rawSz = ((Int32)Xsz)*((Int32)Ysz);
   raw = (UInt8 far *)GetFarMemory(rawSz);
#if NEWGIFD
   InitDecoder( fp, 8, Xsz);
   Decode((UInt8 huge *)raw, rawSz);
   ExitDecoder();
#else
   /* Initialize the Compression routines */
   if (fread(&c,1,1,fp)!=1)
   {
     printf("[GIF: read error]\n" );
     FreeFarMemory(raw);
     return 0;
   }
   if (LWZReadByte(fp, TRUE, c) < 0)
   {
     printf("[GIF: bad code in image]\n" );
     FreeFarMemory(raw);
     return 0;
   }
   /* read the file */
   for(rawpos=0;rawpos<rawSz;rawpos++)
   {
     if ((v = LWZReadByte(fp,FALSE,c)) < 0 )
     {
       printf("[GIF: too short]\n");
       FreeFarMemory(raw);
       return 0;
     }
     raw[rawpos]=(v&0xFF);
   }
   while (LWZReadByte(fp,FALSE,c)>=0);  /* ignore extra data */
#endif
   return raw;
}

/*
**  Un-Interlace a GIF
*/
static UInt8 far *GIFintlace(UInt8 far *org,Int16 Xsz,Int16 Ysz)
{
  UInt16 rawpos,orgpos;
  Int16 pass,Ys=0,Y0=0,y;
  UInt8 far *raw;
  rawpos = Xsz * Ysz;
  raw = (UInt8 far *)GetFarMemory(rawpos);
  orgpos = 0;
  for(pass=0;pass<4;pass++)
  {  switch(pass)
     { case 0:	Y0=0;	Ys=8;	break;
       case 1:	Y0=4;	Ys=8;	break;
       case 2:	Y0=2;	Ys=4;	break;
       case 3:	Y0=1;	Ys=2;	break;
     }
     rawpos= Y0 * Xsz;
     for(y=Y0;y<Ysz;y+=Ys)
     {
       memcpy(&raw[rawpos],&org[orgpos],(size_t)Xsz);
       rawpos+= (Int32)Ys*(Int32)Xsz;
       orgpos+= Xsz;
     }
  }
  FreeFarMemory(org);
  return raw;
}

/*
** write GIF
*/

#if NEWGIFE
#else
typedef Int16	      code_int;
extern void compress( Int16 init_bits, FILE *outfile, code_int (* ReadValue)(void));

static code_int NextPixel(void )
{
  UInt8 c;
  if(CountCur>=CountTop)
    return EOF;
  c=Raw[ CountCur];
  CountCur++;
  return ((code_int)c & 0xFF);
}
#endif

/*

  Read Gif file, allocate buffer, and store image into buffer.

  FILE * giffile  - file already open
  UInt8 huge *raw - store image into this buffer


*/

UInt8 far * ReadGIF(FILE *giffile, Int16 *rawX, Int16 *rawY)
{

  UInt16 rawSz;
  UInt8 far *raw=NULL;
/*  struct PIXEL huge * doompal;*/
/*UInt8 GifColor[768];*/

/*  char transparent;*/
  Int16  Xsz=0,Ysz=0;
  static Bool IntLace=FALSE;
  Int16  bitPixel;
  UInt16  rawpos;
  Int16  c;
/*  UInt32 size;*/
/*  UInt32 total;*/

#if NEWGIFD
#else
   decompressInit();
#endif

   /*
   ** screen descriptor
   */
  if (fread(GifIdent,6,1,giffile)!=1)
    {
      printf("[GIF: read error]\n" );
      return 0;
    }
  if(strncmp(GifIdent,"GIF87a",6)!=0)
    if(strncmp(GifIdent,"GIF89a",6)!=0)
      {
        printf("[GIF: not a 87a or 89a GIF]\n" );
        return 0;
      }
  if (fread(&GifHead,GIFHEADsz,1,giffile)!=1)
    {
      printf("[GIF: read error]\n" );
      return 0;
    }
  GifScreen.Width    = GifHead.xsize;
  GifScreen.Height   = GifHead.ysize;
  bitPixel=            1<<((GifHead.info&0x07)+1);
  GifScreen.BitPixel = bitPixel;
  GifScreen.ColorRes = (((GifHead.info>>3)&0xE)+1);
  GifScreen.Backgnd  = GifHead.backgnd;
  GifScreen.AspRatio = GifHead.aspratio;
  memset(GifColor,0,256*sizeof(struct PIXEL));
  /* read Global Color Map */
  if ((GifHead.info)&COLORMAP)
    {
      if(fread(GifColor,sizeof(struct PIXEL),bitPixel,giffile)!=bitPixel)
        {
          printf("[GIF: read error]\n");
          return 0;
        }
    }
  /*
  ** Read extension, images, etc...
  */
  while((c=fgetc(giffile)) !=EOF)
    {
      if(c==';')
        break;        /* GIF terminator */
      /*no need to test imagecount*/
      if(c=='!')         /* Extension */
        GIFextens(giffile);
      else if(c==',')   /*valid image start*/
        {
          if(raw!=NULL)  /* only keep first image*/
            {
              printf("[GIF: Other images discarded]\n");
              break;
            }
          if (fread(&GifImage,GIFIMAGEsz,1,giffile)!=1)
            {
              printf("[GIF: read error]\n");
              return 0;
            }
          /* GifImage.ofsx,ofsy  X,Y offset ignored */
          bitPixel = 1<<((GifImage.info&0x07)+1);
          IntLace= (GifImage.info & INTERLACE)? TRUE:FALSE;
          Xsz= GifImage.xsize;
          Ysz= GifImage.ysize;
          if((Xsz<=0)||(Ysz<=0))
            {
              printf("[GIF: bad size]\n");
              return 0;
            }
          if(GifImage.info & COLORMAP)
            {
              if(fread(GifColor,sizeof(struct PIXEL),bitPixel,giffile)!=bitPixel)
                {
                  printf("[GIF: read error]\n");
                  return 0;
                }
            }
          /*read the GIF. if many pictures, only the last
            one is kept.
          */
          raw = GIFreadPix(giffile, Xsz,Ysz);
          if(raw == 0)
            return 0;
        }
        /*else, not a valid start character, skip to next*/
    }
  if(raw == NULL)
    {
      printf("[GIF: No picture found]\n");
      return 0;
    }

  /*convert colors*/
  for (c=0; c<256; c++)
    {
      Idx2Doom[c]=(UInt8)COLindex((UInt8)GifColor[c].R,(UInt8)GifColor[c].G,(UInt8)GifColor[c].B,(UInt8)c);
    }
  rawSz = Xsz  * Ysz;
  for (rawpos=0; rawpos<rawSz;rawpos++)
    {
      raw[rawpos]=Idx2Doom[((Int16)raw[rawpos])&0xFF];
    }

  /*unInterlace*/
  if(IntLace==TRUE)
    {
      raw = GIFintlace(raw,Xsz,Ysz);
    }

#if NEWGIFD
#else
   decompressFree();
#endif


  *rawX=Xsz;
  *rawY=Ysz;


  return raw;

}



void WriteGIF(FILE *giffile, Int16 rawX, Int16 rawY, UInt8 far *raw,
              struct PIXEL huge * doompal)
{

  Int32 rawSz;

  rawSz = (Int32)rawX * (Int32)rawY;

  /* screen header */
  strncpy(GifIdent,"GIF87a",6);
  fwrite(GifIdent, 1, 6, giffile );         /*header*/
  GifHead.xsize=rawX;
  GifHead.ysize=rawY;
  GifHead.info = COLORMAP| ((8 - 1) << 4)| (8 - 1);

  /* global colormap, 256 colors, 7 bit per pixel*/
  GifHead.backgnd=(char)0;
  GifHead.aspratio=(char)0;
  fwrite(&GifHead,1,GIFHEADsz,giffile);
  fwrite(doompal,sizeof(struct PIXEL),256,giffile); /*color map*/
  fputc(',',giffile);                        /*Image separator*/

  /* image header */
  GifImage.ofsx=0;           /*left offset*/
  GifImage.ofsy=0;
  GifImage.xsize=rawX; /*size*/
  GifImage.ysize=rawY;
  GifImage.info=0; /*INTERLACE*/
  fwrite( &GifImage,1,GIFIMAGEsz,giffile);

  /* image data */
  fputc(8,giffile);            /* Write out the initial code size */
#if NEWGIFE
  InitEncoder(giffile,8);
  Encode((UInt8 huge *)raw,rawSz);
  ExitEncoder();
#else
  Raw = raw;  /* init */
  CountTop = rawSz;
  CountCur = 0;
  compressInit();
  compress( 8+1, giffile, NextPixel );  /*  write picture, InitCodeSize=8 */
  compressFree();
#endif
  /* termination */
  fputc(0,giffile);          /*0 length packet to end*/
  fputc(';',giffile); /*GIF file terminator*/

}



/* ------------------ END GIF module ------------------------------*/



/* ------------------ START BMP module ----------------------------*/


struct BMPPALET { UInt8 B; UInt8 G; UInt8 R; UInt8 Zero;};
struct BMPPIXEL { UInt8 B; UInt8 G; UInt8 R;};
/*
** bitmap conversion
*/

struct BMPHEAD {
Int32  bmplen;                /*02 total file length  Size*/
Int32  reserved;                /*06 void Reserved1 Reserved2*/
Int32  startpix;                /*0A start of pixels   OffBits*/
/*bitmap core header*/
Int32  headsz;                /*0E Size =nb of bits in bc header*/
Int32  szx;                /*12 X size = width         Int16 width*/
Int32  szy;                /*16 Y size = height         Int16 height*/
Int32  planebits;        /*1A equal to 1                word planes*/
                        /*1C nb of bits                word bitcount  1,4,8,24*/
/**/
Int32  compress;                /*1E Int32 compression = BI_RGB = 0*/
Int32  pixlen;              /*22 Int32 SizeImage    size of array necessary for pixels*/
Int32  XpixRes;                /*26   XPelsPerMeter   X resolution no one cares*/
Int32  YpixRes;            /*2A  YPelPerMeter    Y resolution  but code1a=code1b*/
Int32  ColorUsed;                /*2C ClrUsed       nb of colors in palete*/
Int32  ColorImp;                /*32 ClrImportant   nb of important colors in palete*/
/*palete pos: ((UInt8 *)&headsz) + headsz */
/*palette size = 4*nb of colors. order is Blue Green Red (Black? always0)*/
/*bmp line size is xsize*bytes_per_pixel aligned on Int32 */
/*pixlen = ysize * line size */
};
/*
** BMP to RAW conversion
**
** in: UInt8 *bmp;    Int32 bmpsize;     UInt8 COLindex(R,G,B);
**
** out:  UInt8 *raw;  Int16 rawX; Int16 rawY;
*/
UInt8 far * ReadBMP(FILE *bmpfile, Int16 *rawX, Int16 *rawY)
{
   struct BMPHEAD   huge *head;      /*bmp header*/
   struct BMPPALET  huge *palet;     /*palet. 8 bit*/
   Int32 paletsz;
   UInt8 huge *line;                 /*line of BMP*/
   Int32 startpix,linesz=0;
   struct BMPPIXEL  huge *pixs;     /*pixels. 24 bit*/
   UInt8  huge *bmpidxs;                    /*color indexs in BMP 8bit*/
   Int16 szx,szy,x,y,p,nbits;
   Int32 ncols;
   Int32  bmpxy;
   UInt8 far *raw;                  /*point to pixs. 8 bit*/
   UInt16 rawpos;
   UInt8 col='\0';
   char sig[2];


   /*
   ** read BMP header for size
   */

   if(fread(sig,2,1,bmpfile)!=1)
   {
     printf("[Can't read sig of BMP]\n");
     return 0;
   }
   if(strncmp(sig,"BM",2)!=0)
   {
     printf("[Bmp: signature incorrect]\n");
     return 0;
   }

   head=(struct BMPHEAD huge *)GetFarMemory(sizeof(struct BMPHEAD));
   if(fread(head,sizeof(struct BMPHEAD),1,bmpfile)!=1)
   {
     printf("[Can't read header of BMP]\n");
     return 0;
   }
   /*
   ** check the BMP header
   */
   startpix = head->startpix;
   szx = (Int16)head->szx;
   if((szx<1)||(szx>320))
   {
     printf("[Bmp width must be < 320]\n");
     return 0;
   }
   szy=(Int16)head->szy;
   if((szy<1)||(szy>200))
   {
     printf("[Bmp: height must be <200]\n");
     return 0;
   }
   ncols = head->ColorUsed;
   /*
   ** Allocate memory for raw bytes
   */
   raw=(UInt8 far *)GetFarMemory(((Int32)szx)*((Int32)szy));


   /*
   ** Determine line size and palet (if needed)
   */
   nbits=(Int16)((head->planebits>>16)&0xFFFFL);
   switch(nbits)
   {
     case 24:
       printf("[Warning: Color quantisation is slow. Use 8 bit BMP]\n");
       linesz=((((Int32)szx)*sizeof(struct BMPPIXEL))+3L)&~3L; /*RGB, aligned mod 4*/
       break;
     case 8:
       linesz=(((Int32)szx)+3L)&~3L;     /*Idx, aligned mod 4*/
       if(ncols>256)
       {
         printf("[Bmp: palete should be 256 color]\n");
         return 0;
       }
       if(ncols<=0)
         ncols=256;   /*Bug of PaintBrush*/
       paletsz=(ncols)*sizeof(struct BMPPALET);
       /*set position to palete  (a bit hacked)*/
       if(fseek(bmpfile,0xEL+head->headsz,SEEK_SET))
       {
         printf("[Bmp: seek failed]\n");
         return 0;
       }
       /*load palete*/
       palet=(struct BMPPALET huge *)GetFarMemory(paletsz);
       if(fread(palet,(size_t)paletsz,1,bmpfile)!=1)
       {
         printf("[Bmp: can't read palette]\n");
         return 0;
       }
       for(p=0;p<ncols;p++)
       {
         Idx2Doom[p]=COLindex(palet[p].R,palet[p].G,palet[p].B,(UInt8)p);
       }

       FreeFarMemory(palet);
       break;
     default:
       {
         printf("[Bmp: type not supported (%d bits)]\n",nbits);
         return 0;
       }
   }
   bmpxy=((Int32)linesz)*((Int32)szy);
   /*Most windows application bug on one of the other*/
   /*picture publisher: bmplen incorrect*/
   /*Micrographix: bmplen over estimated*/
   /*PaintBrush: pixlen is null*/
   if(head->pixlen<bmpxy)
     if(head->bmplen<(startpix+bmpxy))
     {
       printf("[Bmp: size of pixel area incorrect]\n");
       return 0;
     }

   FreeFarMemory(head);
   /* seek start of pixels */
   if(fseek(bmpfile,startpix,SEEK_SET))
   {
     printf("[Bmp: seek failed]\n");
     return 0;
   }
   /* read lines */

   line = (UInt8 huge *)GetFarMemory(linesz);
   bmpidxs=(UInt8 huge *)line;
   pixs=(struct BMPPIXEL huge *)line;

   /*convert bmp pixels/bmp indexs into doom indexs*/

   for(y=szy-1;y>=0;y--)
   {
     if(fread(line,(size_t)linesz,1,bmpfile)!=1)
     {
       printf("[Can't read BMP line]\n");
       return 0;
     }
     for(x=0;x<szx;x++)
     {
       switch(nbits)
       {
         case 24:
           col=COLindex(pixs[x].R,pixs[x].G,pixs[x].B,0);
           break;
         case 8:
           col=Idx2Doom[((Int16)bmpidxs[x])&0xFF];
           break;
       }
       rawpos=((Int32)x)+((Int32)szx)*((Int32)y);
       raw[rawpos]=col;
     }
   }
   FreeFarMemory(line);

   *rawX=szx;
   *rawY=szy;
   return raw;
}

/*
** Raw to Bmp  8bit
**
** in:   Int16 rawX; Int16 rawY;  struct BMPPALET doompal[256]
**       UInt8 *raw
** out:  Int32 bmpsz;
**       UInt8 bmp[bmpsz];
*/
void WriteBMP(FILE *bmpfile, Int16 rawX, Int16 rawY, UInt8 far *raw,
                struct PIXEL huge * doompal)

{
   struct BMPHEAD   huge *head; /*bmp header*/
   struct BMPPALET  huge *palet;/*palet. 8 bit*/
   UInt8  huge *bmpidxs;                /*color indexs in BMP 8bit*/
   Int16 linesz;                     /*size of line in Bmp*/
   Int32  startpix,pixlen,bmplen,paletsz;
   Int16 x,y,ncol;
   UInt16  rawpos;
   char sig[2];

   /*BMP 8 bits avec DOOM Palete*/
   ncol=256;
   paletsz = ncol*sizeof(struct BMPPALET);
   linesz = (rawX+3)&(~3);  /*aligned mod 4*/
   pixlen = (Int32)linesz * (Int32)rawY;
   startpix=2+sizeof(struct BMPHEAD)+paletsz;
   bmplen=startpix+pixlen;
   strncpy(sig,"BM",2);
   if(fwrite(sig,2,1,bmpfile)!=1)
   {
     printf("[Can't write BMP file]\n");
     return;
   }
   /*Header*/
   head=(struct BMPHEAD huge *)GetFarMemory(sizeof(struct BMPHEAD));
   head->bmplen =   bmplen;
   head->reserved =  0L;
   head->startpix =  startpix;
   head->headsz =    0x28L;
   head->szx =       rawX;
   head->szy =       rawY;
   head->planebits = 0x80001L;/*1 plane  8bits BMP*/
   head->compress =  0L;   /* RGB */
   head->pixlen =    pixlen;
   head->XpixRes =   0;
   head->YpixRes =   0;
   head->ColorUsed = ncol;
   head->ColorImp =  ncol;
   if(fwrite(head,sizeof(struct BMPHEAD),1,bmpfile)!=1)
   {
     printf("[Can't write BMP file]\n");
     return;
   }

   FreeFarMemory(head);
   /*
   ** set palete
   **
   */
   palet=(struct BMPPALET huge *)GetFarMemory(paletsz);
   for(x=0;x<ncol;x++)
   { palet[x].R = doompal[x].R;
     palet[x].G = doompal[x].G;
     palet[x].B = doompal[x].B;
     palet[x].Zero = 0;
   }

   if(fwrite(palet,(size_t)paletsz,1,bmpfile)!=1)
   {
     printf("[Can't write BMP file]\n");
     return;
   }


   FreeFarMemory(palet);

   /*
   ** set data
   **
   */
   bmpidxs=(UInt8 huge *)GetFarMemory(linesz);
   for(y=rawY-1;y>=0;y--)
   {
     for(x=0;x<rawX;x++)
     {
       rawpos = x + rawX * y;
       bmpidxs[((Int32)x)]=raw[rawpos];
     }
     for(;x<linesz;x++)
     {
       bmpidxs[((Int32)x)]=0;
     }
     if(fwrite(bmpidxs,1,linesz,bmpfile)!=linesz)
     {
       printf("[Can't write BMP file]\n");
       return;
     }
   }
   FreeFarMemory(bmpidxs);
}

/* ------------------ END BMP module ------------------------------*/



/* ------------------ START PPM module ------------------------------*/

/******************* PPM module ****************/
UInt8 far * ReadPPM(FILE *ppmfile, Int16 *rawX, Int16 *rawY)
{
  UInt16 rawpos, rawSz;
  Int16 prawX,prawY;
  UInt8 far *raw;                  /*point to pixs. 8 bit*/
  struct PIXEL pix;
  char buff[20];
  UInt8 c;
  Int16 n;
  /*BMP 8 bits avec DOOM Palete*/

  if(fgetc(ppmfile)!='P')
  {
    printf("This is not a PPM file]\n");
    return 0;
  }
  if(fgetc(ppmfile)!='6')
  {
    printf("This is not a PPM file]\n");
    return 0;
  }
  c=fgetc(ppmfile);
  while(isspace(c))
    c=fgetc(ppmfile);
  while(c=='#')
  {
    while(c!='\n')
    {
      c=fgetc(ppmfile);
    }
    c=fgetc(ppmfile);
  };
  while(isspace(c))
    c=fgetc(ppmfile);
  for(n=0;n<10;n++)
  {
    if(!isdigit(c))
      break;
    buff[n]=c;
    c=fgetc(ppmfile);
  }
  buff[n]='\0';
  prawX=atoi(buff);
  while(isspace(c))
    c=fgetc(ppmfile);
  while(c=='#')
  {
    while(c!='\n')
    {
      c=fgetc(ppmfile);
    }
    c=fgetc(ppmfile);
  };
  while(isspace(c))
    c=fgetc(ppmfile);
  for(n=0;n<10;n++)
  {
    if(!isdigit(c))
      break;
    buff[n]=c;
    c=fgetc(ppmfile);
  }
  buff[n]='\0';
  prawY=atoi(buff);
  while(isspace(c))
    c=fgetc(ppmfile);
  while(c=='#')
  {
    while(c!='\n')
    {
      c=fgetc(ppmfile);
    }
    c=fgetc(ppmfile);
  };
  while(isspace(c))
    c=fgetc(ppmfile);
  for(n=0;n<10;n++)
  {
    if(!isdigit(c))
      break;
    buff[n]=c;
    c=fgetc(ppmfile);
  }
  buff[n]='\0';
  /* data */
  rawSz = prawX * prawY;
  raw=(UInt8 far *)GetFarMemory(rawSz);
  printf("[Warning: Color quantisation is slow. use ppmquant]\n");
  for(rawpos=0;rawpos<rawSz;rawpos++)
  {
    fread(&pix,sizeof(struct PIXEL),1,ppmfile);
    raw[rawpos]=COLindex(pix.R,pix.G,pix.B,0);
  }
  *rawX=prawX;
  *rawY=prawY;
  return raw;
}

void WritePPM(FILE *ppmfile, Int16 rawX, Int16 rawY, UInt8 far *raw,
                struct PIXEL huge * doompal)
{
   UInt16 rawpos, rawSz;
   struct PIXEL huge *pix;

   /* header */
   fprintf(ppmfile,"P6\n%d %d\n255\n",rawX,rawY);
   /* data */
   rawSz = rawX * rawY;
   for(rawpos=0;rawpos<rawSz;rawpos++)
   {
     pix = &doompal[(raw[rawpos])];
     fwrite(pix,sizeof(struct PIXEL),1,ppmfile);
   }
}


/**************** end PPM module ***************/


/**************** Start PCX module ***************/

typedef struct
{
  char  ID;
  char  version;
  char  encoding;
  char  bits_per_pixel;
  short int xleft, ytop, xright, ybottom;
  short int xres, yres;
  unsigned char palette[48];
  char  reserved;
  char  no_of_planes;
  short int bytes_per_line;
  short int palette_type;
  char  filler[58];
}pcx_header;



UInt8 far * ReadPCX(FILE *pcxfile, Int16 *rawX, Int16 *rawY)
{

  pcx_header PCX_header;
  Int16 x_size, y_size, index;
  UInt8 ch;
  struct PIXEL palette[256];
  Int32 rawsize, rawpos, count;
  UInt8 far *raw=NULL;


  /* Read 128 bytes for PCX header file */
  if (fread(&PCX_header, 1, sizeof(pcx_header), pcxfile) != 128)
  {
    printf("[PCX: read error]\n" );
    return 0;
  }

  /*
  ** check the PCX header
  */

  if (PCX_header.ID != 0x0A)
  {
    printf("[This is not a PCX file]\n");
    return 0;
  }

  if (PCX_header.encoding != 1)
  {
    printf("[Unknown encoding for PCX file]\n");
    return 0;
  }

  if(PCX_header.version!= 5)
  {
    printf("[Wrong PCX version. Must be 3.0]\n");
    return 0;
  }


  x_size = (PCX_header.xright - PCX_header.xleft) +1;
  if((x_size < 1) || (x_size > 320))
  {
    printf("[PCX width must be < 320]\n");
    return 0;
  }
  y_size = (PCX_header.ybottom - PCX_header.ytop) +1;
  if((y_size < 1) || (y_size > 200))
  {
    printf("[PCX: height must be < 200]\n");
    return 0;
  }


  /* check for value 12. This means 256 color palette is next */
  fseek(pcxfile, -769L, SEEK_END);
  ch = fgetc(pcxfile);
  if(ch != 0x0C)
  {
    printf("[PCX file is not 256 colors]\n");
    return 0;
  }

  /* read palette */
  if(fread(palette,sizeof(struct PIXEL),256,pcxfile) != 256)
  {
    printf("[PCX: read error]\n");
    return 0;
  }

  /* calulate raw size */
  rawsize = PCX_header.bytes_per_line * PCX_header.no_of_planes * y_size;
  raw = (UInt8 far *)GetFarMemory(rawsize);

  /* go to beginning of image */
  fseek(pcxfile,128L,SEEK_SET);

  /* read image and store in raw array */
  rawpos = 0;

  while( rawsize > 0)
  {
    ch=fgetc(pcxfile);
    if ((ch&0xC0) != 0xC0)
    {
      raw[rawpos++] = ch;
      --rawsize;
      continue;
    }
    count = ch & 0x3F;
    ch=fgetc(pcxfile);
    if (count > rawsize)
    {
      printf("[PCX: read error]\n");
      FreeFarMemory(raw);
      return 0;
    }
    rawsize -= count;
    while (--count >= 0)
      raw[rawpos++] = ch;
  }



  /*convert colors*/

  for (index=0; index<256; index++)
  {
    Idx2Doom[index]=(UInt8)COLindex((UInt8)palette[index].R,
                                (UInt8)palette[index].G,
                                (UInt8)palette[index].B,
                                (UInt8)index);
  }
  for (rawpos=0; rawpos<rawsize;rawpos++)
  {
    raw[rawpos]=Idx2Doom[((Int16)raw[rawpos])&0xFF];
  }



  *rawX = x_size;
  *rawY = y_size;

  return raw;

}


void WritePCX(FILE *pcxfile, Int16 rawX, Int16 rawY, UInt8 far *raw,
              struct PIXEL huge * doompal)
{

  pcx_header PCX_header;
  UInt16 number, rawpos;
  Int16 col ,row;
  UInt8 ch, old_ch;

  memset(&PCX_header, 0, sizeof(pcx_header));

  PCX_header.ID = 0x0A;
  PCX_header.version  = 5;
  PCX_header.encoding = 1;
  PCX_header.bits_per_pixel = 8;
  PCX_header.xright = rawX-1;
  PCX_header.ybottom = rawY-1;
  PCX_header.xres = 320;
  PCX_header.yres = 200;

  PCX_header.no_of_planes = 1;
  PCX_header.bytes_per_line = ((rawX * 8) + 7) / 8;
  PCX_header.palette_type = 1;

  /* write header */
  WriteBytes(pcxfile, &PCX_header, sizeof(pcx_header));

  /* write image */

  rawpos = 0;

  for (row=0; row < rawY; ++row)
  {
    number = 1;
    old_ch = raw[rawpos];
    rawpos++;
    for (col=0; col < (rawX-1); ++col)
    {
      ch = raw[rawpos];
      rawpos++;

      if(ch==old_ch && number<63 )
      {
        ++number;
        continue;
      }


      if( number>1 || (old_ch&0xC0) == 0xC0 )
      {
        number |= 0xC0;
        if(fputc(number&0xFF,pcxfile)==EOF)
        {
          printf("[PCX: write error]\n");
          return;
        }
      }
      if(fputc(old_ch&0xFF,pcxfile)==EOF)
      {
        printf("[PCX: write error]\n");
        return;
      }
      old_ch = ch;
      number = 1;

    }
    if( number>1 || (old_ch&0xC0) == 0xC0 )
    {
      number |= 0xC0;
      if(fputc(number&0xFF,pcxfile)==EOF)
      {
        printf("[PCX: write error]\n");
        return;
      }
    }
    if(fputc(old_ch&0xFF,pcxfile)==EOF)
    {
      printf("[PCX: write error]\n");
      return;
    }

  }

  /* mark beginning of palette info */
  if(fputc(0x0C,pcxfile)==EOF)
    {
      printf("[PCX: write error]\n");
      return;
    }

  /* write palette */
  WriteBytes(pcxfile, doompal, 768);

}


/**************** End PCX module ***************/
