/*==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