/*=========================================================================
 *
 * PCX.CPP - 1 December 1998, Dave Humphrey
 *
 * General routines for handling PCX files.
 *
 *=======================================================================*/


	/* Required Includes */
#include "pcx.h"


/*=========================================================================
 *
 * Class CPCXHeader Method - void Destroy (void);
 *
 *=======================================================================*/
void CPCXHeader::Destroy (void) {
  Manufacturer = 10;
  Version = 5;
  Encoding = 1;
  BitsPerPixel = 8;
  X = Y = 0;
  Width = Height = 0;
  HorzResolution = VertResolution = 150;
  Reserved = 0;
  NumColorPlanes = 1;
  BytesPerLine = 0;
  PaletteType = 1;
  memset (EGAPalette, 0, 48);
  memset (Padding, 0, 58);
  Padding[0] = 0;
  Padding[1] = 4;
  Padding[2] = 0;
  Padding[3] = 3;
 }
/*=========================================================================
 *		End of Class Method CPCXHeader::Destroy()
 *=======================================================================*/


/*=========================================================================
 *
 * Class CPCXHeader Method - boolean Read (pFileHandle);
 *
 * Attempts to read in the header from the given file handle.  Returns
 * FALSE on any error.
 *
 *=======================================================================*/
boolean CPCXHeader::Read (FILE* pFileHandle) {
  int Result;

	/* Load the header all at once */
  Result = fread (&Manufacturer, sizeof(char), 128, pFileHandle);

  if (Result != 128) {
    SET_EXT_ERROR(ERR_READ);
    return (FALSE);
   }

	/* Adjust now to make life easier */
  Width++;
  Height++;

  return (TRUE);
 }
/*=========================================================================
 *		End of Class Method CPCXHeader::Read()
 *=======================================================================*/


/*=========================================================================
 *
 * Class CPCXHeader Method - boolean Write (pFileHandle);
 *
 * Attempts to write the header to the given file handle.  Returns
 * FALSE on any error.
 *
 *=======================================================================*/
boolean CPCXHeader::Write (FILE* pFileHandle) {
  int Result;

	/* Adjust to make life easier */
  Width--;
  Height--;

	/* Write the header all at once */
  Result = fwrite (&Manufacturer, sizeof(char), 128, pFileHandle);

  if (Result != 128) {
    SET_EXT_ERROR(ERR_WRITE);
    return (FALSE);
   }

	/* Readjust */
  Width++;
  Height++;

  return (TRUE);
 }
/*=========================================================================
 *		End of Class Method CPCXHeader::Write()
 *=======================================================================*/


/*=========================================================================
 *
 * Class CPCXImage Destructor
 *
 *=======================================================================*/
void CPCXImage::Destroy (void) {

	/* Delete the allocated image, if any */
  DestroyPointer(pData);

	/* Clear the header info */
  Header.Destroy();
 }
/*=========================================================================
 *		End of Class CPCXImage Destructor
 *=======================================================================*/


/*=========================================================================
 *
 * Class PCXImage Method - boolean ExportLBM (pFilename, Width, Height, pImage, pPalette);
 *
 * Allows the specification of a custom palette when saving the PCX.
 * The palette is a RGB color entries at least 256 colors in size (768 bytes).  
 *
 *=======================================================================*/
boolean CPCXImage::ExportLBM (const char* pFilename, const int Width, const int Height, byte* pImage, const byte* pPalette) {

	/* Make sure all the pointers and inputs are valid */
  if (!pPalette) {
    SET_EXT_ERROR(ERR_NULL);
    return (FALSE);
   }
	
	/* Set the appropiate values */
  memcpy ((void*)pPalette, Palette, 768);

	/* Call the non-palette version */
  return (ExportLBM(pFilename, Width, Height, pImage));
 }
/*=========================================================================
 *		End of Class Method CPCXImage::ExportLBM()
 *=======================================================================*/


/*=========================================================================
 *
 * Class PCXImage Method - boolean ExportLBM (pFilename, Width, Height, pImage);
 *
 * Use this function for exporting a LBM image format to a PCX file.
 * The image is a Linear-BitMap, the pixels lie one after another in a
 * linear fashion (total length is width*height).  The current
 * PCX image is destroyed, if any.  Returns FALSE on any error.
 *
 *=======================================================================*/
boolean CPCXImage::ExportLBM (const char* pFilename, const int Width, const int Height, byte* pImage) {

	/* Make sure all the pointers and inputs are valid */
  if (!pFilename || !pImage || !Width || !Height) {
    SET_EXT_ERROR(ERR_NULL);
    return (FALSE);
   }

	/* Destroy the current image */
  Destroy();

	/* Set the appropiate values */
  Header.Width = Width;
  Header.Height = Height;
  Header.BytesPerLine = Width;
  pData = pImage;	/* Reference image pointer _temporarily_ */
  
	/* Attempt to save the image */
  if (!Save(pFilename)) {
    pData = NULL;
    Destroy();
    return (FALSE);
   }

  pData = NULL;
  Destroy();
  return (TRUE);
 }
/*=========================================================================
 *		End of Class Method CPCXImage::ExportLBM()
 *=======================================================================*/


/*=========================================================================
 *
 * Class CPCXImage Method - boolean Load (pFilename);
 *
 * Attempts to read in the specified PCX file.  Returns FALSE on any error.
 *
 *=======================================================================*/
boolean CPCXImage::Load (const char* pFilename) {
  FILE*		pFileHandle;
  unsigned long ImageSize;
  size_t	Count = 0;
  int		i = 0;
  int		RowDiff = 0;
  int		Row = 0;
  int		Bytes = 0;
  int		NumBytes;
  int		Result;
  int		ch;

	/* Attempt to open the file for input */
  pFileHandle = openfile (pFilename, "rb");
  if (pFileHandle == NULL) return (FALSE);

	/* Clear the current image contents, if any */
  Destroy();

	/* Read in the header */
  if (!Header.Read(pFileHandle)) {
    fclose (pFileHandle);
    return (FALSE);
   }

	/* Make sure the image is small enough to fit */
  ImageSize = ((unsigned long)Header.Width) * ((unsigned long)Header.Height) + 1;

  if (ImageSize > UINT_MAX) {
    SET_EXT_ERROR(ERR_UINTMAX);
    fclose (pFileHandle);
    return (FALSE);
   }

	/* Attempt to allocate the image data */
  CreatePointerArray(pData, byte, ImageSize);

	/* Read in the image data */
  RowDiff = Header.BytesPerLine - Header.Width;

  while (Count <= ImageSize) {

	/* Remove any row padding */
    if (RowDiff > 0 && i >= Header.Width) {

      if (i == Header.BytesPerLine) {
	i = 0;
	Row++;
	Count--;
       }
      else if (i >= Header.Width) {
	i = 0;
	Row++;

	while (i != RowDiff) {
	  ch = fgetc(pFileHandle);
	  Bytes++;

	  if (ch >= 192) {
	    ch = fgetc(pFileHandle);
	    Bytes++;
	   }

	  i++;
	 }

	i = 0;
       }

     }
    else {
		/* Get some data */
      ch = fgetc(pFileHandle);
      Bytes++;

		/* Is this byte a RLE code? */
      if (ch >= 192) {

		/* How many bytes in run? */
	NumBytes = ch - 192;

		/* Get the actual data for the run */
	ch = fgetc(pFileHandle);
	Bytes++;

		/* Replicate data in image buffer */
	while (NumBytes-- > 0) {
	  pData[Count] = (unsigned char) ch;
	  Count++;
	  i++;
	 }

       }    	/* Copy data into image buffer */
      else {
	pData[Count] = (unsigned char) ch;
	Count++;
	i++;
       }
     }
   } /* End while loop */

	/* Read in the image palette */
  fseek (pFileHandle, -768l, SEEK_END);

  Result = fread(Palette, sizeof(char), 768, pFileHandle);

  if (Result != 768) {
    SET_EXT_ERROR(ERR_READ);
    return (FALSE);
   }

	/* Close the file and return success */
  fclose (pFileHandle);
  return (TRUE);
 }
/*=========================================================================
 *		End of Class Method CPCXImage::Load()
 *=======================================================================*/


/*=========================================================================
 *
 * Class CPCXImage Method - boolean Save (pFilename);
 *
 * Attempts to save the PCX image to the specified file.  Returns FALSE
 * on any error.
 *
 *=======================================================================*/
boolean CPCXImage::Save (const char *pFilename) {
  FILE*  pFileHandle;
  byte*  pDataPtr;
  byte   Pre;
  size_t ImageSize;
  size_t i;
  int    X = 1;
  int    Count;
  int    Result;

	/* Attempt to open the file for output */
  pFileHandle = openfile(pFilename, "wb");
  if (pFileHandle == NULL) return (FALSE);

	/* Write the header information */
  if (!Header.Write(pFileHandle)) {
    fclose (pFileHandle);
    return (FALSE);
   }

	/* Compute the image size */
  ImageSize = Header.Width * Header.Height;
  pDataPtr = pData;
  Pre = *pDataPtr;
  pDataPtr++;

	/* Write the main image */
  for (i = 0; i < ImageSize; i++) {
    Count = 1;

    while (*pDataPtr == Pre && Count < 63 && X != Header.Width) {
      pDataPtr++;
      Count++;
      i++;
      X++;
     }

    if (Pre < 192 && Count == 1)
      fputc (Pre, pFileHandle);
    else if (Count == 1) {
      fputc(193, pFileHandle);
      fputc(Pre, pFileHandle);
     }
    else { /* RLE encoded bytes */
      fputc ((unsigned char)(192+Count), pFileHandle);
      fputc (Pre, pFileHandle);
     }

    if (X == Header.Width) X = 0;
    Pre = *pDataPtr;
    pDataPtr++;
    X++;
   }

	/* Need this for some reason? */
  fputc(0x0C, pFileHandle);

	/* Write palette data */
  Result = fwrite (Palette, sizeof(char), 768, pFileHandle);

  if (Result != 768) {
    fclose (pFileHandle);
    SET_EXT_ERROR(ERR_WRITE);
    return (FALSE);
   }

	/* Close the file and return success */
  fclose (pFileHandle);
  return (TRUE);
 }
/*=========================================================================
 *		End of Class Method CPCXImage::Save()
 *=======================================================================*/


/*===========================================================================
 *
 * Class CPCXImage Method - void SetVGAPalette (pVGAPal);
 *
 * Copies entries from the given VGA palette into the PCX palette.  VGA
 * palette enries range from 0 to 63 so the values are multiplied by
 * 4 to range from 0 to 255 as a proper PCX palette.
 *
 *=========================================================================*/
void CPCXImage::SetVGAPalette (const byte* pVGAPal) {
  int LoopCounter;

	/* Ensure valid input */
  if (pVGAPal == NULL) {
    SET_EXT_ERROR(ERR_NULL);
    return;
   }

	/* Translate all values */
  for (LoopCounter = 0; LoopCounter < 256; LoopCounter++) {
    Palette[LoopCounter].R = (*pVGAPal * 4)&0xFF;
    pVGAPal++;
    Palette[LoopCounter].G = (*pVGAPal * 4)&0xFF;
    pVGAPal++;
    Palette[LoopCounter].B = (*pVGAPal * 4)&0xFF;
    pVGAPal++;
   }

 }
/*===========================================================================
 *		End of Class Method CPCXImage::SetVGAPalette()
 *=========================================================================*/