/*===========================================================================
 *
 * File:	Dfimg.CPP
 * Author:	Dave Humphrey (uesp@m0use.net)
 * Created On:	Monday, June 25, 2001
 *
 * Implements the CDFImgFile class for handling Daggerfall IMG files.
 *
 *=========================================================================*/

	/* Include Files */
#include "dfimg.h"


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


/*===========================================================================
 *
 * Class CDFImgFile Constructor
 *
 *=========================================================================*/
CDFImgFile::CDFImgFile() {

	/* Initialize the image data and palette pointers */
  m_pData    = NULL;
  m_pPalette = NULL;

	/* Initialize the IMG header */
  m_Header.Width     = 0;
  m_Header.Height    = 0;
  m_Header.XOffset   = 0;
  m_Header.YOffset   = 0;
  m_Header.Unknown   = 0;
  m_Header.ImageSize = 0;
  m_ImageSize = 0;
  
	/* Initialize the object flags */
  m_Flags = 0;
 }
/*===========================================================================
 *		End of Class CDFImgFile Constructor
 *=========================================================================*/


/*===========================================================================
 *
 * Class CDFImgFile Destructor
 *
 *=========================================================================*/
void CDFImgFile::Destroy (void) {
  DEFINE_FUNCTION("CDFImgFile::Destroy()");

	/* Delete any allocated image data */
  DestroyArrayPointer(m_pData);
  DestroyArrayPointer(m_pPalette);

  	/* Re-initialize the IMG header */
  m_Header.Width     = 0;
  m_Header.Height    = 0;
  m_Header.XOffset   = 0;
  m_Header.YOffset   = 0;
  m_Header.Unknown   = 0;
  m_Header.ImageSize = 0;
  m_ImageSize = 0;
  
	/* Re-initialize the object flags */
  m_Flags = 0;

 }
/*===========================================================================
 *		End of Class CDFImgFile Destructor
 *=========================================================================*/


/*===========================================================================
 *
 * Class CDFImgFile Method - boolean CheckSpecialIMG (IMGInfo, FileSize);
 *
 * Checks the given IMG filesize to see if it matches any special IMG
 * format.  If it does, TRUE is returned and the Header structure is
 * filled in with the appropiate image width, height, size and flags.  FALSE
 * is returned if the IMG filesize does not match any known special formats.
 *
 *=========================================================================*/
boolean CDFImgFile::CheckSpecialIMG (dfimginfo_t& IMGInfo, const long FileSize) {
  DEFINE_FUNCTION("CDFImgFile::CheckSpecialIMG()");
  static dfimginfo_t s_ImgInfo[] = {	/* Describes the special formats */
  	/* Size, Width, Height, Flags */
	{    720l,    9,   80, DFIMG_FLAG_HASNOHEADER },
	{    990l,   45,   22, DFIMG_FLAG_HASNOHEADER },
	{   1720l,   43,   40, DFIMG_FLAG_HASNOHEADER },
	{   2140l,  107,   20, DFIMG_FLAG_HASNOHEADER },
	{   2916l,   81,   36, DFIMG_FLAG_HASNOHEADER },
	{   3200l,   40,   80, DFIMG_FLAG_HASNOHEADER },
	{   3938l,  179,   22, DFIMG_FLAG_HASNOHEADER },
	{   4508l,  322,   14, DFIMG_FLAG_HASNOHEADER },
	{   4280l,  107,   40, DFIMG_FLAG_HASNOHEADER },
	{  20480l,  320,   64, DFIMG_FLAG_HASNOHEADER },
	{  26496l,  184,  144, DFIMG_FLAG_HASNOHEADER },
	{  64000l,  320,  200, DFIMG_FLAG_HASNOHEADER },
	{  64768l,  320,  200, DFIMG_FLAG_HASNOHEADER | DFIMG_FLAG_HASPALETTE },
	{  68800l,  320,  215, DFIMG_FLAG_HASNOHEADER },
	{ 112128l,  512,  219, DFIMG_FLAG_HASNOHEADER },
	{ 0,	      0,    0, 0 }		/* Must be last element */
     };	
  int Index;

	/* Ensure valid input */
  ASSERT(FileSize > 0);

	/* Search static array for filesize match */
  for (Index = 0; s_ImgInfo[Index].FileSize != 0; Index++) {

    if (s_ImgInfo[Index].FileSize == FileSize) {
      IMGInfo = s_ImgInfo[Index];
      return (TRUE);
     }
   }

	/* Image is not a known special format */
  return (FALSE);
 }
/*===========================================================================
 *		End of Class Method CDFImgFile::CheckSpecialIMG()
 *=========================================================================*/


/*===========================================================================
 *
 * Class CDFImgFile Method - boolean Load (pFilename);
 *
 * Opens and read a IMG file. Returns FALSE on any error.
 *
 *=========================================================================*/
boolean CDFImgFile::Load (const char* pFilename) {
  dfimginfo_t IMGInfo;
  boolean     Result;

  	/* Delete the current image contents */
  Destroy();
  
	/* Attempt to open file for input */
  Result = Open(pFilename, "rb");
  if (!Result) return (FALSE);

	/* Check special IMG files by checking the file size */
  Result = CheckSpecialIMG(IMGInfo, GetFileSize());

  if (Result) {
    m_Flags = IMGInfo.Flags;
    SetWidth(IMGInfo.Width);
    SetHeight(IMGInfo.Height);
    m_ImageSize = (long) IMGInfo.Width * (long) IMGInfo.Height;
    m_Header.ImageSize = (ushort) m_ImageSize;
   }
  
	/* Read the image file */
  Result = Read();

  Close();
  return (Result);
 }
/*===========================================================================
 *		End of Class Method CDFImgFile::Load()
 *=========================================================================*/


/*===========================================================================
 *
 * Class CDFImgFile Method - boolean Save (pFilename);
 *
 * Opens and writes an IMG file. Returns FALSE on any error.
 *
 *=========================================================================*/
boolean CDFImgFile::Save (const char* pFilename) {
  boolean Result;
  
	/* Attempt to open file for output */
  Result = Open(pFilename, "wb");
  if (!Result) return (FALSE);

	/* Write image file */
  Result = Write();

  Close();
  return (Result);
 }
/*===========================================================================
 *		End of Class Method CDFImgFile::Save()
 *=========================================================================*/


/*===========================================================================
 *
 * Class CDFImgFile Method - boolean Read (void);
 *
 * Read an IMG file from the current position in the file. Returns FALSE
 * on any error.
 *
 *=========================================================================*/
boolean CDFImgFile::Read (void) {
  DEFINE_FUNCTION("CDFImgFile::Read()");
  boolean Result = TRUE;
  
	/* Ensure an open file */
  ASSERT(IsOpen());

	/* Read in the image header if required */
  if (HasHeader()) Result = ReadHeader();

	/* Read in the image data */
  if (Result) Result = ReadData(); 

   	/* Read in the palette data if present */
  if (Result && HasPalette()) Result = ReadPalette();

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


/*===========================================================================
 *
 * Class CDFImgFile Method - boolean ReadHeader (void);
 *
 * Protected class method which reads in the IMF file header from the
 * current position in the file. Returns FALSE on any error.
 *
 *=========================================================================*/
boolean CDFImgFile::ReadHeader (void) {
  DEFINE_FUNCTION("CDFImgFile::ReadHeader()");
  boolean Result;

	/* Ensure valid parameters */
  ASSERT(HasHeader());

	/* Input header all at once */
  Result = CGenFile::Read((char*)&m_Header, sizeof(dfimgheader_t));
  if (!Result) return (FALSE);

  m_ImageSize = (long)GetWidth() * (long)GetHeight();
  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CDFImgFile::ReadHeader()
 *=========================================================================*/


/*===========================================================================
 *
 * Class CDFImgFile Method - boolean ReadData (void);
 *
 * Protected class method which reads in the uncompressed image data from
 * the current position in the file. Returns FALSE on any error.
 *
 *=========================================================================*/
boolean CDFImgFile::ReadData (void) {
  DEFINE_FUNCTION("CDFImgFile::ReadData()");
  boolean Result;

	/* Ensure valid object state */
  ASSERT(m_pData == NULL);
  
	/* Check for valid image size */
  if (m_ImageSize < 0 || (ulong)m_ImageSize >= (ulong) UINT_MAX) { 
    ErrorHandler.AddError(DFERR_IMG_IMAGESIZE);
    return (FALSE);
   }

	/* Ignore if no data to load */
  if (m_ImageSize == 0) return (TRUE);
      
	/* Allocate the image data */
  CreateArrayPointer(m_pData, byte, (size_t)m_ImageSize);

	/* Read in image data all at once */
  Result = CGenFile::Read((char*)m_pData, (size_t)m_ImageSize);
  return (Result);
 }
/*===========================================================================
 *		End of Class Method CDFImgFile::ReadData()
 *=========================================================================*/


/*===========================================================================
 *
 * Class CDFImgFile Method - boolean ReadPalette (void);
 *
 * Reads the optional image palette data from the current position in the
 * file stream.  Returns FALSE on any error.  Protected class method.
 *
 *=========================================================================*/
boolean CDFImgFile::ReadPalette (void) {
  DEFINE_FUNCTION("CDFImgFile::ReadPalette()");

	/* Ensure valid state */
  ASSERT(HasPalette() && m_pPalette == NULL);

	/* Allocate and read palette data */
  CreateArrayPointer(m_pPalette, rgbpalraw_t, DFIMG_PALETTE_SIZE);
  return CGenFile::Read((char*)m_pPalette, DFIMG_PALETTE_SIZE*sizeof(rgbpalraw_t));
 }
/*===========================================================================
 *		End of Class Method CDFImgFile::ReadPalette()
 *=========================================================================*/


/*===========================================================================
 *
 * Class CDFImgFile Method - boolean Write (void);
 *
 * Attempts to output the current image data to the current file stream.
 * Returns FALSE on any error.
 *
 *=========================================================================*/
boolean CDFImgFile::Write (void) {
  DEFINE_FUNCTION("CDFImgFile::Write()");
  boolean Result = TRUE;

	/* Ensure a valid file stream */
  ASSERT(IsOpen());

  	/* Write the image header if required */
  if (HasHeader()) Result = WriteHeader();

	/* Write the image data */
  if (Result) Result = WriteData(); 

	/* Write in the palette data if present */
  if (Result && HasPalette()) Result = WritePalette();

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


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

	/* Ensure valid state */
  ASSERT(HasHeader());

	/* Write header all at once */
  Result = CGenFile::Write((char *)&m_Header, sizeof(dfimgheader_t));
  return (Result);
 }
/*===========================================================================
 *		End of Class Method CDFImgFile::WriteHeader()
 *=========================================================================*/


/*===========================================================================
 *
 * Class CDFImgFile Method - boolean WriteData (void);
 *
 * Protected class method which outputs the image data to the current
 * position in the file stream.  Returns FALSE on any error.
 *
 *=========================================================================*/
boolean CDFImgFile::WriteData (void) {
  DEFINE_FUNCTION("CDFImgFile::WriteData()");

	/* Ignore if no image data to output */
  if (m_ImageSize == 0) return (TRUE);

	/* Output data all at once */
  return CGenFile::Write((char *)m_pData, (size_t) m_ImageSize);
 }
/*===========================================================================
 *		End of Class Method CDFImgFile::WriteData()
 *=========================================================================*/


/*===========================================================================
 *
 * Class CDFImgFile Method - boolean WritePalette (void);
 *
 * Protected class method which writes the optional image palette data to
 * the current position in the data file.  Returns FALSE on any error.
 *
 *=========================================================================*/
boolean CDFImgFile::WritePalette (void) {
  DEFINE_FUNCTION("CDFImgFile::WritePalette()");

	/* Ensure valid object state */
  ASSERT(HasPalette());

	/* Write palette data all at once */
  return CGenFile::Write((char*)m_pPalette, DFIMG_PALETTE_SIZE*sizeof(rgbpalraw_t));
 }
/*===========================================================================
 *		End of Class Method CDFImgFile::WritePalette()
 *=========================================================================*/