text: //====== Copyright c 1996-2007, Valve Corporation, All rights reserved. =======//
  //
  // Purpose: 
  //
  //=============================================================================//
  
  #include "mdllib_common.h"
  #include "mdllib_stripinfo.h"
  #include "mdllib_utils.h"
  
  #include "studio.h"
  #include "optimize.h"
  
  #include "materialsystem/imaterial.h"
  #include "materialsystem/hardwareverts.h"
  
  #include "smartptr.h"
  
  
  
  //////////////////////////////////////////////////////////////////////////
  //
  // CMdlStripInfo implementation
  //
  //////////////////////////////////////////////////////////////////////////
  
  
  CMdlStripInfo::CMdlStripInfo() :
  	m_eMode( MODE_UNINITIALIZED ),
  	m_lChecksumOld( 0 ),
  	m_lChecksumNew( 0 )
  {
  	NULL;
  }
  
  
  bool CMdlStripInfo::Serialize( CUtlBuffer &bufStorage ) const
  {
  	char chHeader[ 4 ] = { 'M', 'A', 'P', m_eMode };
  	bufStorage.Put( chHeader, sizeof( chHeader ) );
  
  	switch ( m_eMode )
  	{
  	default:
  	case MODE_UNINITIALIZED:
  		return true;
  	
  	case MODE_NO_CHANGE:
  		bufStorage.PutInt( m_lChecksumOld );
  		bufStorage.PutInt( m_lChecksumNew );
  		return true;
  
  	case MODE_STRIP_LOD_1N:
  		bufStorage.PutInt( m_lChecksumOld );
  		bufStorage.PutInt( m_lChecksumNew );
  
  		bufStorage.PutInt( m_vtxVerts.GetNumBits() );
  		for ( uint32 const *pdwBase = m_vtxVerts.Base(), *pdwEnd = pdwBase + m_vtxVerts.GetNumDWords();
  			pdwBase < pdwEnd; ++ pdwBase )
  			bufStorage.PutUnsignedInt( *pdwBase );
  
  		bufStorage.PutInt( m_vtxIndices.Count() );
  		for ( unsigned short const *pusBase = m_vtxIndices.Base(), *pusEnd = pusBase + m_vtxIndices.Count();
  			pusBase < pusEnd; ++ pusBase )
  			bufStorage.PutUnsignedShort( *pusBase );
  
  		bufStorage.PutInt( m_vtxMdlOffsets.Count() );
  		for ( MdlRangeItem const *pmri = m_vtxMdlOffsets.Base(), *pmriEnd = pmri + m_vtxMdlOffsets.Count();
  			pmri < pmriEnd; ++ pmri )
  			bufStorage.PutInt( pmri->m_offOld ),
  			bufStorage.PutInt( pmri->m_offNew ),
  			bufStorage.PutInt( pmri->m_numOld ),
  			bufStorage.PutInt( pmri->m_numNew );
  
  		return true;
  	}
  }
  
  bool CMdlStripInfo::UnSerialize( CUtlBuffer &bufData )
  {
  	char chHeader[ 4 ];
  	bufData.Get( chHeader, sizeof( chHeader ) );
  
  	if ( memcmp( chHeader, "MAP", 3 ) )
  		return false;
  
  	switch ( chHeader[3] )
  	{
  	default:
  		return false;
  	
  	case MODE_UNINITIALIZED:
  		m_eMode = MODE_UNINITIALIZED;
  		m_lChecksumOld = 0;
  		m_lChecksumNew = 0;
  		return true;
  
  	case MODE_NO_CHANGE:
  		m_eMode = MODE_NO_CHANGE;
  		m_lChecksumOld = bufData.GetInt();
  		m_lChecksumNew = bufData.GetInt();
  		return true;
  
  	case MODE_STRIP_LOD_1N:
  		m_eMode = MODE_STRIP_LOD_1N;
  		m_lChecksumOld = bufData.GetInt();
  		m_lChecksumNew = bufData.GetInt();
  
  		m_vtxVerts.Resize( bufData.GetInt(), true );
  		for ( uint32 *pdwBase = m_vtxVerts.Base(), *pdwEnd = pdwBase + m_vtxVerts.GetNumDWords();
  			pdwBase < pdwEnd; ++ pdwBase )
  			*pdwBase = bufData.GetUnsignedInt();
  
  		m_vtxIndices.SetCount( bufData.GetInt() );
  		for ( unsigned short *pusBase = m_vtxIndices.Base(), *pusEnd = pusBase + m_vtxIndices.Count();
  			pusBase < pusEnd; ++ pusBase )
  			*pusBase = bufData.GetUnsignedShort();
  
  		m_vtxMdlOffsets.SetCount( bufData.GetInt() );
  		for ( MdlRangeItem *pmri = m_vtxMdlOffsets.Base(), *pmriEnd = pmri + m_vtxMdlOffsets.Count();
  			pmri < pmriEnd; ++ pmri )
  			pmri->m_offOld = bufData.GetInt(),
  			pmri->m_offNew = bufData.GetInt(),
  			pmri->m_numOld = bufData.GetInt(),
  			pmri->m_numNew = bufData.GetInt();
  
  		return true;
  	}
  }
  
  
  // Returns the checksums that the stripping info was generated for:
  //	plChecksumOriginal		if non-NULL will hold the checksum of the original model submitted for stripping
  //	plChecksumStripped		if non-NULL will hold the resulting checksum of the stripped model
  bool CMdlStripInfo::GetCheckSum( long *plChecksumOriginal, long *plChecksumStripped ) const
  {
  	if ( m_eMode == MODE_UNINITIALIZED )
  		return false;
  
  	if ( plChecksumOriginal )
  		*plChecksumOriginal = m_lChecksumOld;
  
  	if ( plChecksumStripped )
  		*plChecksumStripped = m_lChecksumNew;
  
  	return true;
  }
  
  
  //
  // StripHardwareVertsBuffer
  //	The main function that strips the vhv buffer
  //		vhvBuffer		- vhv buffer, updated, size reduced
  //
  bool CMdlStripInfo::StripHardwareVertsBuffer( CUtlBuffer &vhvBuffer )
  {
  	if ( m_eMode == MODE_UNINITIALIZED )
  		return false;
  
  	//
  	// Recover vhv header
  	//
  	DECLARE_PTR( HardwareVerts::FileHeader_t, vhvHdr, BYTE_OFF_PTR( vhvBuffer.Base(), vhvBuffer.TellGet() ) );
  	int vhvLength = vhvBuffer.TellPut() - vhvBuffer.TellGet();
  
  	if ( vhvHdr->m_nChecksum != m_lChecksumOld )
  	{
  		DLog( "mdllib", 1, "ERROR: [StripHardwareVertsBuffer] checksum mismatch!\n" );
  		return false;
  	}
  
  	vhvHdr->m_nChecksum = m_lChecksumNew;
  
  
  	// No remapping required
  	if ( m_eMode == MODE_NO_CHANGE )
  		return true;
  
  
  	Assert( m_eMode == MODE_STRIP_LOD_1N );
  
  	//
  	// Now reconstruct the vhv structures to do the mapping
  	//
  
  	CRemoveTracker vhvRemove;
  	size_t vhvVertOffset = ~size_t( 0 ), vhvEndMeshOffset = sizeof( HardwareVerts::FileHeader_t );
  	int numMeshesRemoved = 0, numVertsRemoved = 0;
  
  	ITERATE_CHILDREN( HardwareVerts::MeshHeader_t, vhvMesh, vhvHdr, pMesh, m_nMeshes )
  		if ( vhvMesh->m_nOffset < vhvVertOffset )
  			vhvVertOffset = vhvMesh->m_nOffset;
  		if ( BYTE_DIFF_PTR( vhvHdr, vhvMesh + 1 ) > vhvEndMeshOffset )
  			vhvEndMeshOffset = BYTE_DIFF_PTR( vhvHdr, vhvMesh + 1 );
  		if ( !vhvMesh->m_nLod )
  			continue;
  		vhvRemove.RemoveBytes( BYTE_OFF_PTR( vhvHdr, vhvMesh->m_nOffset ), vhvMesh->m_nVertexes * vhvHdr->m_nVertexSize );
  		vhvRemove.RemoveElements( vhvMesh );
  		numVertsRemoved += vhvMesh->m_nVertexes;
  		++ numMeshesRemoved;
  	ITERATE_END
  	vhvRemove.RemoveBytes( BYTE_OFF_PTR( vhvHdr, vhvEndMeshOffset ), vhvVertOffset - vhvEndMeshOffset );	// Padding
  	vhvRemove.RemoveBytes( BYTE_OFF_PTR( vhvHdr, vhvVertOffset + vhvHdr->m_nVertexes * vhvHdr->m_nVertexSize ), vhvLength - ( vhvVertOffset + vhvHdr->m_nVertexes * vhvHdr->m_nVertexSize ) );
  
  	vhvRemove.Finalize();
  	DLog( "mdllib", 3, " Stripped %d vhv bytes.\n", vhvRemove.GetNumBytesRemoved() );
  
  	// Verts must be aligned from hdr, length must be aligned from hdr
  	size_t vhvNewVertOffset = vhvRemove.ComputeOffset( vhvHdr, vhvVertOffset );
  	size_t vhvAlignedVertOffset = ALIGN_VALUE( vhvNewVertOffset, 4 );
  
  	ITERATE_CHILDREN( HardwareVerts::MeshHeader_t, vhvMesh, vhvHdr, pMesh, m_nMeshes )
  		vhvMesh->m_nOffset = vhvRemove.ComputeOffset( vhvHdr, vhvMesh->m_nOffset ) + vhvAlignedVertOffset - vhvNewVertOffset;
  	ITERATE_END
  	vhvHdr->m_nMeshes -= numMeshesRemoved;
  	vhvHdr->m_nVertexes -= numVertsRemoved;
  
  	// Remove the memory
  	vhvRemove.MemMove( vhvHdr, vhvLength );	// All padding has been removed
  
  	size_t numBytesNewLength = vhvLength + vhvAlignedVertOffset - vhvNewVertOffset;
  	size_t numAlignedNewLength = ALIGN_VALUE( numBytesNewLength, 4 );
  
  	// Now reinsert the padding
  	CInsertionTracker vhvInsertPadding;
  	vhvInsertPadding.InsertBytes( BYTE_OFF_PTR( vhvHdr, vhvNewVertOffset ), vhvAlignedVertOffset - vhvNewVertOffset );
  	vhvInsertPadding.InsertBytes( BYTE_OFF_PTR( vhvHdr, vhvLength ), numAlignedNewLength - numBytesNewLength );
  	
  	vhvInsertPadding.Finalize();
  	DLog( "mdllib", 3, " Inserted %d alignment bytes.\n", vhvInsertPadding.GetNumBytesInserted() );
  
  	vhvInsertPadding.MemMove( vhvHdr, vhvLength );
  
  
  	// Update the buffer length
  	vhvBuffer.SeekPut( CUtlBuffer::SEEK_CURRENT, vhvBuffer.TellGet() + vhvLength - vhvBuffer.TellPut() );
  
  	DLog( "mdllib", 2, " Reduced vhv buffer by %d bytes.\n", vhvRemove.GetNumBytesRemoved() - vhvInsertPadding.GetNumBytesInserted() );
  
  	// Done
  	return true;
  }
  
  //
  // StripModelBuffer
  //	The main function that strips the mdl buffer
  //		mdlBuffer		- mdl buffer, updated
  //
  bool CMdlStripInfo::StripModelBuffer( CUtlBuffer &mdlBuffer )
  {
  	if ( m_eMode == MODE_UNINITIALIZED )
  		return false;
  
  	//
  	// Recover mdl header
  	//
  	DECLARE_PTR( studiohdr_t, mdlHdr, BYTE_OFF_PTR( mdlBuffer.Base(), mdlBuffer.TellGet() ) );
  
  	if ( mdlHdr->checksum != m_lChecksumOld )
  	{
  		DLog( "mdllib", 1, "ERROR: [StripModelBuffer] checksum mismatch!\n" );
  		return false;
  	}
  
  	mdlHdr->checksum = m_lChecksumNew;
  
  
  	// No remapping required
  	if ( m_eMode == MODE_NO_CHANGE )
  		return true;
  
  
  	Assert( m_eMode == MODE_STRIP_LOD_1N );
  
  
  	//
  	// Do the model buffer stripping
  	//
  
  	CUtlSortVector< unsigned short, CLessSimple< unsigned short > > &srcIndices = m_vtxIndices;
  
  	ITERATE_CHILDREN( mstudiobodyparts_t, mdlBodyPart, mdlHdr, pBodypart, numbodyparts )
  		ITERATE_CHILDREN( mstudiomodel_t, mdlModel, mdlBodyPart, pModel, nummodels )
  			
  			DLog( "mdllib", 3, " Stripped %d vertexes (was: %d, now: %d).\n", mdlModel->numvertices - srcIndices.Count(), mdlModel->numvertices, srcIndices.Count() );
  
  			mdlModel->numvertices = srcIndices.Count();
  
  			ITERATE_CHILDREN( mstudiomesh_t, mdlMesh, mdlModel, pMesh, nummeshes )
  				
  				mdlMesh->numvertices = srcIndices.FindLess( mdlMesh->vertexoffset + mdlMesh->numvertices );
  				mdlMesh->vertexoffset = srcIndices.FindLess( mdlMesh->vertexoffset ) + 1;
  				mdlMesh->numvertices -= mdlMesh->vertexoffset - 1;
  
  				// Truncate the number of vertexes
  				for ( int k = 0; k < ARRAYSIZE( mdlMesh->vertexdata.numLODVertexes ); ++ k )
  					mdlMesh->vertexdata.numLODVertexes[ k ] = mdlMesh->numvertices;
  
  			ITERATE_END
  		ITERATE_END
  	ITERATE_END
  
  	//
  	// Update bones not to mention anything below LOD0
  	//
  	ITERATE_CHILDREN( mstudiobone_t, mdlBone, mdlHdr, pBone, numbones )
  		mdlBone->flags &= ( BONE_USED_BY_VERTEX_LOD0 | ~BONE_USED_BY_VERTEX_MASK );
  	ITERATE_END
  
  	DLog( "mdllib", 3, " Updated %d bone(s).\n", mdlHdr->numbones );
  
  	return true;
  }
  
  
  //
  // StripVertexDataBuffer
  //	The main function that strips the vvd buffer
  //		vvdBuffer		- vvd buffer, updated, size reduced
  //
  bool CMdlStripInfo::StripVertexDataBuffer( CUtlBuffer &vvdBuffer )
  {
  	if ( m_eMode == MODE_UNINITIALIZED )
  		return false;
  
  	//
  	// Recover vvd header
  	//
  	DECLARE_PTR( vertexFileHeader_t, vvdHdr, BYTE_OFF_PTR( vvdBuffer.Base(), vvdBuffer.TellGet() ) );
  	int vvdLength = vvdBuffer.TellPut() - vvdBuffer.TellGet();
  
  	if ( vvdHdr->checksum != m_lChecksumOld )
  	{
  		DLog( "mdllib", 1, "ERROR: [StripVertexDataBuffer] checksum mismatch!\n" );
  		return false;
  	}
  
  	vvdHdr->checksum = m_lChecksumNew;
  
  
  	// No remapping required
  	if ( m_eMode == MODE_NO_CHANGE )
  		return true;
  
  
  	Assert( m_eMode == MODE_STRIP_LOD_1N );
  
  
  	//
  	// Do the vertex data buffer stripping
  	//
  
  	CUtlSortVector< unsigned short, CLessSimple< unsigned short > > &srcIndices = m_vtxIndices;
  	int mdlNumVerticesOld = vvdHdr->numLODVertexes[ 0 ];
  
  	vvdHdr->numLODs = 1;
  	for ( int k = 0; k < ARRAYSIZE( vvdHdr->numLODVertexes ); ++ k )
  		vvdHdr->numLODVertexes[ k ] = srcIndices.Count();
  
  	DECLARE_PTR( mstudiovertex_t, vvdVertexSrc, BYTE_OFF_PTR( vvdHdr, vvdHdr->vertexDataStart ) );
  	DECLARE_PTR( Vector4D, vvdTangentSrc, vvdHdr->tangentDataStart ? BYTE_OFF_PTR( vvdHdr, vvdHdr->tangentDataStart ) : NULL );
  	
  	// Apply the fixups first of all
  	if ( vvdHdr->numFixups )
  	{
  		CArrayAutoPtr< byte > memTempVVD( new byte[ vvdLength ] );
  		DECLARE_PTR( mstudiovertex_t, vvdVertexNew, BYTE_OFF_PTR( memTempVVD.Get(), vvdHdr->vertexDataStart ) );
  		DECLARE_PTR( Vector4D, vvdTangentNew, BYTE_OFF_PTR( memTempVVD.Get(), vvdHdr->tangentDataStart ) );
  		DECLARE_PTR( vertexFileFixup_t, vvdFixup, BYTE_OFF_PTR( vvdHdr, vvdHdr->fixupTableStart ) );
  		for ( int k = 0; k < vvdHdr->numFixups; ++ k )
  		{
  			memcpy( vvdVertexNew, vvdVertexSrc + vvdFixup[ k ].sourceVertexID, vvdFixup[ k ].numVertexes * sizeof( *vvdVertexNew ) );
  			vvdVertexNew += vvdFixup[ k ].numVertexes;
  			if ( vvdTangentSrc )
  			{
  				memcpy( vvdTangentNew, vvdTangentSrc + vvdFixup[ k ].sourceVertexID, vvdFixup[ k ].numVertexes * sizeof( *vvdTangentNew ) );
  				vvdTangentNew += vvdFixup[ k ].numVertexes;
  			}
  		}
  
  		// Move back the memory after fixups were applied
  		vvdVertexSrc  ? memcpy( vvdVertexSrc, BYTE_OFF_PTR( memTempVVD.Get(), vvdHdr->vertexDataStart ), mdlNumVerticesOld * sizeof( *vvdVertexSrc ) ) : 0;
  		vvdTangentSrc ? memcpy( vvdTangentSrc, BYTE_OFF_PTR( memTempVVD.Get(), vvdHdr->tangentDataStart ), mdlNumVerticesOld * sizeof( *vvdTangentSrc ) ) : 0;
  	}
  	
  	vvdHdr->vertexDataStart -= ALIGN_VALUE( sizeof( vertexFileFixup_t ) * vvdHdr->numFixups, 16 );
  	vvdHdr->numFixups = 0;
  	DECLARE_PTR( mstudiovertex_t, vvdVertexNew, BYTE_OFF_PTR( vvdHdr, vvdHdr->vertexDataStart ) );
  	for ( int k = 0; k < srcIndices.Count(); ++ k )
  		vvdVertexNew[ k ] = vvdVertexSrc[ srcIndices[ k ] ];
  
  	size_t newVertexDataSize = srcIndices.Count() * sizeof( mstudiovertex_t );
  	int vvdLengthOld = vvdLength;
  	vvdLength = vvdHdr->vertexDataStart + newVertexDataSize;
  
  	if ( vvdTangentSrc )
  	{
  		// Move the tangents
  		vvdHdr->tangentDataStart = vvdLength;
  		DECLARE_PTR( Vector4D, vvdTangentNew, BYTE_OFF_PTR( vvdHdr, vvdHdr->tangentDataStart ) );
  
  		for ( int k = 0; k < srcIndices.Count(); ++ k )
  			vvdTangentNew[ k ] = vvdTangentSrc[ srcIndices[ k ] ];
  
  		vvdLength += srcIndices.Count() * sizeof( Vector4D );
  	}
  	
  	vvdBuffer.SeekPut( CUtlBuffer::SEEK_CURRENT, vvdBuffer.TellGet() + vvdLength - vvdBuffer.TellPut() );
  
  	DLog( "mdllib", 3, " Stripped %d vvd bytes.\n", vvdLengthOld - vvdLength );
  
  	return true;
  }
  
  //
  // StripOptimizedModelBuffer
  //	The main function that strips the vtx buffer
  //		vtxBuffer		- vtx buffer, updated, size reduced
  //
  bool CMdlStripInfo::StripOptimizedModelBuffer( CUtlBuffer &vtxBuffer )
  {
  	if ( m_eMode == MODE_UNINITIALIZED )
  		return false;
  
  	//
  	// Recover vtx header
  	//
  	DECLARE_PTR( OptimizedModel::FileHeader_t, vtxHdr, BYTE_OFF_PTR( vtxBuffer.Base(), vtxBuffer.TellGet() ) );
  	int vtxLength = vtxBuffer.TellPut() - vtxBuffer.TellGet();
  
  	if ( vtxHdr->checkSum != m_lChecksumOld )
  	{
  		DLog( "mdllib", 1, "ERROR: [StripOptimizedModelBuffer] checksum mismatch!\n" );
  		return false;
  	}
  
  	vtxHdr->checkSum = m_lChecksumNew;
  
  	// No remapping required
  	if ( m_eMode == MODE_NO_CHANGE )
  		return true;
  
  	Assert( m_eMode == MODE_STRIP_LOD_1N );
  
  
  	//
  	// Do the optimized model buffer stripping
  	//
  
  	CUtlSortVector< unsigned short, CLessSimple< unsigned short > > &srcIndices = m_vtxIndices;
  	CUtlSortVector< CMdlStripInfo::MdlRangeItem, CLessSimple< CMdlStripInfo::MdlRangeItem > > &arrMdlOffsets = m_vtxMdlOffsets;
  
  	size_t vtxOffIndexBuffer = ~size_t(0), vtxOffIndexBufferEnd = 0;
  	size_t vtxOffVertexBuffer = ~size_t(0), vtxOffVertexBufferEnd = 0;
  	CRemoveTracker vtxRemove;
  	CUtlVector< size_t > vtxOffIndex;
  	CUtlVector< size_t > vtxOffVertex;
  
  	vtxRemove.RemoveElements( CHILD_AT( vtxHdr, pMaterialReplacementList, 1 ), vtxHdr->numLODs - 1 );
  	ITERATE_CHILDREN( OptimizedModel::MaterialReplacementListHeader_t, vtxMatList, vtxHdr, pMaterialReplacementList, numLODs )
  		if ( !vtxMatList_idx ) continue;
  		vtxRemove.RemoveElements( CHILD_AT( vtxMatList, pMaterialReplacement, 0 ), vtxMatList->numReplacements );
  		ITERATE_CHILDREN( OptimizedModel::MaterialReplacementHeader_t, vtxMat, vtxMatList, pMaterialReplacement, numReplacements )
  			char const *szName = vtxMat->pMaterialReplacementName();
  			vtxRemove.RemoveElements( szName, szName ? strlen( szName ) + 1 : 0 );
  		ITERATE_END
  	ITERATE_END
  
  	ITERATE_CHILDREN( OptimizedModel::BodyPartHeader_t, vtxBodyPart, vtxHdr, pBodyPart, numBodyParts )
  		ITERATE_CHILDREN( OptimizedModel::ModelHeader_t, vtxModel, vtxBodyPart, pModel, numModels )
  		
  			vtxRemove.RemoveElements( CHILD_AT( vtxModel, pLOD, 1 ), vtxModel->numLODs - 1 );
  			ITERATE_CHILDREN( OptimizedModel::ModelLODHeader_t, vtxLod, vtxModel, pLOD, numLODs )
  				if ( !vtxLod_idx )	// Process only lod1-N
  					continue;
  				
  				vtxRemove.RemoveElements( CHILD_AT( vtxLod, pMesh, 0 ), vtxLod->numMeshes );
  				ITERATE_CHILDREN( OptimizedModel::MeshHeader_t, vtxMesh, vtxLod, pMesh, numMeshes )
  					vtxRemove.RemoveElements( CHILD_AT( vtxMesh, pStripGroup, 0 ), vtxMesh->numStripGroups );
  					ITERATE_CHILDREN( OptimizedModel::StripGroupHeader_t, vtxStripGroup, vtxMesh, pStripGroup, numStripGroups )
  						vtxRemove.RemoveElements( CHILD_AT( vtxStripGroup, pStrip, 0 ), vtxStripGroup->numStrips );
  						ITERATE_CHILDREN( OptimizedModel::StripHeader_t, vtxStrip, vtxStripGroup, pStrip, numStrips )
  							vtxRemove.RemoveElements( CHILD_AT( vtxStrip, pBoneStateChange, 0 ), vtxStrip->numBoneStateChanges );
  						ITERATE_END
  					ITERATE_END
  				ITERATE_END
  
  			ITERATE_END
  
  			// Use all lods to determine the ranges of vertex and index buffers.
  			// We rely on the fact that vertex and index buffers are laid out as one solid memory block for all lods.
  			ITERATE_CHILDREN( OptimizedModel::ModelLODHeader_t, vtxLod, vtxModel, pLOD, numLODs )
  				ITERATE_CHILDREN( OptimizedModel::MeshHeader_t, vtxMesh, vtxLod, pMesh, numMeshes )
  					ITERATE_CHILDREN( OptimizedModel::StripGroupHeader_t, vtxStripGroup, vtxMesh, pStripGroup, numStripGroups )
  
  						size_t offIndex = BYTE_DIFF_PTR( vtxHdr, CHILD_AT( vtxStripGroup, pIndex, 0 ) );
  						size_t offIndexEnd = BYTE_DIFF_PTR( vtxHdr, CHILD_AT( vtxStripGroup, pIndex, vtxStripGroup->numIndices ) );
  						size_t offVertex = BYTE_DIFF_PTR( vtxHdr, CHILD_AT( vtxStripGroup, pVertex, 0 ) );
  						size_t offVertexEnd = BYTE_DIFF_PTR( vtxHdr, CHILD_AT( vtxStripGroup, pVertex, vtxStripGroup->numVerts ) );
  
  						if ( offIndex < vtxOffIndexBuffer )
  							vtxOffIndexBuffer = offIndex;
  						if ( offIndexEnd > vtxOffIndexBufferEnd )
  							vtxOffIndexBufferEnd = offIndexEnd;
  						if ( offVertex < vtxOffVertexBuffer )
  							vtxOffVertexBuffer = offVertex;
  						if ( offVertexEnd > vtxOffVertexBufferEnd )
  							vtxOffVertexBufferEnd = offVertexEnd;
  
  						if ( !vtxLod_idx )
  						{
  							vtxOffIndex.AddToTail( offIndex );
  							vtxOffIndex.AddToTail( offIndexEnd );
  							vtxOffVertex.AddToTail( offVertex );
  							vtxOffVertex.AddToTail( offVertexEnd );
  						}
  
  					ITERATE_END
  				ITERATE_END
  			ITERATE_END
  
  		ITERATE_END
  	ITERATE_END
  
  	// Fixup the vertex buffer
  	DECLARE_PTR( OptimizedModel::Vertex_t, vtxVertexBuffer, BYTE_OFF_PTR( vtxHdr, vtxOffVertexBuffer ) );
  	DECLARE_PTR( OptimizedModel::Vertex_t, vtxVertexBufferEnd, BYTE_OFF_PTR( vtxHdr, vtxOffVertexBufferEnd ) );
  	CUtlVector< int > vtxIndexDeltas;
  	vtxIndexDeltas.EnsureCapacity( vtxVertexBufferEnd - vtxVertexBuffer );
  	int vtxNumVertexRemoved = 0;
  	for ( OptimizedModel::Vertex_t *vtxVertexElement = vtxVertexBuffer; vtxVertexElement < vtxVertexBufferEnd; ++ vtxVertexElement )
  	{
  		size_t const off = BYTE_DIFF_PTR( vtxHdr, vtxVertexElement );
  		bool bUsed = false;
  		for ( int k = 0; k < vtxOffVertex.Count(); k += 2 )
  		{
  			if ( off >= vtxOffVertex[ k ] && off < vtxOffVertex[ k + 1 ] )
  			{
  				bUsed = true;
  				break;
  			}
  		}
  		if ( !bUsed )
  		{
  			// Index is not in use
  			vtxRemove.RemoveElements( vtxVertexElement );
  			vtxIndexDeltas.AddToTail( 0 );
  			vtxNumVertexRemoved ++;
  		}
  		else
  		{	// Index is in use and must be remapped
  			// Find the mesh where this index belongs
  			int iMesh = arrMdlOffsets.FindLessOrEqual( MdlRangeItem( 0, 0, vtxVertexElement - vtxVertexBuffer ) );
  			Assert( iMesh >= 0 && iMesh < arrMdlOffsets.Count() );
  			
  			MdlRangeItem &mri = arrMdlOffsets[ iMesh ];
  			Assert( ( vtxVertexElement - vtxVertexBuffer >= mri.m_offNew ) && ( vtxVertexElement - vtxVertexBuffer < mri.m_offNew + mri.m_numNew ) );
  			
  			Assert( m_vtxVerts.IsBitSet( vtxVertexElement->origMeshVertID + mri.m_offOld ) );
  			vtxVertexElement->origMeshVertID = srcIndices.Find( vtxVertexElement->origMeshVertID + mri.m_offOld ) - mri.m_offNew;
  			Assert( vtxVertexElement->origMeshVertID < mri.m_numNew );
  			vtxIndexDeltas.AddToTail( vtxNumVertexRemoved );
  		}
  	}
  
  	// Fixup the index buffer
  	DECLARE_PTR( unsigned short, vtxIndexBuffer, BYTE_OFF_PTR( vtxHdr, vtxOffIndexBuffer ) );
  	DECLARE_PTR( unsigned short, vtxIndexBufferEnd, BYTE_OFF_PTR( vtxHdr, vtxOffIndexBufferEnd ) );
  	for ( unsigned short *vtxIndexElement = vtxIndexBuffer; vtxIndexElement < vtxIndexBufferEnd; ++ vtxIndexElement )
  	{
  		size_t const off = BYTE_DIFF_PTR( vtxHdr, vtxIndexElement );
  		bool bUsed = false;
  		for ( int k = 0; k < vtxOffIndex.Count(); k += 2 )
  		{
  			if ( off >= vtxOffIndex[ k ] && off < vtxOffIndex[ k + 1 ] )
  			{
  				bUsed = true;
  				break;
  			}
  		}
  		if ( !bUsed )
  		{
  			// Index is not in use
  			vtxRemove.RemoveElements( vtxIndexElement );
  		}
  		else
  		{
  			// Index is in use and must be remapped
  			*vtxIndexElement -= vtxIndexDeltas[ *vtxIndexElement ];
  		}
  	}
  
  	// By now should have scheduled all removal information
  	vtxRemove.Finalize();
  	DLog( "mdllib", 3, " Stripped %d vtx bytes.\n", vtxRemove.GetNumBytesRemoved() );
  
  	//
  	// Fixup all the offsets
  	//
  	ITERATE_CHILDREN( OptimizedModel::MaterialReplacementListHeader_t, vtxMatList, vtxHdr, pMaterialReplacementList, numLODs )
  		ITERATE_CHILDREN( OptimizedModel::MaterialReplacementHeader_t, vtxMat, vtxMatList, pMaterialReplacement, numReplacements )
  			vtxMat->replacementMaterialNameOffset = vtxRemove.ComputeOffset( vtxMat, vtxMat->replacementMaterialNameOffset );
  		ITERATE_END
  		vtxMatList->replacementOffset = vtxRemove.ComputeOffset( vtxMatList, vtxMatList->replacementOffset );
  	ITERATE_END
  	ITERATE_CHILDREN( OptimizedModel::BodyPartHeader_t, vtxBodyPart, vtxHdr, pBodyPart, numBodyParts )
  		ITERATE_CHILDREN( OptimizedModel::ModelHeader_t, vtxModel, vtxBodyPart, pModel, numModels )
  			ITERATE_CHILDREN( OptimizedModel::ModelLODHeader_t, vtxLod, vtxModel, pLOD, numLODs )
  				ITERATE_CHILDREN( OptimizedModel::MeshHeader_t, vtxMesh, vtxLod, pMesh, numMeshes )
  					ITERATE_CHILDREN( OptimizedModel::StripGroupHeader_t, vtxStripGroup, vtxMesh, pStripGroup, numStripGroups )
  						
  						ITERATE_CHILDREN( OptimizedModel::StripHeader_t, vtxStrip, vtxStripGroup, pStrip, numStrips )
  							vtxStrip->indexOffset =
  								vtxRemove.ComputeOffset( vtxStripGroup, vtxStripGroup->indexOffset + vtxStrip->indexOffset ) -
  								vtxRemove.ComputeOffset( vtxStripGroup, vtxStripGroup->indexOffset );
  							vtxStrip->vertOffset =
  								vtxRemove.ComputeOffset( vtxStripGroup, vtxStripGroup->vertOffset + vtxStrip->vertOffset ) -
  								vtxRemove.ComputeOffset( vtxStripGroup, vtxStripGroup->vertOffset );
  							vtxStrip->boneStateChangeOffset = vtxRemove.ComputeOffset( vtxStrip, vtxStrip->boneStateChangeOffset );
  						ITERATE_END
  
  						vtxStripGroup->vertOffset = vtxRemove.ComputeOffset( vtxStripGroup, vtxStripGroup->vertOffset );
  						vtxStripGroup->indexOffset = vtxRemove.ComputeOffset( vtxStripGroup, vtxStripGroup->indexOffset );
  						vtxStripGroup->stripOffset = vtxRemove.ComputeOffset( vtxStripGroup, vtxStripGroup->stripOffset );
  					ITERATE_END
  					vtxMesh->stripGroupHeaderOffset = vtxRemove.ComputeOffset( vtxMesh, vtxMesh->stripGroupHeaderOffset );
  				ITERATE_END
  				vtxLod->meshOffset = vtxRemove.ComputeOffset( vtxLod, vtxLod->meshOffset );
  			ITERATE_END
  			vtxModel->lodOffset = vtxRemove.ComputeOffset( vtxModel, vtxModel->lodOffset );
  			vtxModel->numLODs = 1;
  		ITERATE_END
  		vtxBodyPart->modelOffset = vtxRemove.ComputeOffset( vtxBodyPart, vtxBodyPart->modelOffset );
  	ITERATE_END
  	vtxHdr->materialReplacementListOffset = vtxRemove.ComputeOffset( vtxHdr, vtxHdr->materialReplacementListOffset );
  	vtxHdr->bodyPartOffset = vtxRemove.ComputeOffset( vtxHdr, vtxHdr->bodyPartOffset );
  	vtxHdr->numLODs = 1;
  
  	// Perform final memory move
  	vtxRemove.MemMove( vtxHdr, vtxLength );
  
  	vtxBuffer.SeekPut( CUtlBuffer::SEEK_CURRENT, vtxBuffer.TellGet() + vtxLength - vtxBuffer.TellPut() );
  
  	return true;
  }
  
  
  
  
  
  //////////////////////////////////////////////////////////////////////////
  //
  // Auxilliary methods
  //
  //////////////////////////////////////////////////////////////////////////
  
  void CMdlStripInfo::DeleteThis()
  {
  	delete this;
  }
  
  void CMdlStripInfo::Reset()
  {
  	m_eMode = MODE_UNINITIALIZED;
  	m_lChecksumOld = 0;
  	m_lChecksumNew = 0;
  
  	m_vtxVerts.Resize( 0 );
  	m_vtxIndices.RemoveAll();
  }
  
  