/*===========================================================================
 *
 * File:	DL_File.CPP
 * Author:	Dave Humphrey (uesp@m0use.net)
 * Created On:	Monday, May 07, 2001
 *
 * Implementation for file related routines for Dave's Library of common
 * code.
 *
 *=========================================================================*/

	/* Include Files */
#include "dl_file.h"
#include <direct.h>
#include <errno.h>
//#include <limits.h>
//#include <sys\stat.h>
//#include <io.h>
#include <ctype.h>

#ifndef _MSC_VER
  #include <dir.h>
#endif


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


/*=========================================================================
 *
 * Function - boolean ChangeDirectory (pPath);
 *
 * Returns TRUE if the specified path is a valid directory.  Does this
 * by attempting to change paths.  If successful the current path is
 * changed to the given directory.  Otherwise the current path is
 * unchanged and FALSE is returned.  ASSERTs on NULL input.  Is
 * currently valid in the following systems:
 *	WIN32 - Uses the _chdir() function
 *	MSDOS - Uses the chdir() function
 * ASSERTs if run under any other system.
 *
 *=======================================================================*/
boolean ChangeDirectory (const char* pPath) {
  DEFINE_FUNCTION("ChangeDirectory()");
  int Result;

	/* Make sure the given path is valid */
  ASSERT(pPath != NULL);

	/* Call the correct function according to the current system */
  #if defined(__BCPLUSPLUS__)
    Result = chdir(pPath);

		/* Change active drives if required */
    if (Result == 0 && pPath[1] == ':') {
      Result = _chdrive(tolower(pPath[0]) - 'a' + 1);
     }

  #elif defined(_WIN32)
    Result = _chdir(pPath);
  #elif defined(__MSDOS__)
    Result = chdir(pPath);

		/* Change active drives if required */
    if (Result == 0 && pPath[1] == ':') {
      Result = _chdrive(tolower(pPath[0]) - 'a' + 1);
     }

  #else
    ASSERT(FALSE);
  #endif


  if (Result != 0) {
    ErrorHandler.AddError(ERR_SYSTEM, (errcode_t)errno, "Directory '%s' is not valid!", pPath);
    return (FALSE);
   }

  return(TRUE);
 }
/*=========================================================================
 *		End of Function ChangeDirectory()
 *=======================================================================*/


/*===========================================================================
 *
 * Function - char* ChangeExtension (pDestFilename, pSourceFilename, 
 *				     pNewExtension, MaxStringLength);
 *
 * Copies the source filename to the destination, changing the file's
 * extension.  The destination filename will be, at most, MaxStringLength
 * bytes in length.  The new extension can include the leading '.'
 * character or not.  A pointer to the destination string is returned.
 * The function ASSERTs if passed a bad pointer. 
 *
 *=========================================================================*/
char* ChangeExtension (char* pDestFilename, const char* pSourceFilename, 
		       const char* pNewExtension, const size_t MaxStringLength) {
  DEFINE_FUNCTION("ChangeExtension()");
  size_t DestLength;

	/* Ensure valid input */
  ASSERT(pDestFilename != NULL && pSourceFilename != NULL && pNewExtension != NULL);

	/* Create the new filename without an extension */
  strnncpy(pDestFilename, pSourceFilename, MaxStringLength);
  RemoveExtension(pDestFilename);
  DestLength = strlen(pDestFilename);

	/* Ensure the '.' extension character is present */
  if (*pNewExtension != '.' && DestLength < MaxStringLength) {
    chrcat(pDestFilename, '.');
    DestLength++;
   }

	/* Add the new extension to the destination filename */
  strncat(pDestFilename, pNewExtension, MaxStringLength - DestLength);
  return (pDestFilename);
 }
/*===========================================================================
 *		End of Function ChangeExtension()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - boolean CompareExtension (pFilename, pExtension);
 *
 * Compares the extension of the given filename with the given extension
 * and returns TRUE if they match (case insensitive). The '.' extension
 * character is not included. ASSERTs if passed invalid string pointers.
 *
 *=========================================================================*/
boolean CompareExtension (const char* pFilename, const char* pExtension) {
  DEFINE_FUNCTION("CompareExtension()");
  char*  pFileExt;
  int	 Result;

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

	/* Attempt to find the file's extension */
  pFileExt = FindExtension(pFilename);

  if (pFileExt != NULL) {
    Result = stricmp(pFileExt, pExtension);
    if (Result == 0) return (TRUE);
   }

	/* No extension found */
  return (FALSE);
 }
/*===========================================================================
 *		End of Function CompareExtension()
 *=========================================================================*/


/*=========================================================================
 *
 * Function - boolean CopyOneFile (pInputFile, pOutputFile);
 *
 * Copies the given input file to the output file.  Returns FALSE on any
 * error.  Overwrites the destination file if it exists.  ASSERTs if
 * given invalid file strings.  Changed the buffer from a stack variable
 * to a dynamic because of problems with 16-bit compiles under Borland.
 *
 *=======================================================================*/
boolean CopyOneFile (const char* pInputFile, const char* pOutputFile) {
  DEFINE_FUNCTION("CopyOneFile()");
  FILE*   pInputHandle = NULL;
  FILE*   pOutputHandle = NULL;
  byte*   Buffer;
  size_t  ReadSize;
  size_t  WriteSize;
  boolean ReturnValue = TRUE;

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

	/* Attempt to open input file */
  pInputHandle = OpenFile(pInputFile, "rb");
  if (pInputHandle == NULL) return (FALSE);

	/* Attempt to open output file */
  pOutputHandle = OpenFile(pOutputFile, "wb");

  if (pOutputHandle == NULL) {
    fclose(pInputHandle);
    return (FALSE);
   }

   	/* Allocate the transfer buffer */
  Buffer = (byte *) CreateString(COPYFILE_BUFFERSIZE);

	/* Read and write file in sections until finished */
  do {

		/* Input data from source file and output to destination */
    ReadSize = fread(Buffer, 1, COPYFILE_BUFFERSIZE, pInputHandle);
    WriteSize = fwrite(Buffer, 1, ReadSize, pOutputHandle);

		/* Ensure both the input and output was successful */
    if (WriteSize != ReadSize || ferror(pInputHandle) || ferror(pOutputHandle)) {
      ErrorHandler.AddError(ERR_SYSTEM, (errcode_t)errno, "Failed to copy file '%s' to '%s'!", pInputFile, pOutputFile);
      ReturnValue = FALSE;
      break;
     }

   } while (ReadSize == COPYFILE_BUFFERSIZE);

	/* Close files */
  DestroyPointer(Buffer);
  fclose (pInputHandle);
  fclose (pOutputHandle);
  return (ReturnValue);
 }
/*=========================================================================
 *		End of Function CopyOneFile()
 *=======================================================================*/


/*=========================================================================
 *
 * Function - char* CreatePath (pNewPath, pString, MaxStringLength);
 *
 * Creates a path from the given string.  Copies up to MaxStringLength
 * bytes into the destination string and ensures that the given path ends
 * in the current LocalePathChar character.  ASSERTs on invalid input.
 * Returns a pointer to the new string.
 *
 *=======================================================================*/
char* CreatePath (char* pNewPath, const char* pString, const size_t MaxStringLength) {
  DEFINE_FUNCTION("CreatePath()");

	/* Ensure all input is valid */
  ASSERT(pNewPath != NULL && pString != NULL);
  
	/* Copy the given string into the new path */
  strnncpy (pNewPath, pString, MaxStringLength);

	/* Ensure the path ends with a path character */
  if ((size_t)strlen(pNewPath) < MaxStringLength) TerminatePath(pNewPath);
  return (pNewPath);
 }
/*=========================================================================
 *		End of Function CreatePath()
 *=======================================================================*/


/*=========================================================================
 *
 * Function - char* ExtractFilename (pFilename, pPath, MaxStringLength);
 *
 * Copies just the filename from the given path into the given file string
 * (up to MaxStringLength bytes).  ASSERTs if passed a bad pointer.  Returns
 * a pointer to the file string.
 *
 *=======================================================================*/
char* ExtractFilename (char* pFilename, const char* pPath, const size_t MaxStringLength) {
  DEFINE_FUNCTION("ExtractFilename()");

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

	/* Find the start of the filename in the path string */
  pPath = FindFilename(pPath);

	/* Check to ensure a filename was found in the path */
  if (pPath == NULL) 
    *pFilename = NULL_CHAR;
  else
    strnncpy (pFilename, pPath, MaxStringLength);

  return (pFilename);
 }
/*=========================================================================
 *		End of Function ExtractFilename()
 *=======================================================================*/


/*=========================================================================
 *
 * Function - char* ExtractPath (pPath, pString, MaxStringLength);
 *
 * Copies just the path from the given string and copies into the 
 * specified path (at most MaxStringLength bytes).  Returns a pointer
 * to the new path string.  Ensures the path ends in the current 
 * LocalePathChar character.  ASSERTs if passed a bad pointer.
 * Assumes the string has the format:
 *	drive:\\path1\\path2\\filename
 *	drive:\\filename
 *	drive:filename
 * Both the drive and filename are optional.
 *
 *=======================================================================*/
char* ExtractPath (char* pPath, const char* pString, const size_t MaxStringLength) {
  DEFINE_FUNCTION("ExtractPath()");
  char* pFilePtr;

	/* Ensure all the input is valid */
  ASSERT(pPath != NULL && pString != NULL);

	/* Copy the string into the new path */
  strnncpy (pPath, pString, MaxStringLength);

	/* Remove any filename from the new path string, if any */
  pFilePtr = FindFilename(pPath);
  *pFilePtr = NULL_CHAR;
  
	/* Ensure the path terminates properly, if possible */
  if ((size_t) strlen(pPath) < MaxStringLength) TerminatePath(pPath);
  return (pPath);
 }
/*=========================================================================
 *		End of Function ExtractPath()
 *=======================================================================*/


/*=========================================================================
 *
 * Function - boolean FileExists (pFilename);
 *
 * Returns TRUE if the specified file exists and can be opened for 
 * reading.  ASSERTs if passed a bad pointer.
 *
 *=======================================================================*/
boolean FileExists (const char* pFilename) {
  DEFINE_FUNCTION("FileExists()");
  FILE* pFileHandle;

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

	/* Attempt to open file for reading */
  pFileHandle = fopen(pFilename, "r");
  if (pFileHandle == NULL) return(FALSE);

	/* File was opened and therefore exists, close and return success */
  fclose(pFileHandle);
  return(TRUE);
 }
/*=========================================================================
 *		End of Function FileExists()
 *=======================================================================*/


/*===========================================================================
 *
 * Function - char* FindExtension (pFilename);
 *
 * Returns a pointer to the first character in the file extension, just 
 * after the '.' character.  Returns NULL if the file has no extension.
 * ASSERTs if given an invalid string pointer.  Searches for the first
 * '.' character from the end of the string before a path seperator
 * character (LocalePathChar) or a ':'.  
 *
 *=========================================================================*/
char* FindExtension (const char* pFilename) {
  DEFINE_FUNCTION("FindExtension()");
  size_t StringIndex;

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

	/* Find the end of the filename */
  StringIndex = strlen(pFilename);

	/* Find the last '.' character before the path/drive starts */
  while (pFilename[StringIndex] != LocalePathChar &&
         pFilename[StringIndex] != ':'            &&
         StringIndex != 0) {
    StringIndex--;

	/* Check for the extension marker charaacter */
    if (pFilename[StringIndex] == '.') {
      return (char *)(pFilename + StringIndex + 1);
     }
   }

	/* No extension found */
  return (NULL);
 }
/*===========================================================================
 *		End of Function FindExtension()
 *=========================================================================*/


/*=========================================================================
 *
 * Function - char* FindFilename (pPath);
 *
 * Returns a pointer to the first character in the filename in the given 
 * complete path.  ASSERTs if given an invalid pointer.  Returns an
 * empty string if the given path contains no filename.  Uses the current 
 * LocalePathChar and the drive character ':'.
 *
 *=======================================================================*/
char* FindFilename (const char* pPath) {
  DEFINE_FUNCTION("FindFilename()");
  size_t StringIndex;

	/* Ensure the input is valid */
  ASSERT(pPath != NULL);

	/* Start at the end of the given path */
  StringIndex = strlen(pPath);

  while (StringIndex != 0) {
    StringIndex--;

    if (pPath[StringIndex] == LocalePathChar ||
        pPath[StringIndex] == ':') {
      StringIndex++;
      break;
     }
   }

  return (char *)(pPath + StringIndex);
 }
/*=========================================================================
 *		End of Function FindFilename()
 *=======================================================================*/


/*===========================================================================
 *
 * Function - long GetFileSize (pFilename);
 *
 * Returns the size of the give filename.  Returns 0 on any error and
 * sets the appropiate error code. ASSERTs if given a bad file string pointer.
 *
 *=========================================================================*/
long GetFileSize (const char* pFilename) {
  //DEFINE_FUNCTION("GetFileSize(char*)");
  long    FileSize;
  boolean Result;

  Result = GetFileSize(FileSize, pFilename);
  if (!Result) return (0);
  return (FileSize);
 }
/*===========================================================================
 *		End of Function GetFileSize()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - long GetFileSize (pFileHandle);
 *
 * Returns the size of the give file stream.  Returns 0 on any error and
 * sets the appropiate error code. ASSERTs if given a bad file stream pointer.
 * The current file position remains unchanged.
 *
 *=========================================================================*/
long GetFileSize (FILE* pFileHandle) {
  //DEFINE_FUNCTION("GetFileSize(FILE*)");
  long    FileSize;
  boolean Result;

  Result = GetFileSize(FileSize, pFileHandle);
  if (!Result) return (0);
  return (FileSize);
 }
/*===========================================================================
 *		End of Function GetFileSize()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - boolean GetFileSize (FileSize, pFilename);
 *
 * Attempts to retrieve the size in bytes of the given filename. Returns
 * FALSE on any error (setting the appropiate error), or TRUE on success.
 * ASSERTs if given an invalid file string pointer.
 *
 *=========================================================================*/
boolean GetFileSize (long& FileSize, const char* pFilename) {
  DEFINE_FUNCTION("GetFileSize(long&, char*)");
  FILE*   pFileHandle;
  boolean Result;

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

	/* Attempt to open file for binary input */
  pFileHandle = fopen(pFilename, "rb");

  if (pFileHandle == NULL) {
    ErrorHandler.AddError(ERR_SYSTEM, (errcode_t)errno, "Could not open the file '%s'!", pFilename);
    return (FALSE);
   }

  Result = GetFileSize(FileSize, pFileHandle);
  fclose (pFileHandle);

  return (Result);
 }
/*===========================================================================
 *		End of Function GetFileSize()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - boolean GetFileSize (FileSize, pFileHandle);
 *
 * Attempts to retrieve the number of bytes in the given valid file
 * stream.  The file position of the stream is unchanged.   Returns
 * TRUE on success, or FALSE on any error, setting the appropiate error.
 * ASSERTs if given an  invalid stream pointer.  Files opened in text mode
 * may not report the file size correctly.
 *
 *=========================================================================*/
boolean GetFileSize (long& FileSize, FILE* pFileHandle) {
  DEFINE_FUNCTION("GetFileSize(long&, FILE*)");
  long PrevFilePos;
  int  Result;

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

	/* Save the current file position */
  PrevFilePos = ftell(pFileHandle);

  if (PrevFilePos < 0) {
    ErrorHandler.AddError(ERR_SYSTEM, (errcode_t)errno, "Could not retrieve current position in file!");
    return (FALSE);
   }

	/* Attempt to move to the end of the file */
  Result = fseek(pFileHandle, 0, SEEK_END);

  if (Result < 0) {
    ErrorHandler.AddError(ERR_SYSTEM, (errcode_t)errno, "Could not move file position to end of file!");
    return (FALSE);
   }

	/* Get the size of the file in bytes */  
  FileSize = ftell(pFileHandle);

  if (FileSize < 0) {
    ErrorHandler.AddError(ERR_SYSTEM, (errcode_t)errno, "Could not retrieve current position in file!");
    return (FALSE);
   }

  Result = fseek(pFileHandle, PrevFilePos, SEEK_SET);

  if (Result < 0) {
    ErrorHandler.AddError(ERR_SYSTEM, (errcode_t)errno, "Could not move file position to previous location!");
    return (FALSE);
   }

  return (TRUE);
 }
/*===========================================================================
 *		End of Function GetFileSize()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - boolean HasExtension (pFilename);
 *
 * Returns TRUE if the given filename has an extension.  ASSERTs if given
 * an invalid string pointer.
 *
 *=========================================================================*/
boolean HasExtension (const char* pFilename) {
  DEFINE_FUNCTION("HasExtension()");

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

  if (FindExtension(pFilename) == NULL) return (FALSE);
  return (TRUE);
 }
/*===========================================================================
 *		End of Function HasExtension()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - boolean HasPath (pFilename);
 *
 * Returns TRUE if the given filename contains a path.  ASSERTs if given an
 * invalid string pointer. Uses the current LocalePathChar character and the
 * drive character ':'.
 *
 *=========================================================================*/
boolean HasPath (const char* pFilename) {
  DEFINE_FUNCTION("HasPath()");

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

	/* Look for any drive/path characters in filename */
  while (*pFilename != NULL_CHAR) {
    if (*pFilename == LocalePathChar || *pFilename == ':') return (TRUE);
    pFilename++;
   }

  return (FALSE);
 }
/*===========================================================================
 *		End of Function HasPath()
 *=========================================================================*/


/*=========================================================================
 *
 * Function - boolean IsDirectory (pPath);
 *
 * Returns TRUE if the specified path is a valid but does not change
 * change the current directory.  ASSERTs if the input path pointer is
 * invalid.  Sets the appropiate error on failure.
 *
 *=======================================================================*/
boolean IsDirectory(const char* pPath) {
  DEFINE_FUNCTION("IsDirectory()");
  char  InitialPath[_MAX_PATH+1];
  char* pResult;
  int   Result;

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

	/* Save the initial directory */
  pResult = getcwd(InitialPath, _MAX_PATH);

  if (pResult == NULL) {
    ErrorHandler.AddError(ERR_SYSTEM, (errcode_t)errno, "Failed to retrieve the current directory!");
    return (FALSE);
   }

	/* Attempt to change directories */
  Result = chdir(pPath);
  if (Result != 0) return(FALSE);

	/* Restore the initial path and return success */
  chdir(InitialPath);
  return(TRUE);
 }
/*=========================================================================
 *		End of Function IsDirectory()
 *=======================================================================*/


/*========================================================================
 *
 * Function - boolean IsFileWriteable (pFilename);
 *
 * Returns TRUE if the given file can be written to.  ASSERTs if 
 * input string is invalid.
 *
 *======================================================================*/
boolean IsFileWriteable (const char* pFilename) { 
  DEFINE_FUNCTION("IsFileWriteable()");
  FILE* pFileHandle;

	/* Ensure valid input */
  ASSERT(pFilename != NULL);
  
	/* Attempt to open the file for appending */
  pFileHandle = fopen (pFilename, "ab");
  if (pFileHandle == NULL) return (FALSE);

	/* Close the now open file and return success */
  fclose (pFileHandle);
  return (TRUE);
 }
/*========================================================================
 *		End of Function IsFileWriteable()
 *======================================================================*/


/*=========================================================================
 *
 * Function - boolean IsWildCard (pFilename);
 *
 * Returns TRUE if the given string has any '*' or '?' wildcard
 * characters in it.  ASSERTs if the given string is invalid.
 *
 *=======================================================================*/
boolean IsWildCard (const char* pFilename) {
  DEFINE_FUNCTION("IsWildCard()");

	/* Ensure valid input */
  ASSERT(pFilename != NULL);
  
	/* Search entire string for a wildcard character */
  while (*pFilename != NULL_CHAR) {
    if (*pFilename == '*' || *pFilename == '?') return (TRUE);
    pFilename++;
   }

	/* No wildcard characters found */
  return (FALSE);
 }
/*=========================================================================
 *		End of Function IsWildcard()
 *=======================================================================*/


/*===========================================================================
 *
 * Function - FILE* OpenFile (pFilename, pMode);
 *
 * Wrapper function for opening a file with fopen().  Records error and 
 * SystemLog information automatically.  Returns NULL on any error.  
 * ASSERTs if the filename or mode string is invalid.
 *
 *=========================================================================*/
FILE* OpenFile (const char* pFilename, const char* pMode) {
  DEFINE_FUNCTION("OpenFile(char*, char*)");
  FILE* pFileHandle = NULL;

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

	/* Attempt to open file in desired mode */
  if (*pFilename != NULL_CHAR && *pMode != NULL_CHAR) {
    pFileHandle = fopen(pFilename, pMode);
   }

	/* Check for error conditions and output status */
  if (pFileHandle == NULL)
    ErrorHandler.AddError(ERR_SYSTEM, (errcode_t)errno, "Failed to open the file '%s' (mode was '%s')!", pFilename, pMode);
  else
    SystemLog.Printf ("Opened file '%s' in mode '%s'...", pFilename, pMode);
  
  return (pFileHandle);
 }
/*===========================================================================
 *		End of Function OpenFile()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - boolean OpenFile (ppFileHandle, pFilename, pMode);
 *
 * Attempts to open a file using fopen().  Returns TRUE on success, and
 * FALSE on error, setting the appropiate error.  ASSERTs if any input
 * string pointer is invalid.
 *
 *=========================================================================*/
boolean OpenFile (FILE** ppFileHandle, const char* pFilename, const char* pMode) {
  DEFINE_FUNCTION("OpenFile(FILE**, char*, char*)");

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

  *ppFileHandle = OpenFile(pFilename, pMode);

  if (*ppFileHandle == NULL) return (FALSE);
  return (TRUE);
 }
/*===========================================================================
 *		End of Function OpenFile()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - boolean ReadFile (ppBuffer, BytesRead, pFilename, TextMode);
 *
 * Attempts to read the entire file into a new string buffer.
 * On any error FALSE is returned and the appropiate error code set.
 * On success, the ppBuffer points to the newly allocated string 
 * containing the file data, and BytesRead is the size of the string.
 * ASSERTs if tanyhe input is invalid.  Ensure that any valid
 * returned pointer is at one point unallocated with delete.  If the
 * TextMode flag is TRUE (FILE_TEXT), the file is read in text mode.
 * If FALSE (FILE_BINARY), the file is read in binary mode.  Note that
 * on some systems, the two modes are identical.  
 *
 *=========================================================================*/
boolean ReadFile (char** ppBuffer, size_t& BytesRead, const char* pFilename, const boolean TextMode) {
  DEFINE_FUNCTION("ReadFile()");
  FILE*   pFileHandle;
  long    FileSize;
  size_t  BufferSize;
  boolean Result;
  boolean ReturnValue = TRUE;
  
	/* Ensure valid input */
  ASSERT(pFilename != NULL && ppBuffer != NULL);
  BytesRead = 0;
  *ppBuffer = NULL;

	/* Attempt to open the file for input */
  pFileHandle = OpenFile(pFilename, TextMode ? "rt" : "rb");
  if (pFileHandle == NULL) return (FALSE);

	/* Attempt to get the file size in bytes */
  Result = GetFileSize(FileSize, pFileHandle);
  BufferSize = (size_t) FileSize;

	/* If an error occured getting the file size, do nothing */
  if (!Result) {
    ReturnValue = FALSE;
   }
	/* For systems with long/int having different bit sizes */
  else if (FileSize != (long) BufferSize) {
    ErrorHandler.AddError(ERR_MEM, "Cannot read the file '%s' as it's size exceeds the maximum allocation size!", pFilename);
    ReturnValue = FALSE;
   }
	/* Allocate input buffer and read data from file */
  else {
    *ppBuffer = CreateString(BufferSize);
    BytesRead = fread(*ppBuffer, sizeof(char), BufferSize, pFileHandle);
    
		/* Ensure the input was entirely successfull */
    if (ferror(pFileHandle)) {
      ErrorHandler.AddError(ERR_SYSTEM, (errcode_t)errno, "Could not read the entire file '%s' (%u of %u bytes read)!", pFilename, BytesRead, BufferSize);
      DestroyPointer(*ppBuffer);
      ReturnValue = FALSE;
     }	
   }

  fclose (pFileHandle);
  return (ReturnValue);
 }
/*===========================================================================
 *		End of Function ReadFile()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - boolean ReadFileBuffer (ppBuffer, BytesRead, pFilename, MaxSize, TextMode);
 *
 * Attempts to read the entire, or part of, a file into an existing string
 * buffer. On any error FALSE is returned and the appropiate error code set.
 * On success, at most MaxSize bytes of the file is read into the ppBuffer,
 * BytesRead is the number of bytes read. ASSERTs if tanyhe input is invalid.
 * If the TextMode flag is TRUE (FILE_TEXT), the file is read in text mode.
 * If FALSE (FILE_BINARY), the file is read in binary mode.  Note that
 * on some systems, the two modes are identical.  The buffer should be
 * at least one byte bigger in text mode to allow for the '\0' terminating8
 * byte.
 *
 *=========================================================================*/
boolean ReadFileBuffer (char** ppBuffer, size_t& BytesRead, const char* pFilename, 
		        const size_t MaxInputSize, const boolean TextMode) {
  DEFINE_FUNCTION("ReadFileBuffer()");
  FILE*   pFileHandle;
  long    FileSize;
  size_t  BufferSize;
  boolean Result;
  boolean ReturnValue = TRUE;
  
	/* Ensure valid input */
  ASSERT(pFilename != NULL && ppBuffer != NULL && *ppBuffer != NULL);
  BytesRead = 0;

	/* Attempt to open the file for input */
  pFileHandle = OpenFile(pFilename, TextMode ? "rt" : "rb");
  if (pFileHandle == NULL) return (FALSE);

	/* Attempt to get the file size in bytes */
  Result = GetFileSize(FileSize, pFileHandle);
  BufferSize = (size_t) FileSize;

	/* If an error occured getting the file size, do nothing */
  if (!Result) {
    ReturnValue = FALSE;
   }
	/* For systems with long/int having different bit sizes */
  else if (FileSize != (long) BufferSize) {
    ErrorHandler.AddError(ERR_MEM, "Cannot read the file '%s' as it's size exceeds the maximum allocation size!", pFilename);
    ReturnValue = FALSE;
   }
	/* Read data from file */
  else {
    if (BufferSize > MaxInputSize) BufferSize = MaxInputSize;
    BytesRead = fread(*ppBuffer, sizeof(char), BufferSize, pFileHandle);
    
		/* Ensure the input was entirely successfull */
    if (ferror(pFileHandle)) {
      ErrorHandler.AddError(ERR_SYSTEM, (errcode_t)errno, "Could not read the file '%s' (%u of %u bytes read)!", pFilename, BytesRead, BufferSize);
      ReturnValue = FALSE;
     }

		/* NULL terminate if in text mode */
    if (TextMode) (*ppBuffer)[BufferSize] = NULL_CHAR;
   }

  fclose (pFileHandle);
  return (ReturnValue);
 }
/*===========================================================================
 *		End of Function ReadFileBuffer()
 *=========================================================================*/


/*=========================================================================
 *
 * Function - int ReadLine (pFileHandle, pString, MaxStringLength);
 *
 * Reads up to MaxStringLength characters or to the first Linefeed
 * from the given file into the given string.  If string is NULL, 
 * characters are merely read and not stored in string.  Returns:
 *	READLINE_OK    : Success
 *	READLINE_MSL   : If the maximum string length was reached
 *	READLINE_EOF   : End-of-file was reached
 *	READLINE_ERROR : An error was encountered. Sets the appropiate error.
 * ASSERTs if the input file handle is invalid.  The CR character is not
 * included at the end of the string.  MaxStringLength must be greater 
 * than 0.
 *
 *=======================================================================*/
int ReadLine (FILE* pFileHandle, char* pString, const size_t MaxStringLength) {
  DEFINE_FUNCTION("ReadLine()");
  int    ReturnValue = READLINE_OK;
  int    InputChar;
  size_t StringLength = 0;

	/* Ignore any invalid file handle input */
  ASSERT(pFileHandle != NULL);
  ASSERT(MaxStringLength != 0);

	/* Check if at the eof already */
  if (feof(pFileHandle)) {
    ErrorHandler.AddError(ERR_READFILE, "Could not read line, already at the end of the file!");
    return (READLINE_ERROR);
   }

	/* Main input loop (infinite) */
  do {
		/* Read in next character from file */
    InputChar = fgetc(pFileHandle);

		/* Check for EOF or Error conditions */
    if (InputChar == EOF) {
      if (!feof(pFileHandle)) {
        ErrorHandler.AddError(ERR_SYSTEM, (errcode_t)errno, "Failed to read line from file!");
	ReturnValue = READLINE_ERROR;
       }
      else
        ReturnValue = READLINE_EOF;

      break;
     }
		/* Check for end of line */
    else if (InputChar == LF_CHAR) {
      break;
     }
		/* Add character to string buffer */
    else if (pString != NULL) {
      pString[StringLength] = (char) InputChar;
      StringLength++;

		/* Ensure string buffer does not exceed its maximum length */
      if (StringLength >= MaxStringLength) {
        SystemLog.Printf ("ReadLine() - Maximum string length %u reached!", MaxStringLength);
        ReturnValue = READLINE_MSL;
	break;
       }
     }

  } while (TRUE);	/* Loop is exited using break */

	/* Ensure the string is NULL terminated */
  if (pString != NULL) pString[StringLength] = NULL_CHAR;
  return (ReturnValue);
 }
/*=========================================================================
 *		End of Function ReadLine()
 *=======================================================================*/


/*=========================================================================
 *
 * Function - boolean read_int (pFileHandle, Value);
 *
 * Reads an integer from the specified file. Returns FALSE on any error
 * and sets the appropiate code in ErrorHandler. The size of an integer
 * depends on the platform compiled under. In DOS it is usually 16 bit,
 * in Windows it is 32 bit, etc...  ASSERTs if the input handle is invalid.
 *
 *=======================================================================*/
boolean read_int (FILE* pFileHandle, int& Value) {
  DEFINE_FUNCTION("read_int()");
  size_t InputSize;

	/* Make sure the file handle is valid */
  ASSERT(pFileHandle != NULL);

	/* Read in the integer value */
  InputSize = fread(&Value, 1, sizeof(int), pFileHandle);

	/* Check for any read error */
  if (InputSize != sizeof(int)) {
    ErrorHandler.AddError(ERR_SYSTEM, (errcode_t)errno, "Error reading binary integer value (read only %u of %u bytes)!", InputSize, sizeof(int));
    return (FALSE);
   }

  return (TRUE);
 }
/*=========================================================================
 *		End of Function read_int()
 *=======================================================================*/


/*=========================================================================
 *
 * Function - boolean read_long (pFileHandle, Value);
 *
 * Reads a long integer from the specified file. Returns FALSE on any error
 * and sets the appropiate code in ErrorHandler. The size of a long may
 * depend on the platform compiled under, though it is usually 32 bits.
 * ASSERTs if the input handle is invalid.
 *
 *=======================================================================*/
boolean read_long (FILE* pFileHandle, long& Value) {
  DEFINE_FUNCTION("read_long()");
  size_t InputSize;

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

	/* Read the value */
  InputSize = fread (&Value, 1, sizeof(long), pFileHandle);

	/* Ensure the value was correctly read */
  if (InputSize != sizeof(long)) {
    ErrorHandler.AddError(ERR_SYSTEM, (errcode_t)errno, "Error reading binary long integer value (read only %u of %u bytes)!", InputSize, sizeof(long));
    return (FALSE);
   }

  return (TRUE);
 }
/*=========================================================================
 *		End of Function read_long()
 *=======================================================================*/


/*=========================================================================
 *
 * Function - boolean read_short (pFileHandle, Value);
 *
 * Reads a short integer from the specified file. Returns FALSE on any error 
 * and sets the appropiate code in ErrorHandler. The size of a short may
 * depend on the platform compiled under, though it is usually 16 bits.
 * ASSERTs if the input handle is invalid.  
 *
 *=======================================================================*/
boolean read_short (FILE* pFileHandle, short& Value) {
  DEFINE_FUNCTION("read_short()");
  size_t InputSize;

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

	/* Read in the integer value */
  InputSize = fread(&Value, 1, sizeof(short), pFileHandle);

	/* Check for any read error */
  if (InputSize != sizeof(short)) {
    ErrorHandler.AddError(ERR_SYSTEM, (errcode_t)errno, "Error reading binary short integer value (read only %u of %u bytes)!", InputSize, sizeof(short));
    return (FALSE);
   }

  return (TRUE);
 }
/*=========================================================================
 *		End of Function read_short()
 *=======================================================================*/


/*=========================================================================
 *
 * Function - long read_motlong (pFileHandle, Value);
 *
 * Reads a long integer from the specified file using the Motorola byte
 * order (as opposed to the usual Intel byte order).  Returns FALSE on 
 * any error and sets the appropiate code with ErrorHandler.  A long integer
 * is usually 32 bit but may depend on the platform compiled under.
 *
 *=======================================================================*/
boolean read_motlong (FILE* pFileHandle, long& Value) {
  DEFINE_FUNCTION("read_motlong()");
  unsigned char InputData[sizeof(long)];
  size_t	InputSize;
  
	/* Ensure valid input */
  ASSERT(pFileHandle != NULL);

	/* Read in the integer value */
  InputSize = fread(&InputData, 1, sizeof(long), pFileHandle);

	/* Check for any read error */
  if (InputSize != sizeof(long)) {
    ErrorHandler.AddError(ERR_SYSTEM, (errcode_t)errno, "Error reading binary motorola long value (read only %u of %u bytes)!", InputSize, sizeof(long));
    return (FALSE);
   }

	/* Compute the proper long integer value */
  Value = (long) (((unsigned long)InputData[3])      + (((unsigned long)InputData[2])<<8) + 
  		 (((unsigned long)InputData[1])<<16) + (((unsigned long)InputData[0])<<24));

  return (TRUE);
 }
/*=========================================================================
 *		End of Function read_motlong()
 *=======================================================================*/


/*===========================================================================
 *
 * Function - char* RemoveExtension (pFilename);
 *
 * Removes the extension from the given filename. Returns a pointer to
 * the modified file string.  ASSERTs if passed a bad pointer.
 *
 *=========================================================================*/
char* RemoveExtension (char* pFilename) {
  //DEFINE_FUNCTION("RemoveExtension()");
  char* pExtPtr;

	/* Attempt to find the start of the file extension */
  pExtPtr = FindExtension(pFilename);

	/* Terminate the file string at the '.' character */
  if (pExtPtr != NULL) pExtPtr[-1] = NULL_CHAR;
  return (pFilename);
 }
/*===========================================================================
 *		End of Function RemoveExtension()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - char* TerminatePath (char* pPath);
 *
 * Ensures the given path string terminates in the current LocalePathChar
 * character.  Returns a pointer to the given string.  ASSERTs if the 
 * input string is invalid.  Assumes that the string has been allocated
 * to allow an additional character to be added to it.
 *
 *=========================================================================*/
char* TerminatePath (char* pPath) {
  DEFINE_FUNCTION("TerminatePath()");
  size_t PathLength;

	/* Ensure the input path is valid */
  ASSERT(pPath != NULL);
  PathLength = strlen(pPath);

	/* Ensure the string ends in the current path character */
  if (PathLength == 0 || pPath[PathLength-1] != LocalePathChar) {
    pPath[PathLength] = LocalePathChar;
    pPath[PathLength+1] = NULL_CHAR;
   }

  return (pPath);
 }
/*===========================================================================
 *		End of Function TerminatePath()
 *=========================================================================*/


/*===========================================================================
 *
 * Function - boolean WriteFile (pBuffer, Size, pFilename, TextMode);
 *
 * Outputs Size bytes from the string pBuffer to the given filename.
 * Returns TRUE on success or FALSE on any error, setting the appropiate
 * error with ErrorHandler. If TextMode is TRUE (FILE_TEXT), the file
 * is created in text mode, if FALSE (FILE_BINARY), the file is created
 * in binary mode, the default.  ASSERTs if passed any invalid pointers.
 * The file is overwritten if it currently exists.
 *
 *=========================================================================*/
boolean WriteFile (const char* pBuffer, const size_t Size, const char* pFilename, const boolean TextMode) {
  DEFINE_FUNCTION("WriteFile()");
  FILE*   pFileHandle;
  size_t  OutputBytes;
  boolean ReturnValue = TRUE;

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

	/* Attempt to open file for output */
  pFileHandle = OpenFile(pFilename, TextMode ? "wt" : "wb");
  if (pFileHandle == NULL) return (FALSE);

	/* Attempt to output string buffer to file */
  OutputBytes = fwrite(pBuffer, sizeof(char), Size, pFileHandle);
  
  if (ferror(pFileHandle)) {
    ErrorHandler.AddError(ERR_SYSTEM, (errcode_t)errno, "Failed to write to the file '%s' (only %u of %u bytes output)!", pFilename, OutputBytes, Size);
    ReturnValue = FALSE;
   }

  fclose (pFileHandle);
  return (ReturnValue);
 }
/*===========================================================================
 *		End of Function WriteFile()
 *=========================================================================*/


/*=========================================================================
 *
 * Function - boolean write_short (pFileHandle, OutputValue);
 *
 * Writes a short integer to a file. Returns FALSE on any error.
 * The size of a short integer may depend on the system compiled in,
 * but usually it is 16 bits.  ASSERTs if passed an invalid file
 * handle.
 *
 *=======================================================================*/
boolean write_short (FILE* pFileHandle, const short OutputValue) {
  DEFINE_FUNCTION("write_short()");
  size_t OutputSize;

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

	/* Output the data */
  OutputSize = fwrite(&OutputValue, 1, sizeof(short), pFileHandle);
  
	/* Ensure the data was properly output */
  if (OutputSize != sizeof(short)) {
    ErrorHandler.AddError(ERR_SYSTEM, (errcode_t)errno, "Error writing binary short integer to file (%u of %u bytes output)!", OutputSize, sizeof(short));
    return (FALSE);
   }

  return (TRUE);
 }
/*=========================================================================
 *		End of Function write_short()
 *=======================================================================*/


/*=========================================================================
 *
 * Function - boolean write_int (pFileHandle, OutputValue);
 *
 * Writes an  integer to a file. Returns FALSE on any error and sets the
 * appropiate error with ErrorHandler. The size of an integer depends
 * on the system compiled under, though usually it is 16 of 32 bits.
 * ASSERTs if given an invalid file handle.
 *
 *=======================================================================*/
boolean write_int (FILE* pFileHandle, const int OutputValue) {
  DEFINE_FUNCTION("write_int()");
  size_t OutputSize;

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

	/* Output the data */
  OutputSize = fwrite(&OutputValue, 1, sizeof(int), pFileHandle);
  
	/* Ensure the data was properly output */
  if (OutputSize != sizeof(int)) {
    ErrorHandler.AddError(ERR_SYSTEM, (errcode_t)errno, "Error writing binary integer to file (%u of %u bytes output)!", OutputSize, sizeof(int));
    return (FALSE);
   }

  return (TRUE);
 }
/*=========================================================================
 *		End of Function write_int()
 *=======================================================================*/


/*=========================================================================
 *
 * Function - boolean write_long (pFileHandle, OutputValue);
 *
 * Writes a long integer to a file. Returns FALSE on any error, setting
 * the appropiate error code with the ErrorHandler.  ASSERTs if given
 * an invalid file handle.  Long integers are usually 32 bits in size,
 * but may depend on the system compiled under.
 *
 *=======================================================================*/
boolean write_long (FILE* pFileHandle, const long OutputValue) {
  DEFINE_FUNCTION("write_long()");
  size_t OutputSize;

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

	/* Output the data */
  OutputSize = fwrite(&OutputValue, 1, sizeof(long), pFileHandle);
  
	/* Ensure the data was properly output */
  if (OutputSize != sizeof(long)) {
    ErrorHandler.AddError(ERR_SYSTEM, (errcode_t)errno, "Error writing binary long integer to file (%u of %u bytes output)!", OutputSize, sizeof(long));
    return (FALSE);
   }

  return (TRUE);
 }
/*=========================================================================
 *		End of Function write_long()
 *=======================================================================*/


/*=========================================================================
 *
 * Function - boolean write_motlong (pFileHandle, OutputValue);
 *
 * Writes a long integer to a file using the Motorola byte order (as
 * opposed to the usual Intel byte order). Returns FALSE on any error 
 * and sets the appropiate error code with the ErrorHandler.  ASSERTs
 * if given an invalid file handle.  Long integers are usually 32 bits
 * in size, but may depend on the system compiled under.
 *
 *=======================================================================*/
boolean write_motlong (FILE* pFileHandle, const long OutputValue) {
  DEFINE_FUNCTION("write_motlong()");
  unsigned char OutputData[sizeof(long)];
  size_t	OutputSize;

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

	/* Create the output buffer */
  OutputData[0] = (unsigned char) ((OutputValue >> 24) & 0xFF);
  OutputData[1] = (unsigned char) ((OutputValue >> 16) & 0xFF);
  OutputData[2] = (unsigned char) ((OutputValue >> 8) & 0xFF);
  OutputData[3] = (unsigned char) (OutputValue & 0xFF);

	/* Output the data */
  OutputSize = fwrite (OutputData, 1, sizeof(long), pFileHandle);
  
	/* Ensure the data was properly output */
  if (OutputSize != sizeof(long)) {
    ErrorHandler.AddError(ERR_SYSTEM, (errcode_t)errno, "Error writing binary motorola long integer to file (%u of %u bytes output)!", OutputSize, sizeof(long));
    return (FALSE);
   }

  return (TRUE);
 }
/*=========================================================================
 *		End of Function write_motlong()
 *=======================================================================*/