/*===========================================================================
 *
 * File:	Dftextimg.CPP
 * Author:	Dave Humphrey (uesp@m0use.net)
 * Created On:	Thursday, June 28, 2001
 *
 * Implements the CDFTextureImage class for handling one image from a 
 * Daggerfall texture file.
 *
 *=========================================================================*/

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


/*===========================================================================
 *
 * Begin Local Variable Definitions
 *
 *=========================================================================*/
  DEFINE_FILE();
/*===========================================================================
 *		End of Local Variable Definitions
 *=========================================================================*/


/*===========================================================================
 *
 * Class CDFTextureImage Constructor
 *
 *=========================================================================*/
CDFTextureImage::CDFTextureImage() {
  m_HeaderOffset = 0;
  m_ImageSize    = 0;
  m_NumSubImages = 0;
  m_Tag = 0;

	/* Initialize the image header */
  memset(&m_Header, 0, sizeof(dftextimgheader_t));

	/* Initialize the sub-image data array */
  m_pSubImages = NULL;
 }
/*===========================================================================
 *		End of Class CTextureImage Constructor
 *=========================================================================*/


/*===========================================================================
 *
 * Class CDFTextureImage Destructor
 *
 *=========================================================================*/
void CDFTextureImage::Destroy (void) {
  DEFINE_FUNCTION("CDFTextureImage::Destroy");
  int Index;
 
	/* Delete the sub-image data arrays */
  for (Index = 0; Index < m_NumSubImages; Index++) {
    DestroyArrayPointer(m_pSubImages[Index].pData);
   }

   	/* Delete the subimage array */
  DestroyArrayPointer(m_pSubImages);

  memset(&m_Header, 0, sizeof(dftextimgheader_t));
  m_NumSubImages = 0;
  m_HeaderOffset = 0;
  m_ImageSize    = 0;
  m_Tag = 0;

	/* DO NOT call the base class Destroy method! */
 }
/*===========================================================================
 *		End of Class CDFTextureImage Destructor
 *=========================================================================*/


/*===========================================================================
 *
 * Class CDFTextureImage Method - void AllocateSubImages (void);
 *
 * Allocates the array of sub-image data pointers.  
 *
 *=========================================================================*/
void CDFTextureImage::AllocateSubImages (void) {
  DEFINE_FUNCTION("CDFTextureImage::AllocateSubImages()");

	/* Ensure valid object state */
  ASSERT(m_pSubImages == NULL && GetNumSubImages() >= 0);

	/* Check for no sub-images */
  if (GetNumSubImages() == 0) return;

	/* Allocate the array */
  CreateArrayPointer(m_pSubImages, dftextsubimg_t, (size_t)GetNumSubImages());
  memset(m_pSubImages, 0, sizeof(dftextsubimg_t) * GetNumSubImages());
  m_NumSubImages = GetNumSubImages();
 }
/*===========================================================================
 *		End of Class Method CDFTextureImage::AllocateSubImages()
 *=========================================================================*/


/*===========================================================================
 *
 * Class CDFTextureImage Method - byte* AllocateSubImageData (Index, Size);
 *
 * Protected class method to allocate the data for the given sub-image.
 * Returns a pointer to the newly allocated data.  Returns NULL only if 
 * the image size is zero.
 *
 *=========================================================================*/
byte* CDFTextureImage::AllocateSubImageData (const int Index, const size_t Size) {
  DEFINE_FUNCTION("CDFTextureImage::AllocateSubImageData()");

	/* Ensure valid input and object state */
  ASSERT(IsValidImageIndex(Index));
  ASSERT(m_pSubImages != NULL);
  ASSERT(m_pSubImages[Index].pData == NULL);
	
	/* Check for a 0-sized image */
  if (Size == 0) return (NULL);
  
	/* Allocate and return the image data */
  CreateArrayPointer(m_pSubImages[Index].pData, byte, Size);	
  return (m_pSubImages[Index].pData);
 }
/*===========================================================================
 *		End of Class Method CDFTextureImage::AllocateSubImageData()
 *=========================================================================*/


/*===========================================================================
 *
 * Class CDFTextureImage Method - boolean Read (void);
 *
 * 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 (void) {
  DEFINE_FUNCTION("CDFTextureImage::Read()");
  boolean Result;

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

	/* Set the offset of the image header */
  m_HeaderOffset = Tell();

	/* Attempt to input header information */
  Result = ReadHeader();
  if (!Result) return (FALSE);
   
  	/* Check for no images to read */
  if (GetNumSubImages() == 0) {
    Result = TRUE;
   }
	/* Read multiple, compressed, sub-images */
  else if (GetNumSubImages() > 1 && IsCompressed()) {
    Result = ReadRLESubImageData();
   }
	/* Read in uncompressed multiple sub-images */
  else if (GetNumSubImages() > 1) {
    Result =  ReadSubImageInfo();
    if (Result) Result = ReadSubImageData();
   }
	/* Read in single subimage data, RLE encoded */
  else if (IsCompressed()) 
    Result = ReadRLEData();
	/* Read in single, uncompressed, image */
  else 
    Result = ReadData();

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

/*===========================================================================
 *
 * Class CDFTextureImage Method - boolean ReadHeader (void);
 *
 * Attempt to read in the image header from the current position in
 * the file stream. Returns FALSE on any error.  Protected class method.
 *
 *=========================================================================*/
boolean CDFTextureImage::ReadHeader (void) {
  DEFINE_FUNCTION("CDFTextureImage::ReadHeader()");
  boolean Result;

	/* Assume we are at the header position currently and read in the
	 * header data all at once.*/
  Result = CGenFile::Read ((char*)&m_Header, DFTEXTURE_IMAGEHEADER_SIZE);
  if (!Result) return (FALSE);

   	/* Ensure a valid number of sub-images */
  if (GetNumSubImages() >= DFTEXTURE_MAX_SUBIMAGES) {
    ErrorHandler.AddError(DFERR_TEXTURE_SUBIMAGES, "SubImages=%d", (int)GetNumSubImages());
    return (FALSE);
   }

	/* Ensure a valid image height */
  if (GetHeight() >= DFTEXTURE_MAXIMAGE_HEIGHT) {
    ErrorHandler.AddError(DFERR_TEXTURE_BADHEIGHT, "Height=%d", (int)GetHeight());
    return (FALSE);
   }

	/* Ensure a valid true, image size */
  m_ImageSize = (long)GetWidth() * (long)GetHeight();

  if (m_ImageSize < 0 || (ulong)m_ImageSize >= (ulong) UINT_MAX) {
    ErrorHandler.AddError(DFERR_TEXTURE_IMAGESIZE, "ImageSize=%ld", m_ImageSize);
    return (FALSE);
   }

	 /* Allocate the sub-image array */
  AllocateSubImages();
  return (Result);
 }
/*===========================================================================
 *		End of Class Method CDFTextureImage::ReadHeader()
 *=========================================================================*/


/*===========================================================================
 *
 * Class CDFTextureImage Method - boolean ReadData (void);
 *
 * Attempt to read in the image data from the current position in the
 * file stream. Returns FALSE on any error.  Protected class method.
 *
 *=========================================================================*/
boolean CDFTextureImage::ReadData (void) {
  DEFINE_FUNCTION("CDFTextureImage::ReadData()");
  boolean Result;
  int     RowIndex;
  byte*   pDataPtr;

	/* Move to the start of the image data */
  Result = Seek(GetAbsDataOffset(), SEEK_SET);
  if (!Result) return (FALSE);

  	/* Allocate the image data */
  pDataPtr = AllocateSubImageData(0, (size_t)GetTrueImageSize());
  
  	/* Must read data row by row */
  for (RowIndex = 0; RowIndex < GetHeight(); RowIndex++) {

		/* Read in one row of image data */
    Result = CGenFile::Read((char*)pDataPtr, (size_t)GetWidth());
    if (!Result) return (FALSE);

		/* Move to the start of the next row as all texture images
		 * are stored in 256 byte wide rows */
    Result = Seek(DFTEXTURE_IMAGEROW_WIDTH - GetWidth(), SEEK_CUR);
    pDataPtr += GetWidth();
   }

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


/*===========================================================================
 *
 * Class CDFTextureImage Method - boolean ReadRLEData (void);
 *
 * Attempt to read in the RLE image data from the current position in the
 * file stream.  Returns FALSE on any error.  Protected class method.
 *
 *=========================================================================*/
boolean CDFTextureImage::ReadRLEData (void) {
  DEFINE_FUNCTION("CDFTextureImage::ReadRLEData()");
  boolean	  Result;
  int		  RowIndex;
  byte*		  pDataPtr;
  dftextrleinfo_t RowOffsets[DFTEXTURE_MAXIMAGE_HEIGHT+1];

	/* Move to the start of the image data */
  Result = Seek(GetAbsDataOffset(), SEEK_SET);
  if (!Result) return (Result);
  
	/* Allocate the image data */
  pDataPtr = AllocateSubImageData(0, (size_t)GetTrueImageSize());

	/* Read in the row offset data all at once */
  Result = CGenFile::ReadEx((char*)&RowOffsets[0], sizeof(dftextrleinfo_t), GetHeight());
  RowOffsets[GetHeight()].Offset = (short) GetImageSize();
  if (!Result) return (FALSE);

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

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


/*===========================================================================
 *
 * Class CDFTextureImage Method - boolean ReadRLESubImageData (void);
 *
 * Attempt to read in the RLE sub-image data from the current position in
 * the file stream. Returns FALSE on any error.  Protected class method.
 *
 *=========================================================================*/
boolean CDFTextureImage::ReadRLESubImageData (void) {
  DEFINE_FUNCTION("CDFTextureImage::ReadRLESubImageData()");
  boolean	   Result;
  int		   SubImageIndex;
  int		   RowIndex;
  size_t	   NumRowOffsets;
  byte*	  	   pDataPtr;
  dftextrleinfo_t* pRowOffsets;

	/* Ensure valid object state */
  ASSERT(m_pSubImages != NULL && GetNumSubImages() > 1);

	/* Allocate the temporary offset structure */
  NumRowOffsets = (size_t) GetHeight() * GetNumSubImages();
  CreateArrayPointer(pRowOffsets, dftextrleinfo_t,  NumRowOffsets+1);

	/* Read in the row offset data all at once */
  Result = ReadEx((char *)pRowOffsets, sizeof(dftextrleinfo_t), NumRowOffsets);
  if (!Result) return (FALSE);

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

		/* Set the subimage info and allocate data */
    m_pSubImages[SubImageIndex].Width  = GetWidth();
    m_pSubImages[SubImageIndex].Height = GetHeight();
    pDataPtr = AllocateSubImageData(SubImageIndex, (size_t)GetTrueImageSize());

		/* Must read data row by row */
    for (RowIndex = 0; RowIndex < GetHeight(); RowIndex++) {
      Result = UncompressRLERow(pRowOffsets[RowIndex], &pDataPtr);
      if (!Result) break;
     }
   }

	/* Unallocate the temporary offset array */
  DestroyArrayPointer(pRowOffsets);
  return (Result);
 }
/*===========================================================================
 *		End of Class Method CDFTextureImage::ReadRLESubImageData()
 *=========================================================================*/


/*===========================================================================
 *
 * Class CDFTextureImage Method - boolean ReadSubImageData (void);
 *
 * Attempt to read in the subimage data from the current position in the
 * file stream.  Returns FALSE on any error.  Protected class method.
 *
 *=========================================================================*/
boolean CDFTextureImage::ReadSubImageData (void) {
  DEFINE_FUNCTION("CDFTextureImage::ReadSubImageData()");
  boolean Result;
  int     SubImageIndex;
  int     RowIndex;
  long    ImageSize;
  byte*   pDataPtr;

	/* Read in each sub-image */
  for (SubImageIndex = 0; SubImageIndex < GetNumSubImages(); SubImageIndex++) {

		/* Move to the start of the sub-image data and 
		 * read the subimage width and height  */
    Result = Seek(m_pSubImages[SubImageIndex].Offset + GetAbsDataOffset(), SEEK_SET);
    if (Result) Result = ReadShort(m_pSubImages[SubImageIndex].Width);
    if (Result) Result = ReadShort(m_pSubImages[SubImageIndex].Height);

		/* Compute the image size and allocate image data */
    ImageSize = (long)m_pSubImages[SubImageIndex].Width * (long)m_pSubImages[SubImageIndex].Height;
    m_pSubImages[SubImageIndex].ImageSize = ImageSize;

    if (ImageSize < 0 || (ulong)ImageSize >= (ulong) UINT_MAX) {
      ErrorHandler.AddError(DFERR_TEXTURE_IMAGESIZE, "ImageSize=%lu", ImageSize);
      return (FALSE);
     }

    pDataPtr  = AllocateSubImageData(SubImageIndex, (size_t)ImageSize);
    
		/* Must read sub-image data row by row */
    for (RowIndex = 0; RowIndex < m_pSubImages[SubImageIndex].Height; RowIndex++) {
      Result = UncompressSubImageRow(SubImageIndex, &pDataPtr);
      if (!Result) return (FALSE);
     }
   }

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


/*===========================================================================
 *
 * Class CDFTextureImage Method - boolean ReadSubImageInfo (void);
 *
 * Attempt to read in the sub-image offsets from the current position
 * in the file stream. Returns FALSE on any error.  Protected class method.
 *
 *=========================================================================*/
boolean CDFTextureImage::ReadSubImageInfo (void) {
  DEFINE_FUNCTION("CDFTextureImage::ReadSubImageInfo()");
  boolean Result;
  long    Offsets[DFTEXTURE_MAX_SUBIMAGES];
  int     SubImageIndex;

	/* Ensure valid object state */
  ASSERT(GetNumSubImages() > 1 && m_pSubImages != NULL && GetNumSubImages() < DFTEXTURE_MAX_SUBIMAGES);

	/* Jump to the position of the sub-image offsets and read offsets */ 
  Result = Seek(GetAbsDataOffset(), SEEK_SET);
  if (Result) Result = ReadEx((char *) &Offsets[0], sizeof(long), (size_t)GetNumSubImages());
  if (!Result) return (FALSE);

	/* Save offsets to sub-image data */
  for (SubImageIndex = 0; SubImageIndex < GetNumSubImages(); SubImageIndex++) {
    m_pSubImages[SubImageIndex].Offset = Offsets[SubImageIndex];
   }

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


#if defined(WRITE)

/*===========================================================================
 *
 * Class CDFTextureImage Method - boolean Write (void);
 *
 * Attempt to write the image header and data starting from the current
 * position in the file stream. Returns FALSE on any error.
 *
 *=========================================================================*/
boolean CDFTextureImage::Write (void) {
  DEFINE_FUNCTION("CDFTextureImage::Write()");
  boolean Result;

	/* Set the offset of the image header */
  m_HeaderOffset = Tell();

	/* Attempt to output header information */
  Result = WriteHeader();
  if (!Result) return (FALSE);
   
  	/* Check for no images to write */
  if (GetNumSubImages() == 0) {
    Result = TRUE;
   }
	/* Write multiple, compressed, sub-images */
  else if (GetNumSubImages() > 1 && IsCompressed()) {
    Result = WriteRLESubImageData();
   }
	/* Write uncompressed multiple sub-images */
  else if (GetNumSubImages() > 1) {
    Result = WriteSubImageInfo();
    if (Result) Result = WriteSubImageData();
   }
	/* Write single subimage data, RLE encoded */
  else if (IsCompressed()) 
    Result = WriteRLEData();
	/* Write single, uncompressed, image */
  else 
    Result = WriteData();

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


/*===========================================================================
 *
 * Class CDFTextureImage Method - boolean WriteHeader (void);
 *
 * Attempt to write the image header to the current position in
 * the file stream. Returns FALSE on any error.  Protected class method.
 *
 *=========================================================================*/
boolean CDFTextureImage::WriteHeader (void) {
  DEFINE_FUNCTION("CDFTextureImage::WriteHeader()");
  boolean Result;

	/* Assume we are at the header position currently and read in the
	 * header data all at once.*/
  Result = CGenFile::Write ((char*)&m_Header, DFTEXTURE_IMAGEHEADER_SIZE);
  return (Result);
 }
/*===========================================================================
 *		End of Class Method CDFTextureImage::WriteHeader()
 *=========================================================================*/


/*===========================================================================
 *
 * Class CDFTextureImage Method - boolean WriteData (void);
 *
 * Attempt to write a single image data to the file stream. Returns 
 * FALSE on  any error.  Protected class method.
 *
 *=========================================================================*/
boolean CDFTextureImage::WriteData (void) {
  DEFINE_FUNCTION("CDFTextureImage::WriteData()");
  boolean Result;
  int     RowIndex;
  byte*   pDataPtr;

	/* Move to the start of the image data */
  m_Header.DataOffset = sizeof(dftextimgheader_t);
  Result = Seek(GetAbsDataOffset(), SEEK_SET);
  if (!Result) return (FALSE);

  pDataPtr = GetImageData(0);
  ASSERT(pDataPtr != NULL);
  	
  	/* Must write data row by row */
  for (RowIndex = 0; RowIndex < GetHeight(); RowIndex++) {

		/* WRite one row of image data */
    Result = CGenFile::Write((char*)pDataPtr, (size_t)GetWidth());
    if (!Result) return (FALSE);

		/* Move to the start of the next row as all texture images
		 * are stored in 256 byte wide rows */
    Result = Seek(DFTEXTURE_IMAGEROW_WIDTH - GetWidth(), SEEK_CUR);
    pDataPtr += GetWidth();
   }

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


/*===========================================================================
 *
 * Class CDFTextureImage Method - boolean WriteRLEData (void);
 *
 * Attempt to write a single RLE image data to the file stream.  Returns
 * FALSE on any error.  Protected class method.
 *
 *=========================================================================*/
boolean CDFTextureImage::WriteRLEData (void) {
  DEFINE_FUNCTION("CDFTextureImage::WriteRLEData()");
  boolean	  Result;
  long		  RowOffsetPos;
  int		  RowIndex;
  byte*		  pDataPtr;
  dftextrleinfo_t RowOffsets[DFTEXTURE_MAXIMAGE_HEIGHT+1];

	/* Move to the start of the image data */
  m_Header.DataOffset = sizeof(dftextimgheader_t);
  Result = Seek(GetAbsDataOffset(), SEEK_SET);
  if (!Result) return (Result);
  
  RowOffsetPos = Tell();
  pDataPtr = GetImageData(0);
  RowOffsets[0] = sizeof(dftextrleinfo_t) * GetHeight() + sizeof(dftextimgheader_t);

	/* Skip the row offset data for now */
  Result = Seek(sizeof(dftextrleinfo_t) * GetHeight(), SEEK_CUR);
  if (!Result) return (FALSE);

	/* Must write the data row by row */
  for (RowIndex = 0; RowIndex < GetHeight(); RowIndex++) {
    Result = CompressRLERow(RowOffsets[RowIndex+1], &pDataPtr);
    if (!Result) return (FALSE);
   }

	/* Write out the row offset data all at once */
  Result = Seek(RowOffsetPos, SEEK_SET);
  if (Result) Result = CGenFile::WriteEx((char*)&RowOffsets[0], sizeof(dftextrleinfo_t), GetHeight());

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

#endif

/*===========================================================================
 *
 * Class CDFTextureImage Method - 
 *			boolean UnCompressRLERow (RowInfo, ppDataPtr);
 *
 * Protected class method to uncompress one row of RLE image data from the
 * current position in the file stream.  Returns FALSE on any error.
 *
 *=========================================================================*/
boolean CDFTextureImage::UncompressRLERow (const dftextrleinfo_t& RowInfo, 
					   byte** ppDataPtr) {
  DEFINE_FUNCTION("CDFTextureImage::UncompressRLERow()");
  boolean Result;
  int     BytesWritten;
  int     BytesDecoded;
  short   RLECounter;
  short   ByteCount;
  short   RowWidth;
  byte    RLEData;

	/* Ensure valid input */
  ASSERT(ppDataPtr != NULL && *ppDataPtr != NULL);

	/* Jump to the start of the row data */
  Result = Seek(RowInfo.Offset + m_HeaderOffset, SEEK_SET);
  if (!Result) return (Result);

	/* Check if we can input uncompressed data */
  if (RowInfo.Compressed == 0x0000) {

		/* Read in the row data all at once and ensure success */
    Result = CGenFile::Read((char*)*ppDataPtr, GetWidth());
    if (!Result) return (FALSE);
      
    (*ppDataPtr) += GetWidth();
    return (TRUE);
   }

	/* Input the row width value */
  Result = ReadShort(RowWidth);
  if (!Result) return (FALSE);
		
	/* Initialize the RLE variables */
  BytesWritten = 0;
  BytesDecoded = 0;
  ClearError();
		
	/* Uncompress the row data */
  while (BytesWritten < RowWidth) {
    fread(&ByteCount, 1, sizeof(short), GetHandle());
    
		/* Write out pixel values explicitly */
    if (ByteCount > 0) {
        
      for (RLECounter = 0; RLECounter < ByteCount; RLECounter++) {
        **ppDataPtr = fgetc(GetHandle());
        (*ppDataPtr)++;
       }

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

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

      BytesDecoded += 3;
      BytesWritten += ByteCount;
     }

		/* Check for error conditions */
    if (IsError() || IsEOF()) {
      ErrorHandler.AddError(ERR_SYSTEM, errno, "Failed to read compressed image row from file!");
      return (FALSE);
     }
   } /* End of while decoding */

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


/*===========================================================================
 *
 * Class CDFTextureImage Method - 
 *		boolean UnCompressSubImageRow (SubImageIndex, ppDataPtr);
 *
 * Protected class method to uncompress one row of regular subimage data
 * from the current position in the file stream. Returns FALSE on any error.
 *
 *=========================================================================*/
boolean CDFTextureImage::UncompressSubImageRow (const int SubImageIndex, 
						byte** ppDataPtr) {
  DEFINE_FUNCTION("CDFTextureImage::UncompressSubImageRow()");
  int   RowSkipBytes;
  int   RowBytes;
  int   RowWidth;
  int   BytesWritten = 0;

  	/* Ensure valid input */
  ASSERT(ppDataPtr != NULL && *ppDataPtr != NULL);
  RowWidth = m_pSubImages[SubImageIndex].Width;
  ClearError();

	/* Input all bytes from row */
  while (BytesWritten < RowWidth) {

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

		/* 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 */
    fread (*ppDataPtr, 1, RowBytes, GetHandle());
    (*ppDataPtr) += RowBytes;
    BytesWritten += RowBytes;
		
		/* Check for error conditions */
    if (IsError() || IsEOF()) {
      ErrorHandler.AddError(ERR_SYSTEM, errno, "Failed to read sub-image row from file!");
      return (FALSE);
     }

   } /* End of while loop */

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