summaryrefslogtreecommitdiff
path: root/common/steamid.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'common/steamid.cpp')
-rw-r--r--common/steamid.cpp731
1 files changed, 731 insertions, 0 deletions
diff --git a/common/steamid.cpp b/common/steamid.cpp
new file mode 100644
index 0000000..20cdde5
--- /dev/null
+++ b/common/steamid.cpp
@@ -0,0 +1,731 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================
+
+
+#if defined( STEAM ) || defined( HL1 )
+#include "stdafx.h"
+#else
+#include <stdio.h>
+#include "dbg.h"
+#define INCLUDED_STEAM2_USERID_STRUCTS
+#include "steamcommon.h"
+#include "steam/steamclientpublic.h"
+#include "strtools.h"
+#endif
+
+#ifdef HL1
+#include "steamcommon.h"
+#include "steam/steamclientpublic.h"
+#endif
+
+#ifndef UINT64_MAX
+#define UINT64_MAX ((uint64)-1)
+#endif
+
+static const char *DecimalToUint64( const char *pchStr, uint64 unLimit,
+ uint64 *punVal )
+{
+ const char *pchStart = pchStr;
+ uint64 unVal = 0;
+
+ while ( *pchStr >= '0' && *pchStr <= '9' )
+ {
+ uint64 unNext = unVal * 10;
+
+ if ( unNext < unVal )
+ {
+ // 64-bit overflow.
+ return NULL;
+ }
+
+ unVal = unNext + (uint64)( *pchStr - '0' );
+ if ( unVal > unLimit )
+ {
+ // Limit overflow.
+ return NULL;
+ }
+
+ pchStr++;
+ }
+ if ( pchStr == pchStart )
+ {
+ // No number at all.
+ return NULL;
+ }
+
+ *punVal = unVal;
+ return pchStr;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+// Input : pchSteamID - text representation of a Steam ID
+//-----------------------------------------------------------------------------
+CSteamID::CSteamID( const char *pchSteamID, EUniverse eDefaultUniverse /* = k_EUniverseInvalid */ )
+{
+ SetFromString( pchSteamID, eDefaultUniverse );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Initializes a steam ID from a string
+// Input : pchSteamID - text representation of a Steam ID
+//-----------------------------------------------------------------------------
+void CSteamID::SetFromString( const char *pchSteamID, EUniverse eDefaultUniverse )
+{
+ uint nAccountID = 0;
+ uint nInstance = 1;
+ EUniverse eUniverse = eDefaultUniverse;
+ EAccountType eAccountType = k_EAccountTypeIndividual;
+#ifdef DBGFLAG_ASSERT
+ // TF Merge -- Assert is debug-only and we have unused variable warnings on :-/
+ const char *pchSteamIDString = pchSteamID;
+#endif
+ CSteamID StrictID;
+
+ StrictID.SetFromStringStrict( pchSteamID, eDefaultUniverse );
+
+ if ( *pchSteamID == '[' )
+ pchSteamID++;
+
+ // BUGBUG Rich use the Q_ functions
+ if (*pchSteamID == 'A')
+ {
+ // This is test only
+ pchSteamID++; // skip the A
+ eAccountType = k_EAccountTypeAnonGameServer;
+ if (*pchSteamID == '-' || *pchSteamID == ':')
+ pchSteamID++; // skip the optional - or :
+
+ if ( strchr( pchSteamID, '(' ) )
+ sscanf( strchr( pchSteamID, '(' ), "(%u)", &nInstance );
+ const char *pchColon = strchr( pchSteamID, ':' );
+ if ( pchColon && *pchColon != 0 && strchr( pchColon+1, ':' ))
+ {
+ sscanf( pchSteamID, "%u:%u:%u", (uint*)&eUniverse, &nAccountID, &nInstance );
+ }
+ else if ( pchColon )
+ {
+ sscanf( pchSteamID, "%u:%u", (uint*)&eUniverse, &nAccountID );
+ }
+ else
+ {
+ sscanf( pchSteamID, "%u", &nAccountID );
+ }
+
+ if ( nAccountID == 0 )
+ {
+ // i dont care what number you entered
+ CreateBlankAnonLogon(eUniverse);
+ }
+ else
+ {
+ InstancedSet( nAccountID, nInstance, eUniverse, eAccountType );
+ }
+ // Catch cases where we're allowing sloppy input that we
+ // might not want to allow.
+ AssertMsg1( this->operator==( StrictID ), "Steam ID does not pass strict parsing: '%s'", pchSteamIDString );
+ return;
+ }
+ else if (*pchSteamID == 'G')
+ {
+ pchSteamID++; // skip the G
+ eAccountType = k_EAccountTypeGameServer;
+ if (*pchSteamID == '-' || *pchSteamID == ':')
+ pchSteamID++; // skip the optional - or :
+ }
+ else if (*pchSteamID == 'C')
+ {
+ pchSteamID++; // skip the C
+ eAccountType = k_EAccountTypeContentServer;
+ if (*pchSteamID == '-' || *pchSteamID == ':')
+ pchSteamID++; // skip the optional - or :
+ }
+ else if (*pchSteamID == 'g')
+ {
+ pchSteamID++; // skip the g
+ eAccountType = k_EAccountTypeClan;
+ nInstance = 0;
+ if (*pchSteamID == '-' || *pchSteamID == ':')
+ pchSteamID++; // skip the optional - or :
+ }
+ else if (*pchSteamID == 'c')
+ {
+ pchSteamID++; // skip the c
+ eAccountType = k_EAccountTypeChat;
+ nInstance = k_EChatInstanceFlagClan;
+ if (*pchSteamID == '-' || *pchSteamID == ':')
+ pchSteamID++; // skip the optional - or :
+ }
+ else if (*pchSteamID == 'L')
+ {
+ pchSteamID++; // skip the c
+ eAccountType = k_EAccountTypeChat;
+ nInstance = k_EChatInstanceFlagLobby;
+ if (*pchSteamID == '-' || *pchSteamID == ':')
+ pchSteamID++; // skip the optional - or :
+ }
+ else if (*pchSteamID == 'T')
+ {
+ pchSteamID++; // skip the T
+ eAccountType = k_EAccountTypeChat;
+ nInstance = 0; // Anon chat
+ if (*pchSteamID == '-' || *pchSteamID == ':')
+ pchSteamID++; // skip the optional - or :
+ }
+ else if (*pchSteamID == 'U')
+ {
+ pchSteamID++; // skip the U
+ eAccountType = k_EAccountTypeIndividual;
+ nInstance = 1;
+ if (*pchSteamID == '-' || *pchSteamID == ':')
+ pchSteamID++; // skip the optional - or :
+ }
+ else if (*pchSteamID == 'i')
+ {
+ pchSteamID++; // skip the i
+ eAccountType = k_EAccountTypeInvalid;
+ nInstance = 1;
+ if (*pchSteamID == '-' || *pchSteamID == ':')
+ pchSteamID++; // skip the optional - or :
+ }
+
+ if ( strchr( pchSteamID, ':' ) )
+ {
+ if (*pchSteamID == '[')
+ pchSteamID++; // skip the optional [
+ sscanf( pchSteamID, "%u:%u", (uint*)&eUniverse, &nAccountID );
+ if ( eUniverse == k_EUniverseInvalid )
+ eUniverse = eDefaultUniverse;
+ }
+ else
+ {
+ uint64 unVal64 = 0;
+
+ sscanf( pchSteamID, "%llu", &unVal64 );
+ if ( unVal64 > UINT64_MAX )
+ {
+ // Assume a full 64-bit Steam ID.
+ SetFromUint64( unVal64 );
+ // Catch cases where we're allowing sloppy input that we
+ // might not want to allow.
+ AssertMsg1( this->operator==( StrictID ), "Steam ID does not pass strict parsing: '%s'", pchSteamIDString );
+ return;
+ }
+ else
+ {
+ nAccountID = (uint)unVal64;
+ }
+ }
+
+ Assert( (eUniverse > k_EUniverseInvalid) && (eUniverse < k_EUniverseMax) );
+
+ InstancedSet( nAccountID, nInstance, eUniverse, eAccountType );
+
+ // Catch cases where we're allowing sloppy input that we
+ // might not want to allow.
+ AssertMsg1( this->operator==( StrictID ), "Steam ID does not pass strict parsing: '%s'", pchSteamIDString );
+}
+
+// SetFromString allows many partially-correct strings, constraining how
+// we might be able to change things in the future.
+// SetFromStringStrict requires the exact string forms that we support
+// and is preferred when the caller knows it's safe to be strict.
+// Returns whether the string parsed correctly. The ID may
+// still be invalid even if the string parsed correctly.
+// If the string didn't parse correctly the ID will always be invalid.
+bool CSteamID::SetFromStringStrict( const char *pchSteamID, EUniverse eDefaultUniverse )
+{
+ uint nAccountID = 0;
+ uint nInstance = 1;
+ uint unMaxVal = 2;
+ EUniverse eUniverse = eDefaultUniverse;
+ EAccountType eAccountType = k_EAccountTypeIndividual;
+ char chPrefix;
+ bool bBracket = false;
+ bool bValid = true;
+ uint64 unVal[3];
+ const char *pchEnd;
+
+ // Start invalid.
+ Clear();
+
+ if ( !pchSteamID )
+ {
+ return false;
+ }
+
+ if ( *pchSteamID == '[' )
+ {
+ pchSteamID++;
+ bBracket = true;
+ }
+
+ chPrefix = *pchSteamID;
+ switch( chPrefix )
+ {
+ case 'A':
+ // This is test only
+ eAccountType = k_EAccountTypeAnonGameServer;
+ unMaxVal = 3;
+ break;
+
+ case 'G':
+ eAccountType = k_EAccountTypeGameServer;
+ break;
+
+ case 'C':
+ eAccountType = k_EAccountTypeContentServer;
+ break;
+
+ case 'g':
+ eAccountType = k_EAccountTypeClan;
+ nInstance = 0;
+ break;
+
+ case 'c':
+ eAccountType = k_EAccountTypeChat;
+ nInstance = k_EChatInstanceFlagClan;
+ break;
+
+ case 'L':
+ eAccountType = k_EAccountTypeChat;
+ nInstance = k_EChatInstanceFlagLobby;
+ break;
+
+ case 'T':
+ eAccountType = k_EAccountTypeChat;
+ nInstance = 0; // Anon chat
+ break;
+
+ case 'U':
+ eAccountType = k_EAccountTypeIndividual;
+ nInstance = 1;
+ break;
+
+ case 'i':
+ eAccountType = k_EAccountTypeInvalid;
+ nInstance = 1;
+ break;
+
+ default:
+ // We're reserving other leading characters so
+ // this should only be the plain-digits case.
+ if (chPrefix < '0' || chPrefix > '9')
+ {
+ bValid = false;
+ }
+ chPrefix = 0;
+ break;
+ }
+
+ if ( chPrefix )
+ {
+ pchSteamID++; // skip the prefix
+ if (*pchSteamID == '-' || *pchSteamID == ':')
+ pchSteamID++; // skip the optional - or :
+ }
+
+ uint unIdx = 0;
+
+ for (;;)
+ {
+ pchEnd = DecimalToUint64( pchSteamID, UINT64_MAX, &unVal[unIdx] );
+ if ( !pchEnd )
+ {
+ bValid = false;
+ break;
+ }
+
+ unIdx++;
+
+ // For 'A' we can have a trailing instance, which must
+ // be the end of the string.
+ if ( *pchEnd == '(' &&
+ chPrefix == 'A' )
+ {
+ if ( unIdx > 2 )
+ {
+ // Two instance IDs provided.
+ bValid = false;
+ }
+
+ pchEnd = DecimalToUint64( pchEnd + 1, k_unSteamAccountInstanceMask, &unVal[2] );
+ if ( !pchEnd ||
+ *pchEnd != ')' )
+ {
+ bValid = false;
+ break;
+ }
+ else
+ {
+ nInstance = (uint)unVal[2];
+
+ pchEnd++;
+ if ( *pchEnd == ':' )
+ {
+ // Not expecting more values.
+ bValid = false;
+ break;
+ }
+ }
+ }
+
+ if ( *pchEnd != ':' )
+ {
+ if ( bBracket )
+ {
+ if ( *pchEnd != ']' ||
+ *(pchEnd + 1) != 0 )
+ {
+ bValid = false;
+ }
+ }
+ else if ( *pchEnd != 0 )
+ {
+ bValid = false;
+ }
+
+ break;
+ }
+
+ if ( unIdx >= unMaxVal )
+ {
+ bValid = false;
+ break;
+ }
+
+ pchSteamID = pchEnd + 1;
+ }
+
+ if ( unIdx > 2 )
+ {
+ if ( unVal[2] <= k_unSteamAccountInstanceMask )
+ {
+ nInstance = (uint)unVal[2];
+ }
+ else
+ {
+ bValid = false;
+ }
+ }
+ if ( unIdx > 1 )
+ {
+ if ( unVal[0] >= k_EUniverseInvalid &&
+ unVal[0] < k_EUniverseMax )
+ {
+ eUniverse = (EUniverse)unVal[0];
+ if ( eUniverse == k_EUniverseInvalid )
+ eUniverse = eDefaultUniverse;
+ }
+ else
+ {
+ bValid = false;
+ }
+
+ if ( unVal[1] <= k_unSteamAccountIDMask )
+ {
+ nAccountID = (uint)unVal[1];
+ }
+ else
+ {
+ bValid = false;
+ }
+ }
+ else if ( unIdx > 0 )
+ {
+ if ( unVal[0] <= k_unSteamAccountIDMask )
+ {
+ nAccountID = (uint)unVal[0];
+ }
+ else if ( !chPrefix )
+ {
+ if ( bValid )
+ {
+ SetFromUint64( unVal[0] );
+ }
+ return bValid;
+ }
+ else
+ {
+ bValid = false;
+ }
+ }
+ else
+ {
+ bValid = false;
+ }
+
+ if ( bValid )
+ {
+ if ( chPrefix == 'A' )
+ {
+ if ( nAccountID == 0 )
+ {
+ // i dont care what number you entered
+ CreateBlankAnonLogon(eUniverse);
+ return bValid;
+ }
+ }
+
+ InstancedSet( nAccountID, nInstance, eUniverse, eAccountType );
+ }
+
+ return bValid;
+}
+
+
+#if defined( INCLUDED_STEAM2_USERID_STRUCTS )
+//-----------------------------------------------------------------------------
+// Purpose: Initializes a steam ID from a Steam2 ID string
+// Input: pchSteam2ID - Steam2 ID (as a string #:#:#) to convert
+// eUniverse - universe this ID belongs to
+// Output: true if successful, false otherwise
+//-----------------------------------------------------------------------------
+bool CSteamID::SetFromSteam2String( const char *pchSteam2ID, EUniverse eUniverse )
+{
+ Assert( pchSteam2ID );
+
+ // Convert the Steam2 ID string to a Steam2 ID structure
+ TSteamGlobalUserID steam2ID;
+ steam2ID.m_SteamInstanceID = 0;
+ steam2ID.m_SteamLocalUserID.Split.High32bits = 0;
+ steam2ID.m_SteamLocalUserID.Split.Low32bits = 0;
+
+ const char *pchTSteam2ID = pchSteam2ID;
+
+ // Customer support is fond of entering steam IDs in the following form: STEAM_n:x:y
+ const char *pchOptionalLeadString = "STEAM_";
+ if ( V_strnicmp( pchSteam2ID, pchOptionalLeadString, V_strlen( pchOptionalLeadString ) ) == 0 )
+ pchTSteam2ID = pchSteam2ID + V_strlen( pchOptionalLeadString );
+
+ char cExtraCharCheck = 0;
+
+ int cFieldConverted = sscanf( pchTSteam2ID, "%hu:%u:%u%c", &steam2ID.m_SteamInstanceID,
+ &steam2ID.m_SteamLocalUserID.Split.High32bits, &steam2ID.m_SteamLocalUserID.Split.Low32bits, &cExtraCharCheck );
+
+ // Validate the conversion ... a special case is steam2 instance ID 1 which is reserved for special DoD handling
+ if ( cExtraCharCheck != 0 || cFieldConverted == EOF || cFieldConverted < 2 || ( cFieldConverted < 3 && steam2ID.m_SteamInstanceID != 1 ) )
+ return false;
+
+ // Now convert to steam ID from the Steam2 ID structure
+ SetFromSteam2( &steam2ID, eUniverse );
+ return true;
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: Renders the steam ID to a buffer. NOTE: for convenience of calling
+// code, this code returns a pointer to a static buffer and is NOT thread-safe.
+// Output: buffer with rendered Steam ID
+//-----------------------------------------------------------------------------
+const char * CSteamID::Render() const
+{
+ // longest length of returned string is k_cBufLen
+ // [A:%u:%u:%u]
+ // %u == 10 * 3 + 6 == 36, plus terminator == 37
+ const int k_cBufLen = 37;
+
+ const int k_cBufs = 4; // # of static bufs to use (so people can compose output with multiple calls to Render() )
+ static char rgchBuf[k_cBufs][k_cBufLen];
+ static int nBuf = 0;
+ char * pchBuf = rgchBuf[nBuf]; // get pointer to current static buf
+ nBuf ++; // use next buffer for next call to this method
+ nBuf %= k_cBufs;
+
+ if ( k_EAccountTypeAnonGameServer == m_steamid.m_comp.m_EAccountType )
+ {
+ V_snprintf( pchBuf, k_cBufLen, "[A:%u:%u:%u]", m_steamid.m_comp.m_EUniverse, m_steamid.m_comp.m_unAccountID, m_steamid.m_comp.m_unAccountInstance );
+ }
+ else if ( k_EAccountTypeGameServer == m_steamid.m_comp.m_EAccountType )
+ {
+ V_snprintf( pchBuf, k_cBufLen, "[G:%u:%u]", m_steamid.m_comp.m_EUniverse, m_steamid.m_comp.m_unAccountID );
+ }
+ else if ( k_EAccountTypeMultiseat == m_steamid.m_comp.m_EAccountType )
+ {
+ V_snprintf( pchBuf, k_cBufLen, "[M:%u:%u:%u]", m_steamid.m_comp.m_EUniverse, m_steamid.m_comp.m_unAccountID, m_steamid.m_comp.m_unAccountInstance );
+ }
+ else if ( k_EAccountTypePending == m_steamid.m_comp.m_EAccountType )
+ {
+ V_snprintf( pchBuf, k_cBufLen, "[P:%u:%u]", m_steamid.m_comp.m_EUniverse, m_steamid.m_comp.m_unAccountID );
+ }
+ else if ( k_EAccountTypeContentServer == m_steamid.m_comp.m_EAccountType )
+ {
+ V_snprintf( pchBuf, k_cBufLen, "[C:%u:%u]", m_steamid.m_comp.m_EUniverse, m_steamid.m_comp.m_unAccountID );
+ }
+ else if ( k_EAccountTypeClan == m_steamid.m_comp.m_EAccountType )
+ {
+ // 'g' for "group"
+ V_snprintf( pchBuf, k_cBufLen, "[g:%u:%u]", m_steamid.m_comp.m_EUniverse, m_steamid.m_comp.m_unAccountID );
+ }
+ else if ( k_EAccountTypeChat == m_steamid.m_comp.m_EAccountType )
+ {
+ if ( m_steamid.m_comp.m_unAccountInstance & k_EChatInstanceFlagClan )
+ {
+ V_snprintf( pchBuf, k_cBufLen, "[c:%u:%u]", m_steamid.m_comp.m_EUniverse, m_steamid.m_comp.m_unAccountID );
+ }
+ else if ( m_steamid.m_comp.m_unAccountInstance & k_EChatInstanceFlagLobby )
+ {
+ V_snprintf( pchBuf, k_cBufLen, "[L:%u:%u]", m_steamid.m_comp.m_EUniverse, m_steamid.m_comp.m_unAccountID );
+ }
+ else // Anon chat
+ {
+ V_snprintf( pchBuf, k_cBufLen, "[T:%u:%u]", m_steamid.m_comp.m_EUniverse, m_steamid.m_comp.m_unAccountID );
+ }
+ }
+ else if ( k_EAccountTypeInvalid == m_steamid.m_comp.m_EAccountType )
+ {
+ V_snprintf( pchBuf, k_cBufLen, "[I:%u:%u]", m_steamid.m_comp.m_EUniverse, m_steamid.m_comp.m_unAccountID );
+ }
+ else if ( k_EAccountTypeIndividual == m_steamid.m_comp.m_EAccountType )
+ {
+ if ( m_steamid.m_comp.m_unAccountInstance != k_unSteamUserDesktopInstance )
+ V_snprintf( pchBuf, k_cBufLen, "[U:%u:%u:%u]", m_steamid.m_comp.m_EUniverse, m_steamid.m_comp.m_unAccountID, m_steamid.m_comp.m_unAccountInstance );
+ else
+ V_snprintf( pchBuf, k_cBufLen, "[U:%u:%u]", m_steamid.m_comp.m_EUniverse, m_steamid.m_comp.m_unAccountID );
+ }
+ else if ( k_EAccountTypeAnonUser == m_steamid.m_comp.m_EAccountType )
+ {
+ V_snprintf( pchBuf, k_cBufLen, "[a:%u:%u]", m_steamid.m_comp.m_EUniverse, m_steamid.m_comp.m_unAccountID );
+ }
+ else
+ {
+ V_snprintf( pchBuf, k_cBufLen, "[i:%u:%u]", m_steamid.m_comp.m_EUniverse, m_steamid.m_comp.m_unAccountID );
+ }
+ return pchBuf;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Renders the passed-in steam ID to a buffer. NOTE: for convenience of calling
+// code, this code returns a pointer to a static buffer and is NOT thread-safe.
+// Input: 64-bit representation of Steam ID to render
+// Output: buffer with rendered Steam ID
+//-----------------------------------------------------------------------------
+const char * CSteamID::Render( uint64 ulSteamID )
+{
+ CSteamID steamID( ulSteamID );
+ return steamID.Render();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: some steamIDs are for internal use only
+// This is really debug code, but we run with asserts on in retail, so ...
+//-----------------------------------------------------------------------------
+bool CSteamID::BValidExternalSteamID() const
+{
+ if ( m_steamid.m_comp.m_EAccountType == k_EAccountTypePending )
+ return false;
+ if ( m_steamid.m_comp.m_EAccountType != k_EAccountTypeAnonGameServer && m_steamid.m_comp.m_EAccountType != k_EAccountTypeContentServer && m_steamid.m_comp.m_EAccountType != k_EAccountTypeAnonUser )
+ {
+ if ( m_steamid.m_comp.m_unAccountID == 0 && m_steamid.m_comp.m_unAccountInstance == 0 )
+ return false;
+ }
+ return true;
+}
+
+#ifdef STEAM
+//-----------------------------------------------------------------------------
+// Purpose: Returns the matching chat steamID, with the default instance of 0
+// Input: SteamID, either a Clan or a Chat type
+// Output: SteamID with account type changed to chat, and the Clan flag set.
+// If account type was not chat to start with, instance will be set to 0
+//-----------------------------------------------------------------------------
+CSteamID ChatIDFromSteamID( const CSteamID &steamID )
+{
+ if ( steamID.GetEAccountType() == k_EAccountTypeChat )
+ return steamID;
+
+ return ChatIDFromClanID( steamID );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the matching chat steamID, with the default instance of 0
+// Input: SteamID, either a Clan type or a Chat type w/ the Clan flag set
+// Output: SteamID with account type changed to clan.
+// If account type was not clan to start with, instance will be set to 0
+//-----------------------------------------------------------------------------
+CSteamID ClanIDFromSteamID( const CSteamID &steamID )
+{
+ if ( steamID.GetEAccountType() == k_EAccountTypeClan )
+ return steamID;
+
+ return ClanIDFromChatID( steamID );
+}
+
+
+// Asserts steamID type before conversion
+CSteamID ChatIDFromClanID( const CSteamID &steamIDClan )
+{
+ Assert( steamIDClan.GetEAccountType() == k_EAccountTypeClan );
+
+ return CSteamID( steamIDClan.GetAccountID(), k_EChatInstanceFlagClan, steamIDClan.GetEUniverse(), k_EAccountTypeChat );
+}
+
+
+// Asserts steamID type before conversion
+CSteamID ClanIDFromChatID( const CSteamID &steamIDChat )
+{
+ Assert( steamIDChat.GetEAccountType() == k_EAccountTypeChat );
+ Assert( k_EChatInstanceFlagClan & steamIDChat.GetUnAccountInstance() );
+
+ return CSteamID( steamIDChat.GetAccountID(), 0, steamIDChat.GetEUniverse(), k_EAccountTypeClan );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: CGameID "hidden" functions
+// move these somewhere else maybe
+//-----------------------------------------------------------------------------
+CGameID::CGameID( const char *pchGameID )
+{
+ m_ulGameID = 0;
+
+ sscanf( pchGameID, "%llu", &m_ulGameID );
+
+ switch ( m_gameID.m_nType )
+ {
+ default:
+ AssertMsg( false, "Unknown GameID type" );
+ m_ulGameID = 0;
+ break;
+ case k_EGameIDTypeApp:
+ case k_EGameIDTypeGameMod:
+ case k_EGameIDTypeShortcut:
+ case k_EGameIDTypeP2P:
+ break;
+ }
+}
+
+
+// renders this Game ID to string
+const char * CGameID::Render() const
+{
+ // longest buffer is log10(2**64) == 20 + 1 == 21
+ const int k_cBufLen = 21;
+
+ const int k_cBufs = 4; // # of static bufs to use (so people can compose output with multiple calls to Render() )
+ static char rgchBuf[k_cBufs][k_cBufLen];
+ static int nBuf = 0;
+ char * pchBuf = rgchBuf[nBuf]; // get pointer to current static buf
+ nBuf ++; // use next buffer for next call to this method
+ nBuf %= k_cBufs;
+
+ V_snprintf( pchBuf, k_cBufLen, "%llu", m_ulGameID );
+
+ return pchBuf;
+}
+
+// static method to render a uint64 representation of a Game ID to a string
+const char * CGameID::Render( uint64 ulGameID )
+{
+ CGameID nGameID( ulGameID );
+ return nGameID.Render();
+}
+#endif