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

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


#undef  __FUNC__
#define __FUNC__ "CDFSaveTreeRecord::CDFSaveTreeRecord()"
/*===========================================================================
 *
 * Class CDFSaveTreeRecord Constructor
 *
 *=========================================================================*/
CDFSaveTreeRecord::CDFSaveTreeRecord () {
  RecordSize = 0;
  RecordOffset = 0;
  RecordType = 0;
  HeaderInput = FALSE;
  pData = NULL;
  
 }
/*===========================================================================
 *		End of Class CDFSaveTreeRecord Constructor
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDFSaveTreeRecord::Destroy()"
/*===========================================================================
 *
 * Class CDFSaveTreeRecord Destructor
 *
 *=========================================================================*/
void CDFSaveTreeRecord::Destroy (void) {

	/* Unallocate any raw data */
  DestroyPointerArray(pData);

  RecordSize = 0;
  RecordOffset = 0;
  RecordType = 0;
  HeaderInput = FALSE;
  Tagged = FALSE;
 }
/*===========================================================================
 *		End of Class CDFSaveTreeRecord Destructor
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDFSaveTreeRecord::Compare()"
/*===========================================================================
 *
 * Class CDFSaveTreeRecord Method - boolean Compare (CDFSaveTreeRecord* pRecord)
 *
 * Compares two records, outputting any differences to the SystemLog file.
 * Returns FALSE on any error.
 *
 *=========================================================================*/
boolean CDFSaveTreeRecord::Compare (CDFSaveTreeRecord* pRecord) {

	/* Ensure valid input */
  if (pRecord == NULL) {
    SystemLog.Printf ("\t\tInput record is NULL!");
    return (FALSE);
   }

	/* Compare record sizes and types */
  CompareRecordSize(pRecord);
  CompareRecordType(pRecord);

  	/* Compare the record data */
  CompareRawData(pRecord);
  CompareRecordData(pRecord);
  
  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CDFSaveTreeRecord::Compare()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDFSaveTreeRecord::CompareRawData()"
/*===========================================================================
 *
 * Class CDFSaveTreeRecord Method - boolean CompareRawData (CDFSaveTreeRecord* pRecord)
 *
 * Compares the raw data of the two records, outputting any differences to 
 * the SystemLog file. Returns FALSE on any error.  Protected class method.
 *
 *=========================================================================*/
boolean CDFSaveTreeRecord::CompareRawData (CDFSaveTreeRecord* pRecord) {
  int   Result;
  int   Index;
  byte* pDataPtr1;
  byte* pDataPtr2;
	
	/* Compare the raw data, if any */
  if (GetRawDataSize() == 0 && pRecord->GetRawDataSize() != 0) {
   }
  else if (GetRawDataSize() == 0)
    SystemLog.Printf ("\t\tRecord #1 has no raw data!");
  else if (pRecord->GetRawDataSize() == 0)
    SystemLog.Printf ("\t\tRecord #2 has no raw data!");
  else if (GetRawDataSize() != pRecord->GetRawDataSize())
    SystemLog.Printf ("\t\tRecord raw data sizes differ (%ld / %ld bytes)!", GetRawDataSize(), pRecord->GetRawDataSize());
  else {
    Result = memcmp(pData, pRecord->GetRawData(), GetRawDataSize());
    if (Result == 0) return (TRUE);
    SystemLog.Printf ("\t\tRecord raw data differs (0x%08lX, 0x%08lX)!", GetRecordOffset(), pRecord->GetRecordOffset());
    pDataPtr1 = pData;
    pDataPtr2 = pRecord->GetRawData();

    for (Index = 0; Index < GetRawDataSize(); Index++, pDataPtr1++, pDataPtr2++) {
      if (*pDataPtr1 != *pDataPtr2) {
        SystemLog.Printf ("\t\t\t0x%04X: 0x%02X != 0x%02X", Index, *pDataPtr1, *pDataPtr2);
       }
     }
   }

  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CDFSaveTreeRecord::CompareRawData()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDFSaveTreeRecord::CompareRecordSize()"
/*===========================================================================
 *
 * Class CDFSaveTreeRecord Method - boolean CompareRecordSize (CDFSaveTreeRecord* pRecord)
 *
 * Compares the record sizes of the two records, outputting any differences 
 * to the SystemLog file. Returns FALSE on any error. Protected class method.
 *
 *=========================================================================*/
boolean CDFSaveTreeRecord::CompareRecordSize (CDFSaveTreeRecord* pRecord) {

	/* Compare record sizes */
  if (RecordSize != pRecord->GetRecordSize()) {
    SystemLog.Printf ("\t\tRecord sizes are different (%ld / %ld)!", RecordSize, pRecord->GetRecordSize());
   }
	
  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CDFSaveTreeRecord::CompareRecordSize()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDFSaveTreeRecord::CompareRecordType()"
/*===========================================================================
 *
 * Class CDFSaveTreeRecord Method - boolean CompareRecordType (CDFSaveTreeRecord* pRecord)
 *
 * Compares the record types of the two records, outputting any differences 
 * to the SystemLog file. Returns FALSE on any error. Protected class method.
 *
 *=========================================================================*/
boolean CDFSaveTreeRecord::CompareRecordType (CDFSaveTreeRecord* pRecord) {

	/* Compare record types */
  if (RecordType != pRecord->GetRecordType()) {
    SystemLog.Printf ("\t\tRecord types are different (%d / %d)!", RecordType, pRecord->GetRecordType());
   }
	
  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CDFSaveTreeRecord::CompareRecordType()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDFSaveTreeRecord::Read()"
/*===========================================================================
 *
 * Class CDFSaveTreeRecord Method - boolean Read (pFileHandle)
 *
 * Read in the raw record data.  Returns FALSE on any error.
 *
 *=========================================================================*/
boolean CDFSaveTreeRecord::Read (FILE* pFileHandle) {
  boolean Result;

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

	/* Input the record header */
  Result = ReadHeader(pFileHandle);
  if (Result) Result = ReadRecordHeader(pFileHandle);
  if (!Result) return (FALSE);

	/* Debug output */
  SystemLog.Printf ("Opening SaveTree Record at 0x%08lX, 0x%08lX bytes (0x%02X type)...", RecordOffset, GetRawDataSize(), RecordType);

  Result = ReadRawData(pFileHandle);
  if (!Result) return (FALSE);

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


#undef  __FUNC__
#define __FUNC__ "CDFSaveTreeRecord::ReadHeader()"
/*===========================================================================
 *
 * Class CDFSaveTreeRecord Method - boolean ReadHeader (pFileHandle)
 *
 * Read in the record data header.  Returns FALSE on any error.  Protected
 * class method.
 *
 *=========================================================================*/
boolean CDFSaveTreeRecord::ReadHeader (FILE* pFileHandle) {
  boolean Result;

	/* Ignore if header already input */
  if (HasReadHeader()) return (TRUE);
	
	/* Save the record offset */
  RecordOffset = ftell(pFileHandle);

	/* Input the record size */
  Result = read_long(pFileHandle, RecordSize);
  if (!Result) return (FALSE);

	/* Input the record type, if any */
  Result = ReadType(pFileHandle);
  if (!Result) return (FALSE);
 
  HeaderInput = TRUE;
  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CDFSaveTreeRecord::ReadHeader()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDFSaveTreeRecord::ReadRawData()"
/*===========================================================================
 *
 * Class CDFSaveTreeRecord Method - boolean ReadRawData (pFileHandle)
 *
 * Read in the raw record data.  Returns FALSE on any error.  Protected
 * class method.
 *
 *=========================================================================*/
boolean CDFSaveTreeRecord::ReadRawData (FILE* pFileHandle) {
  int Result;

	/* Ignore if recordsize is invalid */
  if (GetRawDataSize() <= 0) return (TRUE);

	/* Allocate the raw record data */
  CreatePointerArray(pData, byte, GetRawDataSize());

	/* Input the raw record data */
  Result = fread(pData, 1, GetRawDataSize(), pFileHandle);

  if (Result != GetRawDataSize()) {
    SET_EXT_ERROR4(ERR_READ, "Failed to input the raw record data (received %d of %d bytes)!", Result, GetRawDataSize());
    return (FALSE);
   }
	
  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CDFSaveTreeRecord::ReadRawData()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDFSaveTreeRecord::ReadType()"
/*===========================================================================
 *
 * Class CDFSaveTreeRecord Method - boolean ReadType (pFileHandle)
 *
 * Read in the record type byte.  Returns FALSE on any error.  Protected
 * class method.
 *
 *=========================================================================*/
boolean CDFSaveTreeRecord::ReadType (FILE* pFileHandle) {
  int Result;
	
	 /* Load the record type */
  Result = fgetc(pFileHandle);

  if (Result < 0) {
    SET_EXT_ERROR2(ERR_READ, "Failed to input the record type!");
    return (FALSE);
   }
 
  RecordType = (byte) Result;
  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CDFSaveTreeRecord::ReadType()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDFSaveTreeBase::CDFSaveTreeBase()"
/*===========================================================================
 *
 * Class CDFSaveTreeBase Constructor
 *
 *=========================================================================*/
CDFSaveTreeBase::CDFSaveTreeBase() {
	
	/* Reset the record header data */
  memset(&RecordHeader, 0, DFSAVETREE_RECORDHEADER_SIZE);
 }
/*===========================================================================
 *		End of Class CDFSaveTreeBase Constructor 
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDFSaveTreeBase::Destroy()"
/*===========================================================================
 *
 * Class CDFSaveTreeBase Destructor
 *
 *=========================================================================*/
void CDFSaveTreeBase::Destroy (void) {
	
	/* Reset the record header data */
  memset(&RecordHeader, 0, DFSAVETREE_RECORDHEADER_SIZE);

	/* Call the base class method */
  CDFSaveTreeRecord::Destroy();
 }
/*===========================================================================
 *		End of Class CDFSaveTreeBase Destructor 
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDFSaveTreeBase::CompareRecordHeader()"
/*===========================================================================
 *
 * Class CDFSaveTreeBase Method - boolean CompareRecordHeader (CDFSaveTreeRecord* pRecord)
 *
 * Compares the record header of the two records, outputting any differences to 
 * the SystemLog file. Returns FALSE on any error.  Protected class method.
 *
 *=========================================================================*/
boolean CDFSaveTreeBase::CompareRecordHeader (CDFSaveTreeRecord* pRecord) {
  dfsavetree_recordheader_t* pHeader;
  int Result;

	/* Get the record header for the second record */
  pHeader = ((CDFSaveTreeBase *)pRecord)->GetRecordHeader();
  	
	/* Compare the object IDs */
  if (RecordHeader.ObjectID != pHeader->ObjectID) 
    SystemLog.Printf ("\t\tObject IDs differ (%d / %d)", RecordHeader.ObjectID, pHeader->ObjectID);
  
  	/* Compare the parent IDs */
  if (RecordHeader.ParentID != pHeader->ParentID) 
    SystemLog.Printf ("\t\tParent IDs differ (%d / %d)", RecordHeader.ParentID, pHeader->ParentID);

  	/* Compare the parent types */
  if (RecordHeader.ParentType != pHeader->ParentType) 
    SystemLog.Printf ("\t\tParent types differ (%d / %d)", RecordHeader.ParentType, pHeader->ParentType);

	/* Compare the quest IDs */
  if (RecordHeader.QuestID != pHeader->QuestID) 
    SystemLog.Printf ("\t\tQuest IDs differ (%d / %d)", RecordHeader.QuestID, pHeader->QuestID);

	/* Compare the remaining portions of the structures */
  Result = memcmp(&RecordHeader, pHeader, DFSAVETREE_RECORDHEADER_SIZE);

  if (Result != 0)
    SystemLog.Printf ("\t\tRecord headers differ!");

  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CDFSaveTreeBase::CompareRecordHeader()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDFSaveTreeBase::ReadRecordHeader()"
/*===========================================================================
 *
 * Class CDFSaveTreeBase Method - boolean ReadRecordHeader (pFileHandle)
 *
 * Read in the base record header.  Returns FALSE on any error.  Protected
 * class method.
 *
 *=========================================================================*/
boolean CDFSaveTreeBase::ReadRecordHeader (FILE* pFileHandle) {
  int Result;

  Result = fread (&RecordHeader, 1, DFSAVETREE_RECORDHEADER_SIZE, pFileHandle);

  if (Result != DFSAVETREE_RECORDHEADER_SIZE) {
    SET_EXT_ERROR4(ERR_READ, "Failed to read base record header (only %d of %d bytes received)!", Result, DFSAVETREE_RECORDHEADER_SIZE);
    return (FALSE);
   }

  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CDFSaveTreeBase::ReadRecordHeader()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDFSaveTreeGuild::CompareRecordData()"
/*===========================================================================
 *
 * Class CDFSaveTreeGuild Method - boolean CompareRecordData (CDFSaveTreeRecord* pRecord)
 *
 * Compares the record data of the two records, outputting any differences to 
 * the SystemLog file. Returns FALSE on any error.  Protected class method.
 *
 *=========================================================================*/
boolean CDFSaveTreeGuild::CompareRecordData (CDFSaveTreeRecord* pRecord) {
  dfsavetree_guild_t* pGuild;

	/* Get the guild data */
  pGuild = ((CDFSaveTreeGuild *) pRecord)->GetGuildData();

	/* Compare guild IDs */
  if (GuildData.GuildID != pGuild->GuildID)
    SystemLog.Printf ("\t\tGuild IDs differ (%d / %d)!", GuildData.GuildID, pGuild->GuildID);

	/* Compare guild levels */
  if (GuildData.Level != pGuild->Level)
    SystemLog.Printf ("\t\tGuild levels differ (%d / %d)!", GuildData.Level, pGuild->Level);

	/* Compare guild times */
  if (GuildData.Time1 != pGuild->Time1)
    SystemLog.Printf ("\t\tGuild time1s differ (%ld / %ld)!", GuildData.Time1, pGuild->Time1);

	/* Compare guild times */
  if (GuildData.Time2 != pGuild->Time2)
    SystemLog.Printf ("\t\tGuild time2s differ (%ld / %ld)!", GuildData.Time2, pGuild->Time2);

	/* Compare guild unknowns */
  if (GuildData.Unknown1 != pGuild->Unknown1)
    SystemLog.Printf ("\t\tGuild unknown1s differ (%d / %d)!", GuildData.Unknown1, pGuild->Unknown1);

  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CDFSaveTreeGuild::CompareRecordData()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDFSaveTreeGuild::ReadRawData()"
/*===========================================================================
 *
 * Class CDFSaveTreeGuild Method - boolean ReadRawData (pFileHandle)
 *
 * Read in the guild and raw record data.  Returns FALSE on any error.  
 * Protected class method.
 *
 *=========================================================================*/
boolean CDFSaveTreeGuild::ReadRawData (FILE* pFileHandle) {
  int Result;

	/* Read in the guild data */
  Result = fread(&GuildData, 1, DFSAVETREE_GUILD_SIZE, pFileHandle);

  if (Result != DFSAVETREE_GUILD_SIZE) {
    SET_EXT_ERROR4(ERR_READ, "Failed to read guild data (only %d of %d bytes received)!", Result, DFSAVETREE_GUILD_SIZE);
    return (FALSE);
   }

	/* Read in any remaining raw data */
  return (CDFSaveTreeBase::ReadRawData(pFileHandle));
 }
/*===========================================================================
 *		End of Class Method CDFSaveTreeGuild::ReadRawData()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDFSaveTree::CDFSaveTree()"
/*===========================================================================
 *
 * Class CDFSaveTree Constructor
 *
 *=========================================================================*/
CDFSaveTree::CDFSaveTree() {
	
	/* Reset the header data */
  memset(&Header, 0, DFSAVETREE_HEADER_SIZE);
  NumRecords = 0;
  DungeonRecordIndex = -1;
  NumZeroSizedRecords = 0;
 }
/*===========================================================================
 *		End of Class CDFSaveTree Constructor 
 *=========================================================================*/


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

	/* Unallocate all the valid savetree records */
  for (LoopCounter = 0; LoopCounter < NumRecords; LoopCounter++) {
    DestroyPointer(pRecords[LoopCounter]);
   }

	/* Reset the header data */
  memset(&Header, 0, DFSAVETREE_HEADER_SIZE);
  NumRecords = 0;
  DungeonRecordIndex = -1;
  NumZeroSizedRecords = 0;
 }
/*===========================================================================
 *		End of Class CDFSaveTree Destructor 
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDFSaveTree::Compare()"
/*===========================================================================
 *
 * Class CDFSaveTree Method - boolean Compare (CDFSaveTree& SaveTree)
 *
 * Compares the given SaveTree file to the current one on a record 
 * by record basis.  Returns FALSE on any error.  Results are output
 * to the SystemLog file object.
 *
 *=========================================================================*/
boolean CDFSaveTree::Compare (CDFSaveTree& SaveTree) {
  CDFSaveTreeRecord*  pRecord;
  CDFSaveTreeTown*    pTown1;
  CDFSaveTreeTown*    pTown2;
  CDFSaveTreeDungeon* pDungeon1;
  CDFSaveTreeDungeon* pDungeon2;
  long		      ObjectID;
  int		      Index;
  int		      LoopCounter;

	/* Output header mesage */
  SystemLog.Printf ("Comparing SaveTree.DAT files...");

	/* Compare overall number of records */
  if (NumRecords != SaveTree.GetNumRecords()) {
    SystemLog.Printf ("\tNumber of records differ (%d / %d)", NumRecords, SaveTree.GetNumRecords());
   }

	/* Compare town records */
  SystemLog.Printf ("Comparing Town Records...");
  pTown1 = GetTownRecord();
  pTown2 = SaveTree.GetTownRecord();

  if (pTown1 == NULL && pTown2 == NULL) {
   }
  else if (pTown1 == NULL) {
    pTown2->SetTag();
    SystemLog.Printf ("\t\tNo town record in file #1!");
   }
  else if (pTown2 == NULL) 
    SystemLog.Printf ("\t\tNo town record in file #2!");
  else {
    pTown1->Compare(pTown2);
    pTown2->SetTag();
   }

	/* Compare the middle records */
  for (LoopCounter = 1; LoopCounter < DungeonRecordIndex && LoopCounter < NumRecords; LoopCounter++) {

		/* Ignore any NULL sized records */
//    if (pRecords[LoopCounter]->GetRecordSize() == 0) continue;

    SystemLog.Printf ("\tComparing record %d of %d (ID 0x%08lX, Size %ld bytes, Type 0x%02X '%s')", LoopCounter+1, NumRecords, pRecords[LoopCounter]->GetObjectID(), pRecords[LoopCounter]->GetRecordSize(), pRecords[LoopCounter]->GetRecordType(), DFGetSaveTreeType(pRecords[LoopCounter]->GetRecordType()));

		/* Attempt to find the given object ID in the supplied SaveTree file */
    ObjectID = pRecords[LoopCounter]->GetObjectID();
    Index = SaveTree.FindObjectID(ObjectID);
    pRecord = SaveTree.GetRecord(Index);
    if (pRecord != NULL) pRecord->SetTag();

		/* Compare the two records */
    pRecords[LoopCounter]->Compare(pRecord);
   }

	/* Compare dungeon records */
  SystemLog.Printf ("Comparing Dungeon Records...");
  pDungeon1 = GetDungeonRecord();
  pDungeon2 = SaveTree.GetDungeonRecord();

  if (pDungeon1 == NULL && pDungeon2 == NULL) {
   }
  else if (pDungeon1 == NULL) {
    SystemLog.Printf ("\tNo dungeon record in file #1!");
    pDungeon2->SetTag();
   }
  else if (pDungeon2 == NULL)  
    SystemLog.Printf ("\tNo dungeon record in file #2!");
  else {
    pDungeon1->Compare(pDungeon2);
    pDungeon2->SetTag();
   }

	/* Output any untagged records in SaveTree data */
  SaveTree.CompareOutputUntagged();
 
  return (TRUE); 
 }
/*===========================================================================
 *		End of Class Method CDFSaveTree::Compare()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDFSaveTree::CompareOutputUntagged()"
/*===========================================================================
 *
 * Class CDFSaveTree Method - boolean CompareOutputUntagged (void)
 *
 * Outputs any untagged records to the SystemLog file.  Returns FALSE
 * on any error.
 *
 *=========================================================================*/
boolean CDFSaveTree::CompareOutputUntagged (void) {
  int LoopCounter;

  SystemLog.Printf ("\tUntagged Records...");

  for (LoopCounter = 0; LoopCounter < NumRecords; LoopCounter++) {

		/* Ignore NULL and tagged records */
    if (pRecords[LoopCounter]->GetRecordSize() == 0) continue;
    if (pRecords[LoopCounter]->IsTagged()) continue;

    SystemLog.Printf ("\t\tRecord %d of %d (ID 0x%08lX, Size %ld, Type 0x%02X '%s')", LoopCounter+1, NumRecords, pRecords[LoopCounter]->GetObjectID(), pRecords[LoopCounter]->GetRecordSize(), pRecords[LoopCounter]->GetRecordType(), DFGetSaveTreeType(pRecords[LoopCounter]->GetRecordType()));
   }

  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CDFSaveTree::CompareOutputUntagged()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDFSaveTree::ClearTags()"
/*===========================================================================
 *
 * Class CDFSaveTree Method - void ClearTags (void)
 *
 * Clears the tag property of all the current records.
 *
 *=========================================================================*/
void CDFSaveTree::ClearTags (void) {
  int LoopCounter;

	/* Clear tags on all records */
  for (LoopCounter = 0; LoopCounter < NumRecords; LoopCounter++) {
    pRecords[LoopCounter]->SetTag(FALSE);
   }

 }
/*===========================================================================
 *		End of Class Method CDFSaveTree::ClearTags()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDFSaveTree::CreateRecord()"
/*===========================================================================
 *
 * Class CDFSaveTree Method - boolean CreateRecord (pFileHandle)
 *
 * Creates a new SaveTree record depending on the content of the record.
 * Protected class method.  Returns FALSE on any error
 *
 *=========================================================================*/
boolean CDFSaveTree::CreateRecord (FILE* pFileHandle) {
  long RecordSize;
  long RecordOffset;
  int  RecordType;
  int  Result;

  	/* Ensure we don't allocate too many records */
  if (NumRecords >= DFSAVETREE_MAX_RECORDS) {
    SET_EXT_ERROR3(ERR_MAXINDEX, "Maximum number of SaveTree records %d exceeded!", DFSAVETREE_MAX_RECORDS);
    return (FALSE);
   }

	/* If this is the first record, it should be the town record */
  if (NumRecords == 0) {
    CreatePointer(pRecords[0], CDFSaveTreeTown);
    NumRecords = 1;
    return (TRUE);
   }

	/* If this is the second to last record, it should be the special
	 * dungeon record. */
  if (NumZeroSizedRecords == 2 && DungeonRecordIndex < 0) {
    CreatePointer(pRecords[NumRecords], CDFSaveTreeDungeon);
    DungeonRecordIndex = NumRecords;
    NumRecords++;
    return (TRUE);
   }

	/* Save the record offset */
  RecordOffset = ftell(pFileHandle);
 	
	/* Load the record size */
  Result = read_long(pFileHandle, RecordSize);
  if (!Result) return (FALSE);

  	/* Check for zero-size records */
  if (RecordSize == 0) {
    CreatePointer(pRecords[NumRecords], CDFSaveTreeNULL);
    pRecords[NumRecords]->SetReadHeader(TRUE);
    pRecords[NumRecords]->SetRecordOffset(RecordOffset);
    pRecords[NumRecords]->SetRecordSize(0);
    NumRecords++;
    NumZeroSizedRecords++;
    return (TRUE);
   }

	/* Load the record type */
  RecordType = fgetc(pFileHandle);

  if (RecordType < 0) {
    SET_EXT_ERROR2(ERR_READ, "Failed to input the record type!");
    return (FALSE);
   }

	/* Create the desired record */
  CreatePointer(pRecords[NumRecords], CDFSaveTreeBase);
  NumRecords++;

	/* Set the record values */ 
  pRecords[NumRecords-1]->SetReadHeader(TRUE);
  pRecords[NumRecords-1]->SetRecordOffset(RecordOffset);
  pRecords[NumRecords-1]->SetRecordSize(RecordSize);
  pRecords[NumRecords-1]->SetRecordType((byte)RecordType);

  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CDFSaveTree::CreateRecord()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDFSaveTree::FindObjectID()"
/*===========================================================================
 *
 * Class CDFSaveTree Method - int FindObjectID (ObjectID)
 *
 * Look through the record array for a matching object ID.  Returns -1 on
 * any error.
 *
 *=========================================================================*/
int CDFSaveTree::FindObjectID (const long ObjectID) {
  int LoopCounter;

	/* Search through entire array */
  for (LoopCounter = 0; LoopCounter < NumRecords; LoopCounter++) {
    if (ObjectID == pRecords[LoopCounter]->GetObjectID()) return (LoopCounter);
   }

	/* No match found */
  return (-1);
 }
/*===========================================================================
 *		End of Class Method CDFSaveTree::FindObjectID()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDFSaveTree::Load()"
/*===========================================================================
 *
 * Class CDFSaveTree Method - boolean Load (const char* pFilename)
 *
 * Loads the desired SaveTree.DAT file.
 *
 *=========================================================================*/
boolean CDFSaveTree::Load (const char* pFilename) {
  FILE*   pFileHandle;
  long    Offset;
  long    FileSize;
  boolean Result;

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

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

	/* Read in the savetree header and records */
  Result = ReadHeader(pFileHandle);
  if (Result) Result = ReadRecords(pFileHandle);

  Offset = ftell(pFileHandle);
  fseek(pFileHandle, 0, SEEK_END);
  FileSize = ftell(pFileHandle);

  SystemLog.Printf ("Extra bytes left over: %ld", FileSize - Offset);
  SystemLog.Printf ("NumRecords = %d", NumRecords);

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


#undef  __FUNC__
#define __FUNC__ "CDFSaveTree::ReadHeader()"
/*===========================================================================
 *
 * Class CDFSaveTree Method - boolean ReadHeader (FILE* pFileHandle)
 *
 * Inputs the savetree header from the given filestream.  Returns FALSE
 * on any error.  Protected class method.
 *
 *=========================================================================*/
boolean CDFSaveTree::ReadHeader (FILE* pFileHandle) {
  int Result;

	/* Move to start of file */
  Result = fseek(pFileHandle, 0, SEEK_SET);

  if (Result != 0) {
    SET_EXT_ERROR2(ERR_READ, "Failed to move to start of file!");
    return (FALSE);
   }

	/* Read in the header all at once */
  Result = fread (&Header, 1, DFSAVETREE_HEADER_SIZE, pFileHandle);

  if (Result != DFSAVETREE_HEADER_SIZE) {
    SET_EXT_ERROR4(ERR_READ, "Failed to read SaveTree header (received %d of %d bytes)!", Result, DFSAVETREE_HEADER_SIZE);
    return (FALSE);
   }

  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CDFSaveTree::ReadHeader()
 *=========================================================================*/


#undef  __FUNC__
#define __FUNC__ "CDFSaveTree::ReadRecords()"
/*===========================================================================
 *
 * Class CDFSaveTree Method - boolean ReadRecords (FILE* pFileHandle)
 *
 * Inputs the record data from the given filestream.  Returns FALSE
 * on any error.  Protected class method.
 *
 *=========================================================================*/
boolean CDFSaveTree::ReadRecords (FILE* pFileHandle) {
  int  Result;
       
	/* Read in all the savetree records */
  while (!feof(pFileHandle) && NumZeroSizedRecords < 3) {

		/* Attempt to create a new record */
    Result = CreateRecord(pFileHandle);
    if (!Result) return (FALSE);

		/* Attempt to read the record */
    Result = pRecords[NumRecords-1]->Read(pFileHandle);
    if (!Result) return (FALSE);
   }

  return (TRUE);
 }
/*===========================================================================
 *		End of Class Method CDFSaveTree::ReadRecords()
 *=========================================================================*/

 
#undef  __FUNC__
#define __FUNC__ "DFGetSaveTreeType()"
/*===========================================================================
 *
 * Function - char* DFGetSaveTreeType (const int Type)
 *
 * Converts a record type from the SaveTree.DAT file into a descriptive 
 * string.  Never returns NULL.
 *
 *=========================================================================*/
char* DFGetSaveTreeType (const int Type) {
  static char Buffer[32];

  switch (Type) {
    case 0x02: return "Item";
    case 0x03: return "Character";
    case 0x04: return "Character Parent";
    case 0x05: return "Character Parent";
    case 0x06: return "Unknown 0x06";   
    case 0x08: return "Unknown 0x08";
    case 0x09: return "Spells";
    case 0x0a: return "Guild";
    case 0x0e: return "QBN Record";
    case 0x10: return "Parent QBN";
    case 0x12: return "Parent QBN";
    case 0x16: return "Spellcasting Creatures";
    case 0x17: return "Control Settings";
    case 0x18: return "Location Names";
    case 0x19: return "Unknown 0x19";
    case 0x1f: return "Potion";
    case 0x20: return "Unknown Town 0x020";
    case 0x21: return "Unknown Dungeon, 0x21";
    case 0x22: return "Creature"; 
    case 0x24: return "Store Items";
    case 0x27: return "Mystery 0x27";
    case 0x28: return "Location names1";
    case 0x29: return "Location names2";
    case 0x2b: return "Mystery 0x2B";
    case 0x2c: return "Creature";
    case 0x2d: return "NPC";
    case 0x2e: return "Generic NPC";
    case 0x33: return "Dungeon Record";
    case 0x34: return "Container Record";   
    case 0x40: return "Container?";
    case 0x41: return "Quest?";
    default:
      sprintf (Buffer, "Unknown 0x%02X", Type);
      return (Buffer);
    }
 }
/*===========================================================================
 *		End of Function DFGetSaveTreeType()
 *=========================================================================*/



/*
Temple of Akatosh
Temple of Arkay
Temple of Dibella
Temple of Julianos
Temple of Kynareth
Temple of Mara
Temple of Stendarr
Temple of Zen
Order of the Raven
Knights of the Dragon
Knights of the Owl
Order of the Candle
Knights of the Flame
Host of the Horn
Knights of the Rose
Knights of the Wheel
Order of the Scarab
Knights of the Hawk
Mages Guild
Fighters Guild


Thieves Guild
Dark Brotherhood
Temple of Theodorus
Knights of Daggerfall
Order of Nyeraad
Gryphon Knights
Order of the Lance
Knights of the Isle
Border Knights
Crusaders
Blades
Dreadknights
Order of the Lamp
Raen
Arkay
Zenithar
Notorgo
Mara
Shandar
Akatosh
Julianos
The Bandit God
Dibella
Jephre
Vigryl
Q'Olwen
Stendarr
Ephen

God of Agriculture
God of Birth and Death
God of Work and Commerce
Messenger God
Goddess of Love
God of War
God of Time
God of Logic
God of Thieves
Goddess of Beauty
the Storyteller
God of the Sea
(wisdom?)
God of Mercy
God of the Wild */