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.
387 lines
9.7 KiB
387 lines
9.7 KiB
5 years ago
|
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||
|
//
|
||
|
// Purpose:
|
||
|
//
|
||
|
// $NoKeywords: $
|
||
|
//
|
||
|
// Serialization/unserialization buffer
|
||
|
//=============================================================================//
|
||
|
|
||
|
|
||
|
#include "tier2/utlstreambuffer.h"
|
||
|
#include "tier2/tier2.h"
|
||
|
#include "filesystem.h"
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// default stream chunk size
|
||
|
//-----------------------------------------------------------------------------
|
||
|
enum
|
||
|
{
|
||
|
DEFAULT_STREAM_CHUNK_SIZE = 16 * 1024
|
||
|
};
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Constructor, destructor
|
||
|
//-----------------------------------------------------------------------------
|
||
|
CUtlStreamBuffer::CUtlStreamBuffer( ) : BaseClass( DEFAULT_STREAM_CHUNK_SIZE, DEFAULT_STREAM_CHUNK_SIZE, 0 )
|
||
|
{
|
||
|
SetUtlBufferOverflowFuncs( &CUtlStreamBuffer::StreamGetOverflow, &CUtlStreamBuffer::StreamPutOverflow );
|
||
|
m_hFileHandle = FILESYSTEM_INVALID_HANDLE;
|
||
|
m_pFileName = NULL;
|
||
|
m_pPath = NULL;
|
||
|
}
|
||
|
|
||
|
CUtlStreamBuffer::CUtlStreamBuffer( const char *pFileName, const char *pPath, int nFlags, bool bDelayOpen ) :
|
||
|
BaseClass( DEFAULT_STREAM_CHUNK_SIZE, DEFAULT_STREAM_CHUNK_SIZE, nFlags )
|
||
|
{
|
||
|
SetUtlBufferOverflowFuncs( &CUtlStreamBuffer::StreamGetOverflow, &CUtlStreamBuffer::StreamPutOverflow );
|
||
|
|
||
|
if ( bDelayOpen )
|
||
|
{
|
||
|
m_pFileName = V_strdup( pFileName );
|
||
|
|
||
|
if ( pPath )
|
||
|
{
|
||
|
int nPathLen = Q_strlen( pPath );
|
||
|
m_pPath = new char[ nPathLen + 1 ];
|
||
|
Q_strcpy( m_pPath, pPath );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_pPath = new char[ 1 ];
|
||
|
m_pPath[0] = 0;
|
||
|
}
|
||
|
|
||
|
m_hFileHandle = FILESYSTEM_INVALID_HANDLE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_pFileName = NULL;
|
||
|
m_pPath = NULL;
|
||
|
m_hFileHandle = OpenFile( pFileName, pPath );
|
||
|
if ( m_hFileHandle == FILESYSTEM_INVALID_HANDLE )
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( IsReadOnly() )
|
||
|
{
|
||
|
// NOTE: MaxPut may not actually be this exact size for text files;
|
||
|
// it could be slightly less owing to the /r/n -> /n conversion
|
||
|
m_nMaxPut = g_pFullFileSystem->Size( m_hFileHandle );
|
||
|
|
||
|
// Read in the first bytes of the file
|
||
|
if ( Size() > 0 )
|
||
|
{
|
||
|
int nSizeToRead = min( Size(), m_nMaxPut );
|
||
|
ReadBytesFromFile( nSizeToRead, 0 );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void CUtlStreamBuffer::Close()
|
||
|
{
|
||
|
if ( !IsReadOnly() )
|
||
|
{
|
||
|
// Write the final bytes
|
||
|
int nBytesToWrite = TellPut() - m_nOffset;
|
||
|
if ( nBytesToWrite > 0 )
|
||
|
{
|
||
|
if ( ( m_hFileHandle == FILESYSTEM_INVALID_HANDLE ) && m_pFileName )
|
||
|
{
|
||
|
m_hFileHandle = OpenFile( m_pFileName, m_pPath );
|
||
|
if( m_hFileHandle == FILESYSTEM_INVALID_HANDLE )
|
||
|
{
|
||
|
Error( "CUtlStreamBuffer::Close() Unable to open file %s!\n", m_pFileName );
|
||
|
}
|
||
|
}
|
||
|
if ( m_hFileHandle != FILESYSTEM_INVALID_HANDLE )
|
||
|
{
|
||
|
if ( g_pFullFileSystem )
|
||
|
{
|
||
|
int nBytesWritten = g_pFullFileSystem->Write( Base(), nBytesToWrite, m_hFileHandle );
|
||
|
if( nBytesWritten != nBytesToWrite )
|
||
|
{
|
||
|
Error( "CUtlStreamBuffer::Close() Write %s failed %d != %d.\n", m_pFileName, nBytesWritten, nBytesToWrite );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( m_hFileHandle != FILESYSTEM_INVALID_HANDLE )
|
||
|
{
|
||
|
if ( g_pFullFileSystem )
|
||
|
g_pFullFileSystem->Close( m_hFileHandle );
|
||
|
m_hFileHandle = FILESYSTEM_INVALID_HANDLE;
|
||
|
}
|
||
|
|
||
|
if ( m_pFileName )
|
||
|
{
|
||
|
delete[] m_pFileName;
|
||
|
m_pFileName = NULL;
|
||
|
}
|
||
|
|
||
|
if ( m_pPath )
|
||
|
{
|
||
|
delete[] m_pPath;
|
||
|
m_pPath = NULL;
|
||
|
}
|
||
|
|
||
|
m_Error = 0;
|
||
|
}
|
||
|
|
||
|
CUtlStreamBuffer::~CUtlStreamBuffer()
|
||
|
{
|
||
|
Close();
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Open the file. normally done in constructor
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CUtlStreamBuffer::Open( const char *pFileName, const char *pPath, int nFlags )
|
||
|
{
|
||
|
if ( IsOpen() )
|
||
|
{
|
||
|
Close();
|
||
|
}
|
||
|
|
||
|
m_Get = 0;
|
||
|
m_Put = 0;
|
||
|
m_nTab = 0;
|
||
|
m_nOffset = 0;
|
||
|
m_Flags = nFlags;
|
||
|
m_hFileHandle = OpenFile( pFileName, pPath );
|
||
|
if ( m_hFileHandle == FILESYSTEM_INVALID_HANDLE )
|
||
|
return;
|
||
|
|
||
|
if ( IsReadOnly() )
|
||
|
{
|
||
|
// NOTE: MaxPut may not actually be this exact size for text files;
|
||
|
// it could be slightly less owing to the /r/n -> /n conversion
|
||
|
m_nMaxPut = g_pFullFileSystem->Size( m_hFileHandle );
|
||
|
|
||
|
// Read in the first bytes of the file
|
||
|
if ( Size() > 0 )
|
||
|
{
|
||
|
int nSizeToRead = min( Size(), m_nMaxPut );
|
||
|
ReadBytesFromFile( nSizeToRead, 0 );
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ( m_Memory.NumAllocated() != 0 )
|
||
|
{
|
||
|
m_nMaxPut = -1;
|
||
|
AddNullTermination();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_nMaxPut = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Is the file open?
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CUtlStreamBuffer::IsOpen() const
|
||
|
{
|
||
|
if ( m_hFileHandle != FILESYSTEM_INVALID_HANDLE )
|
||
|
return true;
|
||
|
|
||
|
// Delayed open case
|
||
|
return ( m_pFileName != 0 );
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Grow allocation size to fit requested size
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CUtlStreamBuffer::GrowAllocatedSize( int nSize )
|
||
|
{
|
||
|
int nNewSize = Size();
|
||
|
if ( nNewSize < nSize + 1 )
|
||
|
{
|
||
|
while ( nNewSize < nSize + 1 )
|
||
|
{
|
||
|
nNewSize += DEFAULT_STREAM_CHUNK_SIZE;
|
||
|
}
|
||
|
m_Memory.Grow( nNewSize - Size() );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Load up more of the stream when we overflow
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CUtlStreamBuffer::StreamPutOverflow( int nSize )
|
||
|
{
|
||
|
if ( !IsValid() || IsReadOnly() )
|
||
|
return false;
|
||
|
|
||
|
// Make sure the allocated size is at least as big as the requested size
|
||
|
if ( nSize > 0 )
|
||
|
{
|
||
|
GrowAllocatedSize( nSize + 2 );
|
||
|
}
|
||
|
|
||
|
// Don't write the last byte (for NULL termination logic to work)
|
||
|
int nBytesToWrite = TellPut() - m_nOffset - 1;
|
||
|
if ( ( nBytesToWrite > 0 ) || ( nSize < 0 ) )
|
||
|
{
|
||
|
if ( m_hFileHandle == FILESYSTEM_INVALID_HANDLE )
|
||
|
{
|
||
|
m_hFileHandle = OpenFile( m_pFileName, m_pPath );
|
||
|
if( m_hFileHandle == FILESYSTEM_INVALID_HANDLE )
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( nBytesToWrite > 0 )
|
||
|
{
|
||
|
int nBytesWritten = g_pFullFileSystem->Write( Base(), nBytesToWrite, m_hFileHandle );
|
||
|
if ( nBytesWritten != nBytesToWrite )
|
||
|
{
|
||
|
m_Error |= FILE_WRITE_ERROR;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// This is necessary to deal with auto-NULL terminiation
|
||
|
m_Memory[0] = *(unsigned char*)PeekPut( -1 );
|
||
|
if ( TellPut() < Size() )
|
||
|
{
|
||
|
m_Memory[1] = *(unsigned char*)PeekPut( );
|
||
|
}
|
||
|
m_nOffset = TellPut() - 1;
|
||
|
}
|
||
|
|
||
|
if ( nSize < 0 )
|
||
|
{
|
||
|
m_nOffset = -nSize-1;
|
||
|
g_pFullFileSystem->Seek( m_hFileHandle, m_nOffset, FILESYSTEM_SEEK_HEAD );
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Reads bytes from the file; fixes up maxput if necessary and null terminates
|
||
|
//-----------------------------------------------------------------------------
|
||
|
int CUtlStreamBuffer::ReadBytesFromFile( int nBytesToRead, int nReadOffset )
|
||
|
{
|
||
|
if ( m_hFileHandle == FILESYSTEM_INVALID_HANDLE )
|
||
|
{
|
||
|
if ( !m_pFileName )
|
||
|
{
|
||
|
Warning( "File has not been opened!\n" );
|
||
|
Assert(0);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
m_hFileHandle = OpenFile( m_pFileName, m_pPath );
|
||
|
if ( m_hFileHandle == FILESYSTEM_INVALID_HANDLE )
|
||
|
{
|
||
|
Error( "Unable to read file %s!\n", m_pFileName );
|
||
|
return 0;
|
||
|
}
|
||
|
if ( m_nOffset != 0 )
|
||
|
{
|
||
|
g_pFullFileSystem->Seek( m_hFileHandle, m_nOffset, FILESYSTEM_SEEK_HEAD );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
char *pReadPoint = (char*)Base() + nReadOffset;
|
||
|
int nBytesRead = g_pFullFileSystem->Read( pReadPoint, nBytesToRead, m_hFileHandle );
|
||
|
if ( nBytesRead != nBytesToRead )
|
||
|
{
|
||
|
// Since max put is a guess at the start,
|
||
|
// we need to shrink it based on the actual # read
|
||
|
if ( m_nMaxPut > TellGet() + nReadOffset + nBytesRead )
|
||
|
{
|
||
|
m_nMaxPut = TellGet() + nReadOffset + nBytesRead;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( nReadOffset + nBytesRead < Size() )
|
||
|
{
|
||
|
// This is necessary to deal with auto-NULL terminiation
|
||
|
pReadPoint[nBytesRead] = 0;
|
||
|
}
|
||
|
|
||
|
return nBytesRead;
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Load up more of the stream when we overflow
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CUtlStreamBuffer::StreamGetOverflow( int nSize )
|
||
|
{
|
||
|
if ( !IsValid() || !IsReadOnly() )
|
||
|
return false;
|
||
|
|
||
|
// Shift the unread bytes down
|
||
|
// NOTE: Can't use the partial overlap path if we're seeking. We'll
|
||
|
// get negative sizes passed in if we're seeking.
|
||
|
int nUnreadBytes;
|
||
|
bool bHasPartialOverlap = ( nSize >= 0 ) && ( TellGet() >= m_nOffset ) && ( TellGet() <= m_nOffset + Size() );
|
||
|
if ( bHasPartialOverlap )
|
||
|
{
|
||
|
nUnreadBytes = Size() - ( TellGet() - m_nOffset );
|
||
|
if ( ( TellGet() != m_nOffset ) && ( nUnreadBytes > 0 ) )
|
||
|
{
|
||
|
memmove( Base(), (const char*)Base() + TellGet() - m_nOffset, nUnreadBytes );
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_nOffset = TellGet();
|
||
|
g_pFullFileSystem->Seek( m_hFileHandle, m_nOffset, FILESYSTEM_SEEK_HEAD );
|
||
|
nUnreadBytes = 0;
|
||
|
}
|
||
|
|
||
|
// Make sure the allocated size is at least as big as the requested size
|
||
|
if ( nSize > 0 )
|
||
|
{
|
||
|
GrowAllocatedSize( nSize );
|
||
|
}
|
||
|
|
||
|
int nBytesToRead = Size() - nUnreadBytes;
|
||
|
int nBytesRead = ReadBytesFromFile( nBytesToRead, nUnreadBytes );
|
||
|
if ( nBytesRead == 0 )
|
||
|
return false;
|
||
|
|
||
|
m_nOffset = TellGet();
|
||
|
return ( nBytesRead + nUnreadBytes >= nSize );
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// open file unless already failed to open
|
||
|
//-----------------------------------------------------------------------------
|
||
|
FileHandle_t CUtlStreamBuffer::OpenFile( const char *pFileName, const char *pPath )
|
||
|
{
|
||
|
if ( m_Error & FILE_OPEN_ERROR )
|
||
|
return FILESYSTEM_INVALID_HANDLE;
|
||
|
|
||
|
char openflags[ 3 ] = "xx";
|
||
|
openflags[ 0 ] = IsReadOnly() ? 'r' : 'w';
|
||
|
openflags[ 1 ] = IsText() && !ContainsCRLF() ? 't' : 'b';
|
||
|
|
||
|
FileHandle_t fh = g_pFullFileSystem->Open( pFileName, openflags, pPath );
|
||
|
if( fh == FILESYSTEM_INVALID_HANDLE )
|
||
|
{
|
||
|
m_Error |= FILE_OPEN_ERROR;
|
||
|
}
|
||
|
|
||
|
return fh;
|
||
|
}
|