Counter Strike : Global Offensive Source Code
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.
 
 
 
 
 
 

966 lines
22 KiB

//===== Copyright © Valve Corporation, All rights reserved. ======//
//
// Purpose: Resource Stream is conceptually memory file where you write the data
// that you want to live both on disk and in memory. The constraint then is
// that the data must be movable in memory, since you don't know in advance,
// when you're creating the data, what will be its start address once it gets
// loaded. To accomplish this, we use CResourcePointer and CResourceArray
// in places of pointers and dynamic arrays. They store offsets rather than
// absolute addresses of the referenced objects, thus they are moveable.
// On Intel, the available addressing modes make the access to CResourcePointer
// as fast as pointer dereferencing in most cases. On architectures with
// rigid addressing, adding the base address is still virtually free.
//
// $NoKeywords: $
//===========================================================================//
#include "resourcefile/schema.h"
#ifndef RESOURCESTREAM_H
#define RESOURCESTREAM_H
#ifdef COMPILER_MSVC
#pragma once
#endif
#include "tier0/platform.h"
#include "tier0/basetypes.h"
#include "tier0/dbg.h"
#include "tier1/strtools.h"
class CResourceStream;
inline byte *ResolveOffset( const int32 *pOffset )
{
int offset = *pOffset;
return offset ? ( ( byte* )pOffset ) + offset : NULL;
}
inline byte *ResolveOffsetFast( const int32 *pOffset )
{
int offset = *pOffset;
AssertDbg( offset != 0 );
return ( ( byte* )pOffset ) + offset;
}
template <typename T>
class CLockedResource
{
private:
T *m_pData; // data; may be const
uint m_nCount; // number of allocated data elements
//uint m_nStride; // normally sizeof(T), but may be not
//uint m_nClassCRC;
public:
CLockedResource(): m_pData( NULL ), m_nCount( 0 ) {}
CLockedResource( T *pData, uint nCount ): m_pData( pData ), m_nCount( nCount ) {}
// emulates pointer arithmetics
CLockedResource<T> operator + ( int nOffset ) { Assert( m_nCount <= (uint)nOffset); return CLockedResource<T>( m_pData + nOffset, m_nCount - (uint)nOffset ); }
operator const T* ()const { return m_pData; }
operator T* () { return m_pData; }
T* operator ->() { return m_pData; }
const T* operator ->() const { return m_pData; }
uint Count()const {return m_nCount;}
};
template <typename T>
class CUnlockedResource
{
private:
CResourceStream *m_pStream; // data; may be const
uint32 m_nOffset;
uint32 m_nCount; // number of allocated data elements
//uint m_nStride; // normally sizeof(T), but may be not
//uint m_nClassCRC;
public:
CUnlockedResource( ): m_pStream( NULL ), m_nOffset( 0 ), m_nCount( 0 ) {}
CUnlockedResource( CResourceStream *pStream, T *pData, uint nCount );
bool IsValid( )const { return m_pStream != NULL; }
void Reset( ) { m_pStream = NULL; }
// emulates pointer arithmetics
CUnlockedResource<T> operator + ( int nOffset )
{
Assert( m_nCount <= ( uint ) nOffset );
return CUnlockedResource<T>( m_pStream, GetPtr() + nOffset, m_nCount - ( uint ) nOffset );
}
operator const T* ( )const { return GetPtr(); }
operator T* ( ) { return GetPtr(); }
T* operator ->( ) { return GetPtr(); }
const T* operator ->( ) const { return GetPtr(); }
uint Count( )const { return m_nCount; }
T* GetPtr( );
const T* GetPtr( )const;
};
// AT RUN-TIME ONLY the offset converts automatically into pointers to the appropritate type
// at tool-time, you should use LinkSource_t and LinkTarget_t
class CResourcePointerBase
{
protected:
int32 m_nOffset;
public:
CResourcePointerBase() : m_nOffset( 0 ) {}
bool operator == ( int zero )const
{
AssertDbg( zero == 0 );
return m_nOffset == zero;
}
bool IsNull()const
{
return m_nOffset == 0;
}
int32 NotNull()const
{
return m_nOffset;
}
int32 GetOffset() const
{
return m_nOffset;
}
const byte* GetUncheckedRawPtr() const
{
// assumes non-null; returns garbage if this is a null pointer
return ResolveOffsetFast( &m_nOffset );
}
byte* GetUncheckedRawPtr()
{
// assumes non-null; returns garbage if this is a null pointer
return ResolveOffsetFast( &m_nOffset );
}
const byte* GetRawPtr() const
{
return ResolveOffset( &m_nOffset );
}
byte* GetRawPtr()
{
return ResolveOffset( &m_nOffset );
}
void SetRawPtr( const void* p )
{
if ( p == NULL )
{
m_nOffset = 0;
}
else
{
intp nOffset = ( intp )p - ( intp )&m_nOffset;
m_nOffset = ( int32 )nOffset;
AssertDbg( m_nOffset == nOffset );
}
}
void SetNull()
{
m_nOffset = 0;
}
};
template <typename T>
class CResourcePointer: public CResourcePointerBase
{
public:
FORCEINLINE const T* GetUncheckedPtr() const
{
// assumes non-null; returns garbage if this is a null pointer
AssertDbg( m_nOffset != 0 );
byte *ptr = ResolveOffsetFast( &m_nOffset );
return ( const T* )ptr;
}
FORCEINLINE const T* GetPtr() const
{
byte *ptr = ResolveOffset( &m_nOffset );
return ( const T* )ptr;
}
FORCEINLINE operator const T*() const
{
return GetPtr();
}
FORCEINLINE const T* operator->() const
{
return GetPtr();
}
void operator = ( const T* pT )
{
SetRawPtr( pT );
}
void SetPtr( const T* pT )
{
SetRawPtr( pT );
}
// FIXME: Should these be in a 'CResourceWritablePointer' subclass?
// There are plenty of cases where we know that a CResourcePointer should be read-only
public:
FORCEINLINE T* GetUncheckedPtr()
{
// assumes non-null; returns garbage if this is a null pointer
Assert( m_nOffset != 0 );
byte *ptr = ResolveOffsetFast( &m_nOffset );
return ( T* )ptr;
}
FORCEINLINE T* GetPtr()
{
byte *ptr = ResolveOffset( &m_nOffset );
return ( T* )ptr;
}
FORCEINLINE operator T*()
{
return GetPtr();
}
FORCEINLINE T* operator->()
{
return GetPtr();
}
public:
void Unsafe_OutOfBoundsAllocate()
{
SetPtr( new T() );
}
void Unsafe_OutOfBoundsFree()
{
delete GetPtr();
}
void SwapBytes()
{
m_nOffset = DWordSwapC( m_nOffset );
}
};
// never construct this - only the Resource stream may construct or load data that contains ResourceArray or ResourcePointer; use LockedResource or naked pointer or CUtlBuffer or something like that instead
class CResourceArrayBase
{
public:
CResourceArrayBase()
{
m_nOffset = 0;
m_nCount = 0;
}
bool operator == ( int zero )const
{
AssertDbg( zero == 0 );
return m_nOffset == zero;
}
bool IsNull()const
{
return m_nOffset == 0;
}
int32 NotNull()const
{
return m_nOffset;
}
const byte* GetRawPtr() const
{
// validate
return ResolveOffset( &m_nOffset );
}
byte* GetRawPtr()
{
// validate
return ResolveOffset( &m_nOffset );
}
int Count() const
{
return m_nCount;
}
void WriteDirect( int nCount, void *pData )
{
if ( pData == NULL )
{
AssertDbg( nCount == 0 );
m_nOffset = 0;
m_nCount = 0;
}
else
{
m_nOffset = ((intp)pData) - (intp)&m_nOffset;
m_nCount = nCount;
}
}
void SwapMemberBytes()
{
m_nCount = DWordSwapC( m_nCount );
m_nOffset = DWordSwapC( m_nOffset );
}
private:
CResourceArrayBase(const CResourceArrayBase&rThat ){} // private: we don't want to recompute offsets every time we copy a structure
protected:
int32 m_nOffset;
uint32 m_nCount;
};
template < typename T >
class CResourceArray: public CResourceArrayBase
{
public:
CResourceArray(): CResourceArrayBase() {}
private:
CResourceArray( const CResourceArray<T>&rThat ){} // private: we don't want to recompute offsets every time we copy a structure
public:
const T& operator []( int nIndex ) const
{
AssertDbg( (uint)nIndex < m_nCount );
return this->GetPtr()[nIndex];
}
T& operator []( int nIndex )
{
AssertDbg( (uint)nIndex < m_nCount );
return this->GetPtr()[nIndex];
}
const T& Element( int nIndex ) const
{
AssertDbg( (uint)nIndex < m_nCount );
return this->GetPtr()[nIndex];
}
T& Element( int nIndex )
{
AssertDbg( (uint)nIndex < m_nCount );
return this->GetPtr()[nIndex];
}
CResourceArray<T>& operator = ( const CLockedResource<T> & lockedResource )
{
m_nOffset = ( lockedResource.Count() ) ? ( ((intp)(const T*)lockedResource) - (intp)&m_nOffset ) : 0;
m_nCount = lockedResource.Count();
return *this;
}
CResourceArray<T>& operator = ( const CResourceArray<T> & that )
{
m_nOffset = ( that.Count() ) ? ( ((intp)(const T*)that.GetPtr()) - (intp)&m_nOffset ) : 0;
m_nCount = that.Count();
return *this;
}
const T* GetPtr() const
{
return ( const T* )GetRawPtr();
}
T* GetPtr()
{
return ( T* )GetRawPtr();
}
const T* Base() const
{
return ( const T* )GetRawPtr();
}
T* Base()
{
return ( T* )GetRawPtr();
}
bool IsEmpty() const
{
return m_nCount == 0;
}
operator CLockedResource<T> () {return CLockedResource<T>( GetPtr(), Count() ) ; }
// Temporary functions
void Unsafe_OutOfBoundsAllocate( int nCount )
{
const T* pAlloc = new T[nCount];
m_nOffset = ((intp)(const T*)pAlloc) - (intp)&m_nOffset;
m_nCount = nCount;
}
void Unsafe_OutOfBoundsPurgeAndFreeRPs()
{
for ( uint32 i = 0; i < m_nCount; ++i )
{
Element(i).Unsafe_OutOfBoundsFree();
}
delete[] GetPtr();
}
// Remove all the NULL pointers from this resource array and shorten it (without changing any allocations)
// (assumes that the elements of the array can be equality-tested with NULL)
inline void CoalescePointerArrayInPlace()
{
int nValidElements = 0;
int nArrayLen = Count();
T* pBase = GetPtr();
for ( int i = 0; i < nArrayLen; ++i )
{
if ( pBase[i] == 0 )
continue;
if ( nValidElements != i )
{
pBase[nValidElements].SetRawPtr( pBase[i].GetRawPtr() );
}
nValidElements++;
}
m_nCount = nValidElements;
}
// Enable support for range-based loops in C++ 11
inline T* begin()
{
return GetPtr();
}
inline const T* begin() const
{
return GetPtr( );
}
inline T* end()
{
return GetPtr() + m_nCount;
}
inline const T* end() const
{
return GetPtr( ) + m_nCount;
}
};
//////////////////////////////////////////////////////////////////////////
// this class may be useful at runtime to use fast serialize interface (without data linking)
//
class CResourceStream
{
public:
// Constructor, destructor
CResourceStream( );
virtual ~CResourceStream( ) {} // make sure to implement proper cleanup in derived classes
// Methods used to allocate space in the stream
// just use Allocate<uint8>(100) to simply allocate 100 bytes of crap
void *AllocateBytes( uint nCount );
template <typename T> CLockedResource<T> Allocate( uint count = 1 );
template <typename T> CUnlockedResource<T> AllocateUnaligned( uint count = 1 );
// Methods that write data into the stream
template <typename T> CLockedResource<T> Write( const T &x );
template <typename T> CUnlockedResource<T> WriteUnaligned( const T &x );
CLockedResource<float> WriteFloat( float x );
CLockedResource<uint64> WriteU64( uint64 x );
CLockedResource<uint32> WriteU32( uint32 x );
CLockedResource<uint16> WriteU16( uint16 x );
CLockedResource<uint8> WriteByte( byte x );
CLockedResource<char> WriteString( const char *pString );
CLockedResource<char> WriteStringMaxLen( const char *pString, int nMaxLen ); // will never read beyond pString[nMaxLen-1] and will force-null-terminate if necessary
// Methods to force alignment of the next data written into the stream
void Align( uint nAlignment, int nOffset = 0 );
void AlignPointer();
// How much data have we written into the stream?
uint32 Tell() const;
uint32 Tell( const void * pPast )const;
const void *TellPtr() const;
void Rollback( uint32 nPreviousTell );
void Rollback( const void *pPreviousTell );
void* Compile( );
template <class Memory>
void* CompileToMemory( Memory &memory );
uint GetTotalSize() const;
void Barrier() {} // no pointers crossing barriers allowed! all pointers are invalidated
void PrintStats();
void ClearStats();
// This is the max alignment ever used within the stream
uint GetStreamAlignment() const;
void* GetDataPtr( uint Offset = 0 );
template <typename T> T* GetDataPtrTypeAligned( uint Offset );
void Clear();
protected:
void EnsureAvailable( uint nAddCapacity )
{
Assert( nAddCapacity < 0x40000000 ); // we don't support >1Gb of data yet
uint nNewCommit = m_nUsed + nAddCapacity;
if ( nNewCommit > m_nCommitted )
{
Commit( nNewCommit ); // Commit is only called when necessary, as it may be expensive
}
}
// the only logic entry the derived class needs to reimplement, besides constructor+destructor pair.
// depending on how expensive it is to call this function, it may commit (allocate) large chunks of extra memory, that's totally ok
virtual void Commit( uint nNewCommit ) = 0;
template <typename T> friend class CUnlockedResource;
protected:
uint8 *m_pData; // the reserved virtual address space address; or just the start of allocated block of memory if virtual memory is not being used
uint m_nCommitted; // how much memory was already committed (or allocated, in case no virtual memory is being used)
uint m_nUsed; // this is the amount of currently used/allocated data, in bytes; may be unaligned
uint m_nAlignBits; // the ( max alignment - 1 ) of the current block of data
uint m_nMaxAlignment;
};
class CResourceStreamVM: public CResourceStream
{
public:
// Constructor, destructor
CResourceStreamVM( uint nReserveSize = 16 * 1024 * 1024 );
virtual ~CResourceStreamVM( ) OVERRIDE;
void CloneStream( CResourceStreamVM& copyFromStream );
protected:
virtual void Commit( uint nNewCommit ) OVERRIDE;
void ReserveVirtualMemory( uint nAddressSize );
void ReleaseVirtualMemory( );
enum
{
COMMIT_STEP = 64 * 1024
};
protected:
uint m_nReserved; // the reserved virtual address space for the block of data
};
// use this class to create resources efficiently in runtime using your own allocator
// supply a fixed buffer; it belongs to the caller
class CResourceStreamFixed: public CResourceStream
{
private:
bool m_bOwnMemory;
public:
CResourceStreamFixed( uint nPreallocateDataSize );// NOTE: the preallocated data is Zeroed in this constructor
CResourceStreamFixed( void *pPreallocatedData, uint nPreallocatedDataSize ); // NOTE: the preallocated data is Zeroed in this constructor
virtual ~CResourceStreamFixed() OVERRIDE;
int GetSlack(); // remaining bytes
virtual void Commit( uint nNewCommit );
};
class CResourceStreamGrowable: public CResourceStream
{
public:
CResourceStreamGrowable( uint nReserveDataSize );
~CResourceStreamGrowable( );
virtual void Commit( uint nNewCommit );
uint8 *Detach( )
{
uint8 *pData = m_pData;
m_pData = 0;
m_nCommitted = 0;
m_nUsed = 0;
m_pData = NULL;
return pData;
}
};
class CLockedResourceAutoAggregator
{
protected:
CResourceStreamVM *m_pStream;
const void * m_pTellStart;
uint m_nTellStart;
public:
CLockedResourceAutoAggregator( CResourceStreamVM *pStream, const void * pTellStart )
{
m_pStream = pStream;
m_pTellStart = pTellStart;
m_nTellStart = pStream->Tell( pTellStart );
}
CLockedResource<uint8> GetAggregate()
{
return CLockedResource<uint8>( ( uint8* )m_pTellStart, m_pStream->Tell() - m_nTellStart );
}
};
//-----------------------------------------------------------------------------
// Specialization for strings
//-----------------------------------------------------------------------------
class CResourceString: public CResourcePointer<char>
{
public:
CResourceString& operator = ( const CLockedResource<char> & lockedCharResource )
{
SetPtr( (const char*)lockedCharResource );
return *this;
}
CResourceString& operator = ( const CResourcePointerBase & lockedCharResource )
{
SetPtr( ( const char* )lockedCharResource.GetRawPtr() );
return *this;
}
// empty strings can be serialized as a null pointer (instead of a pointer to '\0')
const char* GetPtr() const
{
if ( GetRawPtr() == NULL )
{
return "";
}
else
{
return ( const char* )GetRawPtr();
}
}
char* GetPtr()
{
if ( GetRawPtr() == NULL )
{
return ( char* )"";
}
else
{
return ( char* )GetRawPtr();
}
}
operator const char* ()const
{
if ( GetRawPtr() == NULL )
{
return "";
}
else
{
return ( char* )GetRawPtr();
}
}
void Unsafe_OutOfBoundsCopy( const char* pStr )
{
int nLen = V_strlen(pStr);
char* pAlloc = new char[nLen+1];
V_strcpy( pAlloc, pStr );
SetPtr( pAlloc );
}
bool IsEmpty() const
{
return IsNull() || !*GetUncheckedRawPtr();
}
private:
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// Disable comparison operators for ResourceStrings, otherwise they'll
// be implicitly converted to char* (we don't want to actually implement
// these because it's ambiguous whether it's case-sensitive or not.
bool operator==( const CResourceString &rhs ) const;
bool operator!=( const CResourceString &rhs ) const;
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
};
//-----------------------------------------------------------------------------
// Inline methods
//-----------------------------------------------------------------------------
template <typename T>
inline CLockedResource<T> CResourceStream::Allocate( uint nCount )
{
// TODO: insert reflection code here
uint nAlignment = VALIGNOF( T );
Align( nAlignment );
T *ptr = AllocateUnaligned< T >( nCount );
// Construct
for ( uint i = 0; i < nCount; i++ )
{
Construct< T >( &ptr[ i ] );
}
return CLockedResource<T>( ptr, nCount );
}
template <typename T>
inline CUnlockedResource<T> CResourceStream::AllocateUnaligned( uint nCount )
{
// Allocate
return CUnlockedResource< T >( this, ( T* )AllocateBytes( nCount * sizeof( T ) ), nCount );
}
inline void CResourceStream::AlignPointer()
{
Align( sizeof( intp ) );
}
template <typename T>
inline CUnlockedResource<T> CResourceStream::WriteUnaligned( const T &x )
{
CUnlockedResource<T> pMemory = AllocateUnaligned<T>( );
V_memcpy( pMemory, &x, sizeof( T ) );
return pMemory;
}
template <typename T>
inline CLockedResource<T> CResourceStream::Write( const T &x )
{
CLockedResource<T> memory = Allocate<T>();
*memory = x;
return memory;
}
inline CLockedResource<float> CResourceStream::WriteFloat( float x )
{
return Write( x );
}
inline CLockedResource<uint64> CResourceStream::WriteU64( uint64 x )
{
return Write( x );
}
inline CLockedResource<uint32> CResourceStream::WriteU32( uint32 x )
{
return Write( x );
}
inline CLockedResource<uint16> CResourceStream::WriteU16( uint16 x )
{
return Write( x );
}
inline CLockedResource<uint8> CResourceStream::WriteByte( byte x )
{
return Write( x );
}
inline CLockedResource<char> CResourceStream::WriteString( const char *pString )
{
int nLength = pString ? V_strlen( pString ) : 0;
if ( nLength == 0 )
{
return CLockedResource<char>( NULL, 0 );
}
else
{
CLockedResource<char> memory = Allocate<char>(nLength+1);
memcpy( (char*)memory, pString, nLength+1 );
return memory;
}
}
inline CLockedResource<char> CResourceStream::WriteStringMaxLen( const char *pString, int nMaxLen )
{
int nStrLen = 0;
while ( pString && nStrLen < nMaxLen && pString[nStrLen] != '\0' )
{
nStrLen++;
}
if ( nStrLen == 0 )
{
return CLockedResource<char>( NULL, 0 );
}
else
{
CLockedResource<char> memory = Allocate<char>( nStrLen + 1 ); // +1 for null term
memcpy( (char*)memory, pString, nStrLen );
((char*)memory)[nStrLen] = '\0';
return memory;
}
}
inline uint32 CResourceStream::Tell() const
{
return m_nUsed;
}
inline void CResourceStream::Rollback( uint32 nPreviousTell )
{
Assert( nPreviousTell <= m_nUsed );
m_nUsed = nPreviousTell;
}
inline void CResourceStream::Rollback( const void *pPreviousTell )
{
Assert( pPreviousTell > m_pData && pPreviousTell <= m_pData + m_nUsed );
m_nUsed = ( ( uint8* ) pPreviousTell ) - m_pData;
}
inline uint32 CResourceStream::Tell( const void * pPast )const
{
// we do not reallocate the reserved memory , so the pointers do not invalidate and m_pData stays the same, and all absolute addresses stay the same in memory
uint nTell = uintp( pPast ) - uintp( m_pData );
AssertDbg( nTell <= m_nUsed );
return nTell;
}
inline const void *CResourceStream::TellPtr() const
{
return m_pData + m_nUsed;
}
inline void* CResourceStream::Compile( )
{
return m_pData;
}
template< class Memory >
inline void* CResourceStream::CompileToMemory( Memory &memory )
{
memory.Init( 0, GetTotalSize() );
V_memcpy( memory.Base(), Compile(), GetTotalSize() );
return memory.Base();
}
inline uint CResourceStream::GetTotalSize() const
{
return m_nUsed;
}
inline uint CResourceStream::GetStreamAlignment() const
{
return m_nMaxAlignment;
}
inline void* CResourceStream::GetDataPtr( uint nOffset )
{
if ( nOffset > m_nUsed )
{
return NULL;
}
else
{
return m_pData + nOffset;
}
}
inline void CResourceStream::Clear()
{
V_memset( m_pData, 0, m_nCommitted );
m_nUsed = 0;
}
template <typename T>
inline T* CResourceStream::GetDataPtrTypeAligned( uint Offset )
{
uint nAlignment = VALIGNOF( T );
Offset += ( ( 0 - Offset ) & ( nAlignment - 1 ) );
return ( T* ) GetDataPtr( Offset );
}
template <typename T>
inline const T* OffsetPointer( const T *p, intp nOffsetBytes )
{
return p ? ( const T* )( intp( p ) + nOffsetBytes ) : NULL;
}
template <typename T>
inline T* ConstCastOffsetPointer( const T *p, intp nOffsetBytes )
{
return p ? ( T* ) ( intp( p ) + nOffsetBytes ) : NULL;
}
template < typename T >
inline CLockedResource< T > CloneArray( CResourceStream *pStream, const T *pArray, uint nCount )
{
if ( nCount > 0 && pArray )
{
CLockedResource< T > pOut = pStream->Allocate< T >( nCount );
V_memcpy( pOut, pArray, nCount * sizeof( T ) );
return pOut;
}
else
{
return CLockedResource< T >( );
}
}
inline int CResourceStreamFixed::GetSlack( )
{
return m_nCommitted - m_nUsed;
}
template < typename T >
CUnlockedResource< T >::CUnlockedResource( CResourceStream *pStream, T *pData, uint nCount ):
m_pStream( pStream ),
m_nOffset( ( ( uint8* ) pData ) - pStream->m_pData ),
m_nCount( nCount )
{
AssertDbg( ( ( uint8* ) pData ) >= pStream->m_pData && ( ( uint8* ) ( pData + nCount ) <= pStream->m_pData + pStream->m_nUsed ) );
AssertDbg( m_nOffset <= pStream->m_nUsed && m_nOffset + nCount * sizeof( T ) <= pStream->m_nUsed );
}
template < typename T >
inline T* CUnlockedResource< T >::GetPtr( )
{
return ( T* )m_pStream->GetDataPtr( m_nOffset );
}
template < typename T >
inline const T* CUnlockedResource< T >::GetPtr( )const
{
return ( const T * )m_pStream->GetDataPtr( m_nOffset );
}
#endif // RESOURCESTREAM_H