#ifndef FILE_H
#define FILE_H
  #include <stdio.h>
#include "platform.h"
#include <string>
#include <list>
using namespace std;
  enum {
	FILE_TEXT = 1,
	FILE_BINARY = 2,
	FILE_APPEND = 4,
	FILE_SIMUL = 8,  // simultaneous read/write
	FILE_READ = 16,
	FILE_WRITE = 32,
	FILE_LITTLE = 64,
	FILE_BIG = 128
};
  #define DEFAULT_MAX_SIZE 2048
#ifdef __BIG_ENDIAN__
#define SYSTEM_ENDIAN 128
#else
#define SYSTEM_ENDIAN 64
#endif
  typedef struct REF
{
	REF(FILE *_f) { f = _f;  Count = 1; }
	FILE	*f;
	uint32	Count;
} REF;
  std::list <REF> RefCount;
std::list <REF>::iterator Iter;
  class CFile
{
public:
	CFile()
	{
		this->File = NULL;
		this->OpenMode = TEXT;
		this->TermString = " /\n\r\t";
		this->Endian = SYSTEM_ENDIAN;
	}
  	CFile(char *_FileName, char *_Mode = "rb")
	{
		this->File = fopen(_FileName, _Mode);
		this->OpenMode = this->SetMode(_Mode);
		this->TermString = " /\n\r\t";
		this->Endian = SYSTEM_ENDIAN;
	}
  	CFile(std::string &_FileName, std::string &_Mode = string("rb"))
	{
		this->File = fopen(_FileName.c_str(), _Mode.c_str());
		this->OpenMode = this->SetMode((char*)_Mode.c_str());
		this->TermString = " /\n\r\t";
		this->Endian = SYSTEM_ENDIAN;
	}
  	CFile(char *_FileName, uint32 _Mode)
	{
		std::string Mode;
  		if (_Mode == 0) {
			*this = CFile();
			return;
		}
  		if (_Mode & FILE_APPEND) {
			Mode += "a";
		}
		if (_Mode & FILE_SIMUL) {
			Mode += "+";
		}
		if (_Mode & FILE_READ) {
			Mode += "r";
		}
		if (_Mode & FILE_WRITE) {
			Mode += "w";
		}
		if (_Mode & FILE_TEXT) {
			Mode += "t";
			this->OpenMode = TEXT;
		}
		if (_Mode & FILE_BINARY) {
			Mode += "b";
			this->OpenMode = BINARY;
		}
  		this->TermString = " /\n\r\t";
		this->File = fopen(_FileName, (char*)Mode.c_str());
		this->Endian = SYSTEM_ENDIAN;
  		return;
	}
  	CFile(string &_FileName, uint32 _Mode)
	{
		*this = CFile((char*)_FileName.c_str(), _Mode);
	}
  	CFile(FILE *_File)
	{
		this->File = _File;
		this->OpenMode = BINARY;  // basically just have to assume...
		this->TermString = " /\n\r\t";
		this->Endian = SYSTEM_ENDIAN;
		this->AddRef(_File);
	}
  	CFile(CFile &_File)
	{
		this->File = _File.GetFile();
		this->OpenMode = _File.OpenMode;
		this->TermString = _File.TermString;
		this->Endian = _File.Endian;
		this->AddRef(_File.GetFile());
	}
  	virtual ~CFile()
	{
		if (this->File && SafeToDel(this->File))
			fclose(this->File);
		this->File = NULL;
	}
  	inline boolean Open(char *_FileName, char *_Mode = "rb")
	{
		if (this->File && SafeToDel(this->File))
			fclose(this->File);
		return (this->File = fopen(_FileName, _Mode)) != NULL;
	}
  	inline boolean Open(string &_FileName, string &_Mode = string("rb"))
	{
		return this->Open((char*)_FileName.c_str(), (char*)_Mode.c_str());
	}
  	inline void Close(void)
	{
		if (this->File && SafeToDel(this->File)) {
			fclose(this->File);
			this->File = NULL;
		}
	}
  	inline void SetEndian(int32 _Endian)
	{
		if (_Endian != FILE_LITTLE && _Endian != FILE_BIG)
			return;
		this->Endian = _Endian;
	}
  	inline uint32 Read(void *_Ptr, uint32 _Size)
	{
		return fread(_Ptr, 1, _Size, this->File);
	}
  	inline uint32 Write(void *_Ptr, uint32 _Size)
	{
		return fwrite(_Ptr, 1, _Size, this->File);
	}
  	inline int32 Seek(int32 _Offset, int32 _Mode)
	{
		return fseek(this->File, _Offset, _Mode);
	}
  	inline void Rewind(void)
	{
		rewind(this->File);
	}
  	inline int32 Eof(void)
	{
		return feof(this->File);
	}
  	inline int32 Flush(void)
	{
		return fflush(this->File);
	}
  	inline byte GetChar(void)
	{
		return fgetc(this->File);
	}
  	inline uint16 GetShort(void)
	{
		static uint16 S;
		fread(&S, sizeof(uint16), 1, this->File);
		if (this->Endian != SYSTEM_ENDIAN)
			return (S>>8) | (S<<8);
		else
			return S;
	}
  	inline uint32 GetInt(void)
	{
		static uint32 I;
		fread(&I, sizeof(uint32), 1, this->File);
		if (this->Endian != SYSTEM_ENDIAN)
			return (I>>24) | ((I>>8) & 0xff00) | ((I<<8) & 0xff0000) | (I<<24);
		else
			return I;
	}
  	inline int32 Reverse(void)
	{
		return fseek(this->File, -1, SEEK_CUR);
	}
  	inline char *GetS(char *_String, uint32 _MaxLen = DEFAULT_MAX_SIZE)
	{
		return fgets(_String, _MaxLen, this->File);
	}
  	inline string &GetS(string &_String)
	{
		char *String = new char[DEFAULT_MAX_SIZE];
		this->GetS(String);
		_String = String;
		delete []String;
		return _String;
	}
  	inline string &GetS(void)
	{
		string String;
		return this->GetS(String);
	}
  	inline int32 Print(const char *_Format, ...)
	{
		return fprintf(this->File, _Format);
	}
  	inline int32 Pos(void)
	{
		return ftell(this->File);
	}
  	inline boolean Temp(void)
	{
		if (this->File && SafeToDel(this->File))
			fclose(this->File);
		return (this->File = tmpfile()) != NULL;
	}
  	// Sets the terminating characters for the >> operator
	inline void SetTerm(char *_TermString)
	{
		if (_TermString)
			TermString = _TermString;
		return;
	}
  	// Same as above
	inline void SetTerm(string &_TermString)
	{
		TermString = _TermString;
		return;
	}
  	inline boolean Valid(void)
	{
		return (this->File != NULL);
	}
  	inline FILE *GetFile(void)
	{
		return this->File;
	}
  	inline CFile& operator = (FILE *_File)
	{
		if (this->File && SafeToDel(this->File))
			fclose(this->File);
		this->File = _File;
		this->OpenMode = TEXT;  // Just have to guess...?
		this->TermString = " /\n\r\t";
		this->AddRef(_File);
  		return *this;
	}
  	inline CFile& operator = (CFile &_File)
	{
		if (this->File && SafeToDel(this->File))
			fclose(this->File);
		this->File = _File.GetFile();
		this->OpenMode = _File.OpenMode;
		this->TermString = _File.TermString;
		this->AddRef(_File.GetFile());
  		return *this;
	}
  	// Can possibly go too far...?
	inline CFile& operator >> (string &_String)
	{
		char Current;
  		_String = string();  // reset string
		while (1) {
			fread(&Current, 1, 1, this->File);
			if (!NotTerm(Current) || feof(this->File)) {
				while (1) {  // Take care of multiple terminating characters
					fread(&Current, 1, 1, this->File);
					if (NotTerm(Current) || feof(this->File))
						return *this;
				}
			}
			_String += Current;
		}
  		return *this;
	}
  	inline CFile& operator >> (char *_String)
	{
		string String;
		this->operator >> (String);
		_String = strdup(String.c_str());
		return *this;
	}
  	inline CFile& operator << (string &_String)
	{
		fwrite((void*)_String.c_str(), 1, _String.length(), this->File);
		return *this;
	}
  	inline CFile& operator << (char *_String)
	{
		fwrite(_String, 1, strlen(_String), this->File);
		return *this;
	}
  	inline CFile& operator << (int32 _Num)
	{
		if (this->OpenMode == BINARY)
			fwrite(&_Num, 1, sizeof(_Num), this->File);
		else
			fprintf(this->File, "%d", _Num);
		return *this;
	}
  	inline CFile& operator << (real64 _Num)
	{
		if (this->OpenMode == BINARY)
			fwrite(&_Num, 1, sizeof(_Num), this->File);
		else
			fprintf(this->File, "%f", _Num);
		return *this;
	}
  
	inline boolean operator ! (void)
	{
		return (this->File == NULL);
	}
  
	int32	OpenMode;  // Wish it could be protected...oh well.
	string	TermString;  // Same here
	int32	Endian;
  
protected:
	FILE	*File;
  	enum	{ BINARY, TEXT };
  	// Just returns if _Char is a terminating character in TermString
	inline boolean NotTerm(char _Char)
	{
		uint32 Count = 0;
		while (Count < TermString.length()) {
			if (TermString[Count++] == _Char)
				return false;
		}
		return true;
	}
  	// Returns what kind of mode _String specifies or else TEXT
	inline int32 SetMode(char *_String)
	{
		uint32 Len = strlen(_String);
  		for (uint32 i = 0; i < Len; i++) {
			if (_String[i] == 'b') {
				return BINARY;
			}
			else if (_String[i] == 't') {
				return TEXT;
			}
		}
  		return TEXT;  // Default is text.
	}
  private:
	void AddRef(FILE *_File)
	{
		for (Iter = RefCount.begin(); Iter != RefCount.end(); Iter++) {
			if (Iter->f == File) {
				Iter->Count++;
				return;
			}
		}
  		RefCount.insert(RefCount.begin(), REF(_File));
	}
  	boolean SafeToDel(FILE *_File)
	{
		boolean InList = false;
  		for (Iter = RefCount.begin(); Iter != RefCount.end(); Iter++) {
			if (Iter->f == File) {
				InList = true;
				if (Iter->Count == 0) {
					RefCount.erase(Iter);
					return true;
				}
			}
                  Iter->f--;
		}
  		if (InList)
			return false;
		return true;
	}
  };
  
#endif//FILE_H   |