aboutsummaryrefslogtreecommitdiff
path: root/sp/src/fgdlib
diff options
context:
space:
mode:
authorJoe Ludwig <[email protected]>2013-09-02 11:39:10 -0700
committerJoe Ludwig <[email protected]>2013-09-02 11:39:10 -0700
commita0c29e7dd67abb15c74c85f07741784877edfdcd (patch)
tree35bd6b4580afa14648895b0f321d33a712a5d0fa /sp/src/fgdlib
parentAdded bloom shader and screenspace effect helpers as examples for the SDK. (diff)
downloadsource-sdk-2013-a0c29e7dd67abb15c74c85f07741784877edfdcd.tar.xz
source-sdk-2013-a0c29e7dd67abb15c74c85f07741784877edfdcd.zip
General:
* Fixed a variety of server browser issues with mods based on this SDK * Fixed many warnings on various platforms * Added source code for fgdlib and raytrace * Updated many source files with the latest shared source from TF2. OSX: * Added support for Xcode 4.6 * Switched OSX builds to use Xcode instead of makefiles * Moved libs from src/lib/osx32 to src/lib/public/osx32 or src/lib/common/osx32 to match windows better. Linux: * Moved libs from src/lib/linux32 to src/lib/public/linux32 or src/lib/common/linux32 to match windows better.
Diffstat (limited to 'sp/src/fgdlib')
-rw-r--r--sp/src/fgdlib/fgdlib.vpc40
-rw-r--r--sp/src/fgdlib/gamedata.cpp886
-rw-r--r--sp/src/fgdlib/gdclass.cpp1041
-rw-r--r--sp/src/fgdlib/gdvar.cpp729
-rw-r--r--sp/src/fgdlib/inputoutput.cpp171
-rw-r--r--sp/src/fgdlib/wckeyvalues.cpp282
6 files changed, 3149 insertions, 0 deletions
diff --git a/sp/src/fgdlib/fgdlib.vpc b/sp/src/fgdlib/fgdlib.vpc
new file mode 100644
index 00000000..6ff37694
--- /dev/null
+++ b/sp/src/fgdlib/fgdlib.vpc
@@ -0,0 +1,40 @@
+//-----------------------------------------------------------------------------
+// FGDLIB.VPC
+//
+// Project Script
+//-----------------------------------------------------------------------------
+
+$Macro SRCDIR ".."
+$Include "$SRCDIR\vpc_scripts\source_lib_base.vpc"
+
+$Configuration
+{
+ $Compiler
+ {
+ $AdditionalIncludeDirectories "$BASE,$SRCDIR\utils\common"
+ }
+}
+
+$Project "Fgdlib"
+{
+ $Folder "Source Files"
+ {
+ $File "gamedata.cpp"
+ $File "gdclass.cpp"
+ $File "gdvar.cpp"
+ $File "inputoutput.cpp"
+ $File "wckeyvalues.cpp"
+ }
+
+ $Folder "Header Files"
+ {
+ $File "$SRCDIR\public\fgdlib\fgdlib.h"
+ $File "$SRCDIR\public\fgdlib\gamedata.h"
+ $File "$SRCDIR\public\fgdlib\gdclass.h"
+ $File "$SRCDIR\public\fgdlib\gdvar.h"
+ $File "$SRCDIR\public\fgdlib\helperinfo.h"
+ $File "$SRCDIR\public\fgdlib\ieditortexture.h"
+ $File "$SRCDIR\public\fgdlib\inputoutput.h"
+ $File "$SRCDIR\public\fgdlib\wckeyvalues.h"
+ }
+}
diff --git a/sp/src/fgdlib/gamedata.cpp b/sp/src/fgdlib/gamedata.cpp
new file mode 100644
index 00000000..cd94b23d
--- /dev/null
+++ b/sp/src/fgdlib/gamedata.cpp
@@ -0,0 +1,886 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+//=============================================================================
+
+#include <windows.h>
+#include <tier0/dbg.h>
+#include <io.h>
+#include <WorldSize.h>
+#include "fgdlib/GameData.h"
+#include "fgdlib/HelperInfo.h"
+#include "KeyValues.h"
+#include "filesystem_tools.h"
+#include "tier1/strtools.h"
+#include "utlmap.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#pragma warning(disable:4244)
+
+
+const int MAX_ERRORS = 5;
+
+
+static GameDataMessageFunc_t g_pMsgFunc = NULL;
+
+
+//-----------------------------------------------------------------------------
+// Sets the function used for emitting error messages while loading gamedata files.
+//-----------------------------------------------------------------------------
+void GDSetMessageFunc(GameDataMessageFunc_t pFunc)
+{
+ g_pMsgFunc = pFunc;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Fetches the next token from the file.
+// Input : tr -
+// ppszStore - Destination buffer, one of the following:
+// pointer to NULL - token will be placed in an allocated buffer
+// pointer to non-NULL buffer - token will be placed in buffer
+// ttexpecting -
+// pszExpecting -
+// Output :
+//-----------------------------------------------------------------------------
+static bool DoGetToken(TokenReader &tr, char **ppszStore, int nSize, trtoken_t ttexpecting, const char *pszExpecting)
+{
+ trtoken_t ttype;
+
+ if (*ppszStore != NULL)
+ {
+ // Reads the token into the given buffer.
+ ttype = tr.NextToken(*ppszStore, nSize);
+ }
+ else
+ {
+ // Allocates a buffer to hold the token.
+ ttype = tr.NextTokenDynamic(ppszStore);
+ }
+
+ if (ttype == TOKENSTRINGTOOLONG)
+ {
+ GDError(tr, "unterminated string or string too long");
+ return false;
+ }
+
+ //
+ // Check for a bad token type.
+ //
+ char *pszStore = *ppszStore;
+ bool bBadTokenType = false;
+ if ((ttype != ttexpecting) && (ttexpecting != TOKENNONE))
+ {
+ //
+ // If we were expecting a string and got an integer, don't worry about it.
+ // We can translate from integer to string.
+ //
+ if (!((ttexpecting == STRING) && (ttype == INTEGER)))
+ {
+ bBadTokenType = true;
+ }
+ }
+
+ if (bBadTokenType && (pszExpecting == NULL))
+ {
+ //
+ // We didn't get the expected token type but no expected
+ // string was specified.
+ //
+ char *pszTokenName;
+ switch (ttexpecting)
+ {
+ case IDENT:
+ {
+ pszTokenName = "identifier";
+ break;
+ }
+
+ case INTEGER:
+ {
+ pszTokenName = "integer";
+ break;
+ }
+
+ case STRING:
+ {
+ pszTokenName = "string";
+ break;
+ }
+
+ case OPERATOR:
+ default:
+ {
+ pszTokenName = "symbol";
+ break;
+ }
+ }
+
+ GDError(tr, "expecting %s", pszTokenName);
+ return false;
+ }
+ else if (bBadTokenType || ((pszExpecting != NULL) && !IsToken(pszStore, pszExpecting)))
+ {
+ //
+ // An expected string was specified, and we got either the wrong type or
+ // the right type but the wrong string,
+ //
+ GDError(tr, "expecting '%s', but found '%s'", pszExpecting, pszStore);
+ return false;
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : tr -
+// error -
+// Output :
+//-----------------------------------------------------------------------------
+bool GDError(TokenReader &tr, const char *error, ...)
+{
+ char szBuf[128];
+ va_list vl;
+ va_start(vl, error);
+ vsprintf(szBuf, error, vl);
+ va_end(vl);
+
+ if (g_pMsgFunc)
+ {
+ // HACK: should use an enumeration for error level
+ g_pMsgFunc(1, tr.Error(szBuf));
+ }
+
+ if (tr.GetErrorCount() >= MAX_ERRORS)
+ {
+ if (g_pMsgFunc)
+ {
+ // HACK: should use an enumeration for error level
+ g_pMsgFunc(1, " - too many errors; aborting.");
+ }
+
+ return false;
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Fetches the next token from the file.
+// Input : tr - The token reader object with which to fetch the token.
+// pszStore - Buffer in which to place the token, NULL to discard the token.
+// ttexpecting - The token type that we are expecting. If this is not TOKENNONE
+// and token type read is different, the operation will fail.
+// pszExpecting - The token string that we are expecting. If this string
+// is not NULL and the token string read is different, the operation will fail.
+// Output : Returns TRUE if the operation succeeded, FALSE if there was an error.
+// If there was an error, the error will be reported in the message window.
+//-----------------------------------------------------------------------------
+bool GDGetToken(TokenReader &tr, char *pszStore, int nSize, trtoken_t ttexpecting, const char *pszExpecting)
+{
+ Assert(pszStore != NULL);
+ if (pszStore != NULL)
+ {
+ return DoGetToken(tr, &pszStore, nSize, ttexpecting, pszExpecting);
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Fetches the next token from the file.
+// Input : tr - The token reader object with which to fetch the token.
+// pszStore - Buffer in which to place the token, NULL to discard the token.
+// ttexpecting - The token type that we are expecting. If this is not TOKENNONE
+// and token type read is different, the operation will fail.
+// pszExpecting - The token string that we are expecting. If this string
+// is not NULL and the token string read is different, the operation will fail.
+// Output : Returns TRUE if the operation succeeded, FALSE if there was an error.
+// If there was an error, the error will be reported in the message window.
+//-----------------------------------------------------------------------------
+bool GDSkipToken(TokenReader &tr, trtoken_t ttexpecting, const char *pszExpecting)
+{
+ //
+ // Read the next token into a buffer and discard it.
+ //
+ char szDiscardBuf[MAX_TOKEN];
+ char *pszDiscardBuf = szDiscardBuf;
+ return DoGetToken(tr, &pszDiscardBuf, sizeof(szDiscardBuf), ttexpecting, pszExpecting);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Fetches the next token from the file, allocating a buffer exactly
+// large enough to hold the token.
+// Input : tr -
+// ppszStore -
+// ttexpecting -
+// pszExpecting -
+// Output :
+//-----------------------------------------------------------------------------
+bool GDGetTokenDynamic(TokenReader &tr, char **ppszStore, trtoken_t ttexpecting, const char *pszExpecting)
+{
+ if (ppszStore == NULL)
+ {
+ return false;
+ }
+
+ *ppszStore = NULL;
+ return DoGetToken(tr, ppszStore, -1, ttexpecting, pszExpecting);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor.
+//-----------------------------------------------------------------------------
+GameData::GameData(void)
+{
+ m_nMaxMapCoord = 8192;
+ m_nMinMapCoord = -8192;
+ m_InstanceClass = NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor.
+//-----------------------------------------------------------------------------
+GameData::~GameData(void)
+{
+ ClearData();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void GameData::ClearData(void)
+{
+ // delete classes.
+ int nCount = m_Classes.Count();
+ for (int i = 0; i < nCount; i++)
+ {
+ GDclass *pm = m_Classes.Element(i);
+ delete pm;
+ }
+ m_Classes.RemoveAll();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Loads a gamedata (FGD) file into this object.
+// Input : pszFilename -
+// Output : Returns TRUE on success, FALSE on failure.
+//-----------------------------------------------------------------------------
+BOOL GameData::Load(const char *pszFilename)
+{
+ TokenReader tr;
+
+ if(GetFileAttributes(pszFilename) == 0xffffffff)
+ return FALSE;
+
+ if(!tr.Open(pszFilename))
+ return FALSE;
+
+ trtoken_t ttype;
+ char szToken[128];
+
+ while (1)
+ {
+ if (tr.GetErrorCount() >= MAX_ERRORS)
+ {
+ break;
+ }
+
+ ttype = tr.NextToken(szToken, sizeof(szToken));
+
+ if(ttype == TOKENEOF)
+ break;
+
+ if(ttype != OPERATOR || !IsToken(szToken, "@"))
+ {
+ if(!GDError(tr, "expected @"))
+ return FALSE;
+ }
+
+ // check what kind it is, and parse a new object
+ if (tr.NextToken(szToken, sizeof(szToken)) != IDENT)
+ {
+ if(!GDError(tr, "expected identifier after @"))
+ return FALSE;
+ }
+
+ if (IsToken(szToken, "baseclass") || IsToken(szToken, "pointclass") || IsToken(szToken, "solidclass") || IsToken(szToken, "keyframeclass") ||
+ IsToken(szToken, "moveclass") || IsToken(szToken, "npcclass") || IsToken(szToken, "filterclass"))
+ {
+ //
+ // New class.
+ //
+ GDclass *pNewClass = new GDclass;
+ if (!pNewClass->InitFromTokens(tr, this))
+ {
+ tr.IgnoreTill(OPERATOR, "@"); // go to next section
+ delete pNewClass;
+ }
+ else
+ {
+ if (IsToken(szToken, "baseclass")) // Not directly available to user.
+ {
+ pNewClass->SetBaseClass(true);
+ }
+ else if (IsToken(szToken, "pointclass")) // Generic point class.
+ {
+ pNewClass->SetPointClass(true);
+ }
+ else if (IsToken(szToken, "solidclass")) // Tied to solids.
+ {
+ pNewClass->SetSolidClass(true);
+ }
+ else if (IsToken(szToken, "npcclass")) // NPC class - can be spawned by npc_maker.
+ {
+ pNewClass->SetPointClass(true);
+ pNewClass->SetNPCClass(true);
+ }
+ else if (IsToken(szToken, "filterclass")) // Filter class - can be used as a filter
+ {
+ pNewClass->SetPointClass(true);
+ pNewClass->SetFilterClass(true);
+ }
+ else if (IsToken(szToken, "moveclass")) // Animating
+ {
+ pNewClass->SetMoveClass(true);
+ pNewClass->SetPointClass(true);
+ }
+ else if (IsToken(szToken, "keyframeclass")) // Animation keyframes
+ {
+ pNewClass->SetKeyFrameClass(true);
+ pNewClass->SetPointClass(true);
+ }
+
+ // Check and see if this new class matches an existing one. If so we will override the previous definition.
+ int nExistingClassIndex = 0;
+ GDclass *pExistingClass = ClassForName(pNewClass->GetName(), &nExistingClassIndex);
+ if (NULL != pExistingClass)
+ {
+ m_Classes.InsertAfter(nExistingClassIndex, pNewClass);
+ m_Classes.Remove(nExistingClassIndex);
+ }
+ else
+ {
+ m_Classes.AddToTail(pNewClass);
+ }
+ }
+ }
+ else if (IsToken(szToken, "include"))
+ {
+ if (GDGetToken(tr, szToken, sizeof(szToken), STRING))
+ {
+ // Let's assume it's in the same directory.
+ char justPath[MAX_PATH], loadFilename[MAX_PATH];
+ if ( Q_ExtractFilePath( pszFilename, justPath, sizeof( justPath ) ) )
+ {
+ Q_snprintf( loadFilename, sizeof( loadFilename ), "%s%s", justPath, szToken );
+ }
+ else
+ {
+ Q_strncpy( loadFilename, szToken, sizeof( loadFilename ) );
+ }
+
+ // First try our fully specified directory
+ if (!Load(loadFilename))
+ {
+ // Failing that, try our start directory
+ if (!Load(szToken))
+ {
+ GDError(tr, "error including file: %s", szToken);
+ }
+ }
+ }
+ }
+ else if (IsToken(szToken, "mapsize"))
+ {
+ if (!ParseMapSize(tr))
+ {
+ // Error in map size specifier, skip to next @ sign.
+ tr.IgnoreTill(OPERATOR, "@");
+ }
+ }
+ else if ( IsToken( szToken, "materialexclusion" ) )
+ {
+ if ( !LoadFGDMaterialExclusions( tr ) )
+ {
+ // FGD exclusions not defined; skip to next @ sign.
+ tr.IgnoreTill(OPERATOR, "@");
+ }
+ }
+ else if ( IsToken( szToken, "autovisgroup" ) )
+ {
+ if ( !LoadFGDAutoVisGroups( tr ) )
+ {
+ // FGD AutoVisGroups not defined; skip to next @ sign.
+ tr.IgnoreTill(OPERATOR, "@");
+ }
+ }
+ else
+ {
+ GDError(tr, "unrecognized section name %s", szToken);
+ tr.IgnoreTill(OPERATOR, "@");
+ }
+ }
+
+ if (tr.GetErrorCount() > 0)
+ {
+ return FALSE;
+ }
+
+ tr.Close();
+
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Parses the "mapsize" specifier, which should be of the form:
+//
+// mapsize(min, max)
+//
+// ex: mapsize(-8192, 8192)
+//
+// Input : tr -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool GameData::ParseMapSize(TokenReader &tr)
+{
+ if (!GDSkipToken(tr, OPERATOR, "("))
+ {
+ return false;
+ }
+
+ char szToken[128];
+ if (!GDGetToken(tr, szToken, sizeof(szToken), INTEGER))
+ {
+ return false;
+ }
+ int nMin = atoi(szToken);
+
+ if (!GDSkipToken(tr, OPERATOR, ","))
+ {
+ return false;
+ }
+
+ if (!GDGetToken(tr, szToken, sizeof(szToken), INTEGER))
+ {
+ return false;
+ }
+ int nMax = atoi(szToken);
+
+ if (nMin != nMax)
+ {
+ m_nMinMapCoord = min(nMin, nMax);
+ m_nMaxMapCoord = max(nMin, nMax);
+ }
+
+ if (!GDSkipToken(tr, OPERATOR, ")"))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : pszName -
+// piIndex -
+// Output :
+//-----------------------------------------------------------------------------
+GDclass *GameData::ClassForName(const char *pszName, int *piIndex)
+{
+ int nCount = m_Classes.Count();
+ for (int i = 0; i < nCount; i++)
+ {
+ GDclass *mp = m_Classes.Element(i);
+ if(!strcmp(mp->GetName(), pszName))
+ {
+ if(piIndex)
+ piIndex[0] = i;
+ return mp;
+ }
+ }
+
+ return NULL;
+}
+
+
+// These are 'standard' keys that every entity uses, but they aren't specified that way in the .fgd
+static const char *RequiredKeys[] =
+{
+ "Origin",
+ "Angles",
+ NULL
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: this function will set up the initial class about to be instanced
+// Input : pszClassName - the class name of the entity to be instanced
+// pszInstancePrefix - the prefix to be used for all name fields
+// Origin - the origin offset of the instance
+// Angles - the angle rotation of the instance
+// Output : if successful, will return the game data class of the class name
+//-----------------------------------------------------------------------------
+GDclass *GameData::BeginInstanceRemap( const char *pszClassName, const char *pszInstancePrefix, Vector &Origin, QAngle &Angle )
+{
+ m_InstanceOrigin = Origin;
+ m_InstanceAngle = Angle;
+ AngleMatrix( m_InstanceAngle, m_InstanceOrigin, m_InstanceMat );
+
+ strcpy( m_InstancePrefix, pszInstancePrefix );
+
+ if ( m_InstanceClass )
+ {
+ delete m_InstanceClass;
+ m_InstanceClass = NULL;
+ }
+
+ if ( strcmpi( pszClassName, "info_overlay_accessor" ) == 0 )
+ { // yucky hack for a made up entity in the bsp process
+ pszClassName = "info_overlay";
+ }
+
+ GDclass *BaseClass = ClassForName( pszClassName );
+ if ( BaseClass )
+ {
+ m_InstanceClass = new GDclass();
+ m_InstanceClass->Parent = this;
+ m_InstanceClass->AddBase( BaseClass );
+
+ for( int i = 0; RequiredKeys[ i ]; i++ )
+ {
+ if ( m_InstanceClass->VarForName( RequiredKeys[ i ] ) == NULL )
+ {
+ BaseClass = ClassForName( RequiredKeys[ i ] );
+ if ( BaseClass )
+ {
+ m_InstanceClass->AddBase( BaseClass );
+ }
+ }
+ }
+ }
+ else
+ {
+ m_InstanceClass = NULL;
+ }
+
+ return m_InstanceClass;
+}
+
+
+enum tRemapOperation
+{
+ REMAP_NAME = 0,
+ REMAP_POSITION,
+ REMAP_ANGLE,
+ REMAP_ANGLE_NEGATIVE_PITCH,
+};
+
+
+static CUtlMap< GDIV_TYPE, tRemapOperation > RemapOperation;
+
+
+//-----------------------------------------------------------------------------
+// Purpose: function to sort the class type for the RemapOperations map
+// Input : type1 - the first type to compare against
+// type2 - the second type to compare against
+// Output : returns true if the first type is less than the second one
+//-----------------------------------------------------------------------------
+static bool CUtlType_LessThan( const GDIV_TYPE &type1, const GDIV_TYPE &type2 )
+{
+ return ( type1 < type2 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: this function will attempt to remap a key's value
+// Input : pszKey - the name of the key
+// pszInvalue - the original value
+// AllowNameRemapping - only do name remapping if this parameter is true.
+// this is generally only false on the instance level.
+// Output : returns true if the value changed
+// pszOutValue - the new value if changed
+//-----------------------------------------------------------------------------
+bool GameData::RemapKeyValue( const char *pszKey, const char *pszInValue, char *pszOutValue, TNameFixup NameFixup )
+{
+ if ( RemapOperation.Count() == 0 )
+ {
+ RemapOperation.SetLessFunc( &CUtlType_LessThan );
+ RemapOperation.Insert( ivAngle, REMAP_ANGLE );
+ RemapOperation.Insert( ivTargetDest, REMAP_NAME );
+ RemapOperation.Insert( ivTargetSrc, REMAP_NAME );
+ RemapOperation.Insert( ivOrigin, REMAP_POSITION );
+ RemapOperation.Insert( ivAxis, REMAP_ANGLE );
+ RemapOperation.Insert( ivAngleNegativePitch, REMAP_ANGLE_NEGATIVE_PITCH );
+ }
+
+ if ( !m_InstanceClass )
+ {
+ return false;
+ }
+
+ GDinputvariable *KVVar = m_InstanceClass->VarForName( pszKey );
+ if ( !KVVar )
+ {
+ return false;
+ }
+
+ GDIV_TYPE KVType = KVVar->GetType();
+ int KVRemapIndex = RemapOperation.Find( KVType );
+ if ( KVRemapIndex == RemapOperation.InvalidIndex() )
+ {
+ return false;
+ }
+
+ strcpy( pszOutValue, pszInValue );
+
+ switch( RemapOperation[ KVRemapIndex ] )
+ {
+ case REMAP_NAME:
+ if ( KVType != ivInstanceVariable )
+ {
+ RemapNameField( pszInValue, pszOutValue, NameFixup );
+ }
+ break;
+
+ case REMAP_POSITION:
+ {
+ Vector inPoint( 0.0f, 0.0f, 0.0f ), outPoint;
+
+ sscanf ( pszInValue, "%f %f %f", &inPoint.x, &inPoint.y, &inPoint.z );
+ VectorTransform( inPoint, m_InstanceMat, outPoint );
+ sprintf( pszOutValue, "%g %g %g", outPoint.x, outPoint.y, outPoint.z );
+ }
+ break;
+
+ case REMAP_ANGLE:
+ if ( m_InstanceAngle.x != 0.0f || m_InstanceAngle.y != 0.0f || m_InstanceAngle.z != 0.0f )
+ {
+ QAngle inAngles( 0.0f, 0.0f, 0.0f ), outAngles;
+ matrix3x4_t angToWorld, localMatrix;
+
+ sscanf ( pszInValue, "%f %f %f", &inAngles.x, &inAngles.y, &inAngles.z );
+
+ AngleMatrix( inAngles, angToWorld );
+ MatrixMultiply( m_InstanceMat, angToWorld, localMatrix );
+ MatrixAngles( localMatrix, outAngles );
+
+ sprintf( pszOutValue, "%g %g %g", outAngles.x, outAngles.y, outAngles.z );
+ }
+ break;
+
+ case REMAP_ANGLE_NEGATIVE_PITCH:
+ if ( m_InstanceAngle.x != 0.0f || m_InstanceAngle.y != 0.0f || m_InstanceAngle.z != 0.0f )
+ {
+ QAngle inAngles( 0.0f, 0.0f, 0.0f ), outAngles;
+ matrix3x4_t angToWorld, localMatrix;
+
+ sscanf ( pszInValue, "%f", &inAngles.x ); // just the pitch
+ inAngles.x = -inAngles.x;
+
+ AngleMatrix( inAngles, angToWorld );
+ MatrixMultiply( m_InstanceMat, angToWorld, localMatrix );
+ MatrixAngles( localMatrix, outAngles );
+
+ sprintf( pszOutValue, "%g", -outAngles.x ); // just the pitch
+ }
+ break;
+ }
+
+ return ( strcmpi( pszInValue, pszOutValue ) != 0 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: this function will attempt to remap a name field.
+// Input : pszInvalue - the original value
+// AllowNameRemapping - only do name remapping if this parameter is true.
+// this is generally only false on the instance level.
+// Output : returns true if the value changed
+// pszOutValue - the new value if changed
+//-----------------------------------------------------------------------------
+bool GameData::RemapNameField( const char *pszInValue, char *pszOutValue, TNameFixup NameFixup )
+{
+ strcpy( pszOutValue, pszInValue );
+
+ if ( pszInValue[ 0 ] && pszInValue[ 0 ] != '@' )
+ { // ! at the start of a value means it is global and should not be remaped
+ switch( NameFixup )
+ {
+ case NAME_FIXUP_PREFIX:
+ sprintf( pszOutValue, "%s-%s", m_InstancePrefix, pszInValue );
+ break;
+
+ case NAME_FIXUP_POSTFIX:
+ sprintf( pszOutValue, "%s-%s", pszInValue, m_InstancePrefix );
+ break;
+ }
+ }
+
+ return ( strcmpi( pszInValue, pszOutValue ) != 0 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Gathers any FGD-defined material directory exclusions
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+bool GameData::LoadFGDMaterialExclusions( TokenReader &tr )
+{
+ if ( !GDSkipToken( tr, OPERATOR, "[" ) )
+ {
+ return false;
+ }
+ while ( 1 )
+ {
+ char szToken[128];
+ bool bMatchFound = false;
+
+ if ( tr.PeekTokenType( szToken, sizeof( szToken ) ) == OPERATOR )
+ {
+ break;
+ }
+ else if ( GDGetToken( tr, szToken, sizeof( szToken ), STRING ) )
+ {
+ // Make sure we haven't loaded this from another FGD
+ for ( int i = 0; i < m_FGDMaterialExclusions.Count(); i++ )
+ {
+ if ( !stricmp( szToken, m_FGDMaterialExclusions[i].szDirectory ) )
+ {
+ bMatchFound = true;
+ break;
+ }
+ }
+
+ // Parse the string
+ if ( bMatchFound == false )
+ {
+ int index = m_FGDMaterialExclusions.AddToTail();
+ Q_strncpy( m_FGDMaterialExclusions[index].szDirectory, szToken, sizeof( m_FGDMaterialExclusions[index].szDirectory ) );
+ m_FGDMaterialExclusions[index].bUserGenerated = false;
+ }
+ }
+ }
+
+ //
+ // Closing square brace.
+ //
+ if ( !GDSkipToken( tr, OPERATOR, "]" ) )
+ {
+ return( FALSE );
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Gathers any FGD-defined Auto VisGroups
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+bool GameData::LoadFGDAutoVisGroups( TokenReader &tr )
+{
+ int gindex = 0; // Index of AutoVisGroups
+ int cindex = 0; // Index of Classes
+
+ char szToken[128];
+
+ // Handle the Parent -- World Geometry, Entities, World Detail
+ if ( GDSkipToken( tr, OPERATOR, "=" ) )
+ {
+ // We expect a name
+ if ( !GDGetToken( tr, szToken, sizeof( szToken ), STRING ) )
+ {
+ return( FALSE );
+ }
+
+ gindex = m_FGDAutoVisGroups.AddToTail();
+ Q_strncpy( m_FGDAutoVisGroups[gindex].szParent, szToken, sizeof( m_FGDAutoVisGroups[gindex].szParent ) );
+
+ // We expect a Class
+ if ( !GDSkipToken( tr, OPERATOR, "[" ) )
+ {
+ return( FALSE );
+ }
+ }
+
+ // Handle the Class(es) -- Brush Entities, Occluders, Lights
+ while ( 1 )
+ {
+ if ( GDGetToken( tr, szToken, sizeof( szToken ), STRING ) )
+ {
+ cindex = m_FGDAutoVisGroups[gindex].m_Classes.AddToTail();
+ Q_strncpy( m_FGDAutoVisGroups[gindex].m_Classes[cindex].szClass, szToken, sizeof( m_FGDAutoVisGroups[gindex].m_Classes[cindex].szClass ) );
+
+ if ( !GDSkipToken( tr, OPERATOR, "[" ) )
+ {
+ return( FALSE );
+ }
+
+ // Parse objects/entities -- func_detail, point_template, light_spot
+ while ( 1 )
+ {
+ if ( tr.PeekTokenType( szToken, sizeof( szToken ) ) == OPERATOR )
+ {
+ break;
+ }
+
+ if ( !GDGetToken( tr, szToken, sizeof( szToken ), STRING ) )
+ {
+ return( FALSE );
+ }
+
+ m_FGDAutoVisGroups[gindex].m_Classes[cindex].szEntities.CopyAndAddToTail( szToken );
+
+ }
+
+ if ( !GDSkipToken( tr, OPERATOR, "]" ) )
+ {
+ return( FALSE );
+ }
+
+ // See if we have another Class coming up
+ if ( tr.PeekTokenType( szToken, sizeof( szToken ) ) == STRING )
+ {
+ continue;
+ }
+
+ // If no more Classes, we now expect a terminating ']'
+ if ( !GDSkipToken( tr, OPERATOR, "]" ) )
+ {
+ return( FALSE );
+ }
+
+ // We're done
+ return true;
+ }
+ // We don't have another Class; look for a terminating brace
+ else
+ {
+ if ( !GDSkipToken( tr, OPERATOR, "]" ) )
+ {
+ return( FALSE );
+ }
+ }
+ }
+
+ // Safety net
+ GDError( tr, "Malformed AutoVisGroup -- Last processed: %s", szToken );
+ return( FALSE );
+}
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgoff.h"
diff --git a/sp/src/fgdlib/gdclass.cpp b/sp/src/fgdlib/gdclass.cpp
new file mode 100644
index 00000000..be07610e
--- /dev/null
+++ b/sp/src/fgdlib/gdclass.cpp
@@ -0,0 +1,1041 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "fgdlib/GameData.h" // FGDLIB: eliminate dependency
+#include "fgdlib/GDClass.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor.
+//-----------------------------------------------------------------------------
+GDclass::GDclass(void)
+{
+ m_nVariables = 0;
+ m_bBase = false;
+ m_bSolid = false;
+ m_bBase = false;
+ m_bSolid = false;
+ m_bModel = false;
+ m_bMove = false;
+ m_bKeyFrame = false;
+ m_bPoint = false;
+ m_bNPC = false;
+ m_bFilter = false;
+
+ m_bHalfGridSnap = false;
+
+ m_bGotSize = false;
+ m_bGotColor = false;
+
+ m_rgbColor.r = 220;
+ m_rgbColor.g = 30;
+ m_rgbColor.b = 220;
+ m_rgbColor.a = 0;
+
+ m_pszDescription = NULL;
+
+ for (int i = 0; i < 3; i++)
+ {
+ m_bmins[i] = -8;
+ m_bmaxs[i] = 8;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor. Frees variable and helper lists.
+//-----------------------------------------------------------------------------
+GDclass::~GDclass(void)
+{
+ //
+ // Free variables.
+ //
+ int nCount = m_Variables.Count();
+ for (int i = 0; i < nCount; i++)
+ {
+ GDinputvariable *pvi = m_Variables.Element(i);
+ delete pvi;
+ }
+ m_Variables.RemoveAll();
+
+ //
+ // Free helpers.
+ //
+ nCount = m_Helpers.Count();
+ for (int i = 0; i < nCount; i++)
+ {
+ CHelperInfo *pHelper = m_Helpers.Element(i);
+ delete pHelper;
+ }
+ m_Helpers.RemoveAll();
+
+ //
+ // Free inputs.
+ //
+ nCount = m_Inputs.Count();
+ for (int i = 0; i < nCount; i++)
+ {
+ CClassInput *pInput = m_Inputs.Element(i);
+ delete pInput;
+ }
+ m_Inputs.RemoveAll();
+
+ //
+ // Free outputs.
+ //
+ nCount = m_Outputs.Count();
+ for (int i = 0; i < nCount; i++)
+ {
+ CClassOutput *pOutput = m_Outputs.Element(i);
+ delete pOutput;
+ }
+ m_Outputs.RemoveAll();
+
+ delete m_pszDescription;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Adds the base class's variables to our variable list. Acquires the
+// base class's bounding box and color, if any.
+// Input : pszBase - Name of base class to add.
+//-----------------------------------------------------------------------------
+void GDclass::AddBase(GDclass *pBase)
+{
+ int iBaseIndex;
+ Parent->ClassForName(pBase->GetName(), &iBaseIndex);
+
+ //
+ // Add variables from base - update variable table
+ //
+ for (int i = 0; i < pBase->GetVariableCount(); i++)
+ {
+ GDinputvariable *pVar = pBase->GetVariableAt(i);
+ AddVariable(pVar, pBase, iBaseIndex, i);
+ }
+
+ //
+ // Add inputs from the base.
+ // UNDONE: need to use references to inputs & outputs to conserve memory
+ //
+ int nCount = pBase->GetInputCount();
+ for (int i = 0; i < nCount; i++)
+ {
+ CClassInput *pInput = pBase->GetInput(i);
+
+ CClassInput *pNew = new CClassInput;
+ *pNew = *pInput;
+ AddInput(pNew);
+ }
+
+ //
+ // Add outputs from the base.
+ //
+ nCount = pBase->GetOutputCount();
+ for (int i = 0; i < nCount; i++)
+ {
+ CClassOutput *pOutput = pBase->GetOutput(i);
+
+ CClassOutput *pNew = new CClassOutput;
+ *pNew = *pOutput;
+ AddOutput(pNew);
+ }
+
+ //
+ // If we don't have a bounding box, try to get the base's box.
+ //
+ if (!m_bGotSize)
+ {
+ if (pBase->GetBoundBox(m_bmins, m_bmaxs))
+ {
+ m_bGotSize = true;
+ }
+ }
+
+ //
+ // If we don't have a color, use the base's color.
+ //
+ if (!m_bGotColor)
+ {
+ m_rgbColor = pBase->GetColor();
+ m_bGotColor = true;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Adds the given GDInputVariable to this GDClass's list of variables.
+// Input : pVar -
+// pBase -
+// iBaseIndex -
+// iVarIndex -
+// Output : Returns TRUE if the pVar pointer was copied directly into this GDClass,
+// FALSE if not. If this function returns TRUE, pVar should not be
+// deleted by the caller.
+//-----------------------------------------------------------------------------
+BOOL GDclass::AddVariable(GDinputvariable *pVar, GDclass *pBase, int iBaseIndex, int iVarIndex)
+{
+ int iThisIndex;
+ GDinputvariable *pThisVar = VarForName(pVar->GetName(), &iThisIndex);
+
+ //
+ // Check to see if we are overriding an existing variable definition.
+ //
+ if (pThisVar != NULL)
+ {
+ //
+ // Same name, different type. Flag this as an error.
+ //
+ if (pThisVar->GetType() != pVar->GetType())
+ {
+ return(false);
+ }
+
+ GDinputvariable *pAddVar;
+ bool bReturn;
+
+ //
+ // Check to see if we need to combine a choices/flags array.
+ //
+ if (pVar->GetType() == ivFlags || pVar->GetType() == ivChoices)
+ {
+ //
+ // Combine two variables' flags into a new variable. Add the new
+ // variable to the local variable list and modify the old variable's
+ // position in our variable map to reflect the new local variable.
+ // This way, we can have multiple inheritance.
+ //
+ GDinputvariable *pNewVar = new GDinputvariable;
+
+ *pNewVar = *pVar;
+ pNewVar->Merge(*pThisVar);
+
+ pAddVar = pNewVar;
+ bReturn = false;
+ }
+ else
+ {
+ pAddVar = pVar;
+ bReturn = true;
+ }
+
+ if (m_VariableMap[iThisIndex][0] == -1)
+ {
+ //
+ // "pThisVar" is a leaf variable - we can remove since it is overridden.
+ //
+ int nIndex = m_Variables.Find(pThisVar);
+ Assert(nIndex != -1);
+ delete pThisVar;
+
+ m_Variables.Element(nIndex) = pAddVar;
+
+ //
+ // No need to modify variable map - we just replaced
+ // the pointer in the local variable list.
+ //
+ }
+ else
+ {
+ //
+ // "pThisVar" was declared in a base class - we can replace the reference in
+ // our variable map with the new variable.
+ //
+ m_VariableMap[iThisIndex][0] = iBaseIndex;
+
+ if (iBaseIndex == -1)
+ {
+ m_Variables.AddToTail(pAddVar);
+ m_VariableMap[iThisIndex][1] = m_Variables.Count() - 1;
+ }
+ else
+ {
+ m_VariableMap[iThisIndex][1] = iVarIndex;
+ }
+ }
+
+ return(bReturn);
+ }
+
+ //
+ // New variable.
+ //
+ if (iBaseIndex == -1)
+ {
+ //
+ // Variable declared in the leaf class definition - add it to the list.
+ //
+ m_Variables.AddToTail(pVar);
+ }
+
+ //
+ // Too many variables already declared in this class definition - abort.
+ //
+ if (m_nVariables == GD_MAX_VARIABLES)
+ {
+ //CUtlString str;
+ //str.Format("Too many gamedata variables for class \"%s\"", m_szName);
+ //AfxMessageBox(str);
+
+ return(false);
+ }
+
+ //
+ // Add the variable to our list.
+ //
+ m_VariableMap[m_nVariables][0] = iBaseIndex;
+ m_VariableMap[m_nVariables][1] = iVarIndex;
+ ++m_nVariables;
+
+ //
+ // We added the pointer to our list of items (see Variables.AddToTail, above) so
+ // we must return true here.
+ //
+ return(true);
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds an input by name.
+//-----------------------------------------------------------------------------
+CClassInput *GDclass::FindInput(const char *szName)
+{
+ int nCount = GetInputCount();
+ for (int i = 0; i < nCount; i++)
+ {
+ CClassInput *pInput = GetInput(i);
+ if (!stricmp(pInput->GetName(), szName))
+ {
+ return(pInput);
+ }
+ }
+
+ return(NULL);
+}
+
+
+//-----------------------------------------------------------------------------
+// Finds an output by name.
+//-----------------------------------------------------------------------------
+CClassOutput *GDclass::FindOutput(const char *szName)
+{
+ int nCount = GetOutputCount();
+ for (int i = 0; i < nCount; i++)
+ {
+ CClassOutput *pOutput = GetOutput(i);
+ if (!stricmp(pOutput->GetName(), szName))
+ {
+ return(pOutput);
+ }
+ }
+
+ return(NULL);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Gets the mins and maxs of the class's bounding box as read from the
+// FGD file. This controls the onscreen representation of any entities
+// derived from this class.
+// Input : pfMins - Receives minimum X, Y, and Z coordinates for the class.
+// pfMaxs - Receives maximum X, Y, and Z coordinates for the class.
+// Output : Returns TRUE if this class has a specified bounding box, FALSE if not.
+//-----------------------------------------------------------------------------
+BOOL GDclass::GetBoundBox(Vector& pfMins, Vector& pfMaxs)
+{
+ if (m_bGotSize)
+ {
+ pfMins[0] = m_bmins[0];
+ pfMins[1] = m_bmins[1];
+ pfMins[2] = m_bmins[2];
+
+ pfMaxs[0] = m_bmaxs[0];
+ pfMaxs[1] = m_bmaxs[1];
+ pfMaxs[2] = m_bmaxs[2];
+ }
+
+ return(m_bGotSize);
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+CHelperInfo *GDclass::GetHelper(int nIndex)
+{
+ return m_Helpers.Element(nIndex);
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+CClassInput *GDclass::GetInput(int nIndex)
+{
+ return m_Inputs.Element(nIndex);
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+CClassOutput *GDclass::GetOutput(int nIndex)
+{
+ return m_Outputs.Element(nIndex);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : tr -
+// pGD -
+// Output : Returns TRUE if worth continuing, FALSE otherwise.
+//-----------------------------------------------------------------------------
+BOOL GDclass::InitFromTokens(TokenReader& tr, GameData *pGD)
+{
+ Parent = pGD;
+
+ //
+ // Initialize VariableMap
+ //
+ for (int i = 0; i < GD_MAX_VARIABLES; i++)
+ {
+ m_VariableMap[i][0] = -1;
+ m_VariableMap[i][1] = -1;
+ }
+
+ //
+ // Parse all specifiers (base, size, color, etc.)
+ //
+ if (!ParseSpecifiers(tr))
+ {
+ return(FALSE);
+ }
+
+ //
+ // Specifiers should be followed by an "="
+ //
+ if (!GDSkipToken(tr, OPERATOR, "="))
+ {
+ return(FALSE);
+ }
+
+ //
+ // Parse the class name.
+ //
+ if (!GDGetToken(tr, m_szName, sizeof(m_szName), IDENT))
+ {
+ return(FALSE);
+ }
+
+ //
+ // Check next operator - if ":", we have a description - if "[",
+ // we have no description.
+ //
+ char szToken[MAX_TOKEN];
+ if ((tr.PeekTokenType(szToken,sizeof(szToken)) == OPERATOR) && IsToken(szToken, ":"))
+ {
+ // Skip ":"
+ tr.NextToken(szToken, sizeof(szToken));
+
+ //
+ // Free any existing description and set the pointer to NULL so that GDGetToken
+ // allocates memory for us.
+ //
+ delete m_pszDescription;
+ m_pszDescription = NULL;
+
+ // Load description
+ if (!GDGetTokenDynamic(tr, &m_pszDescription, STRING))
+ {
+ return(FALSE);
+ }
+ }
+
+ //
+ // Opening square brace.
+ //
+ if (!GDSkipToken(tr, OPERATOR, "["))
+ {
+ return(FALSE);
+ }
+
+ //
+ // Get class variables.
+ //
+ if (!ParseVariables(tr))
+ {
+ return(FALSE);
+ }
+
+ //
+ // Closing square brace.
+ //
+ if (!GDSkipToken(tr, OPERATOR, "]"))
+ {
+ return(FALSE);
+ }
+
+ return(TRUE);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : &tr -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool GDclass::ParseBase(TokenReader &tr)
+{
+ char szToken[MAX_TOKEN];
+
+ while (1)
+ {
+ if (!GDGetToken(tr, szToken, sizeof(szToken), IDENT))
+ {
+ return(false);
+ }
+
+ //
+ // Find base class in list of classes.
+ //
+ GDclass *pBase = Parent->ClassForName(szToken);
+ if (pBase == NULL)
+ {
+ GDError(tr, "undefined base class '%s", szToken);
+ return(false);
+ }
+
+ AddBase(pBase);
+
+ if (!GDGetToken(tr, szToken, sizeof(szToken), OPERATOR))
+ {
+ return(false);
+ }
+
+ if (IsToken(szToken, ")"))
+ {
+ break;
+ }
+ else if (!IsToken(szToken, ","))
+ {
+ GDError(tr, "expecting ',' or ')', but found %s", szToken);
+ return(false);
+ }
+ }
+
+ return(true);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : &tr -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool GDclass::ParseColor(TokenReader &tr)
+{
+ char szToken[MAX_TOKEN];
+
+ //
+ // Red.
+ //
+ if (!GDGetToken(tr, szToken, sizeof(szToken), INTEGER))
+ {
+ return(false);
+ }
+ BYTE r = atoi(szToken);
+
+ //
+ // Green.
+ //
+ if (!GDGetToken(tr, szToken, sizeof(szToken), INTEGER))
+ {
+ return(false);
+ }
+ BYTE g = atoi(szToken);
+
+ //
+ // Blue.
+ //
+ if (!GDGetToken(tr, szToken, sizeof(szToken), INTEGER))
+ {
+ return(false);
+ }
+ BYTE b = atoi(szToken);
+
+ m_rgbColor.r = r;
+ m_rgbColor.g = g;
+ m_rgbColor.b = b;
+ m_rgbColor.a = 0;
+
+ m_bGotColor = true;
+
+ if (!GDGetToken(tr, szToken, sizeof(szToken), OPERATOR, ")"))
+ {
+ return(false);
+ }
+
+ return(true);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Parses a helper from the FGD file. Helpers are of the following format:
+//
+// <helpername>(<parameter 0> <parameter 1> ... <parameter n>)
+//
+// When this function is called, the helper name has already been parsed.
+// Input : tr - Tokenreader to use for parsing.
+// pszHelperName - Name of the helper being declared.
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool GDclass::ParseHelper(TokenReader &tr, char *pszHelperName)
+{
+ char szToken[MAX_TOKEN];
+
+ CHelperInfo *pHelper = new CHelperInfo;
+ pHelper->SetName(pszHelperName);
+
+ bool bCloseParen = false;
+ while (!bCloseParen)
+ {
+ trtoken_t eType = tr.PeekTokenType(szToken,sizeof(szToken));
+
+ if (eType == OPERATOR)
+ {
+ if (!GDGetToken(tr, szToken, sizeof(szToken), OPERATOR))
+ {
+ delete pHelper;
+ return(false);
+ }
+
+ if (IsToken(szToken, ")"))
+ {
+ bCloseParen = true;
+ }
+ else if (IsToken(szToken, "="))
+ {
+ delete pHelper;
+ return(false);
+ }
+ }
+ else
+ {
+ if (!GDGetToken(tr, szToken, sizeof(szToken), eType))
+ {
+ delete pHelper;
+ return(false);
+ }
+ else
+ {
+ pHelper->AddParameter(szToken);
+ }
+ }
+ }
+
+ m_Helpers.AddToTail(pHelper);
+
+ return(true);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : &tr -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool GDclass::ParseSize(TokenReader &tr)
+{
+ char szToken[MAX_TOKEN];
+
+ //
+ // Mins.
+ //
+ for (int i = 0; i < 3; i++)
+ {
+ if (!GDGetToken(tr, szToken, sizeof(szToken), INTEGER))
+ {
+ return(false);
+ }
+
+ m_bmins[i] = (float)atof(szToken);
+ }
+
+ if (tr.PeekTokenType(szToken,sizeof(szToken)) == OPERATOR && IsToken(szToken, ","))
+ {
+ //
+ // Skip ","
+ //
+ tr.NextToken(szToken, sizeof(szToken));
+
+ //
+ // Get maxes.
+ //
+ for (int i = 0; i < 3; i++)
+ {
+ if (!GDGetToken(tr, szToken, sizeof(szToken), INTEGER))
+ {
+ return(false);
+ }
+ m_bmaxs[i] = (float)atof(szToken);
+ }
+ }
+ else
+ {
+ //
+ // Split mins across origin.
+ //
+ for (int i = 0; i < 3; i++)
+ {
+ float div2 = m_bmins[i] / 2;
+ m_bmaxs[i] = div2;
+ m_bmins[i] = -div2;
+ }
+ }
+
+ m_bGotSize = true;
+
+ if (!GDGetToken(tr, szToken, sizeof(szToken), OPERATOR, ")"))
+ {
+ return(false);
+ }
+
+ return(true);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : &tr -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool GDclass::ParseSpecifiers(TokenReader &tr)
+{
+ char szToken[MAX_TOKEN];
+
+ while (tr.PeekTokenType() == IDENT)
+ {
+ tr.NextToken(szToken, sizeof(szToken));
+
+ //
+ // Handle specifiers that don't have any parens after them.
+ //
+ if (IsToken(szToken, "halfgridsnap"))
+ {
+ m_bHalfGridSnap = true;
+ }
+ else
+ {
+ //
+ // Handle specifiers require parens after them.
+ //
+ if (!GDSkipToken(tr, OPERATOR, "("))
+ {
+ return(false);
+ }
+
+ if (IsToken(szToken, "base"))
+ {
+ if (!ParseBase(tr))
+ {
+ return(false);
+ }
+ }
+ else if (IsToken(szToken, "size"))
+ {
+ if (!ParseSize(tr))
+ {
+ return(false);
+ }
+ }
+ else if (IsToken(szToken, "color"))
+ {
+ if (!ParseColor(tr))
+ {
+ return(false);
+ }
+ }
+ else if (!ParseHelper(tr, szToken))
+ {
+ return(false);
+ }
+ }
+ }
+
+ return(true);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Reads an input using a given token reader. If the input is
+// read successfully, the input is added to this class. If not, a
+// parsing failure is returned.
+// Input : tr - Token reader to use for parsing.
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool GDclass::ParseInput(TokenReader &tr)
+{
+ char szToken[MAX_TOKEN];
+
+ if (!GDGetToken(tr, szToken, sizeof(szToken), IDENT, "input"))
+ {
+ return(false);
+ }
+
+ CClassInput *pInput = new CClassInput;
+
+ bool bReturn = ParseInputOutput(tr, pInput);
+ if (bReturn)
+ {
+ AddInput(pInput);
+ }
+ else
+ {
+ delete pInput;
+ }
+
+ return(bReturn);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Reads an input or output using a given token reader.
+// Input : tr - Token reader to use for parsing.
+// pInputOutput - Input or output to fill out.
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool GDclass::ParseInputOutput(TokenReader &tr, CClassInputOutputBase *pInputOutput)
+{
+ char szToken[MAX_TOKEN];
+
+ //
+ // Read the name.
+ //
+ if (!GDGetToken(tr, szToken, sizeof(szToken), IDENT))
+ {
+ return(false);
+ }
+
+ pInputOutput->SetName(szToken);
+
+ //
+ // Read the type.
+ //
+ if (!GDGetToken(tr, szToken, sizeof(szToken), OPERATOR, "("))
+ {
+ return(false);
+ }
+
+ if (!GDGetToken(tr, szToken, sizeof(szToken), IDENT))
+ {
+ return(false);
+ }
+
+ InputOutputType_t eType = pInputOutput->SetType(szToken);
+ if (eType == iotInvalid)
+ {
+ GDError(tr, "bad input/output type '%s'", szToken);
+ return(false);
+ }
+
+ if (!GDGetToken(tr, szToken, sizeof(szToken), OPERATOR, ")"))
+ {
+ return(false);
+ }
+
+ //
+ // Check the next operator - if ':', we have a description.
+ //
+ if ((tr.PeekTokenType(szToken,sizeof(szToken)) == OPERATOR) && (IsToken(szToken, ":")))
+ {
+ //
+ // Skip the ":".
+ //
+ tr.NextToken(szToken, sizeof(szToken));
+
+ //
+ // Read the description.
+ //
+ char *pszDescription;
+ if (!GDGetTokenDynamic(tr, &pszDescription, STRING))
+ {
+ return(false);
+ }
+
+ pInputOutput->SetDescription(pszDescription);
+ }
+
+ return(true);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Reads an output using a given token reader. If the output is
+// read successfully, the output is added to this class. If not, a
+// parsing failure is returned.
+// Input : tr - Token reader to use for parsing.
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool GDclass::ParseOutput(TokenReader &tr)
+{
+ char szToken[MAX_TOKEN];
+
+ if (!GDGetToken(tr, szToken, sizeof(szToken), IDENT, "output"))
+ {
+ return(false);
+ }
+
+ CClassOutput *pOutput = new CClassOutput;
+
+ bool bReturn = ParseInputOutput(tr, pOutput);
+ if (bReturn)
+ {
+ AddOutput(pOutput);
+ }
+ else
+ {
+ delete pOutput;
+ }
+
+ return(bReturn);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : &tr -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool GDclass::ParseVariables(TokenReader &tr)
+{
+ while (1)
+ {
+ char szToken[MAX_TOKEN];
+
+ if (tr.PeekTokenType(szToken,sizeof(szToken)) == OPERATOR)
+ {
+ break;
+ }
+
+ if (!stricmp(szToken, "input"))
+ {
+ if (!ParseInput(tr))
+ {
+ return(false);
+ }
+
+ continue;
+ }
+
+ if (!stricmp(szToken, "output"))
+ {
+ if (!ParseOutput(tr))
+ {
+ return(false);
+ }
+
+ continue;
+ }
+
+ if (!stricmp(szToken, "key"))
+ {
+ GDGetToken(tr, szToken, sizeof(szToken));
+ }
+
+ GDinputvariable * var = new GDinputvariable;
+ if (!var->InitFromTokens(tr))
+ {
+ delete var;
+ return(false);
+ }
+
+ int nDupIndex;
+ GDinputvariable *pDupVar = VarForName(var->GetName(), &nDupIndex);
+
+ // check for duplicate variable definitions
+ if (pDupVar)
+ {
+ // Same name, different type.
+ if (pDupVar->GetType() != var->GetType())
+ {
+ char szError[_MAX_PATH];
+
+ sprintf(szError, "%s: Variable '%s' is multiply defined with different types.", GetName(), var->GetName());
+ GDError(tr, szError);
+ }
+ }
+
+ if (!AddVariable(var, this, -1, m_Variables.Count()))
+ {
+ delete var;
+ }
+ }
+
+ return(true);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : iIndex -
+// Output : GDinputvariable *
+//-----------------------------------------------------------------------------
+GDinputvariable *GDclass::GetVariableAt(int iIndex)
+{
+ if ( iIndex < 0 || iIndex >= m_nVariables )
+ return NULL;
+
+ if (m_VariableMap[iIndex][0] == -1)
+ {
+ return m_Variables.Element(m_VariableMap[iIndex][1]);
+ }
+
+ // find var's owner
+ GDclass *pVarClass = Parent->GetClass(m_VariableMap[iIndex][0]);
+
+ // find var in pVarClass
+ return pVarClass->GetVariableAt(m_VariableMap[iIndex][1]);
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+GDinputvariable *GDclass::VarForName(const char *pszName, int *piIndex)
+{
+ for(int i = 0; i < GetVariableCount(); i++)
+ {
+ GDinputvariable *pVar = GetVariableAt(i);
+ if(!strcmpi(pVar->GetName(), pszName))
+ {
+ if(piIndex)
+ piIndex[0] = i;
+ return pVar;
+ }
+ }
+
+ return NULL;
+}
+
+void GDclass::GetHelperForGDVar( GDinputvariable *pVar, CUtlVector<const char *> *pszHelperName )
+{
+ const char *pszName = pVar->GetName();
+ for( int i = 0; i < GetHelperCount(); i++ )
+ {
+ CHelperInfo *pHelper = GetHelper( i );
+ int nParamCount = pHelper->GetParameterCount();
+ for ( int j = 0; j < nParamCount; j++ )
+ {
+ if ( !strcmpi( pszName, pHelper->GetParameter( j ) ) )
+ {
+ pszHelperName->AddToTail(pHelper->GetName());
+ }
+ }
+ }
+}
+
+
+
diff --git a/sp/src/fgdlib/gdvar.cpp b/sp/src/fgdlib/gdvar.cpp
new file mode 100644
index 00000000..5b9a21be
--- /dev/null
+++ b/sp/src/fgdlib/gdvar.cpp
@@ -0,0 +1,729 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+//=============================================================================
+
+#include "fgdlib/fgdlib.h"
+#include "fgdlib/GameData.h"
+#include "fgdlib/WCKeyValues.h"
+#include "fgdlib/gdvar.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+
+typedef struct
+{
+ GDIV_TYPE eType; // The enumeration of this type.
+ char *pszName; // The name of this type.
+ trtoken_t eStoreAs; // How this type is stored (STRING, INTEGER, etc).
+} TypeMap_t;
+
+
+//-----------------------------------------------------------------------------
+// Maps type names to type enums and parsing logic for values.
+//-----------------------------------------------------------------------------
+static TypeMap_t TypeMap[] =
+{
+ { ivAngle, "angle", STRING },
+ { ivChoices, "choices", STRING },
+ { ivColor1, "color1", STRING },
+ { ivColor255, "color255", STRING },
+ { ivDecal, "decal", STRING },
+ { ivFlags, "flags", INTEGER },
+ { ivInteger, "integer", INTEGER },
+ { ivSound, "sound", STRING },
+ { ivSprite, "sprite", STRING },
+ { ivString, "string", STRING },
+ { ivStudioModel, "studio", STRING },
+ { ivTargetDest, "target_destination", STRING },
+ { ivTargetSrc, "target_source", STRING },
+ { ivTargetNameOrClass, "target_name_or_class", STRING }, // Another version of target_destination that accepts class names
+ { ivVector, "vector", STRING },
+ { ivNPCClass, "npcclass", STRING },
+ { ivFilterClass, "filterclass", STRING },
+ { ivFloat, "float", STRING },
+ { ivMaterial, "material", STRING },
+ { ivScene, "scene", STRING },
+ { ivSide, "side", STRING },
+ { ivSideList, "sidelist", STRING },
+ { ivOrigin, "origin", STRING },
+ { ivAxis, "axis", STRING },
+ { ivVecLine, "vecline", STRING },
+ { ivPointEntityClass, "pointentityclass", STRING },
+ { ivNodeDest, "node_dest", INTEGER },
+ { ivInstanceFile, "instance_file", STRING },
+ { ivAngleNegativePitch, "angle_negative_pitch", STRING },
+ { ivInstanceVariable, "instance_variable", STRING },
+ { ivInstanceParm, "instance_parm", STRING },
+};
+
+
+char *GDinputvariable::m_pszEmpty = "";
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+GDinputvariable::GDinputvariable(void)
+{
+ m_szDefault[0] = 0;
+ m_nDefault = 0;
+ m_szValue[0] = 0;
+ m_bReportable = FALSE;
+ m_bReadOnly = false;
+ m_pszDescription = NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: construct generally used for creating a temp instance parm type
+// Input : szType - the textual type of this variable
+// szName - the name description of this variable
+//-----------------------------------------------------------------------------
+GDinputvariable::GDinputvariable( const char *szType, const char *szName )
+{
+ m_szDefault[0] = 0;
+ m_nDefault = 0;
+ m_szValue[0] = 0;
+ m_bReportable = FALSE;
+ m_bReadOnly = false;
+ m_pszDescription = NULL;
+
+ m_eType = GetTypeFromToken( szType );
+ strcpy( m_szName, szName );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor.
+//-----------------------------------------------------------------------------
+GDinputvariable::~GDinputvariable(void)
+{
+ delete [] m_pszDescription;
+ m_Items.RemoveAll();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Implements the copy operator.
+//-----------------------------------------------------------------------------
+GDinputvariable &GDinputvariable::operator =(GDinputvariable &Other)
+{
+ m_eType = Other.GetType();
+ strcpy(m_szName, Other.m_szName);
+ strcpy(m_szLongName, Other.m_szLongName);
+ strcpy(m_szDefault, Other.m_szDefault);
+
+ //
+ // Copy the description.
+ //
+ delete [] m_pszDescription;
+ if (Other.m_pszDescription != NULL)
+ {
+ m_pszDescription = new char[strlen(Other.m_pszDescription) + 1];
+ strcpy(m_pszDescription, Other.m_pszDescription);
+ }
+ else
+ {
+ m_pszDescription = NULL;
+ }
+
+ m_nDefault = Other.m_nDefault;
+ m_bReportable = Other.m_bReportable;
+ m_bReadOnly = Other.m_bReadOnly;
+
+ m_Items.RemoveAll();
+
+ int nCount = Other.m_Items.Count();
+ for (int i = 0; i < nCount; i++)
+ {
+ m_Items.AddToTail(Other.m_Items.Element(i));
+ }
+
+ return(*this);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the storage format of a given variable type.
+// Input : pszToken - Sting containing the token.
+// Output : GDIV_TYPE corresponding to the token in the string, ivBadType if the
+// string does not correspond to a valid type.
+//-----------------------------------------------------------------------------
+trtoken_t GDinputvariable::GetStoreAsFromType(GDIV_TYPE eType)
+{
+ for (int i = 0; i < sizeof(TypeMap) / sizeof(TypeMap[0]); i++)
+ {
+ if (TypeMap[i].eType == eType)
+ {
+ return(TypeMap[i].eStoreAs);
+ }
+ }
+
+ Assert(FALSE);
+ return(STRING);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the enumerated type of a string token.
+// Input : pszToken - Sting containing the token.
+// Output : GDIV_TYPE corresponding to the token in the string, ivBadType if the
+// string does not correspond to a valid type.
+//-----------------------------------------------------------------------------
+GDIV_TYPE GDinputvariable::GetTypeFromToken(const char *pszToken)
+{
+ for (int i = 0; i < sizeof(TypeMap) / sizeof(TypeMap[0]); i++)
+ {
+ if (IsToken(pszToken, TypeMap[i].pszName))
+ {
+ return(TypeMap[i].eType);
+ }
+ }
+
+ return(ivBadType);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns a string representing the type of this variable, eg. "integer".
+//-----------------------------------------------------------------------------
+const char *GDinputvariable::GetTypeText(void)
+{
+ for (int i = 0; i < sizeof(TypeMap) / sizeof(TypeMap[0]); i++)
+ {
+ if (TypeMap[i].eType == m_eType)
+ {
+ return(TypeMap[i].pszName);
+ }
+ }
+
+ return("unknown");
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : tr -
+// Output : Returns TRUE on success, FALSE on failure.
+//-----------------------------------------------------------------------------
+BOOL GDinputvariable::InitFromTokens(TokenReader& tr)
+{
+ char szToken[128];
+
+ if (!GDGetToken(tr, m_szName, sizeof(m_szName), IDENT))
+ {
+ return FALSE;
+ }
+
+ if (!GDSkipToken(tr, OPERATOR, "("))
+ {
+ return FALSE;
+ }
+
+ // check for "reportable" marker
+ trtoken_t ttype = tr.NextToken(szToken, sizeof(szToken));
+ if (ttype == OPERATOR)
+ {
+ if (!strcmp(szToken, "*"))
+ {
+ m_bReportable = true;
+ }
+ }
+ else
+ {
+ tr.Stuff(ttype, szToken);
+ }
+
+ // get type
+ if (!GDGetToken(tr, szToken, sizeof(szToken), IDENT))
+ {
+ return FALSE;
+ }
+
+ if (!GDSkipToken(tr, OPERATOR, ")"))
+ {
+ return FALSE;
+ }
+
+ //
+ // Check for known variable types.
+ //
+ m_eType = GetTypeFromToken(szToken);
+ if (m_eType == ivBadType)
+ {
+ GDError(tr, "'%s' is not a valid variable type", szToken);
+ return FALSE;
+ }
+
+ //
+ // Look ahead at the next token.
+ //
+ ttype = tr.PeekTokenType(szToken,sizeof(szToken));
+
+ //
+ // Check for the "readonly" specifier.
+ //
+ if ((ttype == IDENT) && IsToken(szToken, "readonly"))
+ {
+ tr.NextToken(szToken, sizeof(szToken));
+ m_bReadOnly = true;
+
+ //
+ // Look ahead at the next token.
+ //
+ ttype = tr.PeekTokenType(szToken,sizeof(szToken));
+ }
+
+ //
+ // Check for the ':' indicating a long name.
+ //
+ if (ttype == OPERATOR && IsToken(szToken, ":"))
+ {
+ //
+ // Eat the ':'.
+ //
+ tr.NextToken(szToken, sizeof(szToken));
+
+ if (m_eType == ivFlags)
+ {
+ GDError(tr, "flag sets do not have long names");
+ return FALSE;
+ }
+
+ //
+ // Get the long name.
+ //
+ if (!GDGetToken(tr, m_szLongName, sizeof(m_szLongName), STRING))
+ {
+ return(FALSE);
+ }
+
+ //
+ // Look ahead at the next token.
+ //
+ ttype = tr.PeekTokenType(szToken,sizeof(szToken));
+
+ //
+ // Check for the ':' indicating a default value.
+ //
+ if (ttype == OPERATOR && IsToken(szToken, ":"))
+ {
+ //
+ // Eat the ':'.
+ //
+ tr.NextToken(szToken, sizeof(szToken));
+
+ //
+ // Look ahead at the next token.
+ //
+ ttype = tr.PeekTokenType(szToken,sizeof(szToken));
+ if (ttype == OPERATOR && IsToken(szToken, ":"))
+ {
+ //
+ // No default value provided, skip to the description.
+ //
+ }
+ else
+ {
+ //
+ // Determine how to parse the default value. If this is a choices field, the
+ // default could either be a string or an integer, so we must look ahead and
+ // use whichever is there.
+ //
+ trtoken_t eStoreAs = GetStoreAsFromType(m_eType);
+
+ if (eStoreAs == STRING)
+ {
+ if (!GDGetToken(tr, m_szDefault, sizeof(m_szDefault), STRING))
+ {
+ return(FALSE);
+ }
+ }
+ else if (eStoreAs == INTEGER)
+ {
+ if (!GDGetToken(tr, szToken, sizeof(szToken), INTEGER))
+ {
+ return(FALSE);
+ }
+
+ m_nDefault = atoi(szToken);
+ }
+
+ //
+ // Look ahead at the next token.
+ //
+ ttype = tr.PeekTokenType(szToken,sizeof(szToken));
+ }
+ }
+
+ //
+ // Check for the ':' indicating a description.
+ //
+ if (ttype == OPERATOR && IsToken(szToken, ":"))
+ {
+ //
+ // Eat the ':'.
+ //
+ tr.NextToken(szToken, sizeof(szToken));
+
+ //
+ // Read the description.
+ //
+
+ // If we've already read a description then free it to avoid memory leaks.
+ if ( m_pszDescription )
+ {
+ delete [] m_pszDescription;
+ m_pszDescription = NULL;
+ }
+ if (!GDGetTokenDynamic(tr, &m_pszDescription, STRING))
+ {
+ return(FALSE);
+ }
+
+ //
+ // Look ahead at the next token.
+ //
+ ttype = tr.PeekTokenType(szToken,sizeof(szToken));
+ }
+ }
+ else
+ {
+ //
+ // Default long name is short name.
+ //
+ strcpy(m_szLongName, m_szName);
+ }
+
+ //
+ // Check for the ']' indicating the end of the class definition.
+ //
+ if ((ttype == OPERATOR && IsToken(szToken, "]")) || ttype != OPERATOR)
+ {
+ if (m_eType == ivFlags || m_eType == ivChoices)
+ {
+ //
+ // Can't define a flags or choices variable without providing any flags or choices.
+ //
+ GDError(tr, "no %s specified", m_eType == ivFlags ? "flags" : "choices");
+ return(FALSE);
+ }
+ return(TRUE);
+ }
+
+ if (!GDSkipToken(tr, OPERATOR, "="))
+ {
+ return(FALSE);
+ }
+
+ if (m_eType != ivFlags && m_eType != ivChoices)
+ {
+ GDError(tr, "didn't expect '=' here");
+ return(FALSE);
+ }
+
+ // should be '[' to start flags/choices
+ if (!GDSkipToken(tr, OPERATOR, "["))
+ {
+ return(FALSE);
+ }
+
+ // get flags?
+ if (m_eType == ivFlags)
+ {
+ GDIVITEM ivi;
+
+ while (1)
+ {
+ ttype = tr.PeekTokenType();
+ if (ttype != INTEGER)
+ {
+ break;
+ }
+
+ // store bitflag value
+ GDGetToken(tr, szToken, sizeof(szToken), INTEGER);
+ sscanf( szToken, "%lu", &ivi.iValue );
+
+ // colon..
+ if (!GDSkipToken(tr, OPERATOR, ":"))
+ {
+ return FALSE;
+ }
+
+ // get description
+ if (!GDGetToken(tr, szToken, sizeof(szToken), STRING))
+ {
+ return FALSE;
+ }
+ strcpy(ivi.szCaption, szToken);
+
+ // colon..
+ if (!GDSkipToken(tr, OPERATOR, ":"))
+ {
+ return FALSE;
+ }
+
+ // get default setting
+ if (!GDGetToken(tr, szToken, sizeof(szToken), INTEGER))
+ {
+ return FALSE;
+ }
+ ivi.bDefault = atoi(szToken) ? TRUE : FALSE;
+
+ // add item to array of items
+ m_Items.AddToTail(ivi);
+ }
+
+ // Set the default value.
+ unsigned long nDefault = 0;
+ for (int i = 0; i < m_Items.Count(); i++)
+ {
+ if (m_Items[i].bDefault)
+ nDefault |= m_Items[i].iValue;
+ }
+ m_nDefault = (int)nDefault;
+ Q_snprintf( m_szDefault, sizeof( m_szDefault ), "%d", m_nDefault );
+ }
+ else if (m_eType == ivChoices)
+ {
+ GDIVITEM ivi;
+
+ while (1)
+ {
+ ttype = tr.PeekTokenType();
+ if ((ttype != INTEGER) && (ttype != STRING))
+ {
+ break;
+ }
+
+ // store choice value
+ GDGetToken(tr, szToken, sizeof(szToken), ttype);
+ ivi.iValue = 0;
+ strcpy(ivi.szValue, szToken);
+
+ // colon
+ if (!GDSkipToken(tr, OPERATOR, ":"))
+ {
+ return FALSE;
+ }
+
+ // get description
+ if (!GDGetToken(tr, szToken, sizeof(szToken), STRING))
+ {
+ return FALSE;
+ }
+
+ strcpy(ivi.szCaption, szToken);
+
+ m_Items.AddToTail(ivi);
+ }
+ }
+
+ if (!GDSkipToken(tr, OPERATOR, "]"))
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Decodes a key value from a string.
+// Input : pkv - Pointer to the key value object containing string encoded value.
+//-----------------------------------------------------------------------------
+void GDinputvariable::FromKeyValue(MDkeyvalue *pkv)
+{
+ trtoken_t eStoreAs = GetStoreAsFromType(m_eType);
+
+ if (eStoreAs == STRING)
+ {
+ strcpy(m_szValue, pkv->szValue);
+ }
+ else if (eStoreAs == INTEGER)
+ {
+ m_nValue = atoi(pkv->szValue);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Determines whether the given flag is set (assuming this is an ivFlags).
+// Input : uCheck - Flag to check.
+// Output : Returns TRUE if flag is set, FALSE if not.
+//-----------------------------------------------------------------------------
+BOOL GDinputvariable::IsFlagSet(unsigned int uCheck)
+{
+ Assert(m_eType == ivFlags);
+ return (((unsigned int)m_nValue & uCheck) == uCheck) ? TRUE : FALSE;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Combines the flags or choices items from another variable into our
+// list of flags or choices. Ours take priority if collisions occur.
+// Input : Other - The variable whose items are being merged with ours.
+//-----------------------------------------------------------------------------
+void GDinputvariable::Merge(GDinputvariable &Other)
+{
+ //
+ // Only valid if we are of the same type.
+ //
+ if (Other.GetType() != GetType())
+ {
+ return;
+ }
+
+ //
+ // Add Other's items to this ONLY if there is no same-value entry
+ // for a specific item.
+ //
+ bool bFound = false;
+ int nOurItems = m_Items.Count();
+ for (int i = 0; i < Other.m_Items.Count(); i++)
+ {
+ GDIVITEM &TheirItem = Other.m_Items[i];
+ for (int j = 0; j < nOurItems; j++)
+ {
+ GDIVITEM &OurItem = m_Items[j];
+ if (TheirItem.iValue == OurItem.iValue)
+ {
+ bFound = true;
+ break;
+ }
+ }
+
+ if (!bFound)
+ {
+ //
+ // Not found in our list - add their item to our list.
+ //
+ m_Items.AddToTail(TheirItem);
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Determines whether the given flag is set (assuming this is an ivFlags).
+// Input : uFlags - Flags to set.
+// bSet - TRUE to set the flags, FALSE to clear them.
+//-----------------------------------------------------------------------------
+void GDinputvariable::SetFlag(unsigned int uFlags, BOOL bSet)
+{
+ Assert(m_eType == ivFlags);
+ if (bSet)
+ {
+ m_nValue |= uFlags;
+ }
+ else
+ {
+ m_nValue &= ~uFlags;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets this keyvalue to its default value.
+//-----------------------------------------------------------------------------
+void GDinputvariable::ResetDefaults(void)
+{
+ if (m_eType == ivFlags)
+ {
+ m_nValue = 0;
+
+ //
+ // Run thru flags and set any default flags.
+ //
+ int nCount = m_Items.Count();
+ for (int i = 0; i < nCount; i++)
+ {
+ if (m_Items[i].bDefault)
+ {
+ m_nValue |= GetFlagMask(i);
+ }
+ }
+ }
+ else
+ {
+ m_nValue = m_nDefault;
+ strcpy(m_szValue, m_szDefault);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Encodes a key value as a string.
+// Input : pkv - Pointer to the key value object to receive the encoded string.
+//-----------------------------------------------------------------------------
+void GDinputvariable::ToKeyValue(MDkeyvalue *pkv)
+{
+ strcpy(pkv->szKey, m_szName);
+
+ trtoken_t eStoreAs = GetStoreAsFromType(m_eType);
+
+ if (eStoreAs == STRING)
+ {
+ strcpy(pkv->szValue, m_szValue);
+ }
+ else if (eStoreAs == INTEGER)
+ {
+ itoa(m_nValue, pkv->szValue, 10);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the description string that corresponds to a value string
+// for a choices list.
+// Input : pszString - The choices value string.
+// Output : Returns the description string.
+//-----------------------------------------------------------------------------
+const char *GDinputvariable::ItemStringForValue(const char *szValue)
+{
+ int nCount = m_Items.Count();
+ for (int i = 0; i < nCount; i++)
+ {
+ if (!stricmp(m_Items[i].szValue, szValue))
+ {
+ return(m_Items[i].szCaption);
+ }
+ }
+
+ return(NULL);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the value string that corresponds to a description string
+// for a choices list.
+// Input : pszString - The choices description string.
+// Output : Returns the value string.
+//-----------------------------------------------------------------------------
+const char *GDinputvariable::ItemValueForString(const char *szString)
+{
+ int nCount = m_Items.Count();
+ for (int i = 0; i < nCount; i++)
+ {
+ if (!strcmpi(m_Items[i].szCaption, szString))
+ {
+ return(m_Items[i].szValue);
+ }
+ }
+
+ return(NULL);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: this function will let you iterate through the text names of the variable types
+// Input : eType - the type to get the text of
+// Output : returns the textual name
+//-----------------------------------------------------------------------------
+const char *GDinputvariable::GetVarTypeName( GDIV_TYPE eType )
+{
+ return TypeMap[ eType ].pszName;
+}
+
+
diff --git a/sp/src/fgdlib/inputoutput.cpp b/sp/src/fgdlib/inputoutput.cpp
new file mode 100644
index 00000000..11c97df0
--- /dev/null
+++ b/sp/src/fgdlib/inputoutput.cpp
@@ -0,0 +1,171 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+
+#include <tier0/dbg.h>
+#include "fgdlib/InputOutput.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+
+typedef struct
+{
+ InputOutputType_t eType; // The enumeration of this type.
+ char *pszName; // The name of this type.
+} TypeMap_t;
+
+
+char *CClassInputOutputBase::g_pszEmpty = "";
+
+
+//-----------------------------------------------------------------------------
+// Maps type names to type enums for inputs and outputs.
+//-----------------------------------------------------------------------------
+static TypeMap_t TypeMap[] =
+{
+ { iotVoid, "void" },
+ { iotInt, "integer" },
+ { iotBool, "bool" },
+ { iotString, "string" },
+ { iotFloat, "float" },
+ { iotVector, "vector" },
+ { iotEHandle, "target_destination" },
+ { iotColor, "color255" },
+ { iotEHandle, "ehandle" }, // for backwards compatibility
+};
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CClassInputOutputBase::CClassInputOutputBase(void)
+{
+ m_eType = iotInvalid;
+ m_pszDescription = NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : pszName -
+// eType -
+//-----------------------------------------------------------------------------
+CClassInputOutputBase::CClassInputOutputBase(const char *pszName, InputOutputType_t eType)
+{
+ m_pszDescription = NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor.
+//-----------------------------------------------------------------------------
+CClassInputOutputBase::~CClassInputOutputBase(void)
+{
+ delete m_pszDescription;
+ m_pszDescription = NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns a string representing the type of this I/O, eg. "integer".
+//-----------------------------------------------------------------------------
+const char *CClassInputOutputBase::GetTypeText(void)
+{
+ for (int i = 0; i < sizeof(TypeMap) / sizeof(TypeMap[0]); i++)
+ {
+ if (TypeMap[i].eType == m_eType)
+ {
+ return(TypeMap[i].pszName);
+ }
+ }
+
+ return("unknown");
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : szType -
+// Output : InputOutputType_t
+//-----------------------------------------------------------------------------
+InputOutputType_t CClassInputOutputBase::SetType(const char *szType)
+{
+ for (int i = 0; i < sizeof(TypeMap) / sizeof(TypeMap[0]); i++)
+ {
+ if (!stricmp(TypeMap[i].pszName, szType))
+ {
+ m_eType = TypeMap[i].eType;
+ return(m_eType);
+ }
+ }
+
+ return(iotInvalid);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Assignment operator.
+//-----------------------------------------------------------------------------
+CClassInputOutputBase &CClassInputOutputBase::operator =(CClassInputOutputBase &Other)
+{
+ strcpy(m_szName, Other.m_szName);
+ m_eType = Other.m_eType;
+
+ //
+ // Copy the description.
+ //
+ delete m_pszDescription;
+ if (Other.m_pszDescription != NULL)
+ {
+ m_pszDescription = new char[strlen(Other.m_pszDescription) + 1];
+ strcpy(m_pszDescription, Other.m_pszDescription);
+ }
+ else
+ {
+ m_pszDescription = NULL;
+ }
+
+ return(*this);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CClassInput::CClassInput(void)
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : pszName -
+// eType -
+//-----------------------------------------------------------------------------
+CClassInput::CClassInput(const char *pszName, InputOutputType_t eType)
+ : CClassInputOutputBase(pszName, eType)
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CClassOutput::CClassOutput(void)
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : pszName -
+// eType -
+//-----------------------------------------------------------------------------
+CClassOutput::CClassOutput(const char *pszName, InputOutputType_t eType)
+ : CClassInputOutputBase(pszName, eType)
+{
+}
diff --git a/sp/src/fgdlib/wckeyvalues.cpp b/sp/src/fgdlib/wckeyvalues.cpp
new file mode 100644
index 00000000..d802eb42
--- /dev/null
+++ b/sp/src/fgdlib/wckeyvalues.cpp
@@ -0,0 +1,282 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "fgdlib/WCKeyValues.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor.
+//-----------------------------------------------------------------------------
+MDkeyvalue::~MDkeyvalue(void)
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Assignment operator.
+//-----------------------------------------------------------------------------
+MDkeyvalue &MDkeyvalue::operator =(const MDkeyvalue &other)
+{
+ V_strcpy_safe(szKey, other.szKey);
+ V_strcpy_safe(szValue, other.szValue);
+
+ return(*this);
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void WCKVBase_Vector::RemoveKeyAt(int nIndex)
+{
+ Assert(nIndex >= 0);
+ Assert(nIndex < (int)m_KeyValues.Count());
+
+ if ((nIndex >= 0) && (nIndex < (int)m_KeyValues.Count()))
+ {
+ m_KeyValues.Remove(nIndex);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Adds the key to the keyvalue array. Allows duplicate keys.
+//
+// NOTE: This should only be used for keyvalue lists that do not require
+// unique key names! If you use this function then you should use GetCount
+// and GetKey/Value by index rather than GetValue by key name.
+//-----------------------------------------------------------------------------
+void WCKVBase_Vector::AddKeyValue(const char *pszKey, const char *pszValue)
+{
+ if (!pszKey || !pszValue)
+ {
+ return;
+ }
+
+ char szTmpKey[KEYVALUE_MAX_KEY_LENGTH];
+ char szTmpValue[KEYVALUE_MAX_VALUE_LENGTH];
+
+ V_strcpy_safe(szTmpKey, pszKey);
+ V_strcpy_safe(szTmpValue, pszValue);
+
+ StripEdgeWhiteSpace(szTmpKey);
+ StripEdgeWhiteSpace(szTmpValue);
+
+ //
+ // Add the keyvalue to our list.
+ //
+ MDkeyvalue newkv;
+ V_strcpy_safe(newkv.szKey, szTmpKey);
+ V_strcpy_safe(newkv.szValue, szTmpValue);
+ m_KeyValues.AddToTail(newkv);
+}
+
+int WCKVBase_Vector::FindByKeyName( const char *pKeyName ) const
+{
+ for ( int i=0; i < m_KeyValues.Count(); i++ )
+ {
+ if ( V_stricmp( m_KeyValues[i].szKey, pKeyName ) == 0 )
+ return i;
+ }
+ return GetInvalidIndex();
+}
+
+void WCKVBase_Vector::InsertKeyValue( const MDkeyvalue &kv )
+{
+ m_KeyValues.AddToTail( kv );
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void WCKVBase_Dict::RemoveKeyAt(int nIndex)
+{
+ m_KeyValues.RemoveAt(nIndex);
+}
+
+
+int WCKVBase_Dict::FindByKeyName( const char *pKeyName ) const
+{
+ return m_KeyValues.Find( pKeyName );
+}
+
+void WCKVBase_Dict::InsertKeyValue( const MDkeyvalue &kv )
+{
+ m_KeyValues.Insert( kv.szKey, kv );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor. Sets the initial size of the keyvalue array.
+//-----------------------------------------------------------------------------
+template<class Base>
+WCKeyValuesT<Base>::WCKeyValuesT(void)
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor. Deletes the contents of this keyvalue array.
+//-----------------------------------------------------------------------------
+template<class Base>
+WCKeyValuesT<Base>::~WCKeyValuesT(void)
+{
+ //int i = 0;
+ //while (i < m_KeyValues.GetSize())
+ //{
+ // delete m_KeyValues.GetAt(i++);
+ //}
+
+ RemoveAll();
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+template<class Base>
+const char *WCKeyValuesT<Base>::GetValue(const char *pszKey, int *piIndex) const
+{
+ int i = FindByKeyName( pszKey );
+ if ( i == GetInvalidIndex() )
+ {
+ return NULL;
+ }
+ else
+ {
+ if(piIndex)
+ piIndex[0] = i;
+
+ return m_KeyValues[i].szValue;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+template<class Base>
+void WCKeyValuesT<Base>::RemoveKey(const char *pszKey)
+{
+ SetValue(pszKey, (const char *)NULL);
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+template<class Base>
+void WCKeyValuesT<Base>::SetValue(const char *pszKey, int iValue)
+{
+ char szValue[100];
+ itoa(iValue, szValue, 10);
+
+ SetValue(pszKey, szValue);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Strips leading and trailing whitespace from the string.
+// Input : psz -
+//-----------------------------------------------------------------------------
+void StripEdgeWhiteSpace(char *psz)
+{
+ if (!psz || !*psz)
+ return;
+
+ char *pszBase = psz;
+
+ while (V_isspace(*psz))
+ {
+ psz++;
+ }
+
+ int iLen = strlen(psz) - 1;
+
+ if ( iLen >= 0 )
+ {
+ while (V_isspace(psz[iLen]))
+ {
+ psz[iLen--] = 0;
+ }
+ }
+
+ if (psz != pszBase)
+ {
+ memmove(pszBase, psz, iLen + 2);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : pszKey -
+// pszValue -
+//-----------------------------------------------------------------------------
+template<class Base>
+void WCKeyValuesT<Base>::SetValue(const char *pszKey, const char *pszValue)
+{
+ char szTmpKey[KEYVALUE_MAX_KEY_LENGTH];
+ char szTmpValue[KEYVALUE_MAX_VALUE_LENGTH];
+
+ V_strcpy_safe(szTmpKey, pszKey);
+
+ if (pszValue != NULL)
+ {
+ V_strcpy_safe(szTmpValue, pszValue);
+ }
+ else
+ {
+ szTmpValue[0] = 0;
+ }
+
+ StripEdgeWhiteSpace(szTmpKey);
+ StripEdgeWhiteSpace(szTmpValue);
+
+ int i = FindByKeyName( szTmpKey );
+ if ( i == GetInvalidIndex() )
+ {
+ if ( pszValue )
+ {
+ //
+ // Add the keyvalue to our list.
+ //
+ MDkeyvalue newkv;
+ Q_strncpy( newkv.szKey, szTmpKey, sizeof( newkv.szKey ) );
+ Q_strncpy( newkv.szValue, szTmpValue, sizeof( newkv.szValue ) );
+ InsertKeyValue( newkv );
+ }
+ }
+ else
+ {
+ if (pszValue != NULL)
+ {
+ V_strncpy(m_KeyValues[i].szValue, szTmpValue, sizeof(m_KeyValues[i].szValue));
+ }
+ //
+ // If we are setting to a NULL value, delete the key.
+ //
+ else
+ {
+ RemoveKeyAt( i );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+template<class Base>
+void WCKeyValuesT<Base>::RemoveAll(void)
+{
+ m_KeyValues.RemoveAll();
+}
+
+
+// Explicit instantiations.
+template class WCKeyValuesT<WCKVBase_Dict>;
+template class WCKeyValuesT<WCKVBase_Vector>;
+
+
+