summaryrefslogtreecommitdiff
path: root/tier2
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /tier2
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'tier2')
-rw-r--r--tier2/beamsegdraw.cpp235
-rw-r--r--tier2/camerautils.cpp99
-rw-r--r--tier2/defaultfilesystem.cpp57
-rw-r--r--tier2/dmconnect.cpp62
-rw-r--r--tier2/fileutils.cpp304
-rw-r--r--tier2/keybindings.cpp143
-rw-r--r--tier2/keyvaluesmacros.cpp462
-rw-r--r--tier2/meshutils.cpp104
-rw-r--r--tier2/p4helpers.cpp138
-rw-r--r--tier2/renderutils.cpp916
-rw-r--r--tier2/riff.cpp506
-rw-r--r--tier2/soundutils.cpp237
-rw-r--r--tier2/tier2.cpp117
-rw-r--r--tier2/tier2.vpc58
-rw-r--r--tier2/util_init.cpp50
-rw-r--r--tier2/utlstreambuffer.cpp386
-rw-r--r--tier2/vconfig.cpp149
17 files changed, 4023 insertions, 0 deletions
diff --git a/tier2/beamsegdraw.cpp b/tier2/beamsegdraw.cpp
new file mode 100644
index 0000000..fca4471
--- /dev/null
+++ b/tier2/beamsegdraw.cpp
@@ -0,0 +1,235 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//===========================================================================//
+
+#include "tier2/beamsegdraw.h"
+#include "materialsystem/imaterialvar.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+//
+// CBeamSegDraw implementation.
+//
+//-----------------------------------------------------------------------------
+void CBeamSegDraw::Start( IMatRenderContext *pRenderContext, int nSegs, IMaterial *pMaterial, CMeshBuilder *pMeshBuilder, int nMeshVertCount )
+{
+ m_pRenderContext = pRenderContext;
+ Assert( nSegs >= 2 );
+
+ m_nSegsDrawn = 0;
+ m_nTotalSegs = nSegs;
+
+ if ( pMeshBuilder )
+ {
+ m_pMeshBuilder = pMeshBuilder;
+ m_nMeshVertCount = nMeshVertCount;
+ }
+ else
+ {
+ m_pMeshBuilder = NULL;
+ m_nMeshVertCount = 0;
+
+ IMesh *pMesh = m_pRenderContext->GetDynamicMesh( true, NULL, NULL, pMaterial );
+ m_Mesh.Begin( pMesh, MATERIAL_TRIANGLE_STRIP, (nSegs-1) * 2 );
+ }
+}
+
+inline void CBeamSegDraw::ComputeNormal( const Vector &vecCameraPos, const Vector &vStartPos, const Vector &vNextPos, Vector *pNormal )
+{
+ // vTangentY = line vector for beam
+ Vector vTangentY;
+ VectorSubtract( vStartPos, vNextPos, vTangentY );
+
+ // vDirToBeam = vector from viewer origin to beam
+ Vector vDirToBeam;
+ VectorSubtract( vStartPos, vecCameraPos, vDirToBeam );
+
+ // Get a vector that is perpendicular to us and perpendicular to the beam.
+ // This is used to fatten the beam.
+ CrossProduct( vTangentY, vDirToBeam, *pNormal );
+ VectorNormalizeFast( *pNormal );
+}
+
+inline void CBeamSegDraw::SpecifySeg( const Vector &vecCameraPos, const Vector &vNormal )
+{
+ // SUCKY: Need to do a fair amount more work to get the tangent owing to the averaged normal
+ Vector vDirToBeam, vTangentY;
+ VectorSubtract( m_Seg.m_vPos, vecCameraPos, vDirToBeam );
+ CrossProduct( vDirToBeam, vNormal, vTangentY );
+ VectorNormalizeFast( vTangentY );
+
+ // Build the endpoints.
+ Vector vPoint1, vPoint2;
+ VectorMA( m_Seg.m_vPos, m_Seg.m_flWidth*0.5f, vNormal, vPoint1 );
+ VectorMA( m_Seg.m_vPos, -m_Seg.m_flWidth*0.5f, vNormal, vPoint2 );
+
+ if ( m_pMeshBuilder )
+ {
+ // Specify the points.
+ m_pMeshBuilder->Position3fv( vPoint1.Base() );
+ m_pMeshBuilder->Color4f( VectorExpand( m_Seg.m_vColor ), m_Seg.m_flAlpha );
+ m_pMeshBuilder->TexCoord2f( 0, 0, m_Seg.m_flTexCoord );
+ m_pMeshBuilder->TexCoord2f( 1, 0, m_Seg.m_flTexCoord );
+ m_pMeshBuilder->TangentS3fv( vNormal.Base() );
+ m_pMeshBuilder->TangentT3fv( vTangentY.Base() );
+ m_pMeshBuilder->AdvanceVertex();
+
+ m_pMeshBuilder->Position3fv( vPoint2.Base() );
+ m_pMeshBuilder->Color4f( VectorExpand( m_Seg.m_vColor ), m_Seg.m_flAlpha );
+ m_pMeshBuilder->TexCoord2f( 0, 1, m_Seg.m_flTexCoord );
+ m_pMeshBuilder->TexCoord2f( 1, 1, m_Seg.m_flTexCoord );
+ m_pMeshBuilder->TangentS3fv( vNormal.Base() );
+ m_pMeshBuilder->TangentT3fv( vTangentY.Base() );
+ m_pMeshBuilder->AdvanceVertex();
+
+ if ( m_nSegsDrawn > 1 )
+ {
+ int nBase = ( ( m_nSegsDrawn - 2 ) * 2 ) + m_nMeshVertCount;
+
+ m_pMeshBuilder->FastIndex( nBase );
+ m_pMeshBuilder->FastIndex( nBase + 1 );
+ m_pMeshBuilder->FastIndex( nBase + 2 );
+ m_pMeshBuilder->FastIndex( nBase + 1 );
+ m_pMeshBuilder->FastIndex( nBase + 3 );
+ m_pMeshBuilder->FastIndex( nBase + 2 );
+ }
+ }
+ else
+ {
+ // Specify the points.
+ m_Mesh.Position3fv( vPoint1.Base() );
+ m_Mesh.Color4f( VectorExpand( m_Seg.m_vColor ), m_Seg.m_flAlpha );
+ m_Mesh.TexCoord2f( 0, 0, m_Seg.m_flTexCoord );
+ m_Mesh.TexCoord2f( 1, 0, m_Seg.m_flTexCoord );
+ m_Mesh.TangentS3fv( vNormal.Base() );
+ m_Mesh.TangentT3fv( vTangentY.Base() );
+ m_Mesh.AdvanceVertex();
+
+ m_Mesh.Position3fv( vPoint2.Base() );
+ m_Mesh.Color4f( VectorExpand( m_Seg.m_vColor ), m_Seg.m_flAlpha );
+ m_Mesh.TexCoord2f( 0, 1, m_Seg.m_flTexCoord );
+ m_Mesh.TexCoord2f( 1, 1, m_Seg.m_flTexCoord );
+ m_Mesh.TangentS3fv( vNormal.Base() );
+ m_Mesh.TangentT3fv( vTangentY.Base() );
+ m_Mesh.AdvanceVertex();
+ }
+}
+
+void CBeamSegDraw::NextSeg( BeamSeg_t *pSeg )
+{
+ Vector vecCameraPos;
+ m_pRenderContext->GetWorldSpaceCameraPosition( &vecCameraPos );
+
+ if ( m_nSegsDrawn > 0 )
+ {
+ // Get a vector that is perpendicular to us and perpendicular to the beam.
+ // This is used to fatten the beam.
+ Vector vNormal, vAveNormal;
+ ComputeNormal( vecCameraPos, m_Seg.m_vPos, pSeg->m_vPos, &vNormal );
+
+ if ( m_nSegsDrawn > 1 )
+ {
+ // Average this with the previous normal
+ VectorAdd( vNormal, m_vNormalLast, vAveNormal );
+ vAveNormal *= 0.5f;
+ VectorNormalizeFast( vAveNormal );
+ }
+ else
+ {
+ vAveNormal = vNormal;
+ }
+
+ m_vNormalLast = vNormal;
+ SpecifySeg( vecCameraPos, vAveNormal );
+ }
+
+ m_Seg = *pSeg;
+ ++m_nSegsDrawn;
+
+ if( m_nSegsDrawn == m_nTotalSegs )
+ {
+ SpecifySeg( vecCameraPos, m_vNormalLast );
+ }
+}
+
+void CBeamSegDraw::End()
+{
+ if ( m_pMeshBuilder )
+ {
+ m_pMeshBuilder = NULL;
+ return;
+ }
+
+ m_Mesh.End( false, true );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBeamSegDrawArbitrary::SetNormal( const Vector &normal )
+{
+ m_vNormalLast = normal;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBeamSegDrawArbitrary::NextSeg( BeamSeg_t *pSeg )
+{
+ if ( m_nSegsDrawn > 0 )
+ {
+ Vector segDir = ( m_PrevSeg.m_vPos - pSeg->m_vPos );
+ VectorNormalize( segDir );
+
+ Vector normal = CrossProduct( segDir, m_vNormalLast );
+ SpecifySeg( normal );
+ }
+
+ m_PrevSeg = m_Seg;
+ m_Seg = *pSeg;
+ ++m_nSegsDrawn;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : &vNextPos -
+//-----------------------------------------------------------------------------
+void CBeamSegDrawArbitrary::SpecifySeg( const Vector &vNormal )
+{
+ // Build the endpoints.
+ Vector vPoint1, vPoint2;
+ Vector vDelta;
+ VectorMultiply( vNormal, m_Seg.m_flWidth*0.5f, vDelta );
+ VectorAdd( m_Seg.m_vPos, vDelta, vPoint1 );
+ VectorSubtract( m_Seg.m_vPos, vDelta, vPoint2 );
+
+ // Specify the points.
+ Assert( IsFinite(m_Seg.m_vColor.x) && IsFinite(m_Seg.m_vColor.y) && IsFinite(m_Seg.m_vColor.z) && IsFinite(m_Seg.m_flAlpha) );
+ Assert( (m_Seg.m_vColor.x >= 0.0) && (m_Seg.m_vColor.y >= 0.0) && (m_Seg.m_vColor.z >= 0.0) && (m_Seg.m_flAlpha >= 0.0) );
+ Assert( (m_Seg.m_vColor.x <= 1.0) && (m_Seg.m_vColor.y <= 1.0) && (m_Seg.m_vColor.z <= 1.0) && (m_Seg.m_flAlpha <= 1.0) );
+
+ unsigned char r = FastFToC( m_Seg.m_vColor.x );
+ unsigned char g = FastFToC( m_Seg.m_vColor.y );
+ unsigned char b = FastFToC( m_Seg.m_vColor.z );
+ unsigned char a = FastFToC( m_Seg.m_flAlpha );
+ m_Mesh.Position3fv( vPoint1.Base() );
+ m_Mesh.Color4ub( r, g, b, a );
+ m_Mesh.TexCoord2f( 0, 0, m_Seg.m_flTexCoord );
+ m_Mesh.TexCoord2f( 1, 0, m_Seg.m_flTexCoord );
+ m_Mesh.AdvanceVertex();
+
+ m_Mesh.Position3fv( vPoint2.Base() );
+ m_Mesh.Color4ub( r, g, b, a );
+ m_Mesh.TexCoord2f( 0, 1, m_Seg.m_flTexCoord );
+ m_Mesh.TexCoord2f( 1, 1, m_Seg.m_flTexCoord );
+ m_Mesh.AdvanceVertex();
+}
diff --git a/tier2/camerautils.cpp b/tier2/camerautils.cpp
new file mode 100644
index 0000000..41d1dc7
--- /dev/null
+++ b/tier2/camerautils.cpp
@@ -0,0 +1,99 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+#include "tier2/camerautils.h"
+#include "tier0/dbg.h"
+#include "mathlib/vector.h"
+#include "mathlib/vmatrix.h"
+#include "tier2/tier2.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// accessors for generated matrices
+//-----------------------------------------------------------------------------
+void ComputeViewMatrix( matrix3x4_t *pWorldToCamera, const Camera_t &camera )
+{
+ matrix3x4_t transform;
+ AngleMatrix( camera.m_angles, camera.m_origin, transform );
+
+ VMatrix matRotate( transform );
+ VMatrix matRotateZ;
+ MatrixBuildRotationAboutAxis( matRotateZ, Vector(0,0,1), -90 );
+ MatrixMultiply( matRotate, matRotateZ, matRotate );
+
+ VMatrix matRotateX;
+ MatrixBuildRotationAboutAxis( matRotateX, Vector(1,0,0), 90 );
+ MatrixMultiply( matRotate, matRotateX, matRotate );
+ transform = matRotate.As3x4();
+
+ MatrixInvert( transform, *pWorldToCamera );
+}
+
+void ComputeViewMatrix( VMatrix *pWorldToCamera, const Camera_t &camera )
+{
+ matrix3x4_t transform, invTransform;
+ AngleMatrix( camera.m_angles, camera.m_origin, transform );
+
+ VMatrix matRotate( transform );
+ VMatrix matRotateZ;
+ MatrixBuildRotationAboutAxis( matRotateZ, Vector(0,0,1), -90 );
+ MatrixMultiply( matRotate, matRotateZ, matRotate );
+
+ VMatrix matRotateX;
+ MatrixBuildRotationAboutAxis( matRotateX, Vector(1,0,0), 90 );
+ MatrixMultiply( matRotate, matRotateX, matRotate );
+ transform = matRotate.As3x4();
+
+ MatrixInvert( transform, invTransform );
+ *pWorldToCamera = invTransform;
+}
+
+void ComputeProjectionMatrix( VMatrix *pCameraToProjection, const Camera_t &camera, int width, int height )
+{
+ float flFOV = camera.m_flFOV;
+ float flZNear = camera.m_flZNear;
+ float flZFar = camera.m_flZFar;
+ float flApsectRatio = (float)width / (float)height;
+
+// MatrixBuildPerspective( proj, flFOV, flFOV * flApsectRatio, flZNear, flZFar );
+
+#if 1
+ float halfWidth = tan( flFOV * M_PI / 360.0 );
+ float halfHeight = halfWidth / flApsectRatio;
+#else
+ float halfHeight = tan( flFOV * M_PI / 360.0 );
+ float halfWidth = flApsectRatio * halfHeight;
+#endif
+ memset( pCameraToProjection, 0, sizeof( VMatrix ) );
+ pCameraToProjection->m[0][0] = 1.0f / halfWidth;
+ pCameraToProjection->m[1][1] = 1.0f / halfHeight;
+ pCameraToProjection->m[2][2] = flZFar / ( flZNear - flZFar );
+ pCameraToProjection->m[3][2] = -1.0f;
+ pCameraToProjection->m[2][3] = flZNear * flZFar / ( flZNear - flZFar );
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes the screen space position given a screen size
+//-----------------------------------------------------------------------------
+void ComputeScreenSpacePosition( Vector2D *pScreenPosition, const Vector &vecWorldPosition,
+ const Camera_t &camera, int width, int height )
+{
+ VMatrix view, proj, viewproj;
+ ComputeViewMatrix( &view, camera );
+ ComputeProjectionMatrix( &proj, camera, width, height );
+ MatrixMultiply( proj, view, viewproj );
+
+ Vector vecScreenPos;
+ Vector3DMultiplyPositionProjective( viewproj, vecWorldPosition, vecScreenPos );
+
+ pScreenPosition->x = ( vecScreenPos.x + 1.0f ) * width / 2.0f;
+ pScreenPosition->y = ( -vecScreenPos.y + 1.0f ) * height / 2.0f;
+}
+
+
diff --git a/tier2/defaultfilesystem.cpp b/tier2/defaultfilesystem.cpp
new file mode 100644
index 0000000..b3a1662
--- /dev/null
+++ b/tier2/defaultfilesystem.cpp
@@ -0,0 +1,57 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: A higher level link library for general use in the game and tools.
+//
+//===========================================================================//
+
+#include <tier0/platform.h>
+#include <tier2/tier2.h>
+#include <filesystem_init.h>
+
+
+static CSysModule *g_pFullFileSystemModule = NULL;
+
+void* DefaultCreateInterfaceFn(const char *pName, int *pReturnCode)
+{
+ if ( pReturnCode )
+ {
+ *pReturnCode = 0;
+ }
+ return NULL;
+}
+
+void InitDefaultFileSystem( void )
+{
+ AssertMsg( !g_pFullFileSystem, "Already set up the file system" );
+
+ if ( !Sys_LoadInterface( "filesystem_stdio", FILESYSTEM_INTERFACE_VERSION,
+ &g_pFullFileSystemModule, (void**)&g_pFullFileSystem ) )
+ {
+ if ( !Sys_LoadInterface( "filesystem_steam", FILESYSTEM_INTERFACE_VERSION,
+ &g_pFullFileSystemModule, (void**)&g_pFullFileSystem ) )
+ {
+ exit(0);
+ }
+ }
+
+ if ( !g_pFullFileSystem->Connect( DefaultCreateInterfaceFn ) )
+ {
+ exit(0);
+ }
+
+ if ( g_pFullFileSystem->Init() != INIT_OK )
+ {
+ exit(0);
+ }
+
+ g_pFullFileSystem->RemoveAllSearchPaths();
+ g_pFullFileSystem->AddSearchPath( "", "LOCAL", PATH_ADD_TO_HEAD );
+}
+
+void ShutdownDefaultFileSystem(void)
+{
+ AssertMsg( g_pFullFileSystem, "File system not set up" );
+ g_pFullFileSystem->Shutdown();
+ g_pFullFileSystem->Disconnect();
+ Sys_UnloadModule( g_pFullFileSystemModule );
+}
diff --git a/tier2/dmconnect.cpp b/tier2/dmconnect.cpp
new file mode 100644
index 0000000..b8b41f1
--- /dev/null
+++ b/tier2/dmconnect.cpp
@@ -0,0 +1,62 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: A higher level link library for general use in the game and tools.
+//
+//===========================================================================//
+
+#include <tier2/tier2.h>
+#include "datamodel/idatamodel.h"
+#include "dmserializers/idmserializers.h"
+
+
+//-----------------------------------------------------------------------------
+// Set up methods related to datamodel interfaces
+//-----------------------------------------------------------------------------
+bool ConnectDataModel( CreateInterfaceFn factory )
+{
+ if ( !g_pDataModel->Connect( factory ) )
+ return false;
+
+ if ( !g_pDmElementFramework->Connect( factory ) )
+ return false;
+
+ if ( !g_pDmSerializers->Connect( factory ) )
+ return false;
+
+ return true;
+}
+
+InitReturnVal_t InitDataModel()
+{
+ InitReturnVal_t nRetVal;
+
+ nRetVal = g_pDataModel->Init( );
+ if ( nRetVal != INIT_OK )
+ return nRetVal;
+
+ nRetVal = g_pDmElementFramework->Init();
+ if ( nRetVal != INIT_OK )
+ return nRetVal;
+
+ nRetVal = g_pDmSerializers->Init();
+ if ( nRetVal != INIT_OK )
+ return nRetVal;
+
+ return INIT_OK;
+}
+
+void ShutdownDataModel()
+{
+ g_pDmSerializers->Shutdown();
+ g_pDmElementFramework->Shutdown();
+ g_pDataModel->Shutdown( );
+}
+
+void DisconnectDataModel()
+{
+ g_pDmSerializers->Disconnect();
+ g_pDmElementFramework->Disconnect();
+ g_pDataModel->Disconnect();
+}
+
+
diff --git a/tier2/fileutils.cpp b/tier2/fileutils.cpp
new file mode 100644
index 0000000..2a0ca59
--- /dev/null
+++ b/tier2/fileutils.cpp
@@ -0,0 +1,304 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Helper methods + classes for file access
+//
+//===========================================================================//
+
+#include "tier2/fileutils.h"
+#include "tier2/tier2.h"
+#include "tier1/strtools.h"
+#include "filesystem.h"
+#include "tier0/icommandline.h"
+#include "tier1/utlbuffer.h"
+
+// NOTE: This has to be the last file included!
+#include "tier0/memdbgon.h"
+
+//-----------------------------------------------------------------------------
+// Builds a directory which is a subdirectory of the current mod
+//-----------------------------------------------------------------------------
+void GetModSubdirectory( const char *pSubDir, char *pBuf, int nBufLen )
+{
+ // Compute starting directory
+ Assert( g_pFullFileSystem->GetSearchPath( "MOD_WRITE", false, NULL, 0 ) < nBufLen );
+ if ( g_pFullFileSystem->GetSearchPath( "MOD_WRITE", false, pBuf, nBufLen ) == 0 )
+ {
+ // if we didn't find MOD_WRITE, back to the old MOD
+ Assert( g_pFullFileSystem->GetSearchPath( "MOD", false, NULL, 0 ) < nBufLen );
+ g_pFullFileSystem->GetSearchPath( "MOD", false, pBuf, nBufLen );
+ }
+
+ char *pSemi = strchr( pBuf, ';' );
+ if ( pSemi )
+ {
+ *pSemi = 0;
+ }
+
+ Q_StripTrailingSlash( pBuf );
+ if ( pSubDir )
+ {
+ int nLen = Q_strlen( pSubDir );
+ Q_strncat( pBuf, "\\", nBufLen, 1 );
+ Q_strncat( pBuf, pSubDir, nBufLen, nLen );
+ }
+
+ Q_FixSlashes( pBuf );
+}
+
+
+//-----------------------------------------------------------------------------
+// Builds a directory which is a subdirectory of the current mod's *content*
+//-----------------------------------------------------------------------------
+void GetModContentSubdirectory( const char *pSubDir, char *pBuf, int nBufLen )
+{
+ char pTemp[ MAX_PATH ];
+ GetModSubdirectory( pSubDir, pTemp, sizeof(pTemp) );
+ ComputeModContentFilename( pTemp, pBuf, nBufLen );
+}
+
+
+//-----------------------------------------------------------------------------
+// Generates a filename under the 'game' subdirectory given a subdirectory of 'content'
+//-----------------------------------------------------------------------------
+void ComputeModFilename( const char *pContentFileName, char *pBuf, size_t nBufLen )
+{
+ char pRelativePath[ MAX_PATH ];
+ if ( !g_pFullFileSystem->FullPathToRelativePathEx( pContentFileName, "CONTENTROOT", pRelativePath, sizeof(pRelativePath) ) )
+ {
+ Q_strncpy( pBuf, pContentFileName, (int)nBufLen );
+ return;
+ }
+
+ char pGameRoot[ MAX_PATH ];
+ g_pFullFileSystem->GetSearchPath( "GAMEROOT", false, pGameRoot, sizeof(pGameRoot) );
+ char *pSemi = strchr( pGameRoot, ';' );
+ if ( pSemi )
+ {
+ *pSemi = 0;
+ }
+
+ Q_ComposeFileName( pGameRoot, pRelativePath, pBuf, (int)nBufLen );
+}
+
+
+//-----------------------------------------------------------------------------
+// Generates a filename under the 'content' subdirectory given a subdirectory of 'game'
+//-----------------------------------------------------------------------------
+void ComputeModContentFilename( const char *pGameFileName, char *pBuf, size_t nBufLen )
+{
+ char pRelativePath[ MAX_PATH ];
+ if ( !g_pFullFileSystem->FullPathToRelativePathEx( pGameFileName, "GAMEROOT", pRelativePath, sizeof(pRelativePath) ) )
+ {
+ Q_strncpy( pBuf, pGameFileName, (int)nBufLen );
+ return;
+ }
+
+ char pContentRoot[ MAX_PATH ];
+ g_pFullFileSystem->GetSearchPath( "CONTENTROOT", false, pContentRoot, sizeof(pContentRoot) );
+ char *pSemi = strchr( pContentRoot, ';' );
+ if ( pSemi )
+ {
+ *pSemi = 0;
+ }
+
+ Q_ComposeFileName( pContentRoot, pRelativePath, pBuf, (int)nBufLen );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Generates an Xbox 360 filename from a PC filename
+//-----------------------------------------------------------------------------
+char *CreateX360Filename( const char *pSourceName, char *pTargetName, int targetLen )
+{
+ Q_StripExtension( pSourceName, pTargetName, targetLen );
+ int idx = Q_strlen( pTargetName );
+
+ // restore extension
+ Q_snprintf( pTargetName, targetLen, "%s.360%s", pTargetName, &pSourceName[idx] );
+
+ return pTargetName;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Generates a PC filename from a possible 360 name.
+// Strips the .360. from filename.360.extension.
+// Filenames might have multiple '.', need to be careful and only consider the
+// last true extension. Complex filenames do occur:
+// d:\foo\.\foo.dat
+// d:\zip0.360.zip\foo.360.dat
+// Returns source if no change needs to occur, othwerwise generates and
+// returns target.
+//-----------------------------------------------------------------------------
+char *RestoreFilename( const char *pSourceName, char *pTargetName, int targetLen )
+{
+ // find extension
+ // scan backward for '.', but not past a seperator
+ int end = V_strlen( pSourceName ) - 1;
+ while ( end > 0 && pSourceName[end] != '.' && !( pSourceName[end] == '\\' || pSourceName[end] == '/' ) )
+ {
+ --end;
+ }
+
+ if ( end >= 4 && pSourceName[end] == '.' && !V_strncmp( pSourceName + end - 4 , ".360", 4 ) )
+ {
+ // cull the .360, leave the trailing extension
+ end -= 4;
+ int length = MIN( end + 1, targetLen );
+ V_strncpy( pTargetName, pSourceName, length );
+ V_strncat( pTargetName, pSourceName + end + 4, targetLen );
+
+ return pTargetName;
+ }
+
+ // source filename is as expected
+ return (char *)pSourceName;
+}
+
+//-----------------------------------------------------------------------------
+// Generate an Xbox 360 file if it doesn't exist or is out of date. This function determines
+// the source and target path and whether the file needs to be generated. The caller provides
+// a callback function to do the actual creation of the 360 file. "pExtraData" is for the caller to
+// pass the address of any data that the callback function may need to access. This function
+// is ONLY to be called by caller's who expect to have 360 versions of their file.
+//-----------------------------------------------------------------------------
+int UpdateOrCreate( const char *pSourceName, char *pTargetName, int targetLen, const char *pPathID, CreateCallback_t pfnCreate, bool bForce, void *pExtraData )
+{
+ if ( pTargetName )
+ {
+ // caller could supply source as PC or 360 name, we want the PC filename
+ char szFixedSourceName[MAX_PATH];
+ pSourceName = RestoreFilename( pSourceName, szFixedSourceName, sizeof( szFixedSourceName ) );
+ // caller wants us to provide 360 named version of source
+ CreateX360Filename( pSourceName, pTargetName, targetLen );
+ }
+
+ // no conversion are performed by the game at runtime anymore
+ // SMB access was removed by the XDK for Vista....
+ return UOC_NOT_CREATED;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the search path as a list of paths
+//-----------------------------------------------------------------------------
+void GetSearchPath( CUtlVector< CUtlString > &path, const char *pPathID )
+{
+ int nMaxLen = g_pFullFileSystem->GetSearchPath( pPathID, false, NULL, 0 );
+ char *pBuf = (char*)stackalloc( nMaxLen );
+ g_pFullFileSystem->GetSearchPath( pPathID, false, pBuf, nMaxLen );
+
+ char *pSemi;
+ while ( NULL != ( pSemi = strchr( pBuf, ';' ) ) )
+ {
+ *pSemi = 0;
+ path.AddToTail( pBuf );
+ pBuf = pSemi + 1;
+ }
+ path.AddToTail( pBuf );
+}
+
+//-----------------------------------------------------------------------------
+// Given file name in the current dir generate a full path to it.
+//-----------------------------------------------------------------------------
+bool GenerateFullPath( const char *pFileName, char const *pPathID, char *pBuf, int nBufLen )
+{
+ if ( V_IsAbsolutePath( pFileName ) )
+ {
+ V_strncpy( pBuf, pFileName, nBufLen );
+ return true;
+ }
+
+ const char *pFullPath = g_pFullFileSystem->RelativePathToFullPath( pFileName, pPathID, pBuf, nBufLen );
+ if ( pFullPath && Q_IsAbsolutePath( pFullPath ) )
+ return true;
+
+ char pDir[ MAX_PATH ];
+ if ( !g_pFullFileSystem->GetCurrentDirectory( pDir, sizeof( pDir ) ) )
+ return false;
+
+ V_ComposeFileName( pDir, pFileName, pBuf, nBufLen );
+ V_RemoveDotSlashes( pBuf );
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Builds a list of all files under a directory with a particular extension
+//-----------------------------------------------------------------------------
+void AddFilesToList( CUtlVector< CUtlString > &list, const char *pDirectory, const char *pPathID, const char *pExtension )
+{
+ char pSearchString[MAX_PATH];
+ Q_snprintf( pSearchString, MAX_PATH, "%s\\*", pDirectory );
+
+ bool bIsAbsolute = Q_IsAbsolutePath( pDirectory );
+
+ // get the list of files
+ FileFindHandle_t hFind;
+ const char *pFoundFile = g_pFullFileSystem->FindFirstEx( pSearchString, pPathID, &hFind );
+
+ // add all the items
+ CUtlVector< CUtlString > subDirs;
+ for ( ; pFoundFile; pFoundFile = g_pFullFileSystem->FindNext( hFind ) )
+ {
+ char pChildPath[MAX_PATH];
+ Q_snprintf( pChildPath, MAX_PATH, "%s\\%s", pDirectory, pFoundFile );
+
+ if ( g_pFullFileSystem->FindIsDirectory( hFind ) )
+ {
+ if ( Q_strnicmp( pFoundFile, ".", 2 ) && Q_strnicmp( pFoundFile, "..", 3 ) )
+ {
+ subDirs.AddToTail( pChildPath );
+ }
+ continue;
+ }
+
+ // Check the extension matches
+ const char *pExt = Q_GetFileExtension( pFoundFile );
+ if ( !pExt || Q_stricmp( pExt, pExtension ) != 0 )
+ continue;
+
+ char pFullPathBuf[MAX_PATH];
+ char *pFullPath = pFullPathBuf;
+ if ( !bIsAbsolute )
+ {
+ g_pFullFileSystem->RelativePathToFullPath( pChildPath, pPathID, pFullPathBuf, sizeof(pFullPathBuf) );
+ }
+ else
+ {
+ pFullPath = pChildPath;
+ }
+
+ V_strlower( pFullPath );
+ Q_FixSlashes( pFullPath );
+ list.AddToTail( pFullPath );
+ }
+
+ g_pFullFileSystem->FindClose( hFind );
+
+ int nCount = subDirs.Count();
+ for ( int i = 0; i < nCount; ++i )
+ {
+ AddFilesToList( list, subDirs[i], pPathID, pExtension );
+ }
+}
+
+void CBaseFile::ReadLines( CUtlStringList &lineList, int nMaxLineLength )
+{
+ char *pLine = ( char * ) stackalloc( nMaxLineLength );
+ while( ReadLine( pLine, nMaxLineLength ) )
+ {
+ char *pEOL = strchr( pLine, '\n' ); // kill the \n
+ if ( pEOL )
+ *pEOL = 0;
+ lineList.CopyAndAddToTail( pLine );
+ }
+}
+
+void CBaseFile::ReadFile( CUtlBuffer &fileData )
+{
+ int nFileSize = Size();
+ fileData.EnsureCapacity( Size() );
+ int nSize = Read( fileData.Base(), nFileSize );
+ fileData.SeekPut( CUtlBuffer::SEEK_HEAD, nSize );
+}
+
+
diff --git a/tier2/keybindings.cpp b/tier2/keybindings.cpp
new file mode 100644
index 0000000..d2c9257
--- /dev/null
+++ b/tier2/keybindings.cpp
@@ -0,0 +1,143 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//===========================================================================//
+
+#include "tier2/keybindings.h"
+#include "tier2/tier2.h"
+#include "inputsystem/iinputsystem.h"
+#include "tier1/utlbuffer.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+
+//-----------------------------------------------------------------------------
+// Set a key binding
+//-----------------------------------------------------------------------------
+void CKeyBindings::SetBinding( ButtonCode_t code, const char *pBinding )
+{
+ if ( code == BUTTON_CODE_INVALID || code == KEY_NONE )
+ return;
+
+ // free old bindings
+ if ( !m_KeyInfo[code].IsEmpty() )
+ {
+ // Exactly the same, don't re-bind and fragment memory
+ if ( !Q_stricmp( m_KeyInfo[code], pBinding ) )
+ return;
+ }
+
+ // allocate memory for new binding
+ m_KeyInfo[code] = pBinding;
+}
+
+void CKeyBindings::SetBinding( const char *pButtonName, const char *pBinding )
+{
+ ButtonCode_t code = g_pInputSystem->StringToButtonCode( pButtonName );
+ SetBinding( code, pBinding );
+}
+
+void CKeyBindings::Unbind( ButtonCode_t code )
+{
+ if ( code != KEY_NONE && code != BUTTON_CODE_INVALID )
+ {
+ m_KeyInfo[code] = "";
+ }
+}
+
+void CKeyBindings::Unbind( const char *pButtonName )
+{
+ ButtonCode_t code = g_pInputSystem->StringToButtonCode( pButtonName );
+ Unbind( code );
+}
+
+void CKeyBindings::UnbindAll()
+{
+ for ( int i = 0; i < BUTTON_CODE_LAST; i++ )
+ {
+ m_KeyInfo[i] = "";
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Count number of lines of bindings we'll be writing
+//-----------------------------------------------------------------------------
+int CKeyBindings::GetBindingCount( ) const
+{
+ int nCount = 0;
+ for ( int i = 0; i < BUTTON_CODE_LAST; i++ )
+ {
+ if ( !m_KeyInfo[i].IsEmpty() )
+ {
+ nCount++;
+ }
+ }
+
+ return nCount;
+}
+
+
+//-----------------------------------------------------------------------------
+// Writes lines containing "bind key value"
+//-----------------------------------------------------------------------------
+void CKeyBindings::WriteBindings( CUtlBuffer &buf )
+{
+ for ( int i = 0; i < BUTTON_CODE_LAST; i++ )
+ {
+ if ( !m_KeyInfo[i].IsEmpty() )
+ {
+ const char *pButtonCode = g_pInputSystem->ButtonCodeToString( (ButtonCode_t)i );
+ buf.Printf( "bind \"%s\" \"%s\"\n", pButtonCode, m_KeyInfo[i].Get() );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the keyname to which a binding string is bound. E.g., if
+// TAB is bound to +use then searching for +use will return "TAB"
+//-----------------------------------------------------------------------------
+const char *CKeyBindings::ButtonNameForBinding( const char *pBinding )
+{
+ const char *pBind = pBinding;
+ if ( pBinding[0] == '+' )
+ {
+ ++pBind;
+ }
+
+ for ( int i = 0; i < BUTTON_CODE_LAST; i++ )
+ {
+ if ( m_KeyInfo[i].IsEmpty() )
+ continue;
+
+ if ( m_KeyInfo[i][0] == '+' )
+ {
+ if ( !Q_stricmp( &m_KeyInfo[i].Get()[1], pBind ) )
+ return g_pInputSystem->ButtonCodeToString( (ButtonCode_t)i );
+ }
+ else
+ {
+ if ( !Q_stricmp( m_KeyInfo[i], pBind ) )
+ return g_pInputSystem->ButtonCodeToString( (ButtonCode_t)i );
+ }
+ }
+
+ return NULL;
+}
+
+const char *CKeyBindings::GetBindingForButton( ButtonCode_t code )
+{
+ if ( m_KeyInfo[code].IsEmpty() )
+ return NULL;
+
+ return m_KeyInfo[ code ];
+}
+
+
+
diff --git a/tier2/keyvaluesmacros.cpp b/tier2/keyvaluesmacros.cpp
new file mode 100644
index 0000000..11a5c97
--- /dev/null
+++ b/tier2/keyvaluesmacros.cpp
@@ -0,0 +1,462 @@
+//===================== Copyright (c) Valve Corporation. All Rights Reserved. ======================
+//
+//==================================================================================================
+
+
+#include "filesystem.h"
+#include "tier1/KeyValues.h"
+#include "tier2/keyvaluesmacros.h"
+
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//--------------------------------------------------------------------------------------------------
+// Returns true if the passed string matches the filename style glob, false otherwise
+// * matches any characters, ? matches any single character, otherwise case insensitive matching
+//--------------------------------------------------------------------------------------------------
+bool GlobMatch( const char *pszGlob, const char *pszString )
+{
+ while ( ( *pszString != '\0' ) && ( *pszGlob != '*' ) )
+ {
+ if ( ( V_strnicmp( pszGlob, pszString, 1 ) != 0 ) && ( *pszGlob != '?' ) )
+ {
+ return false;
+ }
+
+ ++pszGlob;
+ ++pszString;
+ }
+
+ const char *pszGlobTmp = nullptr;
+ const char *pszStringTmp = nullptr;
+
+ while ( *pszString )
+ {
+ if ( *pszGlob == '*' )
+ {
+ ++pszGlob;
+
+ if ( *pszGlob == '\0' )
+ {
+ return true;
+ }
+
+ pszGlobTmp = pszGlob;
+ pszStringTmp = pszString + 1;
+ }
+ else if ( ( V_strnicmp( pszGlob, pszString, 1 ) == 0 ) || ( *pszGlob == '?' ) )
+ {
+ ++pszGlob;
+ ++pszString;
+ }
+ else
+ {
+ pszGlob = pszGlobTmp;
+ pszString = pszStringTmp++;
+ }
+ }
+
+ while ( *pszGlob == '*' )
+ {
+ ++pszGlob;
+ }
+
+ return *pszGlob == '\0';
+}
+
+
+//--------------------------------------------------------------------------------------------------
+// Inserts pkvToInsert after pkvAfter but setting pkvAfter's NextKey to pkvInsert
+//--------------------------------------------------------------------------------------------------
+static void InsertKeyValuesAfter( KeyValues *pkvAfter, KeyValues *pkvToInsert )
+{
+ Assert( pkvAfter );
+ Assert( pkvToInsert );
+
+ pkvToInsert->SetNextKey( pkvAfter->GetNextKey() );
+ pkvAfter->SetNextKey( pkvToInsert );
+}
+
+
+//--------------------------------------------------------------------------------------------------
+//
+//--------------------------------------------------------------------------------------------------
+static KeyValues *ReplaceSubKeyWithCopy( KeyValues *pkvParent, KeyValues *pkvToReplace, KeyValues *pkvReplaceWith )
+{
+ Assert( pkvReplaceWith->GetFirstSubKey() == nullptr );
+
+ KeyValues *pkvCopy = pkvReplaceWith->MakeCopy();
+ Assert( pkvCopy->GetFirstSubKey() == nullptr );
+ Assert( pkvCopy->GetNextKey() == nullptr );
+
+ InsertKeyValuesAfter( pkvToReplace, pkvCopy );
+ pkvParent->RemoveSubKey( pkvToReplace );
+ pkvToReplace->deleteThis();
+
+ return pkvCopy;
+}
+
+
+//--------------------------------------------------------------------------------------------------
+// Handles a KeyValues #insert macro. Replaces the #insert KeyValues with the specified file
+// if it can be loaded. This is not called #include because base KeyValue's already has #include
+// but it has two issues. The #include is relative to the keyvalues, these paths are resolved
+// normally via IFileSystem and #include only works at the top level, #insert works no matter
+// how deep the #insert macro is
+//--------------------------------------------------------------------------------------------------
+static KeyValues *HandleKeyValuesMacro_Insert( KeyValues *pkvInsert, KeyValues *pkvParent )
+{
+ const char *pszName = pkvInsert->GetName();
+
+ if ( V_stricmp( "#insert", pszName ) != 0 )
+ return nullptr;
+
+ // Have an #insert key
+
+ if ( pkvInsert->GetFirstSubKey() )
+ {
+ // Invalid, has sub keys
+ Msg( "Error: #insert on key with subkeys, can only do #insert with simple key/value with string value\n" );
+ return nullptr;
+ }
+
+ if ( pkvInsert->GetDataType() != KeyValues::TYPE_STRING )
+ {
+ // Invalid, value isn't a string
+ Msg( "Error: #insert on key without a data type of string, can only do #insert with simple key/value with string value\n" );
+ return nullptr;
+ }
+
+ const char *pszInsert = pkvInsert->GetString();
+ if ( !pszInsert && *pszInsert == '\0' )
+ {
+ // Invalid, value is empty string
+ Msg( "Error: #insert on key with empty string value, can only do #insert with simple key/value with string value\n" );
+ return nullptr;
+ }
+
+ FileHandle_t f = g_pFullFileSystem->Open( pszInsert, "rb" );
+ if ( !f )
+ {
+ // Invalid, couldn't open #insert file
+ Msg( "Error: #insert couldn't open file: %s\n", pszInsert );
+ return nullptr;
+ }
+
+ uint nFileSize = g_pFullFileSystem->Size( f );
+ if ( nFileSize == 0 )
+ {
+ // Invalid, empty file
+ Msg( "Error: #insert empty file: %s\n", pszInsert );
+ return nullptr;
+ }
+
+ uint nBufSize = g_pFullFileSystem->GetOptimalReadSize( f, nFileSize + 2 /* null termination */ + 8 /* "\"x\"\n{\n}\n" */ );
+ char *pBuf = ( char* )g_pFullFileSystem->AllocOptimalReadBuffer( f, nBufSize );
+ pBuf[0] = '"';
+ pBuf[1] = 'i';
+ pBuf[2] = '"';
+ pBuf[3] = '\n';
+ pBuf[4] = '{';
+ pBuf[5] = '\n';
+
+ bool bRetOK = ( g_pFullFileSystem->ReadEx( pBuf + 6, nBufSize - 6, nFileSize, f ) != 0 );
+
+ g_pFullFileSystem->Close( f );
+
+ KeyValues *pkvNew = nullptr;
+
+ if ( bRetOK )
+ {
+ pBuf[nFileSize + 6 + 0] = '}';
+ pBuf[nFileSize + 6 + 1] = '\n';
+ pBuf[nFileSize + 6 + 2] = '\0';
+ pBuf[nFileSize + 6 + 3] = '\0'; // Double NULL termination
+
+ pkvNew = new KeyValues( pszInsert );
+
+ bRetOK = pkvNew->LoadFromBuffer( pszInsert, pBuf, g_pFullFileSystem );
+ }
+ else
+ {
+ Msg( "Error: #insert couldn't read file: %s\n", pszInsert );
+ }
+
+ g_pFullFileSystem->FreeOptimalReadBuffer( pBuf );
+
+ KeyValues *pkvReturn = nullptr;
+
+ CUtlVector< KeyValues * > newKeyList;
+
+ if ( bRetOK )
+ {
+ KeyValues *pkvInsertAfter = pkvInsert;
+
+ KeyValues *pkvNewSubKey = pkvNew->GetFirstSubKey();
+ pkvReturn = pkvNewSubKey;
+
+ while ( pkvNewSubKey )
+ {
+ KeyValues *pkvNextNewSubKey = pkvNewSubKey->GetNextKey();
+
+ pkvNew->RemoveSubKey( pkvNewSubKey );
+
+ bool bFound = false;
+
+ if ( pkvNewSubKey->GetFirstSubKey() == nullptr )
+ {
+ for ( KeyValues *pkvChild = pkvParent->GetFirstSubKey(); pkvChild; pkvChild = pkvChild->GetNextKey() )
+ {
+ if ( pkvChild == pkvInsert )
+ continue;
+
+ if ( pkvChild->GetNameSymbol() == pkvNewSubKey->GetNameSymbol() )
+ {
+ bFound = true;
+ break;
+ }
+ }
+ }
+
+ if ( !bFound )
+ {
+ InsertKeyValuesAfter( pkvInsertAfter, pkvNewSubKey );
+
+ pkvInsertAfter = pkvNewSubKey;
+
+ newKeyList.AddToTail( pkvNewSubKey );
+ }
+
+ pkvNewSubKey = pkvNextNewSubKey;
+ }
+
+ pkvParent->RemoveSubKey( pkvInsert );
+ pkvInsert->deleteThis();
+ }
+
+ if ( pkvNew )
+ {
+ pkvNew->deleteThis();
+ }
+
+ for ( int i = 0; i < newKeyList.Count(); ++i )
+ {
+ HandleKeyValuesMacros( pkvParent, newKeyList[i] );
+ }
+
+ return pkvReturn;
+}
+
+
+//-----------------------------------------------------------------------------
+// Merge pkvSrc over pkvDst, adding any new keys from src to dst but overwriting
+// existing keys in dst with keys with matching names from src
+//-----------------------------------------------------------------------------
+static void UpdateKeyValuesBlock( KeyValues *pkvDst, KeyValues *pkvUpdate )
+{
+ Assert( pkvDst->GetFirstSubKey() );
+ Assert( pkvUpdate->GetFirstSubKey() );
+
+ for ( KeyValues *pkvUpdateSubKey = pkvUpdate->GetFirstSubKey(); pkvUpdateSubKey; pkvUpdateSubKey = pkvUpdateSubKey->GetNextKey() )
+ {
+ const int nSrcName = pkvUpdateSubKey->GetNameSymbol();
+
+ if ( pkvUpdateSubKey->GetFirstSubKey() )
+ {
+ Msg( "Error: #update has a key with subkeys, only simple key/values are allowed for #update, skipping: %s\n", pkvUpdateSubKey->GetName() );
+ continue;
+ }
+
+ KeyValues *pkvNew = nullptr;
+
+ // Check for an existing key with the same name
+ for ( KeyValues *pkvDstSubKey = pkvDst->GetFirstSubKey(); pkvDstSubKey; pkvDstSubKey = pkvDstSubKey->GetNextKey() )
+ {
+ if ( pkvDstSubKey == pkvUpdate )
+ continue;
+
+ const int nDstName = pkvDstSubKey->GetNameSymbol();
+
+ if ( nSrcName == nDstName )
+ {
+ pkvNew = ReplaceSubKeyWithCopy( pkvDst, pkvDstSubKey, pkvUpdateSubKey );
+ break;
+ }
+ }
+
+ if ( !pkvNew )
+ {
+ // Didn't update an existing key, add a key
+ pkvNew = pkvUpdateSubKey->MakeCopy();
+ pkvDst->AddSubKey( pkvNew ); // TODO: Perhaps add this right after the #update block?
+ }
+
+ Assert( pkvNew );
+
+ // Do inserts right away
+ if ( !V_strcmp( pkvNew->GetName(), "#insert" ) )
+ {
+ while ( pkvNew )
+ {
+ pkvNew = HandleKeyValuesMacros( pkvNew, pkvDst );
+ }
+ }
+ }
+}
+
+
+//--------------------------------------------------------------------------------------------------
+// Handle's #update macros
+//
+// An #update must be a KeyValue block with a KeyValue block as a parent. It will look at sibling
+// KeyValue blocks which match an optional "#glob", or all sibling KeyValue blocks if no "#glob" is
+// specified and will merge all of the #update block's subkeys into each sibling block.
+// overwriting KeyValues if they already exist and adding new KeyValues if they don't.
+//
+// Example:
+//
+// Before:
+//
+// "example"
+// {
+// "wear_level_1"
+// {
+// "one" "one_val"
+// "two" "two_val"
+// }
+// "wear_level_2"
+// {
+// "one" "one_val"
+// "two" "two_val"
+// }
+// "subblock"
+// {
+// "one" "one_val"
+// "two" "two_val"
+// }
+// "#update"
+// {
+// "#glob" "wear_level_*"
+// "one" "updated_one_val"
+// "three" "three_val"
+// }
+// }
+//
+// After:
+//
+// "example"
+// {
+// "wear_level_1"
+// {
+// "one" "updated_one_val"
+// "two" "two_val"
+// "three" "three_val"
+// }
+// "wear_level_2"
+// {
+// "one" "updated_one_val"
+// "two" "two_val"
+// "three" "three_val"
+// }
+// "subblock"
+// {
+// "one" "one_val"
+// "two" "two_val"
+// }
+// }
+//
+//--------------------------------------------------------------------------------------------------
+static KeyValues *HandleKeyValuesMacro_Update( KeyValues *pkvUpdate, KeyValues *pkvParent, bool *pbDidUpdate )
+{
+ const char *pszName = pkvUpdate->GetName();
+
+ if ( V_stricmp( "#update", pszName ) != 0 )
+ return nullptr;
+
+ // Have an #update key
+
+ if ( pkvUpdate->GetFirstSubKey() == nullptr )
+ {
+ // Invalid, has sub keys
+ Msg( "Error: #insert on key without subkeys, can only do #update with a key with subkeys\n" );
+ return nullptr;
+ }
+
+ if ( pkvUpdate->GetDataType() != KeyValues::TYPE_NONE )
+ {
+ // Invalid, value isn't a TYPE_NONE
+ Msg( "Error: #update on key without a data type of NONE, can only do #update with a key with subkeys\n" );
+ return nullptr;
+ }
+
+ const char *pszGlob = nullptr;
+
+ KeyValues *pkvGlob = pkvUpdate->FindKey( "#glob" );
+ if ( !pkvGlob )
+ {
+ pkvGlob = pkvUpdate->FindKey( "glob" );
+ }
+
+ if ( pkvGlob )
+ {
+ pszGlob = pkvGlob->GetString( nullptr, nullptr );
+ pkvUpdate->RemoveSubKey( pkvGlob );
+ }
+
+ for ( KeyValues *pkvParentSubKey = pkvParent->GetFirstSubKey(); pkvParentSubKey; pkvParentSubKey = pkvParentSubKey->GetNextKey() )
+ {
+ if ( pkvParentSubKey == pkvUpdate )
+ continue;
+
+ if ( pszGlob && !GlobMatch( pszGlob, pkvParentSubKey->GetName() ) )
+ continue;
+
+ UpdateKeyValuesBlock( pkvParentSubKey, pkvUpdate );
+ }
+
+ KeyValues *pkvReturn = pkvUpdate->GetNextKey();
+
+ pkvParent->RemoveSubKey( pkvUpdate );
+ pkvUpdate->deleteThis();
+
+ if ( pbDidUpdate )
+ {
+ *pbDidUpdate = true;
+ }
+
+ return pkvReturn;
+}
+
+
+//--------------------------------------------------------------------------------------------------
+// Main external extry point
+//--------------------------------------------------------------------------------------------------
+KeyValues *HandleKeyValuesMacros( KeyValues *kv, KeyValues *pkvParent /* = nullptr */ )
+{
+ KeyValues *pkvNextKey = HandleKeyValuesMacro_Insert( kv, pkvParent );
+ if ( pkvNextKey )
+ {
+ Assert( kv->GetFirstSubKey() == nullptr );
+
+ return pkvNextKey;
+ }
+
+ bool bDidLocalUpdate = false;
+ pkvNextKey = HandleKeyValuesMacro_Update( kv, pkvParent, &bDidLocalUpdate );
+ if ( bDidLocalUpdate )
+ {
+ Assert( kv->GetFirstSubKey() != nullptr );
+
+ return pkvNextKey;
+ }
+
+ KeyValues *pkvSub = kv->GetFirstSubKey();
+ while ( pkvSub )
+ {
+ pkvSub = HandleKeyValuesMacros( pkvSub, kv );
+ }
+
+ return kv->GetNextKey();
+}
diff --git a/tier2/meshutils.cpp b/tier2/meshutils.cpp
new file mode 100644
index 0000000..4aa0144
--- /dev/null
+++ b/tier2/meshutils.cpp
@@ -0,0 +1,104 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: A set of utilities to render standard shapes
+//
+//===========================================================================//
+
+#include "tier2/meshutils.h"
+
+
+//-----------------------------------------------------------------------------
+// Helper methods to create various standard index buffer types
+//-----------------------------------------------------------------------------
+void GenerateSequentialIndexBuffer( unsigned short* pIndices, int nIndexCount, int nFirstVertex )
+{
+ if ( !pIndices )
+ return;
+
+ // Format the sequential buffer
+ for ( int i = 0; i < nIndexCount; ++i )
+ {
+ pIndices[i] = (unsigned short)( i + nFirstVertex );
+ }
+}
+
+void GenerateQuadIndexBuffer( unsigned short* pIndices, int nIndexCount, int nFirstVertex )
+{
+ if ( !pIndices )
+ return;
+
+ // Format the quad buffer
+ int i;
+ int numQuads = nIndexCount / 6;
+ int baseVertex = nFirstVertex;
+ for ( i = 0; i < numQuads; ++i)
+ {
+ // Triangle 1
+ pIndices[0] = (unsigned short)( baseVertex );
+ pIndices[1] = (unsigned short)( baseVertex + 1 );
+ pIndices[2] = (unsigned short)( baseVertex + 2 );
+
+ // Triangle 2
+ pIndices[3] = (unsigned short)( baseVertex );
+ pIndices[4] = (unsigned short)( baseVertex + 2 );
+ pIndices[5] = (unsigned short)( baseVertex + 3 );
+
+ baseVertex += 4;
+ pIndices += 6;
+ }
+}
+
+void GeneratePolygonIndexBuffer( unsigned short* pIndices, int nIndexCount, int nFirstVertex )
+{
+ if ( !pIndices )
+ return;
+
+ int i;
+ int numPolygons = nIndexCount / 3;
+ for ( i = 0; i < numPolygons; ++i)
+ {
+ // Triangle 1
+ pIndices[0] = (unsigned short)( nFirstVertex );
+ pIndices[1] = (unsigned short)( nFirstVertex + i + 1 );
+ pIndices[2] = (unsigned short)( nFirstVertex + i + 2 );
+ pIndices += 3;
+ }
+}
+
+
+void GenerateLineStripIndexBuffer( unsigned short* pIndices, int nIndexCount, int nFirstVertex )
+{
+ if ( !pIndices )
+ return;
+
+ int i;
+ int numLines = nIndexCount / 2;
+ for ( i = 0; i < numLines; ++i)
+ {
+ pIndices[0] = (unsigned short)( nFirstVertex + i );
+ pIndices[1] = (unsigned short)( nFirstVertex + i + 1 );
+ pIndices += 2;
+ }
+}
+
+void GenerateLineLoopIndexBuffer( unsigned short* pIndices, int nIndexCount, int nFirstVertex )
+{
+ if ( !pIndices )
+ {
+ return;
+ }
+
+ int i;
+ int numLines = nIndexCount / 2;
+
+ pIndices[0] = (unsigned short)( nFirstVertex + numLines - 1 );
+ pIndices[1] = (unsigned short)( nFirstVertex );
+ pIndices += 2;
+
+ for ( i = 1; i < numLines; ++i)
+ {
+ pIndices[0] = (unsigned short)( nFirstVertex + i - 1 );
+ pIndices[1] = (unsigned short)( nFirstVertex + i );
+ pIndices += 2;
+ }
+}
diff --git a/tier2/p4helpers.cpp b/tier2/p4helpers.cpp
new file mode 100644
index 0000000..9b32dfb
--- /dev/null
+++ b/tier2/p4helpers.cpp
@@ -0,0 +1,138 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+#include "p4helpers.h"
+#include "tier2/tier2.h"
+#include "p4lib/ip4.h"
+
+#ifdef PLATFORM_WINDOWS_PC
+#include <Windows.h>
+#endif // PLATFORM_WINDOWS_PC
+
+//////////////////////////////////////////////////////////////////////////
+//
+// CP4File implementation
+//
+//////////////////////////////////////////////////////////////////////////
+
+CP4File::CP4File( char const *szFilename )
+{
+#ifdef PLATFORM_WINDOWS_PC
+
+ // On windows, get the pathname of the file on disk first before using that as a perforce path
+ // this avoids invalid Adds(). Have to go through GetShortPathName and then GetLongPathName from
+ // the short path name
+
+ TCHAR szShortPathName[ MAX_PATH ] = TEXT( "" );
+ const DWORD shortRetVal = GetShortPathName( szFilename, szShortPathName, ARRAYSIZE( szShortPathName ) );
+
+ if ( shortRetVal > 0 && shortRetVal <= ARRAYSIZE( szShortPathName ) )
+ {
+ TCHAR szLongPathName[ MAX_PATH ] = TEXT( "" );
+
+ const DWORD longRetVal = GetLongPathName( szShortPathName, szLongPathName, ARRAYSIZE( szLongPathName ) );
+
+ if ( longRetVal > 0 && longRetVal <= ARRAYSIZE( szLongPathName ) )
+ {
+ m_sFilename = szLongPathName;
+ return;
+ }
+ }
+
+#endif // PLATFORM_WINDOWS_PC
+
+ m_sFilename = szFilename;
+}
+
+CP4File::~CP4File()
+{
+}
+
+bool CP4File::Edit( void )
+{
+ if ( !p4 )
+ return true;
+
+ return p4->OpenFileForEdit( m_sFilename.String() );
+}
+
+bool CP4File::Add( void )
+{
+ if ( !p4 )
+ return true;
+
+ return p4->OpenFileForAdd( m_sFilename.String() );
+}
+
+bool CP4File::Revert( void )
+{
+ if ( !p4 )
+ return true;
+
+ return p4->RevertFile( m_sFilename.String() );
+}
+
+// Is the file in perforce?
+bool CP4File::IsFileInPerforce()
+{
+ if ( !p4 )
+ return false;
+
+ return p4->IsFileInPerforce( m_sFilename.String() );
+}
+
+bool CP4File::SetFileType(const CUtlString& desiredFileType)
+{
+ if ( !p4 )
+ return false;
+
+ return p4->SetFileType( m_sFilename.String(), desiredFileType.String() );
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+//
+// CP4Factory implementation
+//
+//////////////////////////////////////////////////////////////////////////
+
+
+CP4Factory::CP4Factory()
+{
+}
+
+CP4Factory::~CP4Factory()
+{
+}
+
+bool CP4Factory::SetDummyMode( bool bDummyMode )
+{
+ bool bOld = m_bDummyMode;
+ m_bDummyMode = bDummyMode;
+ return bOld;
+}
+
+void CP4Factory::SetOpenFileChangeList( const char *szChangeListName )
+{
+ if ( !m_bDummyMode && p4 )
+ p4->SetOpenFileChangeList( szChangeListName );
+}
+
+CP4File *CP4Factory::AccessFile( char const *szFilename ) const
+{
+ if ( !m_bDummyMode )
+ return new CP4File( szFilename );
+ else
+ return new CP4File_Dummy( szFilename );
+}
+
+
+// Default p4 factory
+static CP4Factory s_static_p4_factory;
+CP4Factory *g_p4factory = &s_static_p4_factory; // NULL before the factory constructs
+
diff --git a/tier2/renderutils.cpp b/tier2/renderutils.cpp
new file mode 100644
index 0000000..41b8bc6
--- /dev/null
+++ b/tier2/renderutils.cpp
@@ -0,0 +1,916 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: A set of utilities to render standard shapes
+//
+//===========================================================================//
+
+#include "tier2/renderutils.h"
+#include "tier2/tier2.h"
+#include "tier1/KeyValues.h"
+#include "materialsystem/imaterialsystem.h"
+#include "materialsystem/imesh.h"
+#include "materialsystem/imaterial.h"
+#include "tier0/vprof.h"
+#include "tier0/basetypes.h"
+#include "togl/rendermechanism.h"
+
+#if !defined(M_PI)
+ #define M_PI 3.14159265358979323846
+#endif
+
+//-----------------------------------------------------------------------------
+// Globals
+//-----------------------------------------------------------------------------
+static bool s_bMaterialsInitialized = false;
+static IMaterial *s_pWireframe;
+static IMaterial *s_pWireframeIgnoreZ;
+static IMaterial *s_pVertexColor;
+static IMaterial *s_pVertexColorIgnoreZ;
+
+
+//-----------------------------------------------------------------------------
+// Initializes standard materials
+//-----------------------------------------------------------------------------
+void InitializeStandardMaterials()
+{
+ if ( s_bMaterialsInitialized )
+ return;
+
+ s_bMaterialsInitialized = true;
+
+ KeyValues *pVMTKeyValues = new KeyValues( "wireframe" );
+ pVMTKeyValues->SetInt( "$vertexcolor", 1 );
+ s_pWireframe = g_pMaterialSystem->CreateMaterial( "__utilWireframe", pVMTKeyValues );
+
+ pVMTKeyValues = new KeyValues( "wireframe" );
+ pVMTKeyValues->SetInt( "$vertexcolor", 1 );
+ pVMTKeyValues->SetInt( "$vertexalpha", 1 );
+ pVMTKeyValues->SetInt( "$ignorez", 1 );
+ s_pWireframeIgnoreZ = g_pMaterialSystem->CreateMaterial( "__utilWireframeIgnoreZ", pVMTKeyValues );
+
+ pVMTKeyValues = new KeyValues( "unlitgeneric" );
+ pVMTKeyValues->SetInt( "$vertexcolor", 1 );
+ pVMTKeyValues->SetInt( "$vertexalpha", 1 );
+ s_pVertexColor = g_pMaterialSystem->CreateMaterial( "__utilVertexColor", pVMTKeyValues );
+
+ pVMTKeyValues = new KeyValues( "unlitgeneric" );
+ pVMTKeyValues->SetInt( "$vertexcolor", 1 );
+ pVMTKeyValues->SetInt( "$vertexalpha", 1 );
+ pVMTKeyValues->SetInt( "$ignorez", 1 );
+ s_pVertexColorIgnoreZ = g_pMaterialSystem->CreateMaterial( "__utilVertexColorIgnoreZ", pVMTKeyValues );
+}
+
+void ShutdownStandardMaterials()
+{
+ if ( !s_bMaterialsInitialized )
+ return;
+
+ s_bMaterialsInitialized = false;
+
+ s_pWireframe->DecrementReferenceCount();
+ s_pWireframe = NULL;
+
+ s_pWireframeIgnoreZ->DecrementReferenceCount();
+ s_pWireframeIgnoreZ = NULL;
+
+ s_pVertexColor->DecrementReferenceCount();
+ s_pVertexColor = NULL;
+
+ s_pVertexColorIgnoreZ->DecrementReferenceCount();
+ s_pVertexColorIgnoreZ = NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Renders a wireframe sphere
+//-----------------------------------------------------------------------------
+void RenderWireframeSphere( const Vector &vCenter, float flRadius, int nTheta, int nPhi, Color c, bool bZBuffer )
+{
+ InitializeStandardMaterials();
+
+ // Make one more coordinate because (u,v) is discontinuous.
+ ++nTheta;
+
+ int nVertices = nPhi * nTheta;
+ int nIndices = ( nTheta - 1 ) * 4 * ( nPhi - 1 );
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->Bind( bZBuffer ? s_pWireframe : s_pWireframeIgnoreZ );
+
+ CMeshBuilder meshBuilder;
+ IMesh* pMesh = pRenderContext->GetDynamicMesh();
+
+ meshBuilder.Begin( pMesh, MATERIAL_LINES, nVertices, nIndices );
+
+ unsigned char chRed = c.r();
+ unsigned char chGreen = c.g();
+ unsigned char chBlue = c.b();
+ unsigned char chAlpha = c.a();
+
+ int i, j;
+ for ( i = 0; i < nPhi; ++i )
+ {
+ for ( j = 0; j < nTheta; ++j )
+ {
+ float u = j / ( float )( nTheta - 1 );
+ float v = i / ( float )( nPhi - 1 );
+ float theta = 2.0f * M_PI * u;
+ float phi = M_PI * v;
+
+ meshBuilder.Position3f( vCenter.x + ( flRadius * sin(phi) * cos(theta) ),
+ vCenter.y + ( flRadius * sin(phi) * sin(theta) ),
+ vCenter.z + ( flRadius * cos(phi) ) );
+ meshBuilder.Color4ub( chRed, chGreen, chBlue, chAlpha );
+ meshBuilder.AdvanceVertex();
+ }
+ }
+
+ for ( i = 0; i < nPhi - 1; ++i )
+ {
+ for ( j = 0; j < nTheta - 1; ++j )
+ {
+ int idx = nTheta * i + j;
+
+ meshBuilder.Index( idx );
+ meshBuilder.AdvanceIndex();
+
+ meshBuilder.Index( idx + nTheta );
+ meshBuilder.AdvanceIndex();
+
+ meshBuilder.Index( idx );
+ meshBuilder.AdvanceIndex();
+
+ meshBuilder.Index( idx + 1 );
+ meshBuilder.AdvanceIndex();
+ }
+ }
+
+ meshBuilder.End();
+ pMesh->Draw();
+}
+
+
+//-----------------------------------------------------------------------------
+// Draws a sphere
+//-----------------------------------------------------------------------------
+void RenderSphere( const Vector &vCenter, float flRadius, int nTheta, int nPhi, Color c, IMaterial *pMaterial )
+{
+ InitializeStandardMaterials();
+
+ CMatRenderContextPtr pRenderContext( materials );
+
+ unsigned char chRed = c.r();
+ unsigned char chGreen = c.g();
+ unsigned char chBlue = c.b();
+ unsigned char chAlpha = c.a();
+
+ // Two extra degenerate triangles per row (except the last one)
+ int nTriangles = 2 * nTheta * ( nPhi - 1 );
+ int nIndices = 2 * ( nTheta + 1 ) * ( nPhi - 1 );
+ if ( nTriangles == 0 )
+ return;
+
+ pRenderContext->Bind( pMaterial );
+ IMesh *pMesh = pRenderContext->GetDynamicMesh( );
+ CMeshBuilder meshBuilder;
+
+ meshBuilder.Begin( pMesh, MATERIAL_TRIANGLE_STRIP, nTriangles, nIndices );
+
+ // Build the index buffer.
+ float flOONPhi = 1.0f / (nPhi-1);
+ float flOONTheta = 1.0f / (nTheta-1);
+
+ int i, j;
+ for ( i = 0; i < nPhi; ++i )
+ {
+ for ( j = 0; j < nTheta; ++j )
+ {
+ float u = j / ( float )( nTheta - 1 );
+ float v = i / ( float )( nPhi - 1 );
+ float theta = 2.0f * M_PI * u;
+ float phi = M_PI * v;
+
+ Vector vecPos;
+ vecPos.x = flRadius * sin(phi) * cos(theta);
+ vecPos.y = flRadius * sin(phi) * sin(theta);
+ vecPos.z = flRadius * cos(phi);
+
+ Vector vecNormal = vecPos;
+ VectorNormalize(vecNormal);
+
+ vecPos += vCenter;
+
+ meshBuilder.Position3f( vecPos.x, vecPos.y, vecPos.z );
+ meshBuilder.Normal3f( vecNormal.x, vecNormal.y, vecNormal.z );
+ meshBuilder.Color4ub( chRed, chGreen, chBlue, chAlpha );
+ meshBuilder.TexCoord2f( 0, j * flOONTheta, i * flOONPhi );
+ meshBuilder.AdvanceVertex();
+ }
+ }
+
+ // Emit the triangle strips.
+ int idx = 0;
+ for ( i = 0; i < nPhi - 1; ++i )
+ {
+ for ( j = 0; j < nTheta; ++j )
+ {
+ idx = nTheta * i + j;
+
+ meshBuilder.Index( idx + nTheta );
+ meshBuilder.AdvanceIndex();
+
+ meshBuilder.Index( idx );
+ meshBuilder.AdvanceIndex();
+ }
+
+ // Emit a degenerate triangle to skip to the next row without a connecting triangle
+ if ( i < nPhi - 2 )
+ {
+ meshBuilder.Index( idx );
+ meshBuilder.AdvanceIndex();
+
+ meshBuilder.Index( idx + nTheta + 1 );
+ meshBuilder.AdvanceIndex();
+ }
+ }
+
+ meshBuilder.End();
+ pMesh->Draw();
+}
+
+void RenderSphere( const Vector &vCenter, float flRadius, int nTheta, int nPhi, Color c, bool bZBuffer )
+{
+ IMaterial *pMaterial = bZBuffer ? s_pVertexColor : s_pVertexColorIgnoreZ;
+ Color cActual( c.r(), c.g(), c.b(), c.a() );
+ RenderSphere( vCenter, flRadius, nTheta, nPhi, cActual, pMaterial );
+}
+
+
+//-----------------------------------------------------------------------------
+// Box vertices
+//-----------------------------------------------------------------------------
+static int s_pBoxFaceIndices[6][4] =
+{
+ { 0, 4, 6, 2 }, // -x
+ { 5, 1, 3, 7 }, // +x
+ { 0, 1, 5, 4 }, // -y
+ { 2, 6, 7, 3 }, // +y
+ { 0, 2, 3, 1 }, // -z
+ { 4, 5, 7, 6 } // +z
+};
+
+static int s_pBoxFaceIndicesInsideOut[6][4] =
+{
+ { 0, 2, 6, 4 }, // -x
+ { 5, 7, 3, 1 }, // +x
+ { 0, 4, 5, 1 }, // -y
+ { 2, 3, 7, 6 }, // +y
+ { 0, 1, 3, 2 }, // -z
+ { 4, 6, 7, 5 } // +z
+};
+
+static void GenerateBoxVertices( const Vector &vOrigin, const QAngle& angles, const Vector &vMins, const Vector &vMaxs, Vector pVerts[8] )
+{
+ // Build a rotation matrix from orientation
+ matrix3x4_t fRotateMatrix;
+ AngleMatrix( angles, fRotateMatrix );
+
+ Vector vecPos;
+ for ( int i = 0; i < 8; ++i )
+ {
+ vecPos[0] = ( i & 0x1 ) ? vMaxs[0] : vMins[0];
+ vecPos[1] = ( i & 0x2 ) ? vMaxs[1] : vMins[1];
+ vecPos[2] = ( i & 0x4 ) ? vMaxs[2] : vMins[2];
+
+ VectorRotate( vecPos, fRotateMatrix, pVerts[i] );
+ pVerts[i] += vOrigin;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Renders a wireframe box relative to an origin
+//-----------------------------------------------------------------------------
+void RenderWireframeBox( const Vector &vOrigin, const QAngle& angles, const Vector &vMins, const Vector &vMaxs, Color c, bool bZBuffer )
+{
+ InitializeStandardMaterials();
+
+ CMatRenderContextPtr pRenderContext( materials );
+ pRenderContext->Bind( bZBuffer ? s_pWireframe : s_pWireframeIgnoreZ );
+
+ Vector p[8];
+ GenerateBoxVertices( vOrigin, angles, vMins, vMaxs, p );
+
+ unsigned char chRed = c.r();
+ unsigned char chGreen = c.g();
+ unsigned char chBlue = c.b();
+ unsigned char chAlpha = c.a();
+
+ IMesh *pMesh = pRenderContext->GetDynamicMesh( );
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_LINES, 24 );
+
+ // Draw the box
+ for ( int i = 0; i < 6; i++ )
+ {
+ int *pFaceIndex = s_pBoxFaceIndices[i];
+
+ for ( int j = 0; j < 4; ++j )
+ {
+ meshBuilder.Position3fv( p[pFaceIndex[j]].Base() );
+ meshBuilder.Color4ub( chRed, chGreen, chBlue, chAlpha );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv( p[pFaceIndex[ (j == 3) ? 0 : j+1 ] ].Base() );
+ meshBuilder.Color4ub( chRed, chGreen, chBlue, chAlpha );
+ meshBuilder.AdvanceVertex();
+ }
+ }
+
+ meshBuilder.End();
+ pMesh->Draw();
+}
+
+
+//-----------------------------------------------------------------------------
+// Renders a solid box
+//-----------------------------------------------------------------------------
+void RenderBox( const Vector& vOrigin, const QAngle& angles, const Vector& vMins, const Vector& vMaxs, Color c, IMaterial *pMaterial, bool bInsideOut )
+{
+ InitializeStandardMaterials();
+
+ CMatRenderContextPtr pRenderContext( materials );
+ pRenderContext->Bind( pMaterial );
+
+ Vector p[8];
+ GenerateBoxVertices( vOrigin, angles, vMins, vMaxs, p );
+
+ unsigned char chRed = c.r();
+ unsigned char chGreen = c.g();
+ unsigned char chBlue = c.b();
+ unsigned char chAlpha = c.a();
+
+ IMesh *pMesh = pRenderContext->GetDynamicMesh( );
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, 12 );
+
+ // Draw the box
+ Vector vecNormal;
+ for ( int i = 0; i < 6; i++ )
+ {
+ vecNormal.Init();
+ vecNormal[ i/2 ] = ( i & 0x1 ) ? 1.0f : -1.0f;
+
+ int *ppFaceIndices = bInsideOut ? s_pBoxFaceIndicesInsideOut[i] : s_pBoxFaceIndices[i];
+ for ( int j = 1; j < 3; ++j )
+ {
+ int i0 = ppFaceIndices[0];
+ int i1 = ppFaceIndices[j];
+ int i2 = ppFaceIndices[j+1];
+
+ meshBuilder.Position3fv( p[i0].Base() );
+ meshBuilder.Color4ub( chRed, chGreen, chBlue, chAlpha );
+ meshBuilder.Normal3fv( vecNormal.Base() );
+ meshBuilder.TexCoord2f( 0, 0.0f, 0.0f );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv( p[i2].Base() );
+ meshBuilder.Color4ub( chRed, chGreen, chBlue, chAlpha );
+ meshBuilder.Normal3fv( vecNormal.Base() );
+ meshBuilder.TexCoord2f( 0, 1.0f, ( j == 1 ) ? 1.0f : 0.0f );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv( p[i1].Base() );
+ meshBuilder.Color4ub( chRed, chGreen, chBlue, chAlpha );
+ meshBuilder.Normal3fv( vecNormal.Base() );
+ meshBuilder.TexCoord2f( 0, ( j == 1 ) ? 0.0f : 1.0f, 1.0f );
+ meshBuilder.AdvanceVertex();
+ }
+ }
+
+ meshBuilder.End();
+ pMesh->Draw();
+}
+
+
+void RenderBox( const Vector& vOrigin, const QAngle& angles, const Vector& vMins, const Vector& vMaxs, Color c, bool bZBuffer, bool bInsideOut )
+{
+ IMaterial *pMaterial = bZBuffer ? s_pVertexColor : s_pVertexColorIgnoreZ;
+ Color cActual( c.r(), c.g(), c.b(), c.a() );
+ RenderBox( vOrigin, angles, vMins, vMaxs, cActual, pMaterial, bInsideOut );
+}
+
+
+//-----------------------------------------------------------------------------
+// Renders axes, red->x, green->y, blue->z
+//-----------------------------------------------------------------------------
+void RenderAxes( const Vector &vOrigin, float flScale, bool bZBuffer )
+{
+ InitializeStandardMaterials();
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->Bind( bZBuffer ? s_pWireframe : s_pWireframeIgnoreZ );
+ IMesh *pMesh = pRenderContext->GetDynamicMesh( );
+
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_LINES, 3 );
+
+ meshBuilder.Position3f( vOrigin.x, vOrigin.y, vOrigin.z );
+ meshBuilder.Color4ub( 255, 0, 0, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( vOrigin.x + flScale, vOrigin.y, vOrigin.z );
+ meshBuilder.Color4ub( 255, 0, 0, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( vOrigin.x, vOrigin.y, vOrigin.z );
+ meshBuilder.Color4ub( 0, 255, 0, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( vOrigin.x, vOrigin.y + flScale, vOrigin.z );
+ meshBuilder.Color4ub( 0, 255, 0, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( vOrigin.x, vOrigin.y, vOrigin.z );
+ meshBuilder.Color4ub( 0, 0, 255, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( vOrigin.x, vOrigin.y, vOrigin.z + flScale );
+ meshBuilder.Color4ub( 0, 0, 255, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.End();
+ pMesh->Draw();
+}
+
+
+void RenderAxes( const matrix3x4_t &transform, float flScale, bool bZBuffer )
+{
+ InitializeStandardMaterials();
+
+ Vector xAxis, yAxis, zAxis, vOrigin, temp;
+ MatrixGetColumn( transform, 0, xAxis );
+ MatrixGetColumn( transform, 1, yAxis );
+ MatrixGetColumn( transform, 2, zAxis );
+ MatrixGetColumn( transform, 3, vOrigin );
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->Bind( bZBuffer ? s_pWireframe : s_pWireframeIgnoreZ );
+ IMesh *pMesh = pRenderContext->GetDynamicMesh( );
+
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_LINES, 3 );
+
+ meshBuilder.Position3fv( vOrigin.Base() );
+ meshBuilder.Color4ub( 255, 0, 0, 255 );
+ meshBuilder.AdvanceVertex();
+
+ VectorMA( vOrigin, flScale, xAxis, temp );
+ meshBuilder.Position3fv( temp.Base() );
+ meshBuilder.Color4ub( 255, 0, 0, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( vOrigin.x, vOrigin.y, vOrigin.z );
+ meshBuilder.Color4ub( 0, 255, 0, 255 );
+ meshBuilder.AdvanceVertex();
+
+ VectorMA( vOrigin, flScale, yAxis, temp );
+ meshBuilder.Position3fv( temp.Base() );
+ meshBuilder.Color4ub( 0, 255, 0, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( vOrigin.x, vOrigin.y, vOrigin.z );
+ meshBuilder.Color4ub( 0, 0, 255, 255 );
+ meshBuilder.AdvanceVertex();
+
+ VectorMA( vOrigin, flScale, zAxis, temp );
+ meshBuilder.Position3fv( temp.Base() );
+ meshBuilder.Color4ub( 0, 0, 255, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.End();
+ pMesh->Draw();
+}
+
+//-----------------------------------------------------------------------------
+// Render a line
+//-----------------------------------------------------------------------------
+void RenderLine( const Vector& v1, const Vector& v2, Color c, bool bZBuffer )
+{
+ InitializeStandardMaterials();
+
+ CMatRenderContextPtr pRenderContext( materials );
+ pRenderContext->Bind( bZBuffer ? s_pWireframe : s_pWireframeIgnoreZ );
+
+ unsigned char chRed = c.r();
+ unsigned char chGreen = c.g();
+ unsigned char chBlue = c.b();
+ unsigned char chAlpha = c.a();
+
+ IMesh *pMesh = pRenderContext->GetDynamicMesh( );
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_LINES, 1 );
+
+ meshBuilder.Position3fv( v1.Base() );
+ meshBuilder.Color4ub( chRed, chGreen, chBlue, chAlpha );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv( v2.Base() );
+ meshBuilder.Color4ub( chRed, chGreen, chBlue, chAlpha );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.End();
+ pMesh->Draw();
+}
+
+
+//-----------------------------------------------------------------------------
+// Draws a triangle
+//-----------------------------------------------------------------------------
+void RenderTriangle( const Vector& p1, const Vector& p2, const Vector& p3, Color c, IMaterial *pMaterial )
+{
+ InitializeStandardMaterials();
+
+ CMatRenderContextPtr pRenderContext( materials );
+ pRenderContext->Bind( pMaterial );
+
+ unsigned char chRed = c.r();
+ unsigned char chGreen = c.g();
+ unsigned char chBlue = c.b();
+ unsigned char chAlpha = c.a();
+
+ Vector vecNormal;
+ Vector vecDelta1, vecDelta2;
+ VectorSubtract( p2, p1, vecDelta1 );
+ VectorSubtract( p3, p1, vecDelta2 );
+ CrossProduct( vecDelta1, vecDelta2, vecNormal );
+ VectorNormalize( vecNormal );
+
+ IMesh *pMesh = pRenderContext->GetDynamicMesh( );
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, 1 );
+
+ meshBuilder.Position3fv( p1.Base() );
+ meshBuilder.Color4ub( chRed, chGreen, chBlue, chAlpha );
+ meshBuilder.Normal3fv( vecNormal.Base() );
+ meshBuilder.TexCoord2f( 0, 0.0f, 0.0f );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv( p2.Base() );
+ meshBuilder.Color4ub( chRed, chGreen, chBlue, chAlpha );
+ meshBuilder.Normal3fv( vecNormal.Base() );
+ meshBuilder.TexCoord2f( 0, 0.0f, 1.0f );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv( p3.Base() );
+ meshBuilder.Color4ub( chRed, chGreen, chBlue, chAlpha );
+ meshBuilder.Normal3fv( vecNormal.Base() );
+ meshBuilder.TexCoord2f( 0, 1.0f, 0.0f );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.End();
+ pMesh->Draw();
+}
+
+
+void RenderTriangle( const Vector& p1, const Vector& p2, const Vector& p3, Color c, bool bZBuffer )
+{
+ IMaterial *pMaterial = bZBuffer ? s_pVertexColor : s_pVertexColorIgnoreZ;
+ Color cActual( c.r(), c.g(), c.b(), c.a() );
+ RenderTriangle( p1, p2, p3, cActual, pMaterial );
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Renders an extruded box
+//-----------------------------------------------------------------------------
+static void DrawAxes( const Vector& origin, Vector* pts, int idx, Color c, CMeshBuilder& meshBuilder )
+{
+ Vector start, temp;
+ VectorAdd( pts[idx], origin, start );
+ meshBuilder.Position3fv( start.Base() );
+ meshBuilder.Color4ub( c.r(), c.g(), c.b(), c.a() );
+ meshBuilder.AdvanceVertex();
+
+ int endidx = (idx & 0x1) ? idx - 1 : idx + 1;
+ VectorAdd( pts[endidx], origin, temp );
+ meshBuilder.Position3fv( temp.Base() );
+ meshBuilder.Color4ub( c.r(), c.g(), c.b(), c.a() );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv( start.Base() );
+ meshBuilder.Color4ub( c.r(), c.g(), c.b(), c.a() );
+ meshBuilder.AdvanceVertex();
+
+ endidx = (idx & 0x2) ? idx - 2 : idx + 2;
+ VectorAdd( pts[endidx], origin, temp );
+ meshBuilder.Position3fv( temp.Base() );
+ meshBuilder.Color4ub( c.r(), c.g(), c.b(), c.a() );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv( start.Base() );
+ meshBuilder.Color4ub( c.r(), c.g(), c.b(), c.a() );
+ meshBuilder.AdvanceVertex();
+
+ endidx = (idx & 0x4) ? idx - 4 : idx + 4;
+ VectorAdd( pts[endidx], origin, temp );
+ meshBuilder.Position3fv( temp.Base() );
+ meshBuilder.Color4ub( c.r(), c.g(), c.b(), c.a() );
+ meshBuilder.AdvanceVertex();
+}
+
+static void DrawExtrusionFace( const Vector& start, const Vector& end,
+ Vector* pts, int idx1, int idx2, Color c, CMeshBuilder& meshBuilder )
+{
+ Vector temp;
+ VectorAdd( pts[idx1], start, temp );
+ meshBuilder.Position3fv( temp.Base() );
+ meshBuilder.Color4ub( c.r(), c.g(), c.b(), c.a() );
+ meshBuilder.AdvanceVertex();
+
+ VectorAdd( pts[idx2], start, temp );
+ meshBuilder.Position3fv( temp.Base() );
+ meshBuilder.Color4ub( c.r(), c.g(), c.b(), c.a() );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv( temp.Base() );
+ meshBuilder.Color4ub( c.r(), c.g(), c.b(), c.a() );
+ meshBuilder.AdvanceVertex();
+
+ VectorAdd( pts[idx2], end, temp );
+ meshBuilder.Position3fv( temp.Base() );
+ meshBuilder.Color4ub( c.r(), c.g(), c.b(), c.a() );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv( temp.Base() );
+ meshBuilder.Color4ub( c.r(), c.g(), c.b(), c.a() );
+ meshBuilder.AdvanceVertex();
+
+ VectorAdd( pts[idx1], end, temp );
+ meshBuilder.Position3fv( temp.Base() );
+ meshBuilder.Color4ub( c.r(), c.g(), c.b(), c.a() );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv( temp.Base() );
+ meshBuilder.Color4ub( c.r(), c.g(), c.b(), c.a() );
+ meshBuilder.AdvanceVertex();
+
+ VectorAdd( pts[idx1], start, temp );
+ meshBuilder.Position3fv( temp.Base() );
+ meshBuilder.Color4ub( c.r(), c.g(), c.b(), c.a() );
+ meshBuilder.AdvanceVertex();
+}
+
+void RenderWireframeSweptBox( const Vector &vStart, const Vector &vEnd, const QAngle &angles, const Vector &vMins, const Vector &vMaxs, Color c, bool bZBuffer )
+{
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->Bind( bZBuffer ? s_pWireframe : s_pWireframeIgnoreZ );
+
+ Color cActual( c.r(), c.g(), c.b(), c.a() );
+
+ // Build a rotation matrix from angles
+ matrix3x4_t fRotateMatrix;
+ AngleMatrix( angles, fRotateMatrix );
+
+ IMesh *pMesh = pRenderContext->GetDynamicMesh( );
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_LINES, 30 );
+
+ Vector vDelta;
+ VectorSubtract( vEnd, vStart, vDelta );
+
+ // Compute the box points, rotated but without the origin added
+ Vector temp;
+ Vector pts[8];
+ float dot[8];
+ int minidx = 0;
+ for ( int i = 0; i < 8; ++i )
+ {
+ temp.x = (i & 0x1) ? vMaxs[0] : vMins[0];
+ temp.y = (i & 0x2) ? vMaxs[1] : vMins[1];
+ temp.z = (i & 0x4) ? vMaxs[2] : vMins[2];
+
+ // Rotate the corner point
+ VectorRotate( temp, fRotateMatrix, pts[i] );
+
+ // Find the dot product with dir
+ dot[i] = DotProduct( pts[i], vDelta );
+ if ( dot[i] < dot[minidx] )
+ {
+ minidx = i;
+ }
+ }
+
+ // Choose opposite corner
+ int maxidx = minidx ^ 0x7;
+
+ // Draw the start + end axes...
+ DrawAxes( vStart, pts, minidx, cActual, meshBuilder );
+ DrawAxes( vEnd, pts, maxidx, cActual, meshBuilder );
+
+ // Draw the extrusion faces
+ for (int j = 0; j < 3; ++j )
+ {
+ int dirflag1 = ( 1 << ((j+1)%3) );
+ int dirflag2 = ( 1 << ((j+2)%3) );
+
+ int idx1, idx2, idx3;
+ idx1 = (minidx & dirflag1) ? minidx - dirflag1 : minidx + dirflag1;
+ idx2 = (minidx & dirflag2) ? minidx - dirflag2 : minidx + dirflag2;
+ idx3 = (minidx & dirflag2) ? idx1 - dirflag2 : idx1 + dirflag2;
+
+ DrawExtrusionFace( vStart, vEnd, pts, idx1, idx3, cActual, meshBuilder );
+ DrawExtrusionFace( vStart, vEnd, pts, idx2, idx3, cActual, meshBuilder );
+ }
+ meshBuilder.End();
+ pMesh->Draw();
+}
+
+
+//-----------------------------------------------------------------------------
+// Draws a axis-aligned quad
+//-----------------------------------------------------------------------------
+void RenderQuad( IMaterial *pMaterial, float x, float y, float w, float h,
+ float z, float s0, float t0, float s1, float t1, const Color& clr )
+{
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ IMesh *pMesh = pRenderContext->GetDynamicMesh( true, NULL, NULL, pMaterial );
+
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
+
+ meshBuilder.Color4ub( clr.r(), clr.g(), clr.b(), clr.a());
+ meshBuilder.TexCoord2f( 0, s0, t0 );
+ meshBuilder.Position3f( x, y, z );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Color4ub( clr.r(), clr.g(), clr.b(), clr.a());
+ meshBuilder.TexCoord2f( 0, s1, t0 );
+ meshBuilder.Position3f( x + w, y, z );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Color4ub( clr.r(), clr.g(), clr.b(), clr.a());
+ meshBuilder.TexCoord2f( 0, s1, t1 );
+ meshBuilder.Position3f( x + w, y + h, z );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Color4ub( clr.r(), clr.g(), clr.b(), clr.a());
+ meshBuilder.TexCoord2f( 0, s0, t1 );
+ meshBuilder.Position3f( x, y + h, z );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.End();
+
+ pMesh->Draw();
+}
+
+//-----------------------------------------------------------------------------
+// Renders a screen space quad
+//-----------------------------------------------------------------------------
+
+void DrawScreenSpaceRectangle( IMaterial *pMaterial,
+ int nDestX, int nDestY, int nWidth, int nHeight, // Rect to draw into in screen space
+ float flSrcTextureX0, float flSrcTextureY0, // which texel you want to appear at destx/y
+ float flSrcTextureX1, float flSrcTextureY1, // which texel you want to appear at destx+width-1, desty+height-1
+ int nSrcTextureWidth, int nSrcTextureHeight, // needed for fixup
+ void *pClientRenderable, // Used to pass to the bind proxies
+ int nXDice, int nYDice, // Amount to tessellate the mesh
+ float fDepth ) // what Z value to put in the verts (def 0.0)
+{
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+
+ if ( ( nWidth <= 0 ) || ( nHeight <= 0 ) )
+ return;
+
+ tmZone( TELEMETRY_LEVEL1, TMZF_NONE, "%s", __FUNCTION__ );
+
+ pRenderContext->MatrixMode( MATERIAL_VIEW );
+ pRenderContext->PushMatrix();
+ pRenderContext->LoadIdentity();
+
+ pRenderContext->MatrixMode( MATERIAL_PROJECTION );
+ pRenderContext->PushMatrix();
+ pRenderContext->LoadIdentity();
+
+ pRenderContext->Bind( pMaterial, pClientRenderable );
+
+ int xSegments = max( nXDice, 1);
+ int ySegments = max( nYDice, 1);
+
+ CMeshBuilder meshBuilder;
+
+ IMesh* pMesh = pRenderContext->GetDynamicMesh( true );
+ meshBuilder.Begin( pMesh, MATERIAL_QUADS, xSegments * ySegments );
+
+ int nScreenWidth, nScreenHeight;
+ pRenderContext->GetRenderTargetDimensions( nScreenWidth, nScreenHeight );
+
+ float flOffset = 0.5f;
+
+ float flLeftX = nDestX - flOffset;
+ float flRightX = nDestX + nWidth - flOffset;
+
+ float flTopY = nDestY - flOffset;
+ float flBottomY = nDestY + nHeight - flOffset;
+
+ float flSubrectWidth = flSrcTextureX1 - flSrcTextureX0;
+ float flSubrectHeight = flSrcTextureY1 - flSrcTextureY0;
+
+ float flTexelsPerPixelX = ( nWidth > 1 ) ? flSubrectWidth / ( nWidth - 1 ) : 0.0f;
+ float flTexelsPerPixelY = ( nHeight > 1 ) ? flSubrectHeight / ( nHeight - 1 ) : 0.0f;
+
+ float flLeftU = flSrcTextureX0 + 0.5f - ( 0.5f * flTexelsPerPixelX );
+ float flRightU = flSrcTextureX1 + 0.5f + ( 0.5f * flTexelsPerPixelX );
+ float flTopV = flSrcTextureY0 + 0.5f - ( 0.5f * flTexelsPerPixelY );
+ float flBottomV = flSrcTextureY1 + 0.5f + ( 0.5f * flTexelsPerPixelY );
+
+ float flOOTexWidth = 1.0f / nSrcTextureWidth;
+ float flOOTexHeight = 1.0f / nSrcTextureHeight;
+ flLeftU *= flOOTexWidth;
+ flRightU *= flOOTexWidth;
+ flTopV *= flOOTexHeight;
+ flBottomV *= flOOTexHeight;
+
+ // Get the current viewport size
+ int vx, vy, vw, vh;
+ pRenderContext->GetViewport( vx, vy, vw, vh );
+
+ // map from screen pixel coords to -1..1
+ flRightX = FLerp( -1, 1, 0, vw, flRightX );
+ flLeftX = FLerp( -1, 1, 0, vw, flLeftX );
+ flTopY = FLerp( 1, -1, 0, vh ,flTopY );
+ flBottomY = FLerp( 1, -1, 0, vh, flBottomY );
+
+ // Dice the quad up...
+ if ( ( xSegments > 1 ) || ( ySegments > 1 ) )
+ {
+ // Screen height and width of a subrect
+ float flWidth = (flRightX - flLeftX) / (float) xSegments;
+ float flHeight = (flTopY - flBottomY) / (float) ySegments;
+
+ // UV height and width of a subrect
+ float flUWidth = (flRightU - flLeftU) / (float) xSegments;
+ float flVHeight = (flBottomV - flTopV) / (float) ySegments;
+
+ for ( int x=0; x < xSegments; x++ )
+ {
+ for ( int y=0; y < ySegments; y++ )
+ {
+ // Top left
+ meshBuilder.Position3f( flLeftX + (float) x * flWidth, flTopY - (float) y * flHeight, fDepth );
+ meshBuilder.Normal3f( 0.0f, 0.0f, 1.0f );
+ meshBuilder.TexCoord2f( 0, flLeftU + (float) x * flUWidth, flTopV + (float) y * flVHeight);
+ meshBuilder.TangentS3f( 0.0f, 1.0f, 0.0f );
+ meshBuilder.TangentT3f( 1.0f, 0.0f, 0.0f );
+ meshBuilder.AdvanceVertex();
+
+ // Top right (x+1)
+ meshBuilder.Position3f( flLeftX + (float) (x+1) * flWidth, flTopY - (float) y * flHeight, fDepth );
+ meshBuilder.Normal3f( 0.0f, 0.0f, 1.0f );
+ meshBuilder.TexCoord2f( 0, flLeftU + (float) (x+1) * flUWidth, flTopV + (float) y * flVHeight);
+ meshBuilder.TangentS3f( 0.0f, 1.0f, 0.0f );
+ meshBuilder.TangentT3f( 1.0f, 0.0f, 0.0f );
+ meshBuilder.AdvanceVertex();
+
+ // Bottom right (x+1), (y+1)
+ meshBuilder.Position3f( flLeftX + (float) (x+1) * flWidth, flTopY - (float) (y+1) * flHeight, fDepth );
+ meshBuilder.Normal3f( 0.0f, 0.0f, 1.0f );
+ meshBuilder.TexCoord2f( 0, flLeftU + (float) (x+1) * flUWidth, flTopV + (float)(y+1) * flVHeight);
+ meshBuilder.TangentS3f( 0.0f, 1.0f, 0.0f );
+ meshBuilder.TangentT3f( 1.0f, 0.0f, 0.0f );
+ meshBuilder.AdvanceVertex();
+
+ // Bottom left (y+1)
+ meshBuilder.Position3f( flLeftX + (float) x * flWidth, flTopY - (float) (y+1) * flHeight, fDepth );
+ meshBuilder.Normal3f( 0.0f, 0.0f, 1.0f );
+ meshBuilder.TexCoord2f( 0, flLeftU + (float) x * flUWidth, flTopV + (float)(y+1) * flVHeight);
+ meshBuilder.TangentS3f( 0.0f, 1.0f, 0.0f );
+ meshBuilder.TangentT3f( 1.0f, 0.0f, 0.0f );
+ meshBuilder.AdvanceVertex();
+ }
+ }
+ }
+ else // just one quad
+ {
+ for ( int corner=0; corner<4; corner++ )
+ {
+ bool bLeft = (corner==0) || (corner==3);
+ meshBuilder.Position3f( (bLeft) ? flLeftX : flRightX, (corner & 2) ? flBottomY : flTopY, fDepth );
+ meshBuilder.Normal3f( 0.0f, 0.0f, 1.0f );
+ meshBuilder.TexCoord2f( 0, (bLeft) ? flLeftU : flRightU, (corner & 2) ? flBottomV : flTopV );
+ meshBuilder.TangentS3f( 0.0f, 1.0f, 0.0f );
+ meshBuilder.TangentT3f( 1.0f, 0.0f, 0.0f );
+ meshBuilder.AdvanceVertex();
+ }
+ }
+
+ meshBuilder.End();
+ pMesh->Draw();
+
+ pRenderContext->MatrixMode( MATERIAL_VIEW );
+ pRenderContext->PopMatrix();
+
+ pRenderContext->MatrixMode( MATERIAL_PROJECTION );
+ pRenderContext->PopMatrix();
+}
diff --git a/tier2/riff.cpp b/tier2/riff.cpp
new file mode 100644
index 0000000..9e36021
--- /dev/null
+++ b/tier2/riff.cpp
@@ -0,0 +1,506 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+// Avoid these warnings:
+#pragma warning(disable : 4512) // warning C4512: 'InFileRIFF' : assignment operator could not be generated
+#pragma warning(disable : 4514) // warning C4514: 'RIFFName' : unreferenced inline function has been removed
+
+#include "riff.h"
+#include <stdio.h>
+#include <string.h>
+#include "tier0/dbg.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#if 0
+//-----------------------------------------------------------------------------
+// Purpose: Test code that implements the interface on stdio
+//-----------------------------------------------------------------------------
+class StdIOReadBinary : public IFileReadBinary
+{
+public:
+ int open( const char *pFileName )
+ {
+ return (int)fopen( pFileName, "rb" );
+ }
+
+ int read( void *pOutput, int size, int file )
+ {
+ FILE *fp = (FILE *)file;
+
+ return fread( pOutput, size, 1, fp );
+ }
+
+ void seek( int file, int pos )
+ {
+ fseek( (FILE *)file, pos, SEEK_SET );
+ }
+
+ unsigned int tell( int file )
+ {
+ return ftell( (FILE *)file );
+ }
+
+ unsigned int size( int file )
+ {
+ FILE *fp = (FILE *)file;
+ if ( !fp )
+ return 0;
+
+ unsigned int pos = ftell( fp );
+ fseek( fp, 0, SEEK_END );
+ unsigned int size = ftell( fp );
+
+ fseek( fp, pos, SEEK_SET );
+ return size;
+ }
+
+ void close( int file )
+ {
+ FILE *fp = (FILE *)file;
+
+ fclose( fp );
+ }
+};
+#endif
+
+
+#define RIFF_ID MAKEID('R','I','F','F')
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Opens a RIFF file using the given I/O mechanism
+// Input : *pFileName
+// &io - I/O interface
+//-----------------------------------------------------------------------------
+InFileRIFF::InFileRIFF( const char *pFileName, IFileReadBinary &io ) : m_io(io)
+{
+ m_file = m_io.open( pFileName );
+
+ int riff = 0;
+ if ( !m_file )
+ {
+ m_riffSize = 0;
+ m_riffName = 0;
+ return;
+ }
+
+ riff = ReadInt();
+ if ( riff != RIFF_ID )
+ {
+ printf( "Not a RIFF File [%s]\n", pFileName );
+ m_riffSize = 0;
+ }
+ else
+ {
+ // we store size as size of all chunks
+ // subtract off the RIFF form type (e.g. 'WAVE', 4 bytes)
+ m_riffSize = ReadInt() - 4;
+ m_riffName = ReadInt();
+
+ // HACKHACK: LWV files don't obey the RIFF format!!!
+ // Do this or miss the linguistic chunks at the end. Lame!
+ // subtract off 12 bytes for (RIFF, size, WAVE)
+ m_riffSize = m_io.size( m_file ) - 12;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Close the file
+//-----------------------------------------------------------------------------
+InFileRIFF::~InFileRIFF( void )
+{
+ m_io.close( m_file );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: read a 4-byte int out of the stream
+// Output : int = read value, default is zero
+//-----------------------------------------------------------------------------
+int InFileRIFF::ReadInt( void )
+{
+ int tmp = 0;
+ m_io.read( &tmp, sizeof(int), m_file );
+ tmp = LittleLong( tmp );
+
+ return tmp;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Read a block of binary data
+// Input : *pOutput - pointer to destination memory
+// dataSize - size of block to read
+// Output : int - number of bytes read
+//-----------------------------------------------------------------------------
+int InFileRIFF::ReadData( void *pOutput, int dataSize )
+{
+ int count = m_io.read( pOutput, dataSize, m_file );
+
+ return count;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Gets the file position
+// Output : int (bytes from start of file)
+//-----------------------------------------------------------------------------
+int InFileRIFF::PositionGet( void )
+{
+ return m_io.tell( m_file );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Seek to file position
+// Input : position - bytes from start of file
+//-----------------------------------------------------------------------------
+void InFileRIFF::PositionSet( int position )
+{
+ m_io.seek( m_file, position );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Used to write a RIFF format file
+//-----------------------------------------------------------------------------
+OutFileRIFF::OutFileRIFF( const char *pFileName, IFileWriteBinary &io ) : m_io( io )
+{
+ m_file = m_io.create( pFileName );
+
+ if ( !m_file )
+ return;
+
+ int riff = RIFF_ID;
+ m_io.write( &riff, 4, m_file );
+
+ m_riffSize = 0;
+ m_nNamePos = m_io.tell( m_file );
+
+ // Save room for the size and name now
+ WriteInt( 0 );
+
+ // Write out the name
+ WriteInt( RIFF_WAVE );
+
+ m_bUseIncorrectLISETLength = false;
+ m_nLISETSize = 0;
+}
+
+OutFileRIFF::~OutFileRIFF( void )
+{
+ if ( !IsValid() )
+ return;
+
+ unsigned int size = m_io.tell( m_file ) -8;
+ m_io.seek( m_file, m_nNamePos );
+
+ if ( m_bUseIncorrectLISETLength )
+ {
+ size = m_nLISETSize - 8;
+ }
+
+ WriteInt( size );
+ m_io.close( m_file );
+}
+
+void OutFileRIFF::HasLISETData( int position )
+{
+ m_bUseIncorrectLISETLength = true;
+ m_nLISETSize = position;
+}
+
+bool OutFileRIFF::WriteInt( int number )
+{
+ if ( !IsValid() )
+ return false;
+
+ m_io.write( &number, sizeof( int ), m_file );
+ return true;
+}
+
+bool OutFileRIFF::WriteData( void *pOutput, int dataSize )
+{
+ if ( !IsValid() )
+ return false;
+
+ m_io.write( pOutput, dataSize, m_file );
+ return true;
+}
+
+int OutFileRIFF::PositionGet( void )
+{
+ if ( !IsValid() )
+ return 0;
+
+ return m_io.tell( m_file );
+}
+
+void OutFileRIFF::PositionSet( int position )
+{
+ if ( !IsValid() )
+ return;
+
+ m_io.seek( m_file, position );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Create an iterator for the given file
+// Input : &riff - riff file
+// size - size of file or sub-chunk
+//-----------------------------------------------------------------------------
+IterateRIFF::IterateRIFF( InFileRIFF &riff, int size )
+ : m_riff(riff), m_size(size)
+{
+ if ( !m_riff.RIFFSize() )
+ {
+ // bad file, just be an empty iterator
+ ChunkClear();
+ return;
+ }
+
+ // get the position and parse a chunk
+ m_start = riff.PositionGet();
+ ChunkSetup();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Set up a sub-chunk iterator
+// Input : &parent - parent iterator
+//-----------------------------------------------------------------------------
+IterateRIFF::IterateRIFF( IterateRIFF &parent )
+ : m_riff(parent.m_riff), m_size(parent.ChunkSize())
+{
+ m_start = parent.ChunkFilePosition();
+ ChunkSetup();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Parse the chunk at the current file position
+// This object will iterate over the sub-chunks of this chunk.
+// This makes for easy hierarchical parsing
+//-----------------------------------------------------------------------------
+void IterateRIFF::ChunkSetup( void )
+{
+ m_chunkPosition = m_riff.PositionGet();
+
+ m_chunkName = m_riff.ReadInt();
+ m_chunkSize = m_riff.ReadInt();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: clear chunk setup, ChunkAvailable will return false
+//-----------------------------------------------------------------------------
+void IterateRIFF::ChunkClear( void )
+{
+ m_chunkSize = -1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: If there are chunks left to read beyond this one, return true
+//-----------------------------------------------------------------------------
+bool IterateRIFF::ChunkAvailable( void )
+{
+ if ( m_chunkSize != -1 && m_chunkSize < 0x10000000 )
+ return true;
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Go to the next chunk in the file, return true if there is one.
+//-----------------------------------------------------------------------------
+bool IterateRIFF::ChunkNext( void )
+{
+ if ( !ChunkAvailable() )
+ return false;
+
+ int nextPos = m_chunkPosition + 8 + m_chunkSize;
+
+ // chunks are aligned
+ nextPos += m_chunkSize & 1;
+
+ if ( nextPos >= (m_start + m_size) )
+ {
+ ChunkClear();
+ return false;
+ }
+
+ m_riff.PositionSet( nextPos );
+
+ ChunkSetup();
+ return ChunkAvailable();
+
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: get the chunk FOURCC as an int
+// Output : unsigned int
+//-----------------------------------------------------------------------------
+unsigned int IterateRIFF::ChunkName( void )
+{
+ return m_chunkName;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: get the size of this chunk
+// Output : unsigned int
+//-----------------------------------------------------------------------------
+unsigned int IterateRIFF::ChunkSize( void )
+{
+ return m_chunkSize;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Read the entire chunk into a buffer
+// Input : *pOutput - dest buffer
+// Output : int bytes read
+//-----------------------------------------------------------------------------
+int IterateRIFF::ChunkRead( void *pOutput )
+{
+ return m_riff.ReadData( pOutput, ChunkSize() );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Read a partial chunk (updates file position for subsequent partial reads).
+// Input : *pOutput - dest buffer
+// dataSize - partial size
+// Output : int - bytes read
+//-----------------------------------------------------------------------------
+int IterateRIFF::ChunkReadPartial( void *pOutput, int dataSize )
+{
+ return m_riff.ReadData( pOutput, dataSize );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Read a 4-byte int
+// Output : int - read int
+//-----------------------------------------------------------------------------
+int IterateRIFF::ChunkReadInt( void )
+{
+ return m_riff.ReadInt();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Used to iterate over an InFileRIFF
+//-----------------------------------------------------------------------------
+IterateOutputRIFF::IterateOutputRIFF( OutFileRIFF &riff )
+: m_riff( riff )
+{
+ if ( !m_riff.IsValid() )
+ return;
+
+ m_start = m_riff.PositionGet();
+ m_chunkPosition = m_start;
+ m_chunkStart = -1;
+}
+
+IterateOutputRIFF::IterateOutputRIFF( IterateOutputRIFF &parent )
+ : m_riff(parent.m_riff)
+{
+ m_start = parent.ChunkFilePosition();
+ m_chunkPosition = m_start;
+ m_chunkStart = -1;
+}
+
+void IterateOutputRIFF::ChunkWrite( unsigned int chunkname, void *pOutput, int size )
+{
+ m_chunkPosition = m_riff.PositionGet();
+
+ m_chunkName = chunkname;
+ m_chunkSize = size;
+
+ m_riff.WriteInt( chunkname );
+ m_riff.WriteInt( size );
+ m_riff.WriteData( pOutput, size );
+
+ m_chunkPosition = m_riff.PositionGet();
+
+ m_chunkPosition += m_chunkPosition & 1;
+
+ m_riff.PositionSet( m_chunkPosition );
+
+ m_chunkStart = -1;
+}
+
+void IterateOutputRIFF::ChunkWriteInt( int number )
+{
+ m_riff.WriteInt( number );
+}
+
+void IterateOutputRIFF::ChunkWriteData( void *pOutput, int size )
+{
+ m_riff.WriteData( pOutput, size );
+}
+
+void IterateOutputRIFF::ChunkFinish( void )
+{
+ Assert( m_chunkStart != -1 );
+
+ m_chunkPosition = m_riff.PositionGet();
+
+ int size = m_chunkPosition - m_chunkStart - 8;
+
+ m_chunkPosition += m_chunkPosition & 1;
+
+ m_riff.PositionSet( m_chunkStart + sizeof( int ) );
+
+ m_riff.WriteInt( size );
+
+ m_riff.PositionSet( m_chunkPosition );
+
+ m_chunkStart = -1;
+}
+
+void IterateOutputRIFF::ChunkStart( unsigned int chunkname )
+{
+ Assert( m_chunkStart == -1 );
+
+ m_chunkStart = m_riff.PositionGet();
+
+ m_riff.WriteInt( chunkname );
+ m_riff.WriteInt( 0 );
+}
+
+void IterateOutputRIFF::ChunkSetPosition( int position )
+{
+ m_riff.PositionSet( position );
+}
+
+unsigned int IterateOutputRIFF::ChunkGetPosition( void )
+{
+ return m_riff.PositionGet();
+}
+
+void IterateOutputRIFF::CopyChunkData( IterateRIFF& input )
+{
+ if ( input.ChunkSize() > 0 )
+ {
+ char *buffer = new char[ input.ChunkSize() ];
+ Assert( buffer );
+
+ input.ChunkRead( buffer );
+
+ // Don't copy/write the name or size, just the data itself
+ ChunkWriteData( buffer, input.ChunkSize() );
+
+ delete[] buffer;
+ }
+}
+
+void IterateOutputRIFF::SetLISETData( int position )
+{
+ m_riff.HasLISETData( position );
+} \ No newline at end of file
diff --git a/tier2/soundutils.cpp b/tier2/soundutils.cpp
new file mode 100644
index 0000000..bebda85
--- /dev/null
+++ b/tier2/soundutils.cpp
@@ -0,0 +1,237 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Helper methods + classes for sound
+//
+//===========================================================================//
+
+#include "tier2/soundutils.h"
+#include "tier2/riff.h"
+#include "tier2/tier2.h"
+#include "filesystem.h"
+
+#ifdef IS_WINDOWS_PC
+
+#include <windows.h> // WAVEFORMATEX, WAVEFORMAT and ADPCM WAVEFORMAT!!!
+#include <mmreg.h>
+
+#else
+
+#ifdef _X360
+#include "xbox/xbox_win32stubs.h" // WAVEFORMATEX, WAVEFORMAT and ADPCM WAVEFORMAT!!!
+#endif
+
+#endif
+
+//-----------------------------------------------------------------------------
+// RIFF reader/writers that use the file system
+//-----------------------------------------------------------------------------
+class CFSIOReadBinary : public IFileReadBinary
+{
+public:
+ // inherited from IFileReadBinary
+ virtual int open( const char *pFileName );
+ virtual int read( void *pOutput, int size, int file );
+ virtual void seek( int file, int pos );
+ virtual unsigned int tell( int file );
+ virtual unsigned int size( int file );
+ virtual void close( int file );
+};
+
+class CFSIOWriteBinary : public IFileWriteBinary
+{
+public:
+ virtual int create( const char *pFileName );
+ virtual int write( void *pData, int size, int file );
+ virtual void close( int file );
+ virtual void seek( int file, int pos );
+ virtual unsigned int tell( int file );
+};
+
+
+//-----------------------------------------------------------------------------
+// Singletons
+//-----------------------------------------------------------------------------
+static CFSIOReadBinary s_FSIoIn;
+static CFSIOWriteBinary s_FSIoOut;
+
+IFileReadBinary *g_pFSIOReadBinary = &s_FSIoIn;
+IFileWriteBinary *g_pFSIOWriteBinary = &s_FSIoOut;
+
+
+//-----------------------------------------------------------------------------
+// RIFF reader that use the file system
+//-----------------------------------------------------------------------------
+int CFSIOReadBinary::open( const char *pFileName )
+{
+ return (int)g_pFullFileSystem->Open( pFileName, "rb" );
+}
+
+int CFSIOReadBinary::read( void *pOutput, int size, int file )
+{
+ if ( !file )
+ return 0;
+
+ return g_pFullFileSystem->Read( pOutput, size, (FileHandle_t)file );
+}
+
+void CFSIOReadBinary::seek( int file, int pos )
+{
+ if ( !file )
+ return;
+
+ g_pFullFileSystem->Seek( (FileHandle_t)file, pos, FILESYSTEM_SEEK_HEAD );
+}
+
+unsigned int CFSIOReadBinary::tell( int file )
+{
+ if ( !file )
+ return 0;
+
+ return g_pFullFileSystem->Tell( (FileHandle_t)file );
+}
+
+unsigned int CFSIOReadBinary::size( int file )
+{
+ if ( !file )
+ return 0;
+
+ return g_pFullFileSystem->Size( (FileHandle_t)file );
+}
+
+void CFSIOReadBinary::close( int file )
+{
+ if ( !file )
+ return;
+
+ g_pFullFileSystem->Close( (FileHandle_t)file );
+}
+
+
+//-----------------------------------------------------------------------------
+// RIFF writer that use the file system
+//-----------------------------------------------------------------------------
+int CFSIOWriteBinary::create( const char *pFileName )
+{
+ g_pFullFileSystem->SetFileWritable( pFileName, true );
+ return (int)g_pFullFileSystem->Open( pFileName, "wb" );
+}
+
+int CFSIOWriteBinary::write( void *pData, int size, int file )
+{
+ return g_pFullFileSystem->Write( pData, size, (FileHandle_t)file );
+}
+
+void CFSIOWriteBinary::close( int file )
+{
+ g_pFullFileSystem->Close( (FileHandle_t)file );
+}
+
+void CFSIOWriteBinary::seek( int file, int pos )
+{
+ g_pFullFileSystem->Seek( (FileHandle_t)file, pos, FILESYSTEM_SEEK_HEAD );
+}
+
+unsigned int CFSIOWriteBinary::tell( int file )
+{
+ return g_pFullFileSystem->Tell( (FileHandle_t)file );
+}
+
+
+#ifndef POSIX
+//-----------------------------------------------------------------------------
+// Returns the duration of a wav file
+//-----------------------------------------------------------------------------
+float GetWavSoundDuration( const char *pWavFile )
+{
+ InFileRIFF riff( pWavFile, *g_pFSIOReadBinary );
+
+ // UNDONE: Don't use printf to handle errors
+ if ( riff.RIFFName() != RIFF_WAVE )
+ return 0.0f;
+
+ int nDataSize = 0;
+
+ // set up the iterator for the whole file (root RIFF is a chunk)
+ IterateRIFF walk( riff, riff.RIFFSize() );
+
+ // This chunk must be first as it contains the wave's format
+ // break out when we've parsed it
+ char pFormatBuffer[ 1024 ];
+ int nFormatSize;
+ bool bFound = false;
+ for ( ; walk.ChunkAvailable( ); walk.ChunkNext() )
+ {
+ switch ( walk.ChunkName() )
+ {
+ case WAVE_FMT:
+ bFound = true;
+ if ( walk.ChunkSize() > sizeof(pFormatBuffer) )
+ {
+ Warning( "oops, format tag too big!!!" );
+ return 0.0f;
+ }
+
+ nFormatSize = walk.ChunkSize();
+ walk.ChunkRead( pFormatBuffer );
+ break;
+
+ case WAVE_DATA:
+ nDataSize += walk.ChunkSize();
+ break;
+ }
+ }
+
+ if ( !bFound )
+ return 0.0f;
+
+ const WAVEFORMATEX *pHeader = (const WAVEFORMATEX *)pFormatBuffer;
+
+ int format = pHeader->wFormatTag;
+
+ int nBits = pHeader->wBitsPerSample;
+ int nRate = pHeader->nSamplesPerSec;
+ int nChannels = pHeader->nChannels;
+ int nSampleSize = ( nBits * nChannels ) / 8;
+
+ // this can never be zero -- other functions divide by this.
+ // This should never happen, but avoid crashing
+ if ( nSampleSize <= 0 )
+ {
+ nSampleSize = 1;
+ }
+
+ int nSampleCount = 0;
+ float flTrueSampleSize = nSampleSize;
+
+ if ( format == WAVE_FORMAT_ADPCM )
+ {
+ nSampleSize = 1;
+
+ ADPCMWAVEFORMAT *pFormat = (ADPCMWAVEFORMAT *)pFormatBuffer;
+ int blockSize = ((pFormat->wSamplesPerBlock - 2) * pFormat->wfx.nChannels ) / 2;
+ blockSize += 7 * pFormat->wfx.nChannels;
+
+ int blockCount = nSampleCount / blockSize;
+ int blockRem = nSampleCount % blockSize;
+
+ // total samples in complete blocks
+ nSampleCount = blockCount * pFormat->wSamplesPerBlock;
+
+ // add remaining in a short block
+ if ( blockRem )
+ {
+ nSampleCount += pFormat->wSamplesPerBlock - (((blockSize - blockRem) * 2) / nChannels);
+ }
+
+ flTrueSampleSize = 0.5f;
+
+ }
+ else
+ {
+ nSampleCount = nDataSize / nSampleSize;
+ }
+
+ float flDuration = (float)nSampleCount / (float)nRate;
+ return flDuration;
+}
+#endif
diff --git a/tier2/tier2.cpp b/tier2/tier2.cpp
new file mode 100644
index 0000000..7fd6143
--- /dev/null
+++ b/tier2/tier2.cpp
@@ -0,0 +1,117 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: A higher level link library for general use in the game and tools.
+//
+//===========================================================================//
+
+#include <tier2/tier2.h>
+#include "tier0/dbg.h"
+#include "filesystem.h"
+#include "materialsystem/imaterialsystem.h"
+#include "materialsystem/imaterialsystemhardwareconfig.h"
+#include "materialsystem/IColorCorrection.h"
+#include "materialsystem/idebugtextureinfo.h"
+#include "materialsystem/ivballoctracker.h"
+#include "inputsystem/iinputsystem.h"
+#include "networksystem/inetworksystem.h"
+#include "p4lib/ip4.h"
+#include "mdllib/mdllib.h"
+#include "filesystem/IQueuedLoader.h"
+
+
+//-----------------------------------------------------------------------------
+// These tier2 libraries must be set by any users of this library.
+// They can be set by calling ConnectTier2Libraries or InitDefaultFileSystem.
+// It is hoped that setting this, and using this library will be the common mechanism for
+// allowing link libraries to access tier2 library interfaces
+//-----------------------------------------------------------------------------
+IFileSystem *g_pFullFileSystem = 0;
+IMaterialSystem *materials = 0;
+IMaterialSystem *g_pMaterialSystem = 0;
+IInputSystem *g_pInputSystem = 0;
+INetworkSystem *g_pNetworkSystem = 0;
+IMaterialSystemHardwareConfig *g_pMaterialSystemHardwareConfig = 0;
+IDebugTextureInfo *g_pMaterialSystemDebugTextureInfo = 0;
+IVBAllocTracker *g_VBAllocTracker = 0;
+IColorCorrectionSystem *colorcorrection = 0;
+IP4 *p4 = 0;
+IMdlLib *mdllib = 0;
+IQueuedLoader *g_pQueuedLoader = 0;
+
+
+//-----------------------------------------------------------------------------
+// Call this to connect to all tier 2 libraries.
+// It's up to the caller to check the globals it cares about to see if ones are missing
+//-----------------------------------------------------------------------------
+void ConnectTier2Libraries( CreateInterfaceFn *pFactoryList, int nFactoryCount )
+{
+ // Don't connect twice..
+ Assert( !g_pFullFileSystem && !materials && !g_pInputSystem && !g_pNetworkSystem &&
+ !p4 && !mdllib && !g_pMaterialSystemDebugTextureInfo && !g_VBAllocTracker &&
+ !g_pMaterialSystemHardwareConfig && !g_pQueuedLoader );
+
+ for ( int i = 0; i < nFactoryCount; ++i )
+ {
+ if ( !g_pFullFileSystem )
+ {
+ g_pFullFileSystem = ( IFileSystem * )pFactoryList[i]( FILESYSTEM_INTERFACE_VERSION, NULL );
+ }
+ if ( !materials )
+ {
+ g_pMaterialSystem = materials = ( IMaterialSystem * )pFactoryList[i]( MATERIAL_SYSTEM_INTERFACE_VERSION, NULL );
+ }
+ if ( !g_pInputSystem )
+ {
+ g_pInputSystem = ( IInputSystem * )pFactoryList[i]( INPUTSYSTEM_INTERFACE_VERSION, NULL );
+ }
+ if ( !g_pNetworkSystem )
+ {
+ g_pNetworkSystem = ( INetworkSystem * )pFactoryList[i]( NETWORKSYSTEM_INTERFACE_VERSION, NULL );
+ }
+ if ( !g_pMaterialSystemHardwareConfig )
+ {
+ g_pMaterialSystemHardwareConfig = ( IMaterialSystemHardwareConfig * )pFactoryList[i]( MATERIALSYSTEM_HARDWARECONFIG_INTERFACE_VERSION, NULL );
+ }
+ if ( !g_pMaterialSystemDebugTextureInfo )
+ {
+ g_pMaterialSystemDebugTextureInfo = (IDebugTextureInfo*)pFactoryList[i]( DEBUG_TEXTURE_INFO_VERSION, 0 );
+ }
+ if ( !g_VBAllocTracker )
+ {
+ g_VBAllocTracker = (IVBAllocTracker*)pFactoryList[i]( VB_ALLOC_TRACKER_INTERFACE_VERSION, 0 );
+ }
+ if ( !colorcorrection )
+ {
+ colorcorrection = ( IColorCorrectionSystem * )pFactoryList[i]( COLORCORRECTION_INTERFACE_VERSION, NULL );
+ }
+ if ( !p4 )
+ {
+ p4 = ( IP4 * )pFactoryList[i]( P4_INTERFACE_VERSION, NULL );
+ }
+ if ( !mdllib )
+ {
+ mdllib = ( IMdlLib * )pFactoryList[i]( MDLLIB_INTERFACE_VERSION, NULL );
+ }
+ if ( !g_pQueuedLoader )
+ {
+ g_pQueuedLoader = (IQueuedLoader *)pFactoryList[i]( QUEUEDLOADER_INTERFACE_VERSION, NULL );
+ }
+ }
+}
+
+void DisconnectTier2Libraries()
+{
+
+ g_pFullFileSystem = 0;
+ materials = g_pMaterialSystem = 0;
+ g_pMaterialSystemHardwareConfig = 0;
+ g_pMaterialSystemDebugTextureInfo = 0;
+ g_pInputSystem = 0;
+ g_pNetworkSystem = 0;
+ colorcorrection = 0;
+ p4 = 0;
+ mdllib = 0;
+ g_pQueuedLoader = 0;
+}
+
+
diff --git a/tier2/tier2.vpc b/tier2/tier2.vpc
new file mode 100644
index 0000000..53ff36d
--- /dev/null
+++ b/tier2/tier2.vpc
@@ -0,0 +1,58 @@
+//-----------------------------------------------------------------------------
+// TIER2.VPC
+//
+// Project Script
+//-----------------------------------------------------------------------------
+
+$macro SRCDIR ".."
+
+$include "$SRCDIR\vpc_scripts\source_lib_base.vpc"
+
+$Configuration
+{
+ $Compiler
+ {
+ $AdditionalIncludeDirectories "$BASE;$SRCDIR\public\tier2"
+ }
+}
+
+$Project "tier2"
+{
+ $Folder "Source Files"
+ {
+ $File "beamsegdraw.cpp"
+ $File "defaultfilesystem.cpp"
+ $File "dmconnect.cpp"
+ $file "fileutils.cpp"
+ $File "keybindings.cpp"
+ $File "$SRCDIR\public\map_utils.cpp"
+ $File "$SRCDIR\public\materialsystem\MaterialSystemUtil.cpp"
+ $File "camerautils.cpp"
+ $File "meshutils.cpp"
+ $File "p4helpers.cpp"
+ $File "renderutils.cpp"
+ $File "riff.cpp"
+ $File "soundutils.cpp"
+ $File "tier2.cpp"
+ $File "util_init.cpp"
+ $File "utlstreambuffer.cpp"
+ $File "vconfig.cpp"
+ $File "keyvaluesmacros.cpp"
+ }
+
+ $Folder "Public Header Files"
+ {
+ $File "$SRCDIR\public\tier2\beamsegdraw.h"
+ $File "$SRCDIR\public\tier2\fileutils.h"
+ $File "$SRCDIR\public\tier2\camerautils.h"
+ $File "$SRCDIR\public\tier2\meshutils.h"
+ $File "$SRCDIR\public\tier2\keybindings.h"
+ $File "$SRCDIR\public\tier2\renderutils.h"
+ $File "$SRCDIR\public\tier2\riff.h"
+ $File "$SRCDIR\public\tier2\soundutils.h"
+ $File "$SRCDIR\public\tier2\tier2.h"
+ $File "$SRCDIR\public\tier2\utlstreambuffer.h"
+ $File "$SRCDIR\public\tier2\vconfig.h"
+ $File "$SRCDIR\public\tier2\keyvaluesmacros.h"
+ }
+}
diff --git a/tier2/util_init.cpp b/tier2/util_init.cpp
new file mode 100644
index 0000000..1fb1ddd
--- /dev/null
+++ b/tier2/util_init.cpp
@@ -0,0 +1,50 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: perform initialization needed in most command line programs
+//
+//===================================================================================-=//
+
+#include <tier0/platform.h>
+#include <tier2/tier2.h>
+#include <mathlib/mathlib.h>
+#include <tier0/icommandline.h>
+#include "tier0/memalloc.h"
+#include "tier0/progressbar.h"
+#include "tier1/strtools.h"
+
+
+static void PrintFReportHandler(char const *job_name, int total_units_to_do, int n_units_completed)
+{
+ static bool work_in_progress=false;
+ static char LastJobName[1024];
+ if ( Q_strncmp( LastJobName, job_name, sizeof( LastJobName ) ) )
+ {
+ if ( work_in_progress )
+ printf("..done\n");
+ Q_strncpy( LastJobName, job_name, sizeof( LastJobName ) );
+ }
+ if ( (total_units_to_do > 0 ) && (total_units_to_do >= n_units_completed) )
+ {
+ int percent_done=(100*n_units_completed)/total_units_to_do;
+ printf("\r%s : %d%%",LastJobName, percent_done );
+ work_in_progress = true;
+ }
+ else
+ {
+ printf("%s\n",LastJobName);
+ work_in_progress = false;
+ }
+}
+
+void InitCommandLineProgram( int argc, char **argv )
+{
+ MathLib_Init( 1,1,1,0,false,true,true,true);
+ CommandLine()->CreateCmdLine( argc, argv );
+ InitDefaultFileSystem();
+ InstallProgressReportHandler( PrintFReportHandler );
+
+ // By default, command line programs should not use the new assert dialog,
+ // and any asserts should be fatal, unless we are being debugged
+ if ( !Plat_IsInDebugSession() )
+ SpewOutputFunc( DefaultSpewFuncAbortOnAsserts );
+}
diff --git a/tier2/utlstreambuffer.cpp b/tier2/utlstreambuffer.cpp
new file mode 100644
index 0000000..023a682
--- /dev/null
+++ b/tier2/utlstreambuffer.cpp
@@ -0,0 +1,386 @@
+//========= 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;
+}
diff --git a/tier2/vconfig.cpp b/tier2/vconfig.cpp
new file mode 100644
index 0000000..ff90afc
--- /dev/null
+++ b/tier2/vconfig.cpp
@@ -0,0 +1,149 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Utilities for setting vproject settings
+//
+//===========================================================================//
+
+#ifdef _WIN32
+#if !defined( _X360 )
+#include <windows.h>
+#endif
+#include <direct.h>
+#include <io.h> // _chmod
+#include <process.h>
+#endif
+#if defined( _X360 )
+#include "xbox/xbox_win32stubs.h"
+#endif
+#include "vconfig.h"
+
+
+#ifdef _WIN32
+//-----------------------------------------------------------------------------
+// Purpose: Returns the string value of a registry key
+// Input : *pName - name of the subKey to read
+// *pReturn - string buffer to receive read string
+// size - size of specified buffer
+//-----------------------------------------------------------------------------
+bool GetVConfigRegistrySetting( const char *pName, char *pReturn, int size )
+{
+ // Open the key
+ HKEY hregkey;
+ // Changed to HKEY_CURRENT_USER from HKEY_LOCAL_MACHINE
+ if ( RegOpenKeyEx( HKEY_CURRENT_USER, VPROJECT_REG_KEY, 0, KEY_QUERY_VALUE, &hregkey ) != ERROR_SUCCESS )
+ return false;
+
+ // Get the value
+ DWORD dwSize = size;
+ if ( RegQueryValueEx( hregkey, pName, NULL, NULL,(LPBYTE) pReturn, &dwSize ) != ERROR_SUCCESS )
+ return false;
+
+ // Close the key
+ RegCloseKey( hregkey );
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sends a global system message to alert programs to a changed environment variable
+//-----------------------------------------------------------------------------
+void NotifyVConfigRegistrySettingChanged( void )
+{
+ DWORD_PTR dwReturnValue = 0;
+
+ // Propagate changes so that environment variables takes immediate effect!
+ SendMessageTimeout( HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM) "Environment", SMTO_ABORTIFHUNG, 5000, &dwReturnValue );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the registry entry to a string value, under the given subKey
+// Input : *pName - name of the subKey to set
+// *pValue - string value
+//-----------------------------------------------------------------------------
+void SetVConfigRegistrySetting( const char *pName, const char *pValue, bool bNotify )
+{
+ HKEY hregkey;
+
+ // Changed to HKEY_CURRENT_USER from HKEY_LOCAL_MACHINE
+ // Open the key
+ if ( RegCreateKeyEx(
+ HKEY_CURRENT_USER, // base key
+ VPROJECT_REG_KEY, // subkey
+ 0, // reserved
+ 0, // lpClass
+ 0, // options
+ (REGSAM)KEY_ALL_ACCESS, // access desired
+ NULL, // security attributes
+ &hregkey, // result
+ NULL // tells if it created the key or not (which we don't care)
+ ) != ERROR_SUCCESS )
+ {
+ return;
+ }
+
+ // Set the value to the string passed in
+ int nType = strchr( pValue, '%' ) ? REG_EXPAND_SZ : REG_SZ;
+ RegSetValueEx( hregkey, pName, 0, nType, (const unsigned char *)pValue, (int) strlen(pValue) );
+
+ // Notify other programs
+ if ( bNotify )
+ {
+ NotifyVConfigRegistrySettingChanged();
+ }
+
+ // Close the key
+ RegCloseKey( hregkey );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Removes the obsolete user keyvalue
+// Input : *pName - name of the subKey to set
+// *pValue - string value
+//-----------------------------------------------------------------------------
+bool RemoveObsoleteVConfigRegistrySetting( const char *pValueName, char *pOldValue, int size )
+{
+ // Open the key
+ HKEY hregkey;
+ if ( RegOpenKeyEx( HKEY_CURRENT_USER, "Environment", 0, (REGSAM)KEY_ALL_ACCESS, &hregkey ) != ERROR_SUCCESS )
+ return false;
+
+ // Return the old state if they've requested it
+ if ( pOldValue != NULL )
+ {
+ DWORD dwSize = size;
+
+ // Get the value
+ if ( RegQueryValueEx( hregkey, pValueName, NULL, NULL,(LPBYTE) pOldValue, &dwSize ) != ERROR_SUCCESS )
+ return false;
+ }
+
+ // Remove the value
+ if ( RegDeleteValue( hregkey, pValueName ) != ERROR_SUCCESS )
+ return false;
+
+ // Close the key
+ RegCloseKey( hregkey );
+
+ // Notify other programs
+ NotifyVConfigRegistrySettingChanged();
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Take a user-defined environment variable and swap it out for the internally used one
+//-----------------------------------------------------------------------------
+
+bool ConvertObsoleteVConfigRegistrySetting( const char *pValueName )
+{
+ char szValue[MAX_PATH];
+ if ( RemoveObsoleteVConfigRegistrySetting( pValueName, szValue, sizeof( szValue ) ) )
+ {
+ // Set it up the correct way
+ SetVConfigRegistrySetting( pValueName, szValue );
+ return true;
+ }
+
+ return false;
+}
+#endif