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.
470 lines
12 KiB
470 lines
12 KiB
//========= Copyright c 1996-2008, Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose:
|
|
//
|
|
// $NoKeywords: $
|
|
//=============================================================================//
|
|
|
|
#include "datalinker.h"
|
|
#include "dbg.h"
|
|
#include "memalloc.h"
|
|
|
|
namespace DataLinker
|
|
{
|
|
const char g_cFillChar = '\xDE';
|
|
|
|
Stream::Stream()
|
|
{
|
|
m_data.reserve( 16 );
|
|
m_lastTarget = 1; // target 0 and 1 are special targets
|
|
m_alignBits = 3; // alignment 4 is default
|
|
m_size = 0;
|
|
m_stackBlocks.reserve( 4 ); // we'll probably never nest deeper than this
|
|
}
|
|
|
|
Chunk::Chunk( uint reserve, uint offset )
|
|
{
|
|
m_offset = offset;
|
|
m_capacity = reserve;
|
|
m_size = 0;
|
|
m_recentSize = 0;
|
|
m_data = ( byte* )MemAlloc_AllocAligned( reserve, 16 );
|
|
memset( m_data, g_cFillChar, reserve );
|
|
}
|
|
|
|
void Chunk::Free()
|
|
{
|
|
MemAlloc_FreeAligned( m_data );
|
|
}
|
|
|
|
Stream::~Stream()
|
|
{
|
|
for ( std::vector<Chunk>::iterator it = m_data.begin(); it != m_data.end(); ++it )
|
|
it->Free();
|
|
}
|
|
|
|
void* Stream::WriteBytes( uint numBytes )
|
|
{
|
|
EnsureAvailable( numBytes );
|
|
Chunk &chunk = m_data.back();
|
|
byte *p = chunk.m_data + chunk.m_size;
|
|
memset( p, 0, numBytes );
|
|
chunk.m_recentSize = m_size;
|
|
chunk.m_size += numBytes;
|
|
m_size += numBytes;
|
|
return p;
|
|
}
|
|
|
|
|
|
void Stream::EnsureAvailable( uint addCapacity )
|
|
{
|
|
Assert( addCapacity < 0x10000000 ); // we don't support >1Gb of data yet
|
|
if ( m_data.empty() )
|
|
{
|
|
// first allocation
|
|
m_data.push_back( Chunk( MAX( addCapacity*4u, 4096u ), 0 ) );
|
|
}
|
|
else if ( m_data.back().GetAvailable() < addCapacity )
|
|
{
|
|
m_data.push_back( Chunk( MAX( addCapacity*4u, m_data.back().m_capacity*2u ), m_size ) );
|
|
}
|
|
}
|
|
|
|
int Stream::GetOffsetTo( const void *ptr )const
|
|
{
|
|
for ( std::vector<Chunk>::const_reverse_iterator it = m_data.rbegin(); it != m_data.rend(); ++it )
|
|
{
|
|
uint offsetOfPtr = ( uint )( ( ( const byte* )ptr ) - it->m_data );
|
|
if ( offsetOfPtr <= it->m_size )
|
|
return offsetOfPtr + it->m_offset;
|
|
}
|
|
return -1; // invalid offset
|
|
}
|
|
|
|
|
|
bool Stream::Compile( void *pBuffer )
|
|
{
|
|
{
|
|
byte *p = ( byte* )pBuffer;
|
|
uint offset = 0;
|
|
for ( std::vector<Chunk>::const_iterator it = m_data.begin(); it != m_data.end(); ++it )
|
|
{
|
|
Assert( it->m_offset == offset );
|
|
memcpy( p + offset, it->m_data, it->m_size );
|
|
offset += it->m_size;
|
|
}
|
|
Assert( m_size == offset );
|
|
}
|
|
|
|
byte *data = ( byte* )pBuffer;
|
|
|
|
// for all link sources, try to resolve them
|
|
uint numUnresolved = 0;
|
|
for ( LinkSourceMap::const_iterator it = m_mapSources.begin(); it != m_mapSources.end(); ++it )
|
|
{
|
|
const LinkSourceDesc_t &source = it->second;
|
|
int offsetOfSource = it->first;
|
|
Assert( uint( offsetOfSource ) < m_size ); // check alignment?
|
|
|
|
int* pOffset = ( int* )( data + offsetOfSource );
|
|
if ( source.m_targetId == kSpecialTargetNull )
|
|
{
|
|
*pOffset = 0; // special case of target fixed to NULL
|
|
}
|
|
else if ( source.m_targetId < 0 || source.m_targetId > m_lastTarget )
|
|
{
|
|
Msg( "Unresolved link source @0x%X '%s'\n", offsetOfSource, source.m_pDescription );
|
|
numUnresolved ++;
|
|
}
|
|
else
|
|
{
|
|
// try to find the target
|
|
// if special target (id == 0): target is not in the target map; all the information is resolved directly through the LinkSourceDesc_t
|
|
const LinkTargetDesc_t &target = source.m_targetId == kSpecialTargetDefault ? source.m_defaultTarget : m_mapTargets[source.m_targetId];
|
|
switch ( target.m_type )
|
|
{
|
|
case kTargetNull:
|
|
*pOffset = 0; // special case of target set to NULL later
|
|
break;
|
|
|
|
case kTargetResolved:
|
|
|
|
switch ( source.m_type )
|
|
{
|
|
case kOtRelative32bit:
|
|
*pOffset = target.m_resolvedOffset - offsetOfSource;
|
|
break;
|
|
case kOtRelative16bit:
|
|
{
|
|
int offset = target.m_resolvedOffset - offsetOfSource;
|
|
if ( offset > 0x7FFF || offset < -0x8000 )
|
|
{
|
|
Msg( "Link source @%d '%s' is out of range (%d doesn't fit to 16 bits)\n", offsetOfSource, source.m_pDescription, offset );
|
|
numUnresolved++;
|
|
}
|
|
else
|
|
{
|
|
*( short* )pOffset = ( short )offset;
|
|
}
|
|
}
|
|
break;
|
|
case kOtAbsolute32bit:
|
|
*pOffset = target.m_resolvedOffset;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
Msg( "Unresolved link source @%d '%s'\n", offsetOfSource, source.m_pDescription );
|
|
numUnresolved++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( numUnresolved )
|
|
{
|
|
Error( "%u unresolved links found\n", numUnresolved );
|
|
return false;
|
|
}
|
|
else
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Stream::Align( uint nAlignment, int nOffset )
|
|
{
|
|
if ( nAlignment & ( nAlignment - 1 ) )
|
|
{
|
|
Error( "Wrong alignment %d\n", nAlignment );
|
|
}
|
|
//WriteBytes(((m_size+nAlignment-1) & ~(nAlignment-1)) - m_size);
|
|
WriteBytes( ( nOffset - m_size ) & ( nAlignment - 1 ) );
|
|
}
|
|
|
|
|
|
void Stream::Link( LinkSource_t linkSource, LinkTarget_t linkTarget, const char *szDescription )
|
|
{
|
|
LinkSourceMap::iterator itFind = m_mapSources.find( linkSource.m_offset );
|
|
if ( itFind == m_mapSources.end() )
|
|
{
|
|
Error( "cannot find source @%d - %s\n", linkSource.m_offset, szDescription );
|
|
}
|
|
|
|
itFind->second.m_targetId = linkTarget.m_id;
|
|
itFind->second.m_defaultTarget.m_type = kTargetUnresolved; // un-set default target for validation; we are not referring to it any more
|
|
Assert( linkTarget.m_id != kSpecialTargetDefault );
|
|
}
|
|
|
|
|
|
|
|
LinkSource_t Stream::LinkToHere( int *pOffset, const char *szDescription )
|
|
{
|
|
return Link( pOffset, NewTargetHere(), szDescription );
|
|
}
|
|
|
|
LinkSource_t Stream::Link( int32 *pOffset, LinkTarget_t linkTarget, const char *szDescription )
|
|
{
|
|
int offsetToOffset = GetOffsetTo( pOffset );
|
|
LinkSource_t linkSource = NewLinkSource( offsetToOffset, kOtRelative32bit, linkTarget.m_id, szDescription );
|
|
return linkSource;
|
|
}
|
|
|
|
void Stream::Link( int32 *pOffset, const void *pTarget )
|
|
{
|
|
if ( !pTarget )
|
|
{
|
|
*pOffset = 0;
|
|
return;
|
|
}
|
|
int offsetToOffset = GetOffsetTo( pOffset );
|
|
int offsetToTarget = GetOffsetTo( pTarget );
|
|
if ( offsetToOffset < 0 || offsetToTarget < 0 )
|
|
{
|
|
Error( "Link(%p,%p) can't find offsets (%d,%d)\n", pOffset, pTarget, offsetToOffset, offsetToTarget );
|
|
}
|
|
if ( offsetToOffset == offsetToTarget )
|
|
{
|
|
Warning( "Link(%p,%p) is self-referencing (%d==%d), this is prohibited by this kind of offset. It'll be interpreted as Null offset at runtime\n", pOffset, pTarget, offsetToOffset, offsetToTarget );
|
|
}
|
|
|
|
*pOffset = offsetToTarget - offsetToOffset;
|
|
}
|
|
|
|
void Stream::Link( int16 *pOffset, const void *pTarget )
|
|
{
|
|
if ( !pTarget )
|
|
{
|
|
*pOffset = 0;
|
|
return;
|
|
}
|
|
int offsetToOffset = GetOffsetTo( pOffset );
|
|
int offsetToTarget = GetOffsetTo( pTarget );
|
|
if ( offsetToOffset < 0 || offsetToTarget < 0 )
|
|
{
|
|
Error( "Link(%p,%p) can't find offsets (%d,%d)\n", pOffset, pTarget, offsetToOffset, offsetToTarget );
|
|
}
|
|
int offset = offsetToTarget - offsetToOffset;
|
|
int16 offset16 = ( int16 )offset;
|
|
if ( offset != ( int )offset16 )
|
|
{
|
|
Error( "Link16(%p,%p) offsets are too far (%d,%d)\n", pOffset, pTarget, offsetToOffset, offsetToTarget );
|
|
}
|
|
|
|
*pOffset = offset16;
|
|
}
|
|
|
|
|
|
|
|
LinkSource_t Stream::NewOffset( int *pOffset, const char *szDescription )
|
|
{
|
|
return NewLinkSource( GetOffsetTo( pOffset ), kOtRelative32bit, kSpecialTargetUndefined, szDescription );
|
|
}
|
|
|
|
LinkSource_t Stream::NewLinkSource( int offsetToOffset, OffsetTypeEnum type, int linkTargetId, const char *szDescription )
|
|
{
|
|
Assert( !( offsetToOffset & 3 ) );
|
|
if ( offsetToOffset < 0 && offsetToOffset + sizeof( int ) > m_size )
|
|
{
|
|
Error( "Wrong offset spec. Most probably you called an extra Write() before calling WriteOffset() - %s\n", szDescription );
|
|
}
|
|
|
|
LinkSource_t linkSource;
|
|
linkSource.m_offset = offsetToOffset;
|
|
LinkSourceDesc_t &lsd = m_mapSources[offsetToOffset];
|
|
lsd.m_targetId = linkTargetId;
|
|
lsd.m_type = type;
|
|
lsd.m_pDescription = szDescription;
|
|
|
|
return linkSource;
|
|
}
|
|
|
|
|
|
LinkSource_t Stream::WriteOffset( const char *szDescription )
|
|
{
|
|
return WriteOffset( LinkTarget_t(), szDescription );
|
|
}
|
|
|
|
LinkSource_t Stream::WriteOffset( LinkTarget_t linkTarget, const char *szDescription )
|
|
{
|
|
int offsetToOffset = m_size;
|
|
Write<int32>();
|
|
return NewLinkSource( offsetToOffset, kOtRelative32bit, linkTarget.m_id, szDescription );
|
|
}
|
|
|
|
|
|
|
|
|
|
LinkTarget_t Stream::NewTarget()// create new, unresolved target
|
|
{
|
|
LinkTarget_t linkTarget;
|
|
linkTarget.m_id = ++m_lastTarget; // shouldn't be 0 or -1
|
|
return linkTarget;
|
|
}
|
|
|
|
|
|
LinkTarget_t Stream::NewTarget( void *pWhere )
|
|
{
|
|
int offsetToTarget = GetOffsetTo( pWhere );
|
|
if ( offsetToTarget < 0 )
|
|
Error( "Invalid new target @%p\n", pWhere );
|
|
LinkTarget_t linkTarget = NewTarget();
|
|
SetTarget( linkTarget, offsetToTarget );
|
|
return linkTarget;
|
|
}
|
|
|
|
|
|
LinkTarget_t Stream::NewTargetHere() // creates a target right here
|
|
{
|
|
LinkTarget_t linkTarget = NewTarget();
|
|
SetTargetHere( linkTarget );
|
|
return linkTarget;
|
|
}
|
|
|
|
|
|
|
|
void Stream::SetTarget( LinkTarget_t linkTarget, int offsetToTarget ) // sets the given target to point to offsetToTarget
|
|
{
|
|
LinkTargetDesc_t &target = m_mapTargets[linkTarget.m_id];
|
|
target.m_type = kTargetResolved;
|
|
target.m_resolvedOffset = offsetToTarget;
|
|
}
|
|
|
|
void Stream::SetTargetHere( LinkTarget_t linkTarget ) // sets the given target to point to right here
|
|
{
|
|
if ( linkTarget.m_id > 0 && linkTarget.m_id <= m_lastTarget )
|
|
{
|
|
SetTarget( linkTarget, m_size );
|
|
}
|
|
else
|
|
{
|
|
Warning( "Trying to set invalid target %d to here (absolute offset %d)\n", linkTarget.m_id, m_size );
|
|
}
|
|
}
|
|
|
|
|
|
void Stream::SetTargetNull( LinkTarget_t linkTarget ) // set this target to point to NULL
|
|
{
|
|
if ( linkTarget.m_id > 0 && linkTarget.m_id <= m_lastTarget )
|
|
{
|
|
m_mapTargets[linkTarget.m_id].m_type = kTargetNull;
|
|
}
|
|
else
|
|
{
|
|
Warning( "Trying to set invalid target %d to null\n", linkTarget.m_id );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
bool Stream::IsDeclared( LinkTarget_t linkTarget )const
|
|
{
|
|
return m_mapTargets.find( linkTarget.m_id ) != m_mapTargets.end();
|
|
}
|
|
|
|
|
|
bool Stream::IsSet( LinkTarget_t linkTarget )const
|
|
{
|
|
LinkTargetMap::const_iterator itFind = m_mapTargets.find( linkTarget.m_id );
|
|
if ( itFind == m_mapTargets.end() )
|
|
return false; // it can't be set when it hasn't been declared
|
|
return itFind->second.m_resolvedOffset >= 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool Stream::IsDefined( LinkSource_t linkSource )const
|
|
{
|
|
LinkSourceMap::const_iterator itFind = m_mapSources.find( linkSource.m_offset );
|
|
return itFind != m_mapSources.end();
|
|
}
|
|
|
|
|
|
bool Stream::IsLinked( LinkSource_t linkSource )const
|
|
{
|
|
LinkSourceMap::const_iterator itFind = m_mapSources.find( linkSource.m_offset );
|
|
if ( itFind == m_mapSources.end() )
|
|
return false;
|
|
return itFind->second.m_targetId >= 0;
|
|
}
|
|
|
|
void Stream::Begin( const char *szName, uint flags )
|
|
{
|
|
Block_t newBlock;
|
|
|
|
if ( !m_stackBlocks.empty() )
|
|
newBlock.m_name = m_stackBlocks.back().m_name + ":" + szName;
|
|
else
|
|
newBlock.m_name = szName;
|
|
|
|
newBlock.m_tell = Tell();
|
|
|
|
m_stackBlocks.push_back( newBlock );
|
|
}
|
|
|
|
|
|
|
|
void Stream::End()
|
|
{
|
|
if ( !m_stackBlocks.empty() )
|
|
{
|
|
const Block_t &curBlock = m_stackBlocks.back();
|
|
m_mapBlockStats[curBlock.m_name].m_size += Tell() - curBlock.m_tell;
|
|
m_stackBlocks.pop_back();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void Stream::PrintStats()
|
|
{
|
|
for ( BlockStatsMap::const_iterator it = m_mapBlockStats.begin(); it != m_mapBlockStats.end(); ++it )
|
|
Msg( "%-60s %7.1f KiB\n", it->first.c_str(), it->second.m_size / 1024. );
|
|
Msg( "%-60s -----------\n", "" );
|
|
Msg( "%-60s %7.1f KiB\n", "DataLinker total stream size", m_size / 1024. );
|
|
}
|
|
|
|
|
|
|
|
void Stream::ClearStats()
|
|
{
|
|
m_mapBlockStats.clear();
|
|
}
|
|
|
|
|
|
|
|
const char* Stream::WriteAndLinkString( Offset_t<char> *pOffset, const char *pString )
|
|
{
|
|
if ( pString )
|
|
{
|
|
StringTableElement_t &ste = m_stringTable[pString];
|
|
if ( !ste.m_pString )
|
|
{
|
|
int nLen = Q_strlen( pString );
|
|
|
|
ste.m_nOffset = Tell();
|
|
ste.m_pString = ( char* )this->WriteBytes( nLen + 1 );
|
|
|
|
Q_memcpy( ste.m_pString, pString, nLen + 1 );
|
|
}
|
|
Assert( !Q_strcmp( ste.m_pString, pString ) );
|
|
|
|
int offsetToOffset = GetOffsetTo( pOffset );
|
|
pOffset->offset = ste.m_nOffset - offsetToOffset;
|
|
|
|
return ste.m_pString;
|
|
}
|
|
else
|
|
{
|
|
pOffset->offset = 0; // make the offset NULL
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
}
|