/*===========================================================================
 *
 * DFTextImg.CPP - Dave Humphrey (uesp@m0use.net), 3 November 2000
 *
 *
 *=========================================================================*/

	/* Include Files */
#include "dftextimg.H"
#include "dfcommon.h"


#undef  __FUNC__
#define __FUNC__ "CDFTextureImage::CDFTextureImage()"
/*===========================================================================
 *
 * Class CDFTextureImage Constructor
 *
 *=========================================================================*/
CDFTextureImage::CDFTextureImage() {
  HeaderOffset = 0;
  NumSubImagesAllocated = 0;
 }
/*===========================================================================
 *		End of Class CTextureImage Constructor
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDFTextureImage::Destroy()"
/*===========================================================================
 *
 * Class CDFTextureImage Destructor
 *
 *=========================================================================*/
void CDFTextureImage::Destroy (void) {
  int LoopCounter;

	/* Delete all allocated subimages */
  for (LoopCounter = 0; LoopCounter < NumSubImagesAllocated; LoopCounter++) {
    DestroyPointer(pData[LoopCounter]);
   }

  NumSubImagesAllocated = 0;
  HeaderOffset = 0;
 }
/*===========================================================================
 *		End of Class CDFTextureImage Destructor
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDFTextureImage::Dump()"
/*===========================================================================
 *
 * Class CDFTextureImage Method - void Dump (DumpMask, FILE* pFileHandle);
 *
 * Output object information to a file stream according to the DumpMask flags.
 *
 *=========================================================================*/
void CDFTextureImage::Dump (FILE* pFileHandle, const dftexture_dumpmask_t DumpMask) {

	/* Ensure valid input */
  if (pFileHandle == NULL) {
    SET_EXT_ERROR2(ERR_NULL, "Invalid NULL file handle received!");
    return;
   }

	/* Ensure we wish to output any information */
  if ((DumpMask & DFTEXTURE_DM_IMAGE) == 0) return;

	/* Output basic and header information */
  fprintf (pFileHandle, "\tTexture Image Object 0x%p (0x%08lX)\n", this, HeaderOffset);

  if (GetNumSubImages() > 1 && GetUnknown2() != 0) fprintf (pFileHandle, "\t\tSubImages = %d,  Unknown2 = 0x%04X\n", GetNumSubImages(), GetUnknown2()&0xFFFF);

	/* Output header information if required */
  if (DumpMask & DFTEXTURE_DM_IMAGEHEADER) {
    if (DumpMask & DFTEXTURE_DM_IMAGEOFFSETS) fprintf (pFileHandle, "\t\tOffsets = %d, %d\n", GetXOffset(), GetYOffset());
    fprintf (pFileHandle, "\t\tSize = %d x %d (%d, Offset = 0x%08lX), %d SubImages\n", GetWidth(), GetHeight(), GetImageSize(), GetDataOffset(), GetNumSubImages());
    if (DumpMask & DFTEXTURE_DM_IMAGEUNKNOWNS) fprintf (pFileHandle, "\t\tUnknowns = 0x%04X 0x%04X 0x%08lX 0x%04X\n", GetUnknown1() & 0xFFFF, GetUnknown2()&0xFFFF, GetUnknown3(), GetUnknown4()&0xFFFF);
   }
  
  fflush(pFileHandle);  
 }
/*===========================================================================
 *		End of Class Method CDFTextureImage::Dump()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDFTextureImage::Read()"
/*===========================================================================
 *
 * Class CDFTextureImage Method - boolean Read (pFileHandle);
 *
 * Attempt to read in the image header and data starting from the current
 * position in the file stream. Returns FALSE on any error.
 *
 *=========================================================================*/
boolean CDFTextureImage::Read (FILE* pFileHandle) {
  boolean Result;

	/* Ensure valid input */
  if (pFileHandle == NULL) { 
    SET_EXT_ERROR2(ERR_NULL, "Invalid NULL file handle received!");
    return (FALSE);
   }

	/* Delete the current contents */
  Destroy();

	/* Set the offset of the image header */
  HeaderOffset = ftell(pFileHandle);

  if (HeaderOffset <= 0) {
    SET_EXT_ERROR2(ERR_READ, "Invalid header offset received!");
    return (FALSE);
   }

	/* Attempt to input header information */
  Result = ReadHeader(pFileHandle);
  if (!Result) return (FALSE);
   
	/* Read the data information depending on the type of image
	 * (single or multiple subimages) */
  if (GetNumSubImages() > 1 && IsRLECompressed()) {
    Result = ReadRLESubImageData(pFileHandle);
   }
  else if (GetNumSubImages() > 1) {
    Result =  ReadSubImageInfo(pFileHandle);
    if (Result) Result = ReadSubImageData(pFileHandle);
   }
	/* Read in single subimage data, RLE encoded */
  else if (IsRLECompressed()) 
    Result = ReadRLEData(pFileHandle);
  else 
    Result = ReadData(pFileHandle);

  return (Result);
 }
/*===========================================================================
 *		End of Class Method CDFTextureImage::Read()
 *=========================================================================*/
 

#undef  __FUNC__
#define __FUNC__ "CDFTextureImage::ReadHeader()"
/*===========================================================================
 *
 * Class CDFTextureImage Method - boolean ReadHeader (pFileHandle);
 *
 * Attempt to read in the image header from the given file stream.
 * Returns FALSE on any error.  Protected class method.
 *
 *=========================================================================*/
boolean CDFTextureImage::ReadHeader (FILE* pFileHandle) {
  int Result;

	/* Assume we are at the header position currently and read in the
	 * header data all at once.*/
  Result = fread (&Header, 1, DFTEXTURE_IMAGEHEADER_SIZE, pFileHandle);

  if (Result != DFTEXTURE_IMAGEHEADER_SIZE) {
    SET_EXT_ERROR4(ERR_READ, "Error reading header information (%d of %d bytes)!", Result, DFTEXTURE_IMAGEHEADER_SIZE);
    return (FALSE);
   }

   	/* Ensure a valid number of sub-images */
  if (GetNumSubImages() >= DFTEXTURE_MAX_SUBIMAGES) {
    SET_EXT_ERROR3(ERR_READ, "Invalid number of sub-images received (%d)!", GetNumSubImages());
    return (FALSE);
   }

  return (Result);
 }
/*===========================================================================
 *		End of Class Method CDFTextureImage::ReadHeader()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDFTextureImage::ReadData()"
/*===========================================================================
 *
 * Class CDFTextureImage Method - boolean ReadData (pFileHandle);
 *
 * Attempt to read in the image data from the given file stream.
 * Returns FALSE on any error.  Protected class method.
 *
 *=========================================================================*/
boolean CDFTextureImage::ReadData (FILE* pFileHandle) {
  int   Result;
  int   LoopCounter;
  byte* pDataPtr;

	/* Allocate the data pointer */
  CreatePointerArray(pData[0], byte, GetDataSize());
  NumSubImagesAllocated = 1;

	/* Move to the start of the image data */
  fseek (pFileHandle, GetAbsDataOffset(), SEEK_SET);
  pDataPtr = pData[0];

	/* Must read data row by row */
  for (LoopCounter = 0; LoopCounter < GetHeight(); LoopCounter++) {

		/* Read in one row of image data */
    Result = fread (pDataPtr, 1, GetWidth(), pFileHandle);

		/* Ensure the read was successful */
    if (Result != GetWidth()) {
      SET_EXT_ERROR4(ERR_READ, "Error reading image data row (%d of %d bytes)!", Result, GetWidth());
      return (FALSE);
     }

		/* Move to next row */
    fseek (pFileHandle, DFTEXTURE_IMAGEROW_WIDTH - GetWidth(), SEEK_CUR);
    pDataPtr += GetWidth();
   }

  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CDFTextureImage::ReadData()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDFTextureImage::ReadRLEData()"
/*===========================================================================
 *
 * Class CDFTextureImage Method - boolean ReadRLEData (pFileHandle);
 *
 * Attempt to read in the RLE image data from the given file stream.
 * Returns FALSE on any error.  Protected class method.
 *
 *=========================================================================*/
boolean CDFTextureImage::ReadRLEData (FILE* pFileHandle) {
  int			Result;
  int			LoopCounter;
  byte*			pDataPtr;
  dftexture_rleoffset_t RowOffsets[DFTEXTURE_MAXIMAGE_HEIGHT+1];

	/* Ensure a valid height */
  if (GetHeight() >= DFTEXTURE_MAXIMAGE_HEIGHT) {
    SET_EXT_ERROR3(ERR_INPUT, "Invalid image height (%d)!", GetHeight());
    return (FALSE);
   }

	/* Allocate the data pointer */
  CreatePointerArray(pData[0], byte, GetDataSize());
  NumSubImagesAllocated = 1;

	/* Move to the start of the image data */
  fseek (pFileHandle, GetAbsDataOffset(), SEEK_SET);
  pDataPtr = pData[0];

	/* Read in the row offset data */
  Result = fread (RowOffsets, sizeof(dftexture_rleoffset_t), GetHeight(), pFileHandle);

	/* Ensure the input was a success */
  if (Result != GetHeight()) {
    SET_EXT_ERROR4(ERR_READ, "Error readin the row offsets (%d of %d offsets)!", Result, GetHeight());
    return (FALSE);
   }

	/* Set the last offset value */
  RowOffsets[GetHeight()].Offset = (short) GetImageSize();

	/* Must read data row by row */
  for (LoopCounter = 0; LoopCounter < GetHeight(); LoopCounter++) {
    Result = UncompressRLERow(pFileHandle, &RowOffsets[LoopCounter], &pDataPtr);
    if (!Result) return (FALSE);
   }

  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CDFTextureImage::ReadRLEData()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDFTextureImage::ReadRLESubImageData()"
/*===========================================================================
 *
 * Class CDFTextureImage Method - boolean ReadRLESubImageData (pFileHandle);
 *
 * Attempt to read in the RLE subimage data from the given file stream.
 * Returns FALSE on any error.  Protected class method.
 *
 *=========================================================================*/
boolean CDFTextureImage::ReadRLESubImageData (FILE* pFileHandle) {
  int			 Result;
  int			 LoopCounter;
  int			 RowCounter;
  int			 NumRowOffsets;
  byte*			 pDataPtr;
  dftexture_rleoffset_t* pRowOffsets;

	/* Allocate the row offset structure */
  NumRowOffsets = GetHeight() * GetNumSubImages();
  CreatePointerArray(pRowOffsets, dftexture_rleoffset_t,  NumRowOffsets + 1);

	/* Read in the row offset data */
  Result = fread (pRowOffsets, sizeof(dftexture_rleoffset_t), NumRowOffsets, pFileHandle);

  if (Result != NumRowOffsets) {
    DestroyPointer(pRowOffsets);
    SET_EXT_ERROR4(ERR_READ, "Error reading the row offsets (%d of %d offsets)!", Result, NumRowOffsets);
    return (FALSE);
   }

	/* Read in each subimage */
  for (LoopCounter = 0; LoopCounter < GetNumSubImages(); LoopCounter++) {

		/* Allocate the data pointer */
    CreatePointerArray(pData[LoopCounter], byte, GetDataSize());
    pDataPtr = pData[LoopCounter];
    NumSubImagesAllocated++;

		/* Set the subimage info */
    SubImageInfo[LoopCounter].Width  = GetWidth();
    SubImageInfo[LoopCounter].Height = GetHeight();
    SubImageInfo[LoopCounter].Offset = -1;

		/* Must read data row by row */
    for (RowCounter = 0; RowCounter < GetHeight(); RowCounter++) {
      Result = UncompressRLERow(pFileHandle, &pRowOffsets[RowCounter], &pDataPtr);

      if (!Result) { 
        DestroyPointer(pRowOffsets);
	return (FALSE);
       }
     }
   }

  DestroyPointer(pRowOffsets);
  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CDFTextureImage::ReadRLESubImageData()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDFTextureImage::ReadSubImageData()"
/*===========================================================================
 *
 * Class CDFTextureImage Method - boolean ReadSubImageData (pFileHandle);
 *
 * Attempt to read in the subimage data from the given file stream.
 * Returns FALSE on any error.  Protected class method.
 *
 *=========================================================================*/
boolean CDFTextureImage::ReadSubImageData (FILE* pFileHandle) {
  int   Result;
  int   LoopCounter;
  int   RowCounter;
  byte* pDataPtr;

	/* Read in each subimage */
  for (LoopCounter = 0; LoopCounter < GetNumSubImages(); LoopCounter++) {

		/* Allocate the data pointer */
    CreatePointerArray(pData[LoopCounter], byte, GetDataSize());
    NumSubImagesAllocated++;

		/* Move to the start of the subimage data */
    fseek (pFileHandle, SubImageInfo[LoopCounter].Offset + GetAbsDataOffset(), SEEK_SET);

		/* Read the subimage width and height  */
    SubImageInfo[LoopCounter].Width  = read_short(pFileHandle);
    SubImageInfo[LoopCounter].Height = read_short(pFileHandle);
    pDataPtr = pData[LoopCounter];

		/* Must read data row by row */
    for (RowCounter = 0; RowCounter < SubImageInfo[LoopCounter].Height; RowCounter++) {
      Result = UncompressSubImageRow(pFileHandle, LoopCounter, &pDataPtr);
      if (!Result) return (FALSE);
     }
   }

  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CDFTextureImage::ReadSubImageData()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDFTextureImage::ReadSubImageInfo()"
/*===========================================================================
 *
 * Class CDFTextureImage Method - boolean ReadSubImageInfo (pFileHandle);
 *
 * Attempt to read in the subimage offsets from the given file stream.
 * Returns FALSE on any error.  Protected class method.
 *
 *=========================================================================*/
boolean CDFTextureImage::ReadSubImageInfo (FILE* pFileHandle) {
  int  Result;
  long Offsets[DFTEXTURE_MAX_SUBIMAGES];
  int  LoopCounter;

	/* Are there are subimage offsets to read? */
  if (GetNumSubImages() <= 1) return (TRUE);

	/* Jump to the position of the subimage offsets */ 
  fseek (pFileHandle, GetAbsDataOffset(), SEEK_SET);

	/* Read in the offsets all at once */
  Result = fread (Offsets, 4, GetNumSubImages(), pFileHandle);

  if (Result != GetNumSubImages()) {
    SET_EXT_ERROR4(ERR_READ, "Error reading subimage offsets (%d of %d offsets)!", Result, GetNumSubImages());
    return (FALSE);
   }

	/* Save offsets to record data */
  for (LoopCounter = 0; LoopCounter < GetNumSubImages(); LoopCounter++) {
    SubImageInfo[LoopCounter].Offset = Offsets[LoopCounter];
   }

  return (Result);
 }
/*===========================================================================
 *		End of Class Method CDFTextureImage::ReadSubImageInfo()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDFTextureImage::UncompressRLERow()"
/*===========================================================================
 *
 * Class CDFTextureImage Method - boolean UnCompressRLERow (pFileHandle, pRowOffsets, ppDataPtr);
 *
 * Protected class method to uncompress one row of RLE image data.  Returns
 * FALSE on any error.
 *
 *=========================================================================*/
boolean CDFTextureImage::UncompressRLERow (FILE* pFileHandle, dftexture_rleoffset_t* pRowOffset, byte** ppDataPtr) {
  int  Result;
  int  BytesWritten;
  int  BytesDecoded;
  int  RLECounter;
  int  RowWidth;
  int  ByteCount;
  byte RLEData;

	/* Jump to the start of the row data */
  fseek (pFileHandle, pRowOffset->Offset + HeaderOffset, SEEK_SET);

	/* Is the row compressed or not? */
  if (pRowOffset->Unknown == 0x0000) {

		/* Read in the row data all at once and ensure success */
    Result = fread (*ppDataPtr, 1, GetWidth(), pFileHandle);

    if (Result != GetWidth()) {
      SET_EXT_ERROR4(ERR_READ, "Error reading row data (%d of %d bytes)!", Result, GetWidth());
      return (FALSE);
     }
      
    (*ppDataPtr) += GetWidth();
    return (TRUE);
   }

	/* Input the row width value */
  RowWidth = read_short(pFileHandle);
		
	/* Initialize the RLE variables */
  BytesWritten = 0;
  BytesDecoded = 0;
		
	/* Uncompress the row data */
  while (BytesWritten < RowWidth) {
    ByteCount = read_short(pFileHandle);

		/* Write out pixel values explicitly */
    if (ByteCount > 0) {
        
      for (RLECounter = 0; RLECounter < ByteCount; RLECounter++) {
        **ppDataPtr = fgetc(pFileHandle);

	if (**ppDataPtr < 0)  {
	  SET_EXT_ERROR2(ERR_READ, "Failed to read data from file!");
	  return (FALSE);
	 }

        (*ppDataPtr)++;
       }

      BytesDecoded += ByteCount + 2;
      BytesWritten += ByteCount;
     }	/* Decode RLE bytes */
    else {
      RLEData = fgetc(pFileHandle);
      ByteCount = -ByteCount;

      if (RLEData < 0)  {
        SET_EXT_ERROR2(ERR_READ, "Failed to read data from file!");
        return (FALSE);
       }      

      for (RLECounter = 0; RLECounter < ByteCount; RLECounter++) {
        **ppDataPtr = RLEData;
        (*ppDataPtr)++;
       }

      BytesDecoded += 3;
      BytesWritten += ByteCount;
     }

   } /* End of while decoding */

  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CDFTextureImage::UnCompressRLERow()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDFTextureImage::UncompressSubImageRow()"
/*===========================================================================
 *
 * Class CDFTextureImage Method - boolean UnCompressSubImageRow (pFileHandle, SubImageIndex, ppDataPtr);
 *
 * Protected class method to uncompress one row of regular subimage data.
 * Returns FALSE on any error.
 *
 *=========================================================================*/
boolean CDFTextureImage::UncompressSubImageRow (FILE* pFileHandle, const int SubImageIndex, byte** ppDataPtr) {
  int   Result;
  int   RowSkipBytes;
  int   RowBytes;
  int   RowWidth = SubImageInfo[SubImageIndex].Width;
  int   BytesWritten = 0;

  while (BytesWritten < RowWidth) {

		/* Read the two compressed rowinfo bytes */
    RowSkipBytes = fgetc(pFileHandle);
    RowBytes     = fgetc(pFileHandle);

		/* Ensure valid input */
    if (RowSkipBytes < 0 || RowBytes < 0) {
      SET_EXT_ERROR4(ERR_READ, "Invalid compressed image data received (%d/%d)", RowSkipBytes, RowBytes);
      return (FALSE);
     }

		/* Fill the first section of the row data */
    memset(*ppDataPtr, 0x00, RowSkipBytes);
    (*ppDataPtr) += RowSkipBytes;
    BytesWritten += RowSkipBytes;
      	
		/* Move to next byte sequence if no bytes to read */
    if (!RowBytes) continue;

    		/* Read in the row data */
    Result = fread (*ppDataPtr, 1, RowBytes, pFileHandle);
    (*ppDataPtr) += RowBytes;
    BytesWritten += RowBytes;

		/* Ensure the read was successful */
    if (Result != RowBytes) {
      SET_EXT_ERROR4(ERR_READ, "Failed to read image data (%d of %d bytes)!", Result, RowBytes);
      return (FALSE);
     }

   } /* End of while loop */

  
  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CDFTextureImage::UnCompressSubImageRow()
 *=========================================================================*/