/*==LICENSE==*

CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011  Cyan Worlds, Inc.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

You can contact Cyan Worlds, Inc. by email legal@cyan.com
 or by snail mail at:
      Cyan Worlds, Inc.
      14617 N Newport Hwy
      Mead, WA   99021

*==LICENSE==*/
#ifndef hsStream_Defined
#define hsStream_Defined

#include <stdarg.h> // Included for GCC 3.2.2+

#include "hsTypes.h"
#include "hsMemory.h"

namespace hsPackFileSys {
struct FileEntry;
}

// Define this for use of Streams with Logging (commonly used w/ a packet sniffer)
// These streams log their reads to an event list
//#define STREAM_LOGGER

#ifndef STREAM_LOGGER
#define hsReadOnlyLoggingStream hsReadOnlyStream
#define LogRead(byteCount, buffer, desc) Read(byteCount, buffer)
#define	LogReadSafeString() ReadSafeString();
#define	LogReadSafeStringLong() ReadSafeStringLong();
#define LogSkip(deltaByteCount, desc) Skip(deltaByteCount)
#define	LogReadSwap(value, desc) ReadSwap(value)
#define	LogReadSwapArray(count, values, desc) ReadSwap(count, values)
#define LogSubStreamStart(desc) LogVoidFunc()
#define LogSubStreamPushDesc(desc) LogVoidFunc()
#define LogSubStreamEnd() LogVoidFunc()
#define LogStringString(s) LogVoidFunc()
#endif

class hsStream {
public:
enum {
	kEolnCode = '\n',
	kComment = '#'
	};
enum  VDB_Type	{// Virtual Database type
	kVDB_GroupObject,
	kVDB_Mesh
	};
protected:
	UInt32		fBytesRead;
    UInt32      fPosition;

	hsBool		IsTokenSeparator(char c);
public:
				hsStream() : fBytesRead(0), fPosition(0) {}
	virtual		~hsStream();

	virtual hsBool	Open(const char *, const char * = "rb")=0;
	virtual hsBool	Open(const wchar *, const wchar * = L"rb")=0;
	virtual hsBool	Close()=0;
	virtual hsBool	AtEnd();
	virtual UInt32	Read(UInt32 byteCount, void * buffer) = 0;
	virtual UInt32	Write(UInt32 byteCount, const void* buffer) = 0;
	virtual void	Skip(UInt32 deltaByteCount) = 0;
	virtual void	Rewind() = 0;
	virtual void	FastFwd();
    virtual UInt32  GetPosition() const;
    virtual void    SetPosition(UInt32 position);
    virtual void    Truncate();
	virtual void	Flush() {}

#ifdef STREAM_LOGGER
	// Logging Reads & Skips
	virtual UInt32	LogRead(UInt32 byteCount, void * buffer, const char* desc) { return Read(byteCount,buffer); }
	virtual char*   LogReadSafeString() { return ReadSafeString(); }
	virtual char*   LogReadSafeStringLong() { return ReadSafeStringLong(); }
	virtual void	LogSkip(UInt32 deltaByteCount, const char* desc) { Skip(deltaByteCount); }

	// Stream Notes for Logging 
	virtual void LogStringString(const char* s) { }
	virtual void LogSubStreamStart(const char* desc) { }
	virtual void LogSubStreamEnd() { }
	virtual void LogSubStreamPushDesc(const char* desc) { }
#endif
	void LogVoidFunc() { }

	// Optimization for small Reads
	virtual UInt8	ReadByte();
	virtual hsBool	Read4Bytes(void *buffer);	// Reads 4 bytes,  return true if success 
	virtual hsBool	Read8Bytes(void *buffer);	// Reads 8 bytes,  return true if success 
	virtual hsBool  Read12Bytes(void *buffer);	// Reads 12 bytes, return true if success

	virtual UInt32	GetEOF();
	UInt32			GetSizeLeft();
	virtual void	CopyToMem(void* mem);
	virtual hsBool	IsCompressed() { return false; }

	UInt32			WriteString(const char cstring[]);
	UInt32			WriteFmt(const char * fmt, ...);
	UInt32			WriteFmtV(const char * fmt, va_list av);

	UInt32		WriteSafeStringLong(const char *string);	// uses 4 bytes for length
	UInt32		WriteSafeWStringLong(const wchar_t *string);
	char *		ReadSafeStringLong();
	wchar_t *	ReadSafeWStringLong();

	UInt32		WriteSafeString(const char *string);		// uses 2 bytes for length
	UInt32		WriteSafeWString(const wchar_t *string);
	char *		ReadSafeString();
	wchar_t *	ReadSafeWString();

	hsBool		GetToken(char *s, UInt32 maxLen=UInt32(-1), const char beginComment=kComment, const char endComment=kEolnCode);
	hsBool		ReadLn(char* s, UInt32 maxLen=UInt32(-1), const char beginComment=kComment, const char endComment=kEolnCode);
	
	bool		Readbool();
	hsBool		ReadBool();
	void		ReadBool(int count, hsBool values[]);
	UInt16		ReadSwap16();
	void		ReadSwap16(int count, UInt16 values[]);
	UInt32		ReadSwap32();
	void 		ReadSwap32(int count, UInt32 values[]);
	UInt32		ReadUnswap32();

	void			Writebool(bool value);
	void			WriteBool(hsBool value);
	void			WriteBool(int count, const hsBool values[]);
	void			WriteByte(UInt8 value);
	void			WriteSwap16(UInt16 value);
	void			WriteSwap16(int count, const UInt16 values[]);
	void			WriteSwap32(UInt32 value);
	void 			WriteSwap32(int count, const  UInt32 values[]);
	void			WriteUnswap32(UInt32 value);


	/* Overloaded  Begin (8 & 16 & 32 int)*/
	/* yes, swapping an 8 bit value does nothing, just useful*/
	void		ReadSwap(bool* value) { *value = this->ReadByte() ? true : false; }
	void		ReadSwap(UInt8* value) { *value = this->ReadByte(); }
	void		ReadSwap(int count, UInt8 values[]) { this->Read(count, values); }
	void		ReadSwap(UInt16* value) { *value = this->ReadSwap16(); }
	void		ReadSwap(int count, UInt16 values[]) { this->ReadSwap16(count, values); }
	void		ReadSwap(UInt32* value) { *value = this->ReadSwap32(); }
	void 		ReadSwap(int count, UInt32 values[]) { this->ReadSwap32(count, values); }
#ifdef STREAM_LOGGER
				// Begin LogReadSwaps
	virtual void	LogReadSwap(bool* value, const char* desc) { this->ReadSwap(value); }
	virtual void	LogReadSwap(UInt8* value, const char* desc) { this->ReadSwap(value); }
	virtual void	LogReadSwapArray(int count, UInt8 values[], const char* desc) { this->ReadSwap(count, values); }
	virtual void	LogReadSwap(UInt16* value, const char* desc) { this->ReadSwap(value); }
	virtual void	LogReadSwapArray(int count, UInt16 values[], const char* desc) { this->ReadSwap(count, values); }
	virtual void	LogReadSwap(UInt32* value, const char* desc) { this->ReadSwap(value); }
	virtual void	LogReadSwapArray(int count, UInt32 values[], const char* desc) { this->ReadSwap(count, values); }
				// End LogReadSwaps
#endif
	void			WriteSwap(bool value) { this->Write(1,&value); }
	void			WriteSwap(UInt8 value) { this->Write(1,&value); }
	void			WriteSwap(int count, const UInt8 values[]) { this->Write(count, values); }
	void			WriteSwap(UInt16 value) { this->WriteSwap16(value); }
	void			WriteSwap(int count, const UInt16 values[]) { this->WriteSwap16(count, values); }
	void			WriteSwap(UInt32 value) { this->WriteSwap32(value); }
	void 			WriteSwap(int count, const  UInt32 values[]) { this->WriteSwap32(count, values); }
	void		ReadSwap(Int8* value) { *value = this->ReadByte(); }
	void		ReadSwap(int count, Int8 values[]) { this->Read(count, values); }
	void		ReadSwap(char* value) { *value = (char)this->ReadByte(); }
	void		ReadSwap(int count, char values[]) { this->Read(count, values); }
	void		ReadSwap(Int16* value) { *value = (Int16)this->ReadSwap16(); }
	void		ReadSwap(int count, Int16 values[]) { this->ReadSwap16(count, (UInt16*)values); }
	void		ReadSwap(Int32* value) { *value = (Int32)this->ReadSwap32(); }
	void 		ReadSwap(int count, Int32 values[]) { this->ReadSwap32(count, (UInt32*)values); }
	void		ReadSwap(int* value) { *value = (Int32)this->ReadSwap32(); }
	void 		ReadSwap(int count, int values[]) { this->ReadSwap32(count, (UInt32*)values); }
#ifdef STREAM_LOGGER
				// Begin LogReadSwaps
	virtual void	LogReadSwap(Int8* value, const char* desc) { this->ReadSwap(value); }
	virtual void	LogReadSwapArray(int count, Int8 values[], const char* desc) { this->ReadSwap(count, values); }
	virtual void	LogReadSwap(char* value, const char* desc) { this->ReadSwap(value); }
	virtual void	LogReadSwapArray(int count, char values[], const char* desc) { this->ReadSwap(count, values); }
	virtual void	LogReadSwap(Int16* value, const char* desc) { this->ReadSwap(value); }
	virtual void	LogReadSwapArray(int count, Int16 values[], const char* desc) { this->ReadSwap(count, (UInt16*)values); }
	virtual void	LogReadSwap(Int32* value, const char* desc) { this->ReadSwap(value); }
	virtual void	LogReadSwapArray(int count, Int32 values[], const char* desc) { this->ReadSwap(count, (UInt32*)values); }
	virtual void	LogReadSwap(int* value, const char* desc) { this->ReadSwap(value); }
	virtual void	LogReadSwapArray(int count, int values[], const char* desc) { this->ReadSwap(count, (UInt32*)values); }
				// End LogReadSwaps
#endif
	void			WriteSwap(Int8 value) { this->Write(1,&value); }
	void			WriteSwap(int count, const Int8 values[]) { this->Write(count, values); }
	void			WriteSwap(char value) { this->Write(1,(UInt8*)&value); }
	void			WriteSwap(int count, const char values[]) { this->Write(count, (UInt8*)values); }
	void			WriteSwap(Int16 value) { this->WriteSwap16((UInt16)value); }
	void			WriteSwap(int count, const Int16 values[]) { this->WriteSwap16(count, (UInt16*)values); }
	void			WriteSwap(Int32 value) { this->WriteSwap32((UInt32)value); }
	void 			WriteSwap(int count, const  Int32 values[]) { this->WriteSwap32(count, (UInt32*)values); }
	void			WriteSwap(int value) { this->WriteSwap32((UInt32)value); }
	void 			WriteSwap(int count, const  int values[]) { this->WriteSwap32(count, (UInt32*)values); }
	/* Overloaded  End */


#if HS_CAN_USE_FLOAT
	float			ReadSwapFloat();
	void			ReadSwapFloat(int count, float values[]);
	double			ReadSwapDouble();
	void			ReadSwapDouble(int count, double values[]);
	float			ReadUnswapFloat();
	void			WriteSwapFloat(float value);
	void			WriteSwapFloat(int count, const float values[]);
	void			WriteSwapDouble(double value);
	void			WriteSwapDouble(int count, const double values[]);
	void			WriteUnswapFloat(float value);


	/* Overloaded  Begin (Float)*/
	void			ReadSwap(float* value) { *value = ReadSwapFloat(); }
	void			ReadSwap(int count, float values[]) { ReadSwapFloat(count, values); }
	void			ReadSwap(double* value) { *value = ReadSwapDouble(); }
	void			ReadSwap(int count, double values[]) { ReadSwapDouble(count, values); }
#ifdef STREAM_LOGGER
					// Begin LogReadSwaps
	virtual void	LogReadSwap(float* value, const char* desc) { ReadSwap(value); }
	virtual void	LogReadSwapArray(int count, float values[], const char* desc) { ReadSwap(count, values); }
	virtual void	LogReadSwap(double* value, const char* desc) { ReadSwap(value); }
	virtual void	LogReadSwapArray(int count, double values[], const char* desc) { ReadSwap(count, values); }
					// End LogReadSwaps
#endif
	void			WriteSwap(float value) { WriteSwapFloat(value); }
	void			WriteSwap(int count, const float values[]) { WriteSwapFloat(count, values); }
	void			WriteSwap(double value) { WriteSwapDouble(value); }
	void			WriteSwap(int count, const double values[]) { WriteSwapDouble(count, values); }
	/* Overloaded End */
#endif

#if HS_SCALAR_IS_FIXED
	hsFixed		ReadSwapScalar() { return (hsFixed)this->ReadSwap32(); }
	void			ReadSwapScalar(int count, hsFixed values[])
				{
					this->ReadSwap32(count, (UInt32*)values);
				}
	hsFixed		ReadUnswapScalar() { return (hsFixed)this->ReadUnswap32(); }


	void			WriteSwapScalar(hsFixed value) { this->WriteSwap32(value); }
	void			WriteSwapScalar(int count, const hsFixed values[])
				{
					this->WriteSwap32(count, (UInt32*)values);
				}
	void			WriteUnswapScalar(hsFixed value) { this->WriteUnswap32(value); }


	/* Overloaded Begin (Scalar) */
	void		ReadSwap(hsFixed* value) { this->ReadSwap((UInt32*)value); }
	void			ReadSwap(int count, hsFixed values[]) { this->ReadSwap(count, (UInt32*)values); }
	void			WriteSwap(hsFixed value) { this->WriteSwap((UInt32)value); }
	void			WriteSwap(int count, const hsFixed values[]) { this->WriteSwap(count, (UInt32*)values); }
	/* Overloaded End */

#else
	float			ReadSwapScalar() { return (float)this->ReadSwapFloat(); }
	void			ReadSwapScalar(int count, float values[])
				{
					this->ReadSwapFloat(count, (float*)values);
				}
	float			ReadUnswapScalar() { return (float)this->ReadUnswapFloat(); }
	void			WriteSwapScalar(float value) { this->WriteSwapFloat(value); }
	void			WriteSwapScalar(int count, const float values[])
				{
					this->WriteSwapFloat(count, (float*)values);
				}
	void			WriteUnswapScalar(float value) { this->WriteUnswapFloat(value); }
#endif

	void			WriteSwapAtom(UInt32 tag, UInt32 size);
	UInt32		ReadSwapAtom(UInt32* size);


	/* Overloaded  Begin (Atom)*/
	void			WriteSwap(UInt32* tag, UInt32 size) { WriteSwapAtom(*tag, size); }
	void			ReadSwap(UInt32* tag, UInt32 *size) { *tag = ReadSwapAtom(size); }
	/* Overloaded  End */
    virtual void    VirtualSetPosition(UInt32 pos, VDB_Type ){ SetPosition(pos); };
	virtual hsPackFileSys::FileEntry *GetFileEntry() { return nil; }  // Streams from Packfiles can return a FileEntry

};

class hsStreamable {
public:
	virtual void	Read(hsStream* stream) = 0;
	virtual void	Write(hsStream* stream) = 0;
	virtual UInt32	GetStreamSize() = 0;
};

class hsFileStream: public hsStream
{	
	UInt32		fRef;
#if HS_BUILD_FOR_PS2
	enum {
		kBufferSize = 2*1024
	};
	UInt32		fFileSize;
	char		fBuffer[kBufferSize];
	Int32		fVirtualFilePointer;
	Int32		fBufferBase;			// offset to top of fBuffer
	hsBool		fBufferIsEmpty;
	hsBool		fWriteBufferUsed;		// In write mode. fBuffer must be flush, when file was closed.
#endif

public:
				hsFileStream();
	virtual		~hsFileStream();
	virtual hsBool	Open(const char *name, const char *mode = "rb");
	virtual hsBool	Open(const wchar *name, const wchar *mode = L"rb");
	virtual hsBool	Close();

	virtual hsBool	AtEnd();
	virtual UInt32	Read(UInt32 byteCount, void* buffer);
	virtual UInt32	Write(UInt32 byteCount, const void* buffer);
	virtual void	Skip(UInt32 deltaByteCount);
	virtual void	Rewind();
    virtual void    Truncate();

	virtual UInt32	GetFileRef();
	virtual void	SetFileRef(UInt32 refNum);
};

#if !HS_BUILD_FOR_PS2
#if !(HS_BUILD_FOR_REFERENCE)

class hsUNIXStream: public hsStream
{	
	FILE*		fRef;
	char*		fBuff;

public:
	hsUNIXStream(): fRef(0), fBuff(nil) {}
	~hsUNIXStream();
	virtual hsBool	Open(const char* name, const char* mode = "rb");
	virtual hsBool	Open(const wchar *name, const wchar *mode = L"rb");
	virtual hsBool	Close();

	virtual hsBool	AtEnd();
	virtual UInt32	Read(UInt32 byteCount, void* buffer);
	virtual UInt32	Write(UInt32 byteCount, const void* buffer);
	virtual void    SetPosition(UInt32 position);
	virtual void	Skip(UInt32 deltaByteCount);
	virtual void	Rewind();
	virtual void	FastFwd();
    virtual void    Truncate();
	virtual void	Flush();

	FILE*			GetFILE() { return fRef; }
	void			SetFILE(FILE* file) { fRef = file; }

	virtual UInt32	GetEOF();
};

// Small substream class: give it a base stream, an offset and a length, and it'll
// treat all ops as if you had a chunk from the base stream as a separate, vanilla 
// stream of the given length.

class plReadOnlySubStream: public hsStream
{	
	hsStream	*fBase;
	UInt32		fOffset, fLength;

	void	IFixPosition( void );

public:
	plReadOnlySubStream(): fBase( nil ), fOffset( 0 ), fLength( 0 ) {}
	~plReadOnlySubStream();

	virtual hsBool	Open(const char *, const char *)	{ hsAssert(0, "plReadOnlySubStream::Open  NotImplemented"); return false; }
	virtual hsBool	Open(const wchar *, const wchar *)	{ hsAssert(0, "plReadOnlySubStream::Open  NotImplemented"); return false; }
	void	Open( hsStream *base, UInt32 offset, UInt32 length );
	virtual hsBool	Close() { fBase = nil; fOffset = 0; fLength = 0; return true; }
	virtual hsBool	AtEnd();
	virtual UInt32	Read(UInt32 byteCount, void* buffer);
	virtual UInt32	Write(UInt32 byteCount, const void* buffer);
	virtual void	Skip(UInt32 deltaByteCount);
	virtual void	Rewind();
	virtual void	FastFwd();
    virtual void    Truncate();

	virtual UInt32	GetEOF();
};

#endif
#endif

class hsRAMStream : public hsStream {
	hsAppender			fAppender;
	hsAppenderIterator	fIter;
public:
				hsRAMStream();
				hsRAMStream(UInt32 chunkSize);
	virtual		~hsRAMStream();

	virtual hsBool	Open(const char *, const char *)	{ hsAssert(0, "hsRAMStream::Open  NotImplemented"); return false; }
	virtual hsBool	Open(const wchar *, const wchar *)	{ hsAssert(0, "hsRAMStream::Open  NotImplemented"); return false; }
	virtual hsBool	Close()				{ hsAssert(0, "hsRAMStream::Close  NotImplemented"); return false; }

	
	virtual hsBool	AtEnd();
	virtual UInt32	Read(UInt32 byteCount, void * buffer);
	virtual UInt32	Write(UInt32 byteCount, const void* buffer);
	virtual void	Skip(UInt32 deltaByteCount);
	virtual void	Rewind();
    virtual void    Truncate();

	virtual UInt32	GetEOF();
	virtual void	CopyToMem(void* mem);

	void			Reset();		// clears the buffers
};

class hsNullStream : public hsStream {
public:

	virtual hsBool	Open(const char *, const char *)	{ return true; }
	virtual hsBool	Open(const wchar *, const wchar *)	{ return true; }
	virtual hsBool	Close()				{ return true; }

	virtual UInt32	Read(UInt32 byteCount, void * buffer);	// throw's exception
	virtual UInt32	Write(UInt32 byteCount, const void* buffer);
	virtual void	Skip(UInt32 deltaByteCount);
	virtual void	Rewind();
    virtual void    Truncate();

	UInt32		GetBytesWritten() const { return fBytesRead; }
	void			Reset( ) { fBytesRead = 0;	 }
};

// read only mem stream
class hsReadOnlyStream : public hsStream {
protected:
	char*	fStart;
	char*	fData;
	char*	fStop;
public:
	hsReadOnlyStream(int size, const void* data) { Init(size, data); }
	hsReadOnlyStream() {}

	virtual void	Init(int size, const void* data) { fStart=((char*)data); fData=((char*)data); fStop=((char*)data + size); }
	virtual hsBool	Open(const char *, const char *)	{ hsAssert(0, "hsReadOnlyStream::Open  NotImplemented"); return false; }
	virtual hsBool	Open(const wchar *, const wchar *)	{ hsAssert(0, "hsReadOnlyStream::Open  NotImplemented"); return false; }
	virtual hsBool	Close()				{ hsAssert(0, "hsReadOnlyStream::Close  NotImplemented"); return false; }
	virtual hsBool	AtEnd();
	virtual UInt32	Read(UInt32 byteCount, void * buffer);
	virtual UInt32	Write(UInt32 byteCount, const void* buffer);	// throws exception
	virtual void	Skip(UInt32 deltaByteCount);
	virtual void	Rewind();
    virtual void    Truncate();
	virtual UInt32	GetBytesRead() const { return fBytesRead; }
	virtual UInt32	GetEOF() { return (UInt32)(fStop-fStart); }
	virtual void	CopyToMem(void* mem);
};

// write only mem stream
class hsWriteOnlyStream : public hsReadOnlyStream {
public:
	hsWriteOnlyStream(int size, const void* data) : hsReadOnlyStream(size, data) {}
	hsWriteOnlyStream() {}

	virtual hsBool	Open(const char *, const char *)	{ hsAssert(0, "hsWriteOnlyStream::Open  NotImplemented"); return false; }
	virtual hsBool	Open(const wchar *, const wchar *)	{ hsAssert(0, "hsWriteOnlyStream::Open  NotImplemented"); return false; }
	virtual hsBool	Close()				{ hsAssert(0, "hsWriteOnlyStream::Close  NotImplemented"); return false; }
	virtual UInt32	Read(UInt32 byteCount, void * buffer);	// throws exception
	virtual UInt32	Write(UInt32 byteCount, const void* buffer);	
	virtual UInt32	GetBytesRead() const { return 0; }
	virtual UInt32	GetBytesWritten() const { return fBytesRead; }
};

// circular queue stream
class hsQueueStream : public hsStream {
private:
	char* fQueue;
	UInt32 fReadCursor;
	UInt32 fWriteCursor;
	UInt32 fSize;
	
public:
	hsQueueStream(Int32 size);
	~hsQueueStream();

	virtual hsBool	Open(const char *, const char *)	{ hsAssert(0, "hsQueueStream::Open  NotImplemented"); return false; }
	virtual hsBool	Open(const wchar *, const wchar *)	{ hsAssert(0, "hsQueueStream::Open  NotImplemented"); return false; }
	virtual hsBool	Close()				{ hsAssert(0, "hsQueueStream::Close  NotImplemented"); return false; }

	virtual UInt32	Read(UInt32 byteCount, void * buffer);
	virtual UInt32	Write(UInt32 byteCount, const void* buffer);
	virtual void	Skip(UInt32 deltaByteCount);
	virtual void	Rewind();
	virtual void	FastFwd();
	virtual hsBool	AtEnd();

	UInt32 GetSize() { return fSize; }
	const char* GetQueue() { return fQueue; }
	UInt32 GetReadCursor() { return fReadCursor; }
	UInt32 GetWriteCursor() { return fWriteCursor; }
};

class hsBufferedStream : public hsStream
{
	FILE* fRef;
	UInt32 fFileSize;

	enum { kBufferSize = 2*1024 };
	char fBuffer[kBufferSize];
	// If the buffer is empty, this is zero.  Otherwise it is the size of the
	// buffer (if we read a full block), or something less than that if we read
	// a partial block at the end of the file.
	UInt32 fBufferLen;

	hsBool fWriteBufferUsed;

#ifdef HS_DEBUGGING
	// For doing statistics on how efficient we are
	int fBufferHits, fBufferMisses;
	UInt32 fBufferReadIn, fBufferReadOut, fReadDirect, fLastReadPos;
	char* fFilename;
	const char* fCloseReason;
#endif

public:
	hsBufferedStream();
	virtual	~hsBufferedStream();

	virtual hsBool	Open(const char* name, const char* mode = "rb");
	virtual hsBool	Open(const wchar* name, const wchar* mode = L"rb");
	virtual hsBool	Close();

	virtual hsBool	AtEnd();
	virtual UInt32	Read(UInt32 byteCount, void* buffer);
	virtual UInt32	Write(UInt32 byteCount, const void* buffer);
	virtual void	Skip(UInt32 deltaByteCount);
	virtual void	Rewind();
	virtual void    Truncate();
	virtual UInt32	GetEOF();

	FILE*	GetFileRef();
	void	SetFileRef(FILE* file);

	// Something optional for when we're doing stats.  Will log the reason why
	// the file was closed.  Really just for plRegistryPageNode.
	void SetCloseReason(const char* reason)
	{
#ifdef HS_DEBUGGING
		fCloseReason = reason;
#endif
	}
};

#endif