You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1618 lines
36 KiB
1618 lines
36 KiB
/*==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/>. |
|
|
|
Additional permissions under GNU GPL version 3 section 7 |
|
|
|
If you modify this Program, or any covered work, by linking or |
|
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, |
|
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent |
|
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK |
|
(or a modified version of those libraries), |
|
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, |
|
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG |
|
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the |
|
licensors of this Program grant you additional |
|
permission to convey the resulting work. Corresponding Source for a |
|
non-source form of such a combination shall include the source code for |
|
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered |
|
work. |
|
|
|
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 |
|
|
|
#include "hsWindows.h" |
|
#if HS_BUILD_FOR_WIN32 |
|
#include <io.h> |
|
#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); |
|
WriteLE32(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); |
|
WriteLE32(len); |
|
if (len > 0) |
|
{ |
|
int i; |
|
for (i=0; i<len; i++) |
|
{ |
|
wchar_t buff = ~string[i]; |
|
WriteLE16((UInt16)buff); |
|
} |
|
WriteLE16((UInt16)L'\0'); |
|
} |
|
return 0; |
|
} |
|
|
|
char *hsStream::ReadSafeStringLong() |
|
{ |
|
char *name = nil; |
|
UInt32 numChars = ReadLE32(); |
|
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 = ReadLE32(); |
|
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)ReadLE16(); |
|
retVal[numChars] = (wchar_t)ReadLE16(); // 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() ); |
|
|
|
WriteLE16(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() ); |
|
|
|
WriteLE16(len | 0xf000); |
|
if (len > 0) |
|
{ |
|
int i; |
|
for (i=0; i<len; i++) |
|
{ |
|
wchar_t buff = ~string[i]; |
|
WriteLE16((UInt16)buff); |
|
} |
|
WriteLE16((UInt16)L'\0'); |
|
} |
|
return 0; |
|
} |
|
|
|
char *hsStream::ReadSafeString() |
|
{ |
|
char *name = nil; |
|
UInt16 numChars = ReadLE16(); |
|
|
|
#ifndef REMOVE_ME_SOON |
|
// Backward compat hack - remove in a week or so (from 6/30/03) |
|
hsBool oldFormat = !(numChars & 0xf000); |
|
if (oldFormat) |
|
ReadLE16(); |
|
#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 = ReadLE16(); |
|
|
|
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)ReadLE16(); |
|
retVal[numChars] = (wchar_t)ReadLE16(); // 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::ReadLE16() |
|
{ |
|
UInt16 value; |
|
this->Read(sizeof(UInt16), &value); |
|
value = hsToLE16(value); |
|
return value; |
|
} |
|
|
|
void hsStream::ReadLE16(int count, UInt16 values[]) |
|
{ |
|
this->Read(count * sizeof(UInt16), values); |
|
for (int i = 0; i < count; i++) |
|
values[i] = hsToLE16(values[i]); |
|
} |
|
|
|
UInt32 hsStream::ReadLE32() |
|
{ |
|
UInt32 value; |
|
Read4Bytes(&value); |
|
value = hsToLE32(value); |
|
return value; |
|
} |
|
|
|
void hsStream::ReadLE32(int count, UInt32 values[]) |
|
{ |
|
this->Read(count * sizeof(UInt32), values); |
|
for (int i = 0; i < count; i++) |
|
values[i] = hsToLE32(values[i]); |
|
} |
|
|
|
UInt32 hsStream::ReadBE32() |
|
{ |
|
UInt32 value; |
|
Read4Bytes(&value); |
|
value = hsToBE32(value); |
|
return value; |
|
} |
|
|
|
#if HS_CAN_USE_FLOAT |
|
double hsStream::ReadLEDouble() |
|
{ |
|
double value; |
|
Read8Bytes(&value); |
|
value = hsToLEDouble(value); |
|
return value; |
|
} |
|
|
|
void hsStream::ReadLEDouble(int count, double values[]) |
|
{ |
|
this->Read(count * sizeof(double), values); |
|
for (int i = 0; i < count; i++) |
|
values[i] = hsToLEDouble(values[i]); |
|
} |
|
|
|
|
|
float hsStream::ReadLEFloat() |
|
{ |
|
float value; |
|
Read4Bytes(&value); |
|
value = hsToLEFloat(value); |
|
return value; |
|
} |
|
|
|
void hsStream::ReadLEFloat(int count, float values[]) |
|
{ |
|
this->Read(count * sizeof(float), values); |
|
for (int i = 0; i < count; i++) |
|
values[i] = hsToLEFloat(values[i]); |
|
} |
|
|
|
float hsStream::ReadBEFloat() |
|
{ |
|
float value; |
|
this->Read(sizeof(float), &value); |
|
value = hsToBEFloat(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::WriteLE16(UInt16 value) |
|
{ |
|
value = hsToLE16(value); |
|
this->Write(sizeof(Int16), &value); |
|
} |
|
|
|
void hsStream::WriteLE16(int count, const UInt16 values[]) |
|
{ |
|
for (int i = 0; i < count; i++) |
|
this->WriteLE16(values[i]); |
|
} |
|
|
|
void hsStream::WriteLE32(UInt32 value) |
|
{ |
|
value = hsToLE32(value); |
|
this->Write(sizeof(Int32), &value); |
|
} |
|
|
|
void hsStream::WriteLE32(int count, const UInt32 values[]) |
|
{ |
|
for (int i = 0; i < count; i++) |
|
this->WriteLE32(values[i]); |
|
} |
|
|
|
void hsStream::WriteBE32(UInt32 value) |
|
{ |
|
value = hsToBE32(value); |
|
this->Write(sizeof(Int32), &value); |
|
} |
|
|
|
#if HS_CAN_USE_FLOAT |
|
void hsStream::WriteLEDouble(double value) |
|
{ |
|
value = hsToLEDouble(value); |
|
this->Write(sizeof(double), &value); |
|
} |
|
|
|
void hsStream::WriteLEDouble(int count, const double values[]) |
|
{ |
|
for (int i = 0; i < count; i++) |
|
this->WriteLEDouble(values[i]); |
|
} |
|
|
|
void hsStream::WriteLEFloat(float value) |
|
{ |
|
value = hsToLEFloat(value); |
|
this->Write(sizeof(float), &value); |
|
} |
|
|
|
void hsStream::WriteLEFloat(int count, const float values[]) |
|
{ |
|
for (int i = 0; i < count; i++) |
|
this->WriteLEFloat(values[i]); |
|
} |
|
|
|
void hsStream::WriteBEFloat(float value) |
|
{ |
|
value = hsToBEFloat(value); |
|
this->Write(sizeof(float), &value); |
|
} |
|
#endif |
|
|
|
void hsStream::WriteLEAtom(UInt32 tag, UInt32 size) |
|
{ |
|
this->WriteLE32(tag); |
|
this->WriteLE32(size); |
|
} |
|
|
|
UInt32 hsStream::ReadLEAtom(UInt32* sizePtr) |
|
{ |
|
UInt32 tag = this->ReadLE32(); |
|
UInt32 size = this->ReadLE32(); |
|
|
|
if (sizePtr) |
|
*sizePtr = size; |
|
return tag; |
|
} |
|
|
|
////////////////////////////////////////////////////////////////////////////////// |
|
|
|
#define kFileStream_Uninitialized ~0 |
|
|
|
hsBool hsFileStream::Open(const char *name, const char *mode) |
|
{ |
|
hsAssert(0, "hsFileStream::Open NotImplemented"); |
|
return false; |
|
} |
|
|
|
hsBool hsFileStream::Open(const wchar *name, const wchar *mode) |
|
{ |
|
hsAssert(0, "hsFileStream::Open NotImplemented"); |
|
return false; |
|
} |
|
|
|
hsBool hsFileStream::Close () |
|
{ |
|
hsAssert(0, "hsFileStream::Close NotImplemented"); |
|
return false; |
|
} |
|
|
|
UInt32 hsFileStream::GetFileRef() |
|
{ |
|
return fRef; |
|
} |
|
|
|
void hsFileStream::SetFileRef(UInt32 ref) |
|
{ |
|
hsAssert(ref != kFileStream_Uninitialized, "bad ref"); |
|
fRef = ref; |
|
} |
|
|
|
hsFileStream::hsFileStream() |
|
{ |
|
fRef = kFileStream_Uninitialized; |
|
} |
|
|
|
hsFileStream::~hsFileStream() |
|
{ |
|
} |
|
|
|
UInt32 hsFileStream::Read(UInt32 bytes, void* buffer) |
|
{ |
|
hsAssert(fRef != kFileStream_Uninitialized, "fRef uninitialized"); |
|
|
|
fBytesRead += bytes; |
|
fPosition += bytes; |
|
|
|
#if HS_BUILD_FOR_WIN32 |
|
UInt32 rBytes; |
|
ReadFile((HANDLE)fRef, buffer, bytes, (LPDWORD)&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_WIN32 |
|
UInt32 wBytes; |
|
WriteFile((HANDLE)fRef, buffer, bytes, (LPDWORD)&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_WIN32 |
|
UInt32 bytes; |
|
PeekNamedPipe((void*)fRef, nil, 0, nil, (LPDWORD)&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_WIN32 |
|
hsDebugMessage("hsFileStream::Skip unimplemented", 0); |
|
#endif |
|
} |
|
|
|
void hsFileStream::Rewind() |
|
{ |
|
fBytesRead = 0; |
|
fPosition = 0; |
|
|
|
#if 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<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"); |
|
}
|
|
|