/*==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"); }