/*==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==*/
#include <ctype.h>
#include "hsStream.h"
#include "hsMemory.h"
#include "hsUtils.h"

#include "hsTemplates.h"
#include "hsStlUtils.h"

#if HS_BUILD_FOR_UNIX
#include <unistd.h>
#endif

#if HS_BUILD_FOR_MAC
	#include <Files.h>
	#include <stdio.h>
	#include <unistd.h>
#endif

#if HS_BUILD_FOR_PS2
#include <eekernel.h>
#include <sifdev.h>
#endif

#include "hsWindows.h"
#if HS_BUILD_FOR_WIN32
#include <io.h>
#endif
#include "hsStlUtils.h"

//////////////////////////////////////////////////////////////////////////////////

#if HS_CPU_BENDIAN
	static void swapIt(Int32 *swap)
	{
		Byte*	c = (Byte*)swap;
		Byte		t = c[0];

		c[0] = c[3];
		c[3] = t;
		t = c[1];
		c[1] = c[2];
		c[2] = t;
	}
	static void swapIt(int *swap)
	{
		swapIt((Int32*)swap);
	}
	static void swapIt(float *swap)
	{
		swapIt((Int32*)swap);
	}

	static void swapIt(double *swap)
	{
		float* a = (float*)&swap;
		float* b = (float*)(((Byte*)&swap)+4);
		swapIt(a);
		swapIt(b);
	}

	static void swapIt(Int16 *swap)
	{
		Byte *c = (Byte*)swap;
		Byte t;
		t = c[0];
		c[0] = c[1];
		c[1] = t;
	}
	#define unswapIt(value)
#else
	#define swapIt(value)
	static void unswapIt(Int32 *swap)
	{
		Byte*	c = (Byte*)swap;
		Byte		t = c[0];

		c[0] = c[3];
		c[3] = t;
		t = c[1];
		c[1] = c[2];
		c[2] = t;
	}
	static void unswapIt(int *swap)
	{
		unswapIt((Int32*)swap);
	}
	static void unswapIt(float *swap)
	{
		unswapIt((Int32*)swap);
	}

	static void unswapIt(double *swap)
	{
		float* a = (float*)&swap;
		float* b = (float*)(((Byte*)&swap)+4);
		swapIt(a);
		swapIt(b);
	}

	static void unswapIt(Int16 *swap)
	{
		Byte *c = (Byte*)swap;
		Byte t;
		t = c[0];
		c[0] = c[1];
		c[1] = t;
	}
#endif

//////////////////////////////////////////////////////////////////////////////////

void hsStream::FastFwd()
{
	hsThrow("FastFwd unimplemented by subclass of stream");
}

UInt32 hsStream::GetPosition() const
{
	return fPosition;
}

void hsStream::SetPosition(UInt32 position)
{
	if (position == fPosition)
		return;
    Rewind();
    Skip(position);
}

void hsStream::Truncate()
{
	hsThrow("Truncate unimplemented by subclass of stream");
}

UInt32 hsStream::GetSizeLeft()
{
	UInt32 ret = 0;
	if (GetPosition() > GetEOF())
	{
		hsThrow("Position is beyond EOF");
	}
	else
	{
		ret = GetEOF() - GetPosition();
	}

	return ret;
}

//////////////////////////////////////////////////////////////////////////////////

UInt32 hsStream::GetEOF()
{
	hsThrow( "GetEOF() unimplemented by subclass of stream");
	return 0;
}

void hsStream::CopyToMem(void* mem)
{
	hsThrow( "CopyToMem unimplemented by subclass of stream");
}

//////////////////////////////////////////////////////////////////////////////////

hsStream::~hsStream()
{
}

UInt32 hsStream::WriteString(const char cstring[])
{
	return Write(hsStrlen(cstring), cstring);
}

UInt32 hsStream::WriteFmt(const char * fmt, ...)
{
	va_list av;
	va_start( av, fmt );
	UInt32 n = WriteFmtV( fmt, av );
	va_end( av );
	return n;
}

UInt32 hsStream::WriteFmtV(const char * fmt, va_list av)
{
	std::string buf;
	xtl::formatv( buf, fmt, av );
	return Write( buf.length(), buf.data() );
}

UInt32 hsStream::WriteSafeStringLong(const char *string)
{
	UInt32 len = hsStrlen(string);		
	WriteSwap32(len);
	if (len > 0)
	{	
		char *buff = TRACKED_NEW char[len+1];
		int i;
		for (i = 0; i < len; i++)
		{
			buff[i] = ~string[i];
		}
		buff[len] = '\0';
		UInt32 result = Write(len, buff);
		delete [] buff;
		return result;
	}
	else
		return 0;
}

UInt32 hsStream::WriteSafeWStringLong(const wchar_t *string)
{
	UInt32 len = wcslen(string);
	WriteSwap32(len);
	if (len > 0)
	{
		int i;
		for (i=0; i<len; i++)
		{
			wchar_t buff = ~string[i];
			WriteSwap16((UInt16)buff);
		}
		WriteSwap16((UInt16)L'\0');
	}
	return 0;
}

char *hsStream::ReadSafeStringLong()
{
	char *name = nil;
	UInt32 numChars = ReadSwap32();
	if (numChars > 0 && numChars <= GetSizeLeft())
	{
		name = TRACKED_NEW char[numChars+1];
		Read(numChars, name);
		name[numChars] = '\0';

		// if the high bit is set, flip the bits. Otherwise it's a normal string, do nothing.
		if (name[0] & 0x80) 
		{
			int i;
			for (i = 0; i < numChars; i++)
				name[i] = ~name[i];
		}		
	}

	return name;
}

wchar_t *hsStream::ReadSafeWStringLong()
{
	wchar_t *retVal = nil;
	UInt32 numChars = ReadSwap32();
	if (numChars > 0 && numChars <= (GetSizeLeft()/2)) // divide by two because each char is two bytes
	{
		retVal = TRACKED_NEW wchar_t[numChars+1];
		int i;
		for (i=0; i<numChars; i++)
			retVal[i] = (wchar_t)ReadSwap16();
		retVal[numChars] = (wchar_t)ReadSwap16(); // we wrote the null out, read it back in

		if (retVal[0]* 0x80)
		{
			int i;
			for (i=0; i<numChars; i++)
				retVal[i] = ~retVal[i];
		}
	}

	return retVal;
}

UInt32 hsStream::WriteSafeString(const char *string)
{
	int len = hsStrlen(string);
	hsAssert(len<0xf000, xtl::format("string len of %d is too long for WriteSafeString %s, use WriteSafeStringLong", 
		string, len).c_str() );

	WriteSwap16(len | 0xf000);
	if (len > 0)
	{
		char *buff = TRACKED_NEW char[len+1];
		int i;
		for (i = 0; i < len; i++)
		{
			buff[i] = ~string[i];
		}
		buff[len] = '\0';
		UInt32 result = Write(len, buff);
		delete [] buff;
		return result;
	}
	else
		return 0;
}

UInt32 hsStream::WriteSafeWString(const wchar_t *string)
{
	int len = wcslen(string);
	hsAssert(len<0xf000, xtl::format("string len of %d is too long for WriteSafeWString, use WriteSafeWStringLong",
		len).c_str() );

	WriteSwap16(len | 0xf000);
	if (len > 0)
	{
		int i;
		for (i=0; i<len; i++)
		{
			wchar_t buff = ~string[i];
			WriteSwap16((UInt16)buff);
		}
		WriteSwap16((UInt16)L'\0');
	}
	return 0;
}

char *hsStream::ReadSafeString()
{
	char *name = nil;
	UInt16 numChars = ReadSwap16();

#ifndef REMOVE_ME_SOON
	// Backward compat hack - remove in a week or so (from 6/30/03)
	hsBool oldFormat = !(numChars & 0xf000);
	if (oldFormat)
		ReadSwap16();
#endif

	numChars &= ~0xf000;
	hsAssert(numChars <= GetSizeLeft(), "Bad string");
	if (numChars > 0 && numChars <= GetSizeLeft())
	{
		name = TRACKED_NEW char[numChars+1];
		Read(numChars, name);
		name[numChars] = '\0';	

		// if the high bit is set, flip the bits. Otherwise it's a normal string, do nothing.
		if (name[0] & 0x80) 
		{
			int i;
			for (i = 0; i < numChars; i++)
				name[i] = ~name[i];
		}
	}

	return name;
}

wchar_t *hsStream::ReadSafeWString()
{
	wchar_t *retVal = nil;
	UInt32 numChars = ReadSwap16();
	
	numChars &= ~0xf000;
	hsAssert(numChars <= GetSizeLeft()/2, "Bad string");
	if (numChars > 0 && numChars <= (GetSizeLeft()/2)) // divide by two because each char is two bytes
	{
		retVal = TRACKED_NEW wchar_t[numChars+1];
		int i;
		for (i=0; i<numChars; i++)
			retVal[i] = (wchar_t)ReadSwap16();
		retVal[numChars] = (wchar_t)ReadSwap16(); // we wrote the null out, read it back in

		if (retVal[0]* 0x80)
		{
			int i;
			for (i=0; i<numChars; i++)
				retVal[i] = ~retVal[i];
		}
	}

	return retVal;
}

hsBool	hsStream::Read4Bytes(void *pv)	// Virtual, faster version in sub classes
{
	int knt = this->Read(sizeof(UInt32), pv);
	if (knt != 4)
		return false;
	return true;
}

hsBool  hsStream::Read12Bytes(void *buffer)	// Reads 12 bytes, return true if success
{
	int knt = this->Read(12,buffer);
	if (knt != 12)
		return false;
	return true;
}

hsBool  hsStream::Read8Bytes(void *buffer)	// Reads 12 bytes, return true if success
{
	int knt = this->Read(8,buffer);
	if (knt !=8)
		return false;
	return true;
}

hsBool hsStream::ReadBool() // Virtual, faster version in sub classes
{
	return this->ReadByte();
}

bool hsStream::Readbool() // Virtual, faster version in sub classes
{
	return this->ReadByte() ? true : false;
}

void hsStream::ReadBool(int count, hsBool values[])
{
	this->Read(count, values);

	if (sizeof(hsBool) > 1)
	{	const UInt8* src = (UInt8*)values;

		//	go backwards so we don't overwrite ourselves
		for (int i = count - 1; i >= 0; --i)
			values[i] = src[i];
	}
}

UInt8 hsStream::ReadByte()
{
	UInt8	value;

	this->Read(sizeof(UInt8), &value);
	return value;
}

hsBool hsStream::AtEnd()
{
	hsAssert(0,"No hsStream::AtEnd() implemented for this stream class");
	return false;
}

hsBool hsStream::IsTokenSeparator(char c)
{
	return (isspace(c) || c==',' || c=='=');
}

hsBool hsStream::GetToken(char *s, UInt32 maxLen, const char beginComment, const char endComment)
{
	char c;
	char endCom;
        endCom = endComment;

	while( true )
	{
		while( !AtEnd() && IsTokenSeparator(c = ReadByte()) )
			c = c;
			;
		if( AtEnd() )
			return false;

		if( beginComment != c )
			break;

		// skip to end of comment
		while( !AtEnd() && (endCom != (c = ReadByte())) )
			c= c;
			;
	}

	s[0] = c;
	UInt32 k = 1;
	while( !AtEnd() && !IsTokenSeparator(c = ReadByte()) )
	{
		if( k < maxLen )
			s[k++] = c;
	}
	s[k] = 0;


	if( (k > 0)&&!_stricmp(s, "skip") )
	{
		int depth = 1;
		while( depth && GetToken(s, maxLen, beginComment, endCom) )
		{
			if( !_stricmp(s, "skip") )
				depth++;
			else
			if( !_stricmp(s, "piks") )
				depth--;
		}
		return GetToken(s, maxLen, beginComment, endCom);
	}

	return true;
}

hsBool hsStream::ReadLn(char *s, UInt32 maxLen, const char beginComment, const char endComment)
{
	char c;
	char endCom;
        endCom = endComment;

	while( true )
	{
		while( !AtEnd() && strchr("\r\n",c = ReadByte()) )
			c = c;
			;
		if( AtEnd() )
			return false;

		if( beginComment != c )
			break;

		// skip to end of comment
		while( !AtEnd() && (endCom != (c = ReadByte())) )
			c= c;
			;
	}

	s[0] = c;
	UInt32 k = 1;
	while( !AtEnd() && !strchr("\r\n",c = ReadByte()) )
	{
		if( k < maxLen )
			s[k++] = c;
	}
	s[k] = 0;


	if( (k > 0)&&!_stricmp(s, "skip") )
	{
		int depth = 1;
		while( depth && ReadLn(s, maxLen, beginComment, endCom) )
		{
			if( !_stricmp(s, "skip") )
				depth++;
			else
			if( !_stricmp(s, "piks") )
				depth--;
		}
		return ReadLn(s, maxLen, beginComment, endCom);
	}

	return true;
}

UInt16 hsStream::ReadSwap16()
{
	UInt16	value;
	this->Read(sizeof(UInt16), &value);
	swapIt((Int16*)&value);
	return value;
}

void hsStream::ReadSwap16(int count, UInt16 values[])
{
	this->Read(count * sizeof(UInt16), values);
#if HS_CPU_BENDIAN
	for (int i = 0; i < count; i++)
		swapIt((Int16*)&values[i]);
#endif
}

UInt32 hsStream::ReadSwap32()
{
	UInt32	value;
	Read4Bytes(&value);
	swapIt((Int32*)&value);
	return value;
}

void hsStream::ReadSwap32(int count, UInt32 values[])
{
	this->Read(count * sizeof(UInt32), values);
#if HS_CPU_BENDIAN
	for (int i = 0; i < count; i++)
		swapIt((Int32*)&values[i]);
#endif
}

UInt32 hsStream::ReadUnswap32()
{
	UInt32	value;
	Read4Bytes(&value);
	unswapIt((Int32*)&value);
	return value;
}

#if HS_CAN_USE_FLOAT
	double hsStream::ReadSwapDouble()
	{
		double  ival;
		Read8Bytes(&ival);
		double *pval = (double *)&ival;		// all in the name of speed, 
		swapIt(pval);
		return *pval;
	}

	void hsStream::ReadSwapDouble(int count, double values[])
	{
		this->Read(count * sizeof(double), values);
#if HS_CPU_BENDIAN
                for (int i = 0; i < count; i++)
                        swapIt(&values[i]);
#endif
        }


	float hsStream::ReadSwapFloat()
	{
		UInt32  ival;
		Read4Bytes(&ival);
		float *pval = (float *)&ival;		// all in the name of speed, 
		swapIt(pval);
		return *pval;
	}

	void hsStream::ReadSwapFloat(int count, float values[])
	{
		this->Read(count * sizeof(float), values);
#if HS_CPU_BENDIAN
		for (int i = 0; i < count; i++)
			swapIt(&values[i]);
#endif
	}

	float hsStream::ReadUnswapFloat()
	{
		float value;
		this->Read(sizeof(float), &value);
		unswapIt(&value);
		return value;
	}
#endif


void hsStream::WriteBool(hsBool value)
{
	UInt8 dst = (value != 0);

	this->Write(sizeof(UInt8), &dst);
}

void hsStream::Writebool(bool value)
{
	UInt8 dst = (value != 0);

	this->Write(sizeof(UInt8), &dst);
}

void hsStream::WriteBool(int count, const hsBool values[])
{
	if (sizeof(hsBool) > 1)
	{	hsTempArray<UInt8> storage(count);
		UInt8*			 dst = (UInt8*)values;
	
		for (int i = 0; i < count; i++)
			dst[i] = (values[i] != 0);
		this->Write(count, dst);
	}
	else
		this->Write(count, values);
}

void hsStream::WriteByte(UInt8 value)
{
	this->Write(sizeof(UInt8), &value);
}

void  hsStream::WriteSwap16(UInt16 value)
{
	swapIt((Int16*)&value);
	this->Write(sizeof(Int16), &value);
}

void  hsStream::WriteSwap16(int count, const UInt16 values[])
{
	for (int i = 0; i < count; i++)
		this->WriteSwap16(values[i]);
}

void  hsStream::WriteSwap32(UInt32 value)
{
	swapIt((Int32*)&value);
	this->Write(sizeof(Int32), &value);
}

void  hsStream::WriteSwap32(int count, const UInt32 values[])
{
	for (int i = 0; i < count; i++)
		this->WriteSwap32(values[i]);
}

void hsStream::WriteUnswap32(UInt32 value)
{
	unswapIt((Int32*)&value);
	this->Write(sizeof(Int32), &value);
}

#if HS_CAN_USE_FLOAT
	void hsStream::WriteSwapDouble(double value)
	{
		swapIt(&value);
		this->Write(sizeof(double), &value);
	}

	void hsStream::WriteSwapDouble(int count, const double values[])
	{
		for (int i = 0; i < count; i++)
			this->WriteSwapDouble(values[i]);
	}

	void hsStream::WriteSwapFloat(float value)
	{
		swapIt(&value);
		this->Write(sizeof(float), &value);
	}

	void hsStream::WriteSwapFloat(int count, const float values[])
	{
		for (int i = 0; i < count; i++)
			this->WriteSwapFloat(values[i]);
	}

	void hsStream::WriteUnswapFloat(float value)
	{
		unswapIt(&value);
		this->Write(sizeof(float), &value);
	}
#endif

void hsStream::WriteSwapAtom(UInt32 tag, UInt32 size)
{
	this->WriteSwap32(tag);
	this->WriteSwap32(size);
}

UInt32 hsStream::ReadSwapAtom(UInt32* sizePtr)
{
	UInt32	tag = this->ReadSwap32();
	UInt32	size = this->ReadSwap32();

	if (sizePtr)
		*sizePtr = size;
	return tag;
}

//////////////////////////////////////////////////////////////////////////////////

#define kFileStream_Uninitialized		~0

hsBool hsFileStream::Open(const char *name, const char *mode)
{
#ifdef HS_BUILD_FOR_PS2
	hsAssert(fRef == kFileStream_Uninitialized, "hsFileStream:Open  Stream already opened");

	Int32 ref = hsPS2Open(name, mode);
	if (ref == -1)
		return false;

	fRef = (UInt32) ref;
	fFileSize = sceLseek(fRef, 0, SCE_SEEK_END);
	sceLseek(fRef, 0, SCE_SEEK_SET);
	fBufferIsEmpty = true;
	fWriteBufferUsed = false;
	fVirtualFilePointer = 0;
	fBufferBase = 0;

	return true;
#else
	hsAssert(0, "hsFileStream::Open  NotImplemented");
	return false;
#endif
}

hsBool hsFileStream::Open(const wchar *name, const wchar *mode)
{
	hsAssert(0, "hsFileStream::Open  NotImplemented");
	return false;
}

hsBool hsFileStream::Close ()
{
#ifdef HS_BUILD_FOR_PS2
	if (fRef != kFileStream_Uninitialized)
	{
		hsPS2Close(fRef);
		fRef = kFileStream_Uninitialized;
	}
	return true;
#else
	hsAssert(0, "hsFileStream::Close  NotImplemented");
	return false;
#endif
}

UInt32 hsFileStream::GetFileRef()
{
	return fRef;
}

void hsFileStream::SetFileRef(UInt32 ref)
{
	hsAssert(ref != kFileStream_Uninitialized, "bad ref");
	fRef = ref;
#if HS_BUILD_FOR_PS2
	fFileSize = sceLseek(fRef, 0, SCE_SEEK_END);
	sceLseek(fRef, 0, SCE_SEEK_SET);
	fBufferIsEmpty= true;
	fWriteBufferUsed= false;
	fVirtualFilePointer= 0;
	fBufferBase= 0;
#endif
}

hsFileStream::hsFileStream()
{
	fRef = kFileStream_Uninitialized;
#if HS_BUILD_FOR_PS2
	fBufferIsEmpty= true;
	fWriteBufferUsed= false;
#endif
}

hsFileStream::~hsFileStream()
{
}

UInt32 hsFileStream::Read(UInt32 bytes,  void* buffer)
{
	hsAssert(fRef != kFileStream_Uninitialized, "fRef uninitialized");

	fBytesRead += bytes;
    fPosition += bytes;

#if HS_BUILD_FOR_MAC
	Int16	err;

	err = FSRead(fRef, (long*)&bytes, buffer);
	if (err == noErr)
		return bytes;
	else
		return 0;
#elif HS_BUILD_FOR_PS2
	Int32 ret;
	Int32 nReadBytes= 0;
	while(bytes){
		if( !fBufferIsEmpty ){	// read at already chatched.
			Int32 DataBytesInBuffer= fBufferBase + kBufferSize - fVirtualFilePointer;
			Int32 ChatchedReadSize= DataBytesInBuffer < bytes ? DataBytesInBuffer : bytes;
			memcpy( buffer, &fBuffer[fVirtualFilePointer-fBufferBase], ChatchedReadSize );
			nReadBytes += ChatchedReadSize;
			buffer= (void *)(((char*)buffer) + ChatchedReadSize);
			fVirtualFilePointer += ChatchedReadSize;
			bytes -= ChatchedReadSize;
			fBufferIsEmpty= (fBufferBase + kBufferSize <= fVirtualFilePointer);
		}
		if( kBufferSize <= bytes ){	// read directry, for Large block read.
			hsAssert( fBufferIsEmpty, "read buffer was not used.");
			Int32 DirectReadSize= bytes - bytes % kBufferSize;
			ret= sceRead(fRef, buffer, DirectReadSize);
			if( ret == -1 ){
				return 0;
			}
			hsAssert( ret == DirectReadSize, "require read size != return size");
			nReadBytes += DirectReadSize;
			buffer= (void *)(((char*)buffer) + DirectReadSize);
			fVirtualFilePointer += DirectReadSize;
			bytes -= DirectReadSize;
		}
		if( 0 < bytes && fBufferIsEmpty ){	// fill buffer
			hsAssert( fVirtualFilePointer % kBufferSize == 0 , "read buffer is not alignment.");
			ret= sceRead(fRef, fBuffer, kBufferSize );
			if( ret == -1 ){
				return 0;
			}
			fBufferBase= fVirtualFilePointer;
			fBufferIsEmpty= false;
		}
	}
	return nReadBytes;

#elif HS_BUILD_FOR_WIN32
	UInt32 rBytes;
	ReadFile((HANDLE)fRef, buffer, bytes, &rBytes, nil);
	if(bytes == rBytes)
		return bytes;
	else
		return 0;
#else
	return 0;
#endif
}

UInt32 hsFileStream::Write(UInt32 bytes, const void* buffer)
{
	hsAssert(fRef != kFileStream_Uninitialized, "fRef uninitialized");

    fBytesRead += bytes;
    fPosition += bytes;

#if HS_BUILD_FOR_MAC
	Int16	err;

	err = FSWrite(fRef, (long*)&bytes, buffer);
	if (err == noErr)
		return bytes;
	else
	{	
		hsDebugMessage("hsFileStream::Write failed", err);
		return 0;
	}
#elif HS_BUILD_FOR_PS2
       Int32 ret;
	
	fWriteBufferUsed =true;	// buffered write was not implement, not yet.
	
	ret = sceWrite(fRef, (void*)buffer ,bytes);
	if(ret != -1)
	  return ret;
	else
	  return 0;
#elif HS_BUILD_FOR_WIN32
	UInt32 wBytes;
	WriteFile((HANDLE)fRef, buffer, bytes, &wBytes, nil);
	if(bytes == wBytes)
		return bytes;
	else
	{
		char str[128];
		sprintf(str, "hsFileStream::Write failed.  err %d", GetLastError());
		hsAssert(false, str);
		return 0;
	}
#else
	return 0;
#endif
}


hsBool hsFileStream::AtEnd()
{
#if HS_BUILD_FOR_MAC
	Int32 eof;
	Int32 pos;
	::GetEOF(fRef, &eof);
	::GetFPos(fRef, &pos);
	return pos >= eof;
#elif HS_BUILD_FOR_PS2
	Int32 rVal = 0;
	if( fWriteBufferUsed || fVirtualFilePointer == 0 ){
		// bufferd write was not implement, yiet.
		rVal = sceLseek(fRef, 0, SCE_SEEK_CUR);
		return rVal >= fFileSize;
	}
	else{	// bufferd read
		return fVirtualFilePointer >= fFileSize;
	}
	
#elif HS_BUILD_FOR_WIN32
	UInt32 bytes;
	PeekNamedPipe((void*)fRef, nil, 0, nil, &bytes, nil);
	return bytes>0;
#else
	hsAssert(0,"No hsStream::AtEnd() implemented for this stream class");
	return false;
#endif
}

void hsFileStream::Skip(UInt32 delta)
{
	fBytesRead += delta;
    fPosition += delta;

#if HS_BUILD_FOR_MAC
	short err = SetFPos(fRef, fsFromMark, delta);
	hsAssert(err == noErr, "SetFPos failed");
#elif HS_BUILD_FOR_PS2
	const Int32 NewPointer= fVirtualFilePointer+delta;
	if( fWriteBufferUsed || fVirtualFilePointer == 0 ){
		// bufferd write was not implement, yiet.
		sceLseek(fRef, delta, SCE_SEEK_CUR);
	}
	else{	// bufferd read.
		if( !fBufferIsEmpty ){
			Int32 CurBlock= fVirtualFilePointer / kBufferSize;
			Int32 NewBlock= NewPointer / kBufferSize;
			if( CurBlock == NewBlock ){
				fVirtualFilePointer += delta;
				return;
			}
			fBufferIsEmpty= false;
		}
		Int32 NewBaseMod= NewPointer % kBufferSize;
		Int32 NewBase= NewPointer - NewBaseMod;
		if( NewBaseMod ){
			sceLseek( fRef, NewBase, SCE_SEEK_SET );
			sceRead( fRef, fBuffer, kBufferSize );
			fVirtualFilePointer= NewPointer;
			fBufferBase= NewBase;
			fBufferIsEmpty= false;
		}
		else{
			// just block border.
			fVirtualFilePointer= NewPointer;
			fBufferBase= NewBase;
		}
	}
#elif HS_BUILD_FOR_WIN32
	hsDebugMessage("hsFileStream::Skip unimplemented", 0);
#endif
}

void hsFileStream::Rewind()
{
	fBytesRead = 0;
    fPosition = 0;

#if HS_BUILD_FOR_MAC
	short err = SetFPos(fRef, fsFromStart, 0);
	hsAssert(err == noErr, "SetFPos failed");
#elif HS_BUILD_FOR_PS2
	if( fWriteBufferUsed || fVirtualFilePointer == 0 ){
		// bufferd write was not implement, yiet.
		sceLseek(fRef,0,SCE_SEEK_SET);
	}
	else{	// bufferd read.
		sceLseek(fRef, 0, SCE_SEEK_SET);
		fBufferIsEmpty= true;
		fVirtualFilePointer= 0;
		fBufferBase= 0;
	}
#elif HS_BUILD_FOR_WIN32
	hsDebugMessage("hsFileStream::Rewind unimplemented", 0);
#endif
}

void hsFileStream::Truncate()
{
	hsDebugMessage("hsFileStream::Truncate unimplemented", 0);
}

//////////////////////////////////////////////////////////////////////////////////////

#if !HS_BUILD_FOR_PS2
#if !(HS_BUILD_FOR_REFERENCE)

hsUNIXStream::~hsUNIXStream()
{
	// Don't Close here, because Sub classes Don't always want that behaviour!
}

hsBool hsUNIXStream::Open(const char *name, const char *mode)
{
	fPosition = 0;
	fRef = hsFopen(name, mode);
	return (fRef) ? true : false;
}

hsBool hsUNIXStream::Open(const wchar *name, const wchar *mode)
{
	fPosition = 0;
	fRef = _wfopen(name, mode);
	return (fRef) ? true : false;
}

hsBool hsUNIXStream::Close()
{
	int rtn = true;
	if (fRef)
		rtn = fclose(fRef);
	fRef = nil;
	delete [] fBuff;
	fBuff = nil;

	return !rtn;
}

UInt32 hsUNIXStream::Read(UInt32 bytes,  void* buffer)
{
	if (!fRef || !bytes)
		return 0;
	int numItems = ::fread(buffer, 1 /*size*/, bytes /*count*/, fRef);
	fBytesRead += numItems;
    fPosition += numItems;
	if ((unsigned)numItems < bytes) {
		if (feof(fRef)) {
			// EOF ocurred
			char str[128];
			sprintf(str, "Hit EOF on UNIX Read, only read %d out of requested %d bytes\n", numItems, bytes);
			hsDebugMessage(str, 0);
		}
		else {
			hsDebugMessage("Error on UNIX Read", ferror(fRef));
		}
	}
	return numItems;
}

hsBool  hsUNIXStream::AtEnd()
{
	if (!fRef)
		return 1;
	hsBool rVal;
	int x = getc(fRef);
	rVal = feof(fRef) != 0;
	ungetc(x, fRef);
	return rVal;
}

UInt32 hsUNIXStream::Write(UInt32 bytes, const void* buffer)
{
	if (!fRef)
		return 0;
    fPosition += bytes;
	return fwrite(buffer, bytes, 1, fRef);
}

void hsUNIXStream::SetPosition(UInt32 position)
{
	if (!fRef || (position == fPosition))
		return;
	fBytesRead = position;
	fPosition = position;
	(void)::fseek(fRef, position, SEEK_SET);
}

void hsUNIXStream::Skip(UInt32 delta)
{
	if (!fRef)
		return;
	fBytesRead += delta;
    fPosition += delta;
	(void)::fseek(fRef, delta, SEEK_CUR);
}

void hsUNIXStream::Rewind()
{
	if (!fRef)
		return;
	fBytesRead = 0;
    fPosition = 0;
	(void)::fseek(fRef, 0, SEEK_SET);
}

void hsUNIXStream::FastFwd()
{
	if (!fRef)
		return;
	(void)::fseek(fRef, 0, SEEK_END);
	fBytesRead = fPosition = ftell(fRef);
}

UInt32	hsUNIXStream::GetEOF()
{
	if( !fRef )
		return 0;

	long oldPos = ftell( fRef );
	(void)::fseek( fRef, 0, SEEK_END );
	UInt32 end = (UInt32)ftell( fRef );
	(void)::fseek( fRef, oldPos, SEEK_SET );

	return end;
}

void hsUNIXStream::Truncate()
{
	if (!fRef)
		return;
#if! __MWERKS__
    int handle = _fileno(fRef);
#if !HS_BUILD_FOR_UNIX
    _chsize(handle, fPosition);
#else
    ftruncate(handle, fPosition);
#endif
#else
#if 1
	UInt32 handle = (UInt32)fRef->handle;
	OSErr err = ::SetEOF(handle, fPosition); 
	if(err != noErr)
	{
		hsThrow("Truncate error!");
	}
#endif
#endif
}

void hsUNIXStream::Flush()
{
	if (!fRef)
		return;
	(void)::fflush(fRef);
}

#endif
#endif

//////////////////////////////////////////////////////////////////////////////////////

plReadOnlySubStream::~plReadOnlySubStream()
{
}

void	plReadOnlySubStream::Open( hsStream *base, UInt32 offset, UInt32 length )
{
	fBase = base;
	fOffset = offset;
	fLength = length;

	fBase->SetPosition( fOffset );
	IFixPosition();
}

void	plReadOnlySubStream::IFixPosition( void )
{
	fPosition = fBase->GetPosition() - fOffset;
}

hsBool	plReadOnlySubStream::AtEnd()
{
	if( fPosition >= fLength )
		return true;
	return false;
}

UInt32	plReadOnlySubStream::Read(UInt32 byteCount, void* buffer)
{
	if( byteCount > GetSizeLeft() )
	{
		hsThrow("Attempting to read past end of stream");
		byteCount = GetSizeLeft();
	}

	UInt32 read = fBase->Read( byteCount, buffer );
	IFixPosition();
	return read;
}

UInt32	plReadOnlySubStream::Write(UInt32 byteCount, const void* buffer)
{
	hsAssert( false, "Write not allowed on an plReadOnlySubStream" );
	return 0;
}

void	plReadOnlySubStream::Skip(UInt32 deltaByteCount)
{
	fBase->Skip( deltaByteCount );
	IFixPosition();
}

void	plReadOnlySubStream::Rewind()
{
	fBase->SetPosition( fOffset );
	IFixPosition();
}

void	plReadOnlySubStream::FastFwd()
{
	fBase->SetPosition( fOffset + fLength );
	IFixPosition();
}

void    plReadOnlySubStream::Truncate()
{
	hsAssert( false, "Can't truncate a read-only stream" );
}

UInt32	plReadOnlySubStream::GetEOF()
{
	return fLength;
}

//////////////////////////////////////////////////////////////////////////////////////

#define kRAMStreamChunkSize		1024

hsRAMStream::hsRAMStream() : fAppender(1, kRAMStreamChunkSize)
{
	fIter.ResetToHead(&fAppender);
}

hsRAMStream::hsRAMStream(UInt32 chunkSize) : fAppender(1, chunkSize)
{
	fIter.ResetToHead(&fAppender);
}

hsRAMStream::~hsRAMStream()
{
}

void hsRAMStream::Reset()
{
	fBytesRead = 0;
    fPosition = 0;

	fAppender.Reset();
	fIter.ResetToHead(&fAppender);
}

hsBool hsRAMStream::AtEnd()
{
	return (fBytesRead >= fAppender.Count() * fAppender.ElemSize());
}

UInt32 hsRAMStream::Read(UInt32 byteCount, void * buffer)
{
	if (fBytesRead + byteCount > fAppender.Count() * fAppender.ElemSize())
	{
		hsThrow("Attempting to read past end of stream");
		byteCount = (fAppender.Count() * fAppender.ElemSize()) - fBytesRead;
	}

	fBytesRead += byteCount;
    fPosition += byteCount;

	fIter.Next(byteCount, buffer);

	return byteCount;
}

UInt32 hsRAMStream::Write(UInt32 byteCount, const void* buffer)
{
    fPosition += byteCount;

	fAppender.PushTail(byteCount, buffer);

	return byteCount;
}

void hsRAMStream::Skip(UInt32 deltaByteCount)
{
    fPosition += deltaByteCount;
	fIter.Next(deltaByteCount, nil);
}

void hsRAMStream::Rewind()
{
	fBytesRead = 0;
    fPosition = 0;
	fIter.ResetToHead(&fAppender);
}

void hsRAMStream::Truncate()
{
	Reset();
}

UInt32 hsRAMStream::GetEOF()
{
	return fAppender.Count() * fAppender.ElemSize();
}

void hsRAMStream::CopyToMem(void* mem)
{
	(void)fAppender.CopyInto(mem);
}

//////////////////////////////////////////////////////////////////////

UInt32 hsNullStream::Read(UInt32 byteCount, void * buffer)
{
	hsThrow("hsNullStream: Can't read from this stream!");
	return 0;
}

UInt32 hsNullStream::Write(UInt32 byteCount, const void* buffer)
{
	fBytesRead += byteCount;
    fPosition += byteCount;

	return byteCount;
}

void hsNullStream::Skip(UInt32 deltaByteCount)
{
	fBytesRead += deltaByteCount;
    fPosition += deltaByteCount;
}

void hsNullStream::Rewind()
{
	fBytesRead = 0;
    fPosition = 0;
}

void hsNullStream::Truncate()
{
}

/////////////////////////////////////////////////////////////////////////////////

hsBool hsReadOnlyStream::AtEnd()
{
	return fData >= fStop;
}

UInt32 hsReadOnlyStream::Read(UInt32 byteCount, void* buffer)
{
	if (fData + byteCount > fStop)
	{
		hsThrow("Attempting to read past end of stream");
		byteCount = GetSizeLeft();
	}

	HSMemory::BlockMove(fData, buffer, byteCount);
	fData += byteCount;
	fBytesRead += byteCount;
    fPosition += byteCount;
	return byteCount;
}

UInt32 hsReadOnlyStream::Write(UInt32 byteCount, const void* buffer)
{
	hsThrow( "can't write to a readonly stream");
	return 0;
}

void hsReadOnlyStream::Skip(UInt32 deltaByteCount)
{
	fBytesRead += deltaByteCount;
    fPosition += deltaByteCount;
	fData += deltaByteCount;
	if (fData > fStop)
		hsThrow( "Skip went past end of stream");
}

void hsReadOnlyStream::Rewind()
{
	fBytesRead = 0;
    fPosition = 0;
	fData = fStart;
}

void hsReadOnlyStream::Truncate()
{
	hsThrow( "can't write to a readonly stream");
}

void hsReadOnlyStream::CopyToMem(void* mem)
{
	if (fData < fStop)
		HSMemory::BlockMove(fData, mem, fStop-fData);
}


////////////////////////////////////////////////////////////////////////////////////
UInt32 hsWriteOnlyStream::Read(UInt32 byteCount, void* buffer)
{
	hsThrow( "can't read to a writeonly stream");
	return 0;
}

UInt32 hsWriteOnlyStream::Write(UInt32 byteCount, const void* buffer)
{
	if (fData + byteCount > fStop)
		hsThrow("Write past end of stream");
	HSMemory::BlockMove(buffer, fData, byteCount);
	fData += byteCount;
	fBytesRead += byteCount;
    fPosition += byteCount;
	return byteCount;
}


///////////////////////////////////////////////////////////////////////////////////

hsQueueStream::hsQueueStream(Int32 size) :
	fSize(size),
	fReadCursor(0),
	fWriteCursor(0)
{
	fQueue = TRACKED_NEW char[fSize];
}

hsQueueStream::~hsQueueStream()
{
	delete [] fQueue;
}

UInt32 hsQueueStream::Read(UInt32 byteCount, void * buffer)
{
	hsAssert(fWriteCursor >= 0 && fWriteCursor < fSize,"hsQueueStream: WriteCursor out of range.");
	hsAssert(fReadCursor >= 0 && fReadCursor < fSize,"hsQueueStream: ReadCursor out of range.");

	Int32 limit, length, total;
	
	limit = fWriteCursor >= fReadCursor ? fWriteCursor : fSize;
	length = hsMinimum(limit-fReadCursor,byteCount);
	HSMemory::BlockMove(fQueue+fReadCursor,buffer,length);
	fReadCursor += length;
	fReadCursor %= fSize;
	total = length;
		
	if (length < byteCount && limit != fWriteCursor)
	{
		limit = fWriteCursor;
		length = hsMinimum(limit,byteCount-length);
		HSMemory::BlockMove(fQueue,static_cast<char*>(buffer)+total,length);
		fReadCursor = length;
		total += length;
	}

	return total;
}

UInt32 hsQueueStream::Write(UInt32 byteCount, const void* buffer)
{
	hsAssert(fWriteCursor >= 0 && fWriteCursor < fSize,"hsQueueStream: WriteCursor out of range.");
	hsAssert(fReadCursor >= 0 && fReadCursor < fSize,"hsQueueStream: ReadCursor out of range.");

	Int32 length;

	length = hsMinimum(fSize-fWriteCursor,byteCount);
	HSMemory::BlockMove(buffer,fQueue+fWriteCursor,length);
	if (fReadCursor > fWriteCursor)
	{
#if 0
		if (fReadCursor < fWriteCursor+length+1)
			hsStatusMessage("ReadCursor wrapped\n");
#endif
		fReadCursor = hsMaximum(fReadCursor,fWriteCursor+length+1);
		fReadCursor %= fSize;
	}
	fWriteCursor += length;
	fWriteCursor %= fSize;

	if (length < byteCount)
	{
		Write(byteCount - length,static_cast<const char*>(buffer)+length);
	}

	return byteCount;
}

void hsQueueStream::Skip(UInt32 deltaByteCount)
{
	Int32 limit, length;
	
	limit = fWriteCursor >= fReadCursor ? fWriteCursor : fSize;
	length = hsMinimum(limit-fReadCursor,deltaByteCount);
	fReadCursor += length;

	if (length < deltaByteCount && limit != fWriteCursor)
	{
		limit = fWriteCursor;
		length = hsMinimum(limit,deltaByteCount-length);
		fReadCursor = length;
	}
	else
	{
		fReadCursor %= fSize;
}
}

void hsQueueStream::Rewind()
{
	fReadCursor = fWriteCursor+1;
	fReadCursor %= fSize;
}

void hsQueueStream::FastFwd()
{
	fReadCursor = fWriteCursor;
}

hsBool hsQueueStream::AtEnd()
{
	return fReadCursor == fWriteCursor;
}

///////////////////////////////////////////////////////////////////////////////
// hsBufferedStream
///////////////////////////////////////////////////////////////////////////////

inline void FastByteCopy(void* dest, const void* src, UInt32 bytes)
{
	// Don't use memcpy if the read is 4 bytes or less, it's faster to just do a
	// direct copy
	switch (bytes)
	{
	case 4:
		*((UInt32*)dest) = *((const UInt32*)src);
		break;
	case 2:
		*((UInt16*)dest) = *((const UInt16*)src);
		break;
	case 1:
		*((UInt8*)dest) = *((const UInt8*)src);
		break;
	default:
		memcpy(dest, src, bytes);
	}
}

//#define LOG_BUFFERED

hsBufferedStream::hsBufferedStream()
: fRef(nil)
, fFileSize(0)
, fBufferLen(0)
, fWriteBufferUsed(false)
#ifdef HS_DEBUGGING
, fBufferHits(0)
, fBufferMisses(0)
, fBufferReadIn(0)
, fBufferReadOut(0)
, fReadDirect(0)
, fLastReadPos(0)
, fFilename(nil)
, fCloseReason(nil)
#endif
{
}

hsBufferedStream::~hsBufferedStream()
{
#ifdef LOG_BUFFERED
	delete [] fFilename;
#endif // LOG_BUFFERED
}

hsBool hsBufferedStream::Open(const char* name, const char* mode)
{
	hsAssert(!fRef, "hsBufferedStream:Open Stream already opened");
	fRef = hsFopen(name, mode);
	if (!fRef)
		return false;

	SetFileRef(fRef);

#ifdef LOG_BUFFERED
	fBufferHits = fBufferMisses = 0;
	fBufferReadIn = fBufferReadOut = fReadDirect = fLastReadPos = 0;
	delete [] fFilename;
	fFilename = hsStrdup(name);
	fCloseReason = nil;
#endif // LOG_BUFFERED

	return true;
}

hsBool hsBufferedStream::Open(const wchar *name, const wchar *mode)
{
	hsAssert(0, "hsFileStream::Open  NotImplemented for wchar");
	return false;
}

hsBool hsBufferedStream::Close()
{
	int rtn = true;
	if (fRef)
		rtn = fclose(fRef);
	fRef = nil;

#ifdef LOG_BUFFERED
	hsUNIXStream s;
	static bool firstClose = true;
	if (firstClose)
	{
		firstClose = false;
		s.Open("log\\BufferedStream.csv", "wt");
		s.WriteString("File,Hits,Misses,Read In,Read Out,Read Direct,% Wasted,Reason\n");
	}
	else
		s.Open("log\\BufferedStream.csv", "at");

	int wasted = 100;
	if (fBufferReadIn + fReadDirect > 0)
		wasted -= int((float(fBufferReadOut+fReadDirect) / float(fBufferReadIn+fReadDirect)) * 100.f);

	s.WriteFmt("%s,%d,%d,%u,%u,%u,%d,%s\n",
		fFilename, fBufferHits, fBufferMisses, fBufferReadIn, fBufferReadOut, fReadDirect,
		wasted,
		fCloseReason ? fCloseReason : "Unknown");

	s.Close();
#endif // LOG_BUFFERED

	return !rtn;
}

FILE* hsBufferedStream::GetFileRef()
{
	return fRef;
}

void hsBufferedStream::SetFileRef(FILE* ref)
{
	hsAssert(ref, "bad ref");
	fRef = ref;

	fseek(fRef, 0, SEEK_END);
	fFileSize = ftell(fRef);
	fseek(fRef, 0, SEEK_SET);

	fBufferLen = 0;
	fPosition = 0;
	fWriteBufferUsed = false;
}

UInt32 hsBufferedStream::Read(UInt32 bytes, void* buffer)
{
	hsAssert(fRef, "fRef uninitialized");
	if (!fRef || bytes == 0)
		return 0;

	UInt32 numReadBytes = 0;

	while (bytes > 0 && fPosition < fFileSize)
	{
		// First, see if we've got anything in the buffer
		if (fBufferLen > 0)
		{
			// Figure out how much we can copy out of the buffer
			UInt32 bufferPos = fPosition % kBufferSize;
			UInt32 bytesInBuffer = fBufferLen - bufferPos;
			UInt32 cachedReadSize = bytesInBuffer < bytes ? bytesInBuffer : bytes;

			FastByteCopy(buffer, &fBuffer[bufferPos], cachedReadSize);

			fPosition += cachedReadSize;
			numReadBytes += cachedReadSize;
			bytes -= cachedReadSize;
			buffer = (void*)(((char*)buffer) + cachedReadSize);

			// If we read all the data out of the buffer, set it to empty
			if ((bufferPos + cachedReadSize) == fBufferLen)
				fBufferLen = 0;

#ifdef HS_DEBUGGING
			fLastReadPos = fPosition;
			fBufferHits++;
			fBufferReadOut += cachedReadSize;
#endif
		}

		// Now see if the remaining read (if any) is the size of the buffer or larger.
		// If it is, read as many complete blocks as possible directly into the output buffer.
		if (bytes >= kBufferSize && fPosition % kBufferSize == 0)
		{
			UInt32 directReadSize = bytes - (bytes % kBufferSize);
			hsAssert(ftell(fRef) % kBufferSize == 0 , "read buffer is not in alignment.");
			int amtRead = ::fread(buffer, 1, directReadSize, fRef);
			fPosition += amtRead;
			numReadBytes += amtRead;
			bytes -= amtRead;
			buffer = (void*)(((char*)buffer) + amtRead);
#ifdef HS_DEBUGGING
			fLastReadPos = fPosition;
			fReadDirect += directReadSize;
#endif
		}

		// If we've got bytes left to read and we didn't pass the end of the file, buffer a new block
		if (bytes > 0 && fPosition < fFileSize)
		{
			hsAssert(ftell(fRef) % kBufferSize == 0 , "read buffer is not in alignment.");
			fBufferLen = ::fread(fBuffer, 1, kBufferSize, fRef);

#ifdef HS_DEBUGGING
			// If our last read wasn't at the start of the new buffer, it's a miss.
			if (fLastReadPos != fPosition)
			{
				fBufferMisses++;
				fBufferHits--;
			}

			fBufferReadIn += fBufferLen;
#endif
		}
	}

	return numReadBytes;
}

UInt32 hsBufferedStream::Write(UInt32 bytes, const void* buffer)
{
	hsAssert(fRef, "fRef uninitialized");
	fWriteBufferUsed = true;
	int amtWritten = fwrite((void*)buffer, 1, bytes, fRef);
	fPosition += amtWritten;
	return amtWritten;
}

hsBool hsBufferedStream::AtEnd()
{
	if (fWriteBufferUsed)
	{
		if (!fRef)
			return true;
		bool rVal;
		int x = getc(fRef);
		rVal = feof(fRef) != 0;
		ungetc(x, fRef);
		return rVal;
	}
	else
	{
		// buffered read
		return fPosition >= fFileSize;
	}
}

void hsBufferedStream::Skip(UInt32 delta)
{
	if (fWriteBufferUsed)
	{
		// buffered write not implemented yet.
		fseek(fRef, delta, SEEK_CUR);
	}
	else
	{
		UInt32 blockStart = ((fPosition + delta) / kBufferSize) * kBufferSize;

		// We've got data in the buffer, see if we can just skip in that
		if (fBufferLen > 0)
		{
			Int32 newBufferPos = Int32(fPosition % kBufferSize) + Int32(delta);

			// If we skipped outside of our buffer, invalidate it
			if (newBufferPos < 0 || UInt32(newBufferPos) >= fBufferLen)
			{
				fBufferLen = 0;
				fseek(fRef, blockStart, SEEK_SET);
			}
		}
		else
			fseek(fRef, blockStart, SEEK_SET);
	}

	fPosition += delta;
}

void hsBufferedStream::Rewind()
{
	if (fWriteBufferUsed)
	{
		// buffered write not implemented yet.
		fseek(fRef, 0, SEEK_SET);
	}
	// If the currently buffered block isn't the first one, invalidate our buffer
	else if (fPosition >= kBufferSize)
		fBufferLen = 0;

	fPosition = 0;
}

UInt32 hsBufferedStream::GetEOF()
{
	if (fWriteBufferUsed)
	{
		if (!fRef)
			return 0;

		long oldPos = ftell(fRef);
		fseek(fRef, 0, SEEK_END);
		UInt32 end = (UInt32)ftell(fRef);
		fseek(fRef, oldPos, SEEK_SET);

		return end;
	}
	else
		return fFileSize;
}

void hsBufferedStream::Truncate()
{
	hsAssert(0, "hsBufferedStream::Truncate unimplemented");
}