/*===========================================================================
 *
 * File:	DFBSA.H
 * Author:	Dave Humphrey (uesp@m0use.net)
 * Created On:	Wednesday, June 20, 2001
 *
 * Defines the base class CDFBsaFile for handling Daggerfall's BSA type
 * data files.
 *
 *=========================================================================*/
#ifndef __DFBSA_H
#define __DFBSA_H


/*===========================================================================
 *
 * Begin Required Include Files
 *
 *=========================================================================*/
  #include "common/file/genfile.h"
  #include "uesp/dagger/common/dfcommon.h"
/*===========================================================================
 *		End of Required Include Files
 *=========================================================================*/


/*===========================================================================
 *
 * Begin Definitions
 *
 *=========================================================================*/

	/* Types of BSA directories */
  #define DFBSA_DIR_NAME  (0x0100)
  #define DFBSA_DIR_VALUE (0x0200)

	/* Size of buffer used to extract records */
  #define DFBSA_EXTRACT_BUFFERSIZE 32000

/*===========================================================================
 *		End of Definitions
 *=========================================================================*/


/*===========================================================================
 *
 * Begin Type Definitions
 *
 *=========================================================================*/
	
	/* Save and turn-off structure alignment */
#pragma pack(push, 1) 


	/* BSA filename directory type record structure */
  typedef struct {
    char Name[14];
    long Size;
   } dfbsa_namedir_t;

	/* BSA value directory type record structure */
  typedef struct {
    long Value;
    long Size;
   } dfbsa_valuedir_t;


	/* Restore structure alignment */
#pragma pack(pop) 

/*===========================================================================
 *		End of Type Definitions
 *=========================================================================*/


/*===========================================================================
 *
 * Begin Class CDFBsaFile Definition
 *
 * Base class for handling a Daggerfall BSA type file.  Derived from the
 * basic CGenFile class.
 *
 *=========================================================================*/
class CDFBsaFile : public CGenFile {

  /*---------- Begin Private Class Members ----------------------*/
private:
  short		m_NumRecords;		/* Number of directory entries */
  short		m_DirType;		/* Type of BSA directory */
  boolean	m_ReadHeader;		/* Flags to indicate whether basic BSA */
  boolean	m_ReadDir;		/* sections have been input */

  char*		m_pFilename;		/* The current BSA filename */

  dfbsa_namedir_t*  m_pNameDir;		/* Pointers to an array of directory records */
  dfbsa_valuedir_t* m_pValueDir;
  long*	            m_pRecordOffsets;	/* Pointer to array of file offsets */


  /*---------- Begin Protected Class Methods --------------------*/
protected:

	/* Create the directory arrays */
  void AllocateDirectory (void);
  void ComputeRecordOffsets (void);

	/* Helper function to dump a directory entry */
  void DumpDirEntry (FILE* pFileHandle, const int Index);

	/* Get a valid pointer to the directory array */
  void* GetDirectory (void);

	/* Input basic BSA sections */
  boolean ReadHeader    (void);
  boolean ReadDirectory (void);


  /*---------- Begin Public Class Methods -----------------------*/
public:

	/* Class Constructors/Destructors */
  CDFBsaFile();
  virtual void Destroy (void);

	/* Ensures the object contains valid data */
  boolean AssertValid (void);

  	/* Close an opened BSA file */
  virtual void Close (void);

	/* Dump object information to a file stream */
  void Dump (FILE* pFileHandle);

	/* Extract a binary record to the given filename */
  boolean Extract (const char* pFilename, const size_t Index);

	/* Search for a directory based on a name/value */
  int FindRecord (const long  Value);
  int FindRecord (const char* pName);

	/* Get class members */
  short GetNumRecords   (void) const;
  short GetDirType      (void) const;
  long  GetRecordOffset (const size_t Index) const;
  long  GetRecordSize   (const size_t Index) const;
  char* GetRecordName   (const size_t Index) const;
  long  GetRecordValue  (const size_t Index) const;

	/* Compute the size, in bytes, of the directory */
  long GetDirSize       (void) const;
  long GetDirRecordSize (void) const;

	/* Check a directory file index */
  boolean IsValidIndex (const size_t Index) const;

	/* Check object state */
  boolean HaveReadDirectory (void) const;
  boolean HaveReadHeader    (void) const;

	/* Open a BSA file for input/output */
  virtual boolean Open (const char* pFilename, const char* pMode);

	/* Set class members */
  void SetFilename (const char* pString);

 };
/*===========================================================================
 *		End of Class CDFBsaFile Definition
 *=========================================================================*/


/*===========================================================================
 *
 * Begin CDFBsaFile Inline Methods
 *
 *=========================================================================*/

	/* Get class members */
inline short CDFBsaFile::GetNumRecords (void) const { return (m_NumRecords); }
inline short CDFBsaFile::GetDirType    (void) const { return (m_DirType); }

	/* Get the size of a BSA file record */
inline long CDFBsaFile::GetRecordSize (const size_t Index) const {
  IASSERT((short)Index < m_NumRecords && m_ReadDir);
  return ((m_DirType == DFBSA_DIR_VALUE) ? m_pValueDir[Index].Size : m_pNameDir[Index].Size);
 }

	/* Get the offset to a BSA file record */
inline long CDFBsaFile::GetRecordOffset (const size_t Index) const {
  IASSERT((short)Index < m_NumRecords && m_ReadDir && m_pRecordOffsets != NULL);
  return m_pRecordOffsets[Index];
 }

	/* Get the name of a BSA file record */
inline char* CDFBsaFile::GetRecordName (const size_t Index) const {
  IASSERT((short)Index < m_NumRecords && m_ReadDir && m_DirType == DFBSA_DIR_NAME);
  return ((m_DirType == DFBSA_DIR_VALUE) ? NULL : m_pNameDir[Index].Name);
 }

	/* Get the value of a BSA file record */
inline long CDFBsaFile::GetRecordValue (const size_t Index) const {
  IASSERT((short)Index < m_NumRecords && m_ReadDir && m_DirType == DFBSA_DIR_VALUE);
  return ((m_DirType == DFBSA_DIR_VALUE) ? m_pValueDir[Index].Value : 0);
 }

	/* Compute the size, in bytes, of the directory */
inline long CDFBsaFile::GetDirSize       (void) const { return (GetDirRecordSize() * (long)m_NumRecords); }
inline long CDFBsaFile::GetDirRecordSize (void) const { return ((m_DirType == DFBSA_DIR_NAME) ? sizeof(dfbsa_namedir_t) : sizeof(dfbsa_valuedir_t)); }

	/* Check a directory file index */
inline boolean CDFBsaFile::IsValidIndex (const size_t Index) const { return ( ((short)Index < m_NumRecords) ? TRUE : FALSE); }

	/* Check object state */
inline boolean CDFBsaFile::HaveReadDirectory (void) const { return (m_ReadDir); }
inline boolean CDFBsaFile::HaveReadHeader    (void) const { return (m_ReadHeader); }


	/* Set class members */
inline void CDFBsaFile::SetFilename (const char* pString) { ReplaceString(&m_pFilename, pString); }

/*===========================================================================
 *		End of CDFBsaFile Inline Methods
 *=========================================================================*/


#endif
/*===========================================================================
 *		End of File Dfbsa.H
 *=========================================================================*/