/*==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 .
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
#include "hsStream.h"
#include "hsMemory.h"
#include "hsUtils.h"
#include "hsTemplates.h"
#include "hsStlUtils.h"
#if HS_BUILD_FOR_UNIX
#include
#endif
#if HS_BUILD_FOR_MAC
#include
#include
#include
#endif
#if HS_BUILD_FOR_PS2
#include
#include
#endif
#include "hsWindows.h"
#if HS_BUILD_FOR_WIN32
#include
#endif
#include "hsStlUtils.h"
//////////////////////////////////////////////////////////////////////////////////
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 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 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 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; iRead(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);
value = hsSWAP16(value);
return value;
}
void hsStream::ReadSwap16(int count, UInt16 values[])
{
this->Read(count * sizeof(UInt16), values);
for (int i = 0; i < count; i++)
values[i] = hsSWAP16(values[i]);
}
UInt32 hsStream::ReadSwap32()
{
UInt32 value;
Read4Bytes(&value);
value = hsSWAP32(value);
return value;
}
void hsStream::ReadSwap32(int count, UInt32 values[])
{
this->Read(count * sizeof(UInt32), values);
for (int i = 0; i < count; i++)
values[i] = hsSWAP32(values[i]);
}
UInt32 hsStream::ReadUnswap32()
{
UInt32 value;
Read4Bytes(&value);
value = hsUNSWAP32(value);
return value;
}
#if HS_CAN_USE_FLOAT
double hsStream::ReadSwapDouble()
{
double value;
Read8Bytes(&value);
value = hsSWAPDouble(value);
return value;
}
void hsStream::ReadSwapDouble(int count, double values[])
{
this->Read(count * sizeof(double), values);
for (int i = 0; i < count; i++)
values[i] = hsSWAPDouble(values[i]);
}
float hsStream::ReadSwapFloat()
{
float value;
Read4Bytes(&value);
value = hsSWAPFloat(value);
return value;
}
void hsStream::ReadSwapFloat(int count, float values[])
{
this->Read(count * sizeof(float), values);
for (int i = 0; i < count; i++)
values[i] = hsSWAPFloat(values[i]);
}
float hsStream::ReadUnswapFloat()
{
float value;
this->Read(sizeof(float), &value);
value = hsUNSWAPFloat(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 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)
{
value = hsSWAP16(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)
{
value = hsSWAP32(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)
{
value = hsUNSWAP32(value);
this->Write(sizeof(Int32), &value);
}
#if HS_CAN_USE_FLOAT
void hsStream::WriteSwapDouble(double value)
{
value = hsSWAPDouble(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)
{
value = hsSWAPFloat(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)
{
value = hsUNSWAPFloat(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);
}
//////////////////////////////////////////////////////////////////////////////////////
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 = hsWFopen(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;
int handle = fileno(fRef);
#if HS_BUILD_FOR_WIN32
_chsize(handle, fPosition);
#else
ftruncate(handle, fPosition);
#endif
}
void hsUNIXStream::Flush()
{
if (!fRef)
return;
(void)::fflush(fRef);
}
//////////////////////////////////////////////////////////////////////////////////////
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(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(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");
}