This commit is contained in:
FluorescentCIAAfricanAmerican
2020-04-22 12:56:21 -04:00
commit 3bf9df6b27
15370 changed files with 5489726 additions and 0 deletions

416
vstdlib/KeyValuesSystem.cpp Normal file
View File

@ -0,0 +1,416 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include <vstdlib/IKeyValuesSystem.h>
#include <KeyValues.h>
#include "mempool.h"
#include "utlsymbol.h"
#include "tier0/threadtools.h"
#include "tier1/memstack.h"
#include "tier1/utlmap.h"
#include "tier1/utlstring.h"
#include "tier1/fmtstr.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
#ifdef NO_SBH // no need to pool if using tier0 small block heap
#define KEYVALUES_USE_POOL 1
#endif
//-----------------------------------------------------------------------------
// Purpose: Central storage point for KeyValues memory and symbols
//-----------------------------------------------------------------------------
class CKeyValuesSystem : public IKeyValuesSystem
{
public:
CKeyValuesSystem();
~CKeyValuesSystem();
// registers the size of the KeyValues in the specified instance
// so it can build a properly sized memory pool for the KeyValues objects
// the sizes will usually never differ but this is for versioning safety
void RegisterSizeofKeyValues(int size);
// allocates/frees a KeyValues object from the shared mempool
void *AllocKeyValuesMemory(int size);
void FreeKeyValuesMemory(void *pMem);
// symbol table access (used for key names)
HKeySymbol GetSymbolForString( const char *name, bool bCreate );
const char *GetStringForSymbol(HKeySymbol symbol);
// returns the wide version of ansi, also does the lookup on #'d strings
void GetLocalizedFromANSI( const char *ansi, wchar_t *outBuf, int unicodeBufferSizeInBytes);
void GetANSIFromLocalized( const wchar_t *wchar, char *outBuf, int ansiBufferSizeInBytes );
// for debugging, adds KeyValues record into global list so we can track memory leaks
virtual void AddKeyValuesToMemoryLeakList(void *pMem, HKeySymbol name);
virtual void RemoveKeyValuesFromMemoryLeakList(void *pMem);
// maintain a cache of KeyValues we load from disk. This saves us quite a lot of time on app startup.
virtual void AddFileKeyValuesToCache( const KeyValues* _kv, const char *resourceName, const char *pathID );
virtual bool LoadFileKeyValuesFromCache( KeyValues* outKv, const char *resourceName, const char *pathID, IBaseFileSystem *filesystem ) const;
virtual void InvalidateCache();
virtual void InvalidateCacheForFile( const char *resourceName, const char *pathID );
private:
#ifdef KEYVALUES_USE_POOL
CUtlMemoryPool *m_pMemPool;
#endif
int m_iMaxKeyValuesSize;
// string hash table
CMemoryStack m_Strings;
struct hash_item_t
{
int stringIndex;
hash_item_t *next;
};
CUtlMemoryPool m_HashItemMemPool;
CUtlVector<hash_item_t> m_HashTable;
int CaseInsensitiveHash(const char *string, int iBounds);
void DoInvalidateCache();
struct MemoryLeakTracker_t
{
int nameIndex;
void *pMem;
};
static bool MemoryLeakTrackerLessFunc( const MemoryLeakTracker_t &lhs, const MemoryLeakTracker_t &rhs )
{
return lhs.pMem < rhs.pMem;
}
CUtlRBTree<MemoryLeakTracker_t, int> m_KeyValuesTrackingList;
CThreadFastMutex m_mutex;
CUtlMap<CUtlString, KeyValues*> m_KeyValueCache;
};
// EXPOSE_SINGLE_INTERFACE(CKeyValuesSystem, IKeyValuesSystem, KEYVALUES_INTERFACE_VERSION);
//-----------------------------------------------------------------------------
// Instance singleton and expose interface to rest of code
//-----------------------------------------------------------------------------
static CKeyValuesSystem g_KeyValuesSystem;
IKeyValuesSystem *KeyValuesSystem()
{
return &g_KeyValuesSystem;
}
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CKeyValuesSystem::CKeyValuesSystem()
: m_HashItemMemPool(sizeof(hash_item_t), 64, UTLMEMORYPOOL_GROW_FAST, "CKeyValuesSystem::m_HashItemMemPool")
, m_KeyValuesTrackingList(0, 0, MemoryLeakTrackerLessFunc)
, m_KeyValueCache( UtlStringLessFunc )
{
// initialize hash table
m_HashTable.AddMultipleToTail(2047);
for (int i = 0; i < m_HashTable.Count(); i++)
{
m_HashTable[i].stringIndex = 0;
m_HashTable[i].next = NULL;
}
m_Strings.Init( 4*1024*1024, 64*1024, 0, 4 );
char *pszEmpty = ((char *)m_Strings.Alloc(1));
*pszEmpty = 0;
#ifdef KEYVALUES_USE_POOL
m_pMemPool = NULL;
#endif
m_iMaxKeyValuesSize = sizeof(KeyValues);
}
//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
CKeyValuesSystem::~CKeyValuesSystem()
{
#ifdef KEYVALUES_USE_POOL
#ifdef _DEBUG
// display any memory leaks
if (m_pMemPool && m_pMemPool->Count() > 0)
{
DevMsg("Leaked KeyValues blocks: %d\n", m_pMemPool->Count());
}
// iterate all the existing keyvalues displaying their names
for (int i = 0; i < m_KeyValuesTrackingList.MaxElement(); i++)
{
if (m_KeyValuesTrackingList.IsValidIndex(i))
{
DevMsg("\tleaked KeyValues(%s)\n", &m_Strings[m_KeyValuesTrackingList[i].nameIndex]);
}
}
#endif
delete m_pMemPool;
#endif
DoInvalidateCache();
}
//-----------------------------------------------------------------------------
// Purpose: registers the size of the KeyValues in the specified instance
// so it can build a properly sized memory pool for the KeyValues objects
// the sizes will usually never differ but this is for versioning safety
//-----------------------------------------------------------------------------
void CKeyValuesSystem::RegisterSizeofKeyValues(int size)
{
if (size > m_iMaxKeyValuesSize)
{
m_iMaxKeyValuesSize = size;
}
}
#ifdef KEYVALUES_USE_POOL
static void KVLeak( char const *fmt, ... )
{
va_list argptr;
char data[1024];
va_start(argptr, fmt);
Q_vsnprintf(data, sizeof( data ), fmt, argptr);
va_end(argptr);
Msg( "%s", data );
}
#endif
//-----------------------------------------------------------------------------
// Purpose: allocates a KeyValues object from the shared mempool
//-----------------------------------------------------------------------------
void *CKeyValuesSystem::AllocKeyValuesMemory(int size)
{
#ifdef KEYVALUES_USE_POOL
// allocate, if we don't have one yet
if (!m_pMemPool)
{
m_pMemPool = new CUtlMemoryPool(m_iMaxKeyValuesSize, 1024, UTLMEMORYPOOL_GROW_FAST, "CKeyValuesSystem::m_pMemPool" );
m_pMemPool->SetErrorReportFunc( KVLeak );
}
return m_pMemPool->Alloc(size);
#else
return malloc( size );
#endif
}
//-----------------------------------------------------------------------------
// Purpose: frees a KeyValues object from the shared mempool
//-----------------------------------------------------------------------------
void CKeyValuesSystem::FreeKeyValuesMemory(void *pMem)
{
#ifdef KEYVALUES_USE_POOL
m_pMemPool->Free(pMem);
#else
free( pMem );
#endif
}
//-----------------------------------------------------------------------------
// Purpose: symbol table access (used for key names)
//-----------------------------------------------------------------------------
HKeySymbol CKeyValuesSystem::GetSymbolForString( const char *name, bool bCreate )
{
if ( !name )
{
return (-1);
}
AUTO_LOCK( m_mutex );
int hash = CaseInsensitiveHash(name, m_HashTable.Count());
int i = 0;
hash_item_t *item = &m_HashTable[hash];
while (1)
{
if (!stricmp(name, (char *)m_Strings.GetBase() + item->stringIndex ))
{
return (HKeySymbol)item->stringIndex;
}
i++;
if (item->next == NULL)
{
if ( !bCreate )
{
// not found
return -1;
}
// we're not in the table
if (item->stringIndex != 0)
{
// first item is used, an new item
item->next = (hash_item_t *)m_HashItemMemPool.Alloc(sizeof(hash_item_t));
item = item->next;
}
// build up the new item
item->next = NULL;
char *pString = (char *)m_Strings.Alloc( V_strlen(name) + 1 );
if ( !pString )
{
Error( "Out of keyvalue string space" );
return -1;
}
item->stringIndex = pString - (char *)m_Strings.GetBase();
strcpy(pString, name);
return (HKeySymbol)item->stringIndex;
}
item = item->next;
}
// shouldn't be able to get here
Assert(0);
return (-1);
}
//-----------------------------------------------------------------------------
// Purpose: symbol table access
//-----------------------------------------------------------------------------
const char *CKeyValuesSystem::GetStringForSymbol(HKeySymbol symbol)
{
if ( symbol == -1 )
{
return "";
}
return ((char *)m_Strings.GetBase() + (size_t)symbol);
}
//-----------------------------------------------------------------------------
// Purpose: adds KeyValues record into global list so we can track memory leaks
//-----------------------------------------------------------------------------
void CKeyValuesSystem::AddKeyValuesToMemoryLeakList(void *pMem, HKeySymbol name)
{
#ifdef _DEBUG
// only track the memory leaks in debug builds
MemoryLeakTracker_t item = { name, pMem };
m_KeyValuesTrackingList.Insert(item);
#endif
}
//-----------------------------------------------------------------------------
// Purpose: used to track memory leaks
//-----------------------------------------------------------------------------
void CKeyValuesSystem::RemoveKeyValuesFromMemoryLeakList(void *pMem)
{
#ifdef _DEBUG
// only track the memory leaks in debug builds
MemoryLeakTracker_t item = { 0, pMem };
int index = m_KeyValuesTrackingList.Find(item);
m_KeyValuesTrackingList.RemoveAt(index);
#endif
}
//-----------------------------------------------------------------------------
// Purpose: Removes a particular key value file (from a particular source) from the cache if present.
//-----------------------------------------------------------------------------
void CKeyValuesSystem::InvalidateCacheForFile(const char *resourceName, const char *pathID)
{
CUtlString identString( CFmtStr( "%s::%s", resourceName ? resourceName : "", pathID ? pathID : "" ) );
CUtlMap<CUtlString, KeyValues*>::IndexType_t index = m_KeyValueCache.Find( identString );
if ( m_KeyValueCache.IsValidIndex( index ) )
{
m_KeyValueCache[ index ]->deleteThis();
m_KeyValueCache.RemoveAt( index );
}
}
//-----------------------------------------------------------------------------
// Purpose: Adds a particular key value file (from a particular source) to the cache if not already present.
//-----------------------------------------------------------------------------
void CKeyValuesSystem::AddFileKeyValuesToCache(const KeyValues* _kv, const char *resourceName, const char *pathID)
{
CUtlString identString( CFmtStr( "%s::%s", resourceName ? resourceName : "", pathID ? pathID : "" ) );
// Some files actually have multiple roots, and if you use regular MakeCopy (without passing true), those
// will be missed. This caused a bug in soundscapes on dedicated servers.
m_KeyValueCache.Insert( identString, _kv->MakeCopy( true ) );
}
//-----------------------------------------------------------------------------
// Purpose: Fetches a particular keyvalue from the cache, and copies into _outKv (clearing anything there already).
//-----------------------------------------------------------------------------
bool CKeyValuesSystem::LoadFileKeyValuesFromCache(KeyValues* outKv, const char *resourceName, const char *pathID, IBaseFileSystem *filesystem) const
{
Assert( outKv );
Assert( resourceName );
COM_TimestampedLog("CKeyValuesSystem::LoadFileKeyValuesFromCache(%s%s%s): Begin", pathID ? pathID : "", pathID && resourceName ? "/" : "", resourceName ? resourceName : "");
CUtlString identString(CFmtStr("%s::%s", resourceName ? resourceName : "", pathID ? pathID : ""));
CUtlMap<CUtlString, KeyValues*>::IndexType_t index = m_KeyValueCache.Find( identString );
if ( m_KeyValueCache.IsValidIndex( index ) ) {
(*outKv) = ( *m_KeyValueCache[ index ] );
COM_TimestampedLog("CKeyValuesSystem::LoadFileKeyValuesFromCache(%s%s%s): End / Hit", pathID ? pathID : "", pathID && resourceName ? "/" : "", resourceName ? resourceName : "");
return true;
}
COM_TimestampedLog("CKeyValuesSystem::LoadFileKeyValuesFromCache(%s%s%s): End / Miss", pathID ? pathID : "", pathID && resourceName ? "/" : "", resourceName ? resourceName : "");
return false;
}
//-----------------------------------------------------------------------------
// Purpose: Evicts everything from the cache, cleans up the memory used.
//-----------------------------------------------------------------------------
void CKeyValuesSystem::InvalidateCache()
{
DoInvalidateCache();
}
//-----------------------------------------------------------------------------
// Purpose: generates a simple hash value for a string
//-----------------------------------------------------------------------------
int CKeyValuesSystem::CaseInsensitiveHash(const char *string, int iBounds)
{
unsigned int hash = 0;
for ( ; *string != 0; string++ )
{
if (*string >= 'A' && *string <= 'Z')
{
hash = (hash << 1) + (*string - 'A' + 'a');
}
else
{
hash = (hash << 1) + *string;
}
}
return hash % iBounds;
}
//-----------------------------------------------------------------------------
// Purpose: Evicts everything from the cache, cleans up the memory used.
//-----------------------------------------------------------------------------
void CKeyValuesSystem::DoInvalidateCache()
{
// Cleanup the cache.
FOR_EACH_MAP_FAST( m_KeyValueCache, mapIndex )
{
m_KeyValueCache[mapIndex]->deleteThis();
}
// Apparently you cannot call RemoveAll on a map without also purging the contents because... ?
// If you do and you continue to use the map, you will eventually wind up in a case where you
// have an empty map but it still iterates over elements. Awesome?
m_KeyValueCache.Purge();
}

1157
vstdlib/coroutine.cpp Normal file

File diff suppressed because it is too large Load Diff

31
vstdlib/coroutine_osx.vpc Normal file
View File

@ -0,0 +1,31 @@
//-----------------------------------------------------------------------------
// COROUTINE_OSX.VPC
//
// Project Script
//-----------------------------------------------------------------------------
$Macro SRCDIR ".."
$include "$SRCDIR\vpc_scripts\source_lib_base.vpc"
$Configuration
{
$Compiler
{
$GCC_ExtraCompilerFlags "-fno-stack-protector"
$PreprocessorDefinitions "$BASE;VSTDLIB_DLL_EXPORT"
}
}
$Project "coroutine_osx"
{
$Folder "Source Files"
{
$File "coroutine.cpp"
}
$Folder "Public Header Files"
{
$File "$SRCDIR\public\vstdlib\coroutine.h"
}
}

View File

@ -0,0 +1,175 @@
option casemap:none
.CODE
; import Coroutine_Finish with its mangled Microsoft Visual C++ name
?Coroutine_Finish@@YAXXZ PROTO
; extern "C" void SaveNonVolatileRegs( uintptr_t regs[8] );
; incoming parameter is rcs
SaveNonVolatileRegs PROC FRAME
.endprolog
mov qword ptr[rcx], rbx
mov qword ptr[rcx+8], rbp
mov qword ptr[rcx+16], rsi
mov qword ptr[rcx+24], rdi
mov qword ptr[rcx+32], r12
mov qword ptr[rcx+40], r13
mov qword ptr[rcx+48], r14
mov qword ptr[rcx+56], r15
ret
SaveNonVolatileRegs ENDP
; extern "C" void NORETURN Coroutine_Launch_ASM( byte **ppStackHigh, uintptr_t **ppLaunchParentFramePtr, void (*pfnExec)( void* ), void *pvParam )
; Per Win64 ABI, incoming params are rcx, rdx, r8, r9. initial stack pointer is half-aligned due to return address
Coroutine_Launch_ASM PROC FRAME
; x64 prolog and prolog description macros:
; save caller's nonvolatile registers (pushed in reverse order to match SaveNonVolatileRegs)
; so that we can slam new values in later to trick the x64 callstack unwind procedure
push r15
.pushreg r15
push r14
.pushreg r14
push r13
.pushreg r13
push r12
.pushreg r12
push rdi
.pushreg rdi
push rsi
.pushreg rsi
push rbp
.pushreg rbp
push rbx
.pushreg rbx
; stack-allocate Win64 function call shadow space for calls to pfnExec and Coroutine_Finish,
; plus 8 additional bytes to align the stack frame properly (comes in off by 8)
sub rsp, 28h
.allocstack 28h
.endprolog
; compute top of stack for coroutine: 40 bytes for stack, 64 for saved regs, 8 for return address
; (we do not bother including the additional unused 32 byte shadow space we own above that)
lea rax, [rsp+70h]
mov qword ptr [rcx], rax
; save off the address of our saved regs so that we can memcpy over them later and trick
; the x64 stack unwind logic into walking up to a different Internal_Coroutine_Continue
lea rax, [rsp+28h]
mov qword ptr [rdx], rax
; call pfnExec(pvParam)
mov rcx, r9
call r8
; call Coroutine_Finish - does not return
call ?Coroutine_Finish@@YAXXZ
Coroutine_Launch_ASM ENDP
; Needs to match definition found in setjmp.h
_JUMP_BUFFER STRUCT
m_Frame QWORD ?
m_Rbx QWORD ?
m_Rsp QWORD ?
m_Rbp QWORD ?
m_Rsi QWORD ?
m_Rdi QWORD ?
m_R12 QWORD ?
m_R13 QWORD ?
m_R14 QWORD ?
m_R15 QWORD ?
m_Rip QWORD ?
m_MxCsr DWORD ?
m_FpCsr WORD ?
m_Spare WORD ?
m_Xmm6 XMMWORD ?
m_Xmm7 XMMWORD ?
m_Xmm8 XMMWORD ?
m_Xmm9 XMMWORD ?
m_Xmm10 XMMWORD ?
m_Xmm11 XMMWORD ?
m_Xmm12 XMMWORD ?
m_Xmm13 XMMWORD ?
m_Xmm14 XMMWORD ?
m_Xmm15 XMMWORD ?
_JUMP_BUFFER ENDS
;This is the reference asm for __intrinsic_setjmp() in VS2015
;mov qword ptr [rcx],rdx ; intrinsic call site does "mov rdx,rbp" followed by "add rdx,0FFFFFFFFFFFFFFC0h", looks like a nonstandard abi
;mov qword ptr [rcx+8],rbx
;mov qword ptr [rcx+18h],rbp
;mov qword ptr [rcx+20h],rsi
;mov qword ptr [rcx+28h],rdi
;mov qword ptr [rcx+30h],r12
;mov qword ptr [rcx+38h],r13
;mov qword ptr [rcx+40h],r14
;mov qword ptr [rcx+48h],r15
;lea r8,[rsp+8] ; rsp set to post-return address
;mov qword ptr [rcx+10h],r8
;mov r8,qword ptr [rsp]
;mov qword ptr [rcx+50h],r8
;stmxcsr dword ptr [rcx+58h]
;fnstcw word ptr [rcx+5Ch]
;movdqa xmmword ptr [rcx+60h],xmm6
;ovdqa xmmword ptr [rcx+70h],xmm7
;movdqa xmmword ptr [rcx+80h],xmm8
;movdqa xmmword ptr [rcx+90h],xmm9
;movdqa xmmword ptr [rcx+0A0h],xmm10
;movdqa xmmword ptr [rcx+0B0h],xmm11
;movdqa xmmword ptr [rcx+0C0h],xmm12
;movdqa xmmword ptr [rcx+0D0h],xmm13
;movdqa xmmword ptr [rcx+0E0h],xmm14
;movdqa xmmword ptr [rcx+0F0h],xmm15
;xor eax,eax
;ret
; extern "C" void NORETURN Coroutine_LongJmp_UnChecked( jmp_buf buf, int nResult )
; Per Win64 ABI, incoming params are rcx, rdx, r8, r9. initial stack pointer is half-aligned due to return address
Coroutine_LongJmp_Unchecked PROC
;load nResult into result from initial setjmp()
xor rax, rax
mov eax, edx
;restore to setjmp() caller state
mov rdx, [rcx]._JUMP_BUFFER.m_Frame ; appears to be an error checking value of (_JUMP_BUFFER.m_Rbp + 0FFFFFFFFFFFFFFC0h) passed non-standardly through rdx to setjmp()
mov rbx, [rcx]._JUMP_BUFFER.m_Rbx
mov rsp, [rcx]._JUMP_BUFFER.m_Rsp
mov rbp, [rcx]._JUMP_BUFFER.m_Rbp
mov rsi, [rcx]._JUMP_BUFFER.m_Rsi
mov rdi, [rcx]._JUMP_BUFFER.m_Rdi
mov r12, [rcx]._JUMP_BUFFER.m_R12
mov r13, [rcx]._JUMP_BUFFER.m_R13
mov r14, [rcx]._JUMP_BUFFER.m_R14
mov r15, [rcx]._JUMP_BUFFER.m_R15
mov r10, [rcx]._JUMP_BUFFER.m_Rip ; store return address in r10 for return
ldmxcsr [rcx]._JUMP_BUFFER.m_MxCsr
fldcw [rcx]._JUMP_BUFFER.m_FpCsr
;[rcx]._JUMP_BUFFER.m_Spare
movaps xmm6, [rcx]._JUMP_BUFFER.m_Xmm6
movaps xmm7, [rcx]._JUMP_BUFFER.m_Xmm7
movaps xmm8, [rcx]._JUMP_BUFFER.m_Xmm8
movaps xmm9, [rcx]._JUMP_BUFFER.m_Xmm9
movaps xmm10, [rcx]._JUMP_BUFFER.m_Xmm10
movaps xmm11, [rcx]._JUMP_BUFFER.m_Xmm11
movaps xmm12, [rcx]._JUMP_BUFFER.m_Xmm12
movaps xmm13, [rcx]._JUMP_BUFFER.m_Xmm13
movaps xmm14, [rcx]._JUMP_BUFFER.m_Xmm14
movaps xmm15, [rcx]._JUMP_BUFFER.m_Xmm15
;jmp instead of ret to _JUMP_BUFFER.m_Rip because setjmp() already set the _JUMP_BUFFER.m_Rsp to the post-return state
db 048h ; emit a REX prefix on the jmp to ensure it's a full qword
jmp qword ptr r10
Coroutine_LongJmp_Unchecked ENDP
_TEXT ENDS
END

899
vstdlib/cvar.cpp Normal file
View File

@ -0,0 +1,899 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//===========================================================================//
#include "vstdlib/cvar.h"
#include <ctype.h>
#include "tier0/icommandline.h"
#include "tier1/utlrbtree.h"
#include "tier1/strtools.h"
#include "tier1/KeyValues.h"
#include "tier1/convar.h"
#include "tier0/vprof.h"
#include "tier1/tier1.h"
#include "tier1/utlbuffer.h"
#ifdef _X360
#include "xbox/xbox_console.h"
#endif
#ifdef POSIX
#include <wctype.h>
#include <wchar.h>
#endif
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
// Default implementation of CvarQuery
//-----------------------------------------------------------------------------
class CDefaultCvarQuery : public CBaseAppSystem< ICvarQuery >
{
public:
virtual void *QueryInterface( const char *pInterfaceName )
{
if ( !Q_stricmp( pInterfaceName, CVAR_QUERY_INTERFACE_VERSION ) )
return (ICvarQuery*)this;
return NULL;
}
virtual bool AreConVarsLinkable( const ConVar *child, const ConVar *parent )
{
return true;
}
};
static CDefaultCvarQuery s_DefaultCvarQuery;
static ICvarQuery *s_pCVarQuery = NULL;
//-----------------------------------------------------------------------------
// Default implementation
//-----------------------------------------------------------------------------
class CCvar : public ICvar
{
public:
CCvar();
// Methods of IAppSystem
virtual bool Connect( CreateInterfaceFn factory );
virtual void Disconnect();
virtual void *QueryInterface( const char *pInterfaceName );
virtual InitReturnVal_t Init();
virtual void Shutdown();
// Inherited from ICVar
virtual CVarDLLIdentifier_t AllocateDLLIdentifier();
virtual void RegisterConCommand( ConCommandBase *pCommandBase );
virtual void UnregisterConCommand( ConCommandBase *pCommandBase );
virtual void UnregisterConCommands( CVarDLLIdentifier_t id );
virtual const char* GetCommandLineValue( const char *pVariableName );
virtual ConCommandBase *FindCommandBase( const char *name );
virtual const ConCommandBase *FindCommandBase( const char *name ) const;
virtual ConVar *FindVar ( const char *var_name );
virtual const ConVar *FindVar ( const char *var_name ) const;
virtual ConCommand *FindCommand( const char *name );
virtual const ConCommand *FindCommand( const char *name ) const;
virtual ConCommandBase *GetCommands( void );
virtual const ConCommandBase *GetCommands( void ) const;
virtual void InstallGlobalChangeCallback( FnChangeCallback_t callback );
virtual void RemoveGlobalChangeCallback( FnChangeCallback_t callback );
virtual void CallGlobalChangeCallbacks( ConVar *var, const char *pOldString, float flOldValue );
virtual void InstallConsoleDisplayFunc( IConsoleDisplayFunc* pDisplayFunc );
virtual void RemoveConsoleDisplayFunc( IConsoleDisplayFunc* pDisplayFunc );
virtual void ConsoleColorPrintf( const Color& clr, const char *pFormat, ... ) const;
virtual void ConsolePrintf( const char *pFormat, ... ) const;
virtual void ConsoleDPrintf( const char *pFormat, ... ) const;
virtual void RevertFlaggedConVars( int nFlag );
virtual void InstallCVarQuery( ICvarQuery *pQuery );
#if defined( _X360 )
virtual void PublishToVXConsole( );
#endif
virtual bool IsMaterialThreadSetAllowed( ) const;
virtual void QueueMaterialThreadSetValue( ConVar *pConVar, const char *pValue );
virtual void QueueMaterialThreadSetValue( ConVar *pConVar, int nValue );
virtual void QueueMaterialThreadSetValue( ConVar *pConVar, float flValue );
virtual bool HasQueuedMaterialThreadConVarSets() const;
virtual int ProcessQueuedMaterialThreadConVarSets();
private:
enum
{
CONSOLE_COLOR_PRINT = 0,
CONSOLE_PRINT,
CONSOLE_DPRINT,
};
void DisplayQueuedMessages( );
CUtlVector< FnChangeCallback_t > m_GlobalChangeCallbacks;
CUtlVector< IConsoleDisplayFunc* > m_DisplayFuncs;
int m_nNextDLLIdentifier;
ConCommandBase *m_pConCommandList;
// temporary console area so we can store prints before console display funs are installed
mutable CUtlBuffer m_TempConsoleBuffer;
protected:
// internals for ICVarIterator
class CCVarIteratorInternal : public ICVarIteratorInternal
{
public:
CCVarIteratorInternal( CCvar *outer )
: m_pOuter( outer )
//, m_pHash( &outer->m_CommandHash ), // remember my CCvar,
//m_hashIter( -1, -1 ) // and invalid iterator
, m_pCur( NULL )
{}
virtual void SetFirst( void );
virtual void Next( void );
virtual bool IsValid( void );
virtual ConCommandBase *Get( void );
protected:
CCvar * const m_pOuter;
//CConCommandHash * const m_pHash;
//CConCommandHash::CCommandHashIterator_t m_hashIter;
ConCommandBase *m_pCur;
};
virtual ICVarIteratorInternal *FactoryInternalIterator( void );
friend class CCVarIteratorInternal;
enum ConVarSetType_t
{
CONVAR_SET_STRING = 0,
CONVAR_SET_INT,
CONVAR_SET_FLOAT,
};
struct QueuedConVarSet_t
{
ConVar *m_pConVar;
ConVarSetType_t m_nType;
int m_nInt;
float m_flFloat;
CUtlString m_String;
};
CUtlVector< QueuedConVarSet_t > m_QueuedConVarSets;
bool m_bMaterialSystemThreadSetAllowed;
private:
// Standard console commands -- DO NOT PLACE ANY HIGHER THAN HERE BECAUSE THESE MUST BE THE FIRST TO DESTRUCT
CON_COMMAND_MEMBER_F( CCvar, "find", Find, "Find concommands with the specified string in their name/help text.", 0 )
};
void CCvar::CCVarIteratorInternal::SetFirst( void ) RESTRICT
{
//m_hashIter = m_pHash->First();
m_pCur = m_pOuter->GetCommands();
}
void CCvar::CCVarIteratorInternal::Next( void ) RESTRICT
{
//m_hashIter = m_pHash->Next( m_hashIter );
if ( m_pCur )
m_pCur = m_pCur->GetNext();
}
bool CCvar::CCVarIteratorInternal::IsValid( void ) RESTRICT
{
//return m_pHash->IsValidIterator( m_hashIter );
return m_pCur != NULL;
}
ConCommandBase *CCvar::CCVarIteratorInternal::Get( void ) RESTRICT
{
Assert( IsValid( ) );
//return (*m_pHash)[m_hashIter];
return m_pCur;
}
ICvar::ICVarIteratorInternal *CCvar::FactoryInternalIterator( void )
{
return new CCVarIteratorInternal( this );
}
//-----------------------------------------------------------------------------
// Factor for CVars
//-----------------------------------------------------------------------------
static CCvar s_Cvar;
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CCvar, ICvar, CVAR_INTERFACE_VERSION, s_Cvar );
//-----------------------------------------------------------------------------
// Returns a CVar dictionary for tool usage
//-----------------------------------------------------------------------------
CreateInterfaceFn VStdLib_GetICVarFactory()
{
return Sys_GetFactoryThis();
}
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
CCvar::CCvar() : m_TempConsoleBuffer( 0, 1024 )
{
m_nNextDLLIdentifier = 0;
m_pConCommandList = NULL;
m_bMaterialSystemThreadSetAllowed = false;
}
//-----------------------------------------------------------------------------
// Methods of IAppSystem
//-----------------------------------------------------------------------------
bool CCvar::Connect( CreateInterfaceFn factory )
{
ConnectTier1Libraries( &factory, 1 );
s_pCVarQuery = (ICvarQuery*)factory( CVAR_QUERY_INTERFACE_VERSION, NULL );
if ( !s_pCVarQuery )
{
s_pCVarQuery = &s_DefaultCvarQuery;
}
ConVar_Register();
return true;
}
void CCvar::Disconnect()
{
ConVar_Unregister();
s_pCVarQuery = NULL;
DisconnectTier1Libraries();
}
InitReturnVal_t CCvar::Init()
{
return INIT_OK;
}
void CCvar::Shutdown()
{
}
void *CCvar::QueryInterface( const char *pInterfaceName )
{
// We implement the ICvar interface
if ( !V_strcmp( pInterfaceName, CVAR_INTERFACE_VERSION ) )
return (ICvar*)this;
return NULL;
}
//-----------------------------------------------------------------------------
// Method allowing the engine ICvarQuery interface to take over
//-----------------------------------------------------------------------------
void CCvar::InstallCVarQuery( ICvarQuery *pQuery )
{
Assert( s_pCVarQuery == &s_DefaultCvarQuery );
s_pCVarQuery = pQuery ? pQuery : &s_DefaultCvarQuery;
}
//-----------------------------------------------------------------------------
// Used by DLLs to be able to unregister all their commands + convars
//-----------------------------------------------------------------------------
CVarDLLIdentifier_t CCvar::AllocateDLLIdentifier()
{
return m_nNextDLLIdentifier++;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *variable -
//-----------------------------------------------------------------------------
void CCvar::RegisterConCommand( ConCommandBase *variable )
{
// Already registered
if ( variable->IsRegistered() )
return;
variable->m_bRegistered = true;
const char *pName = variable->GetName();
if ( !pName || !pName[0] )
{
variable->m_pNext = NULL;
return;
}
// If the variable is already defined, then setup the new variable as a proxy to it.
const ConCommandBase *pOther = FindVar( variable->GetName() );
if ( pOther )
{
if ( variable->IsCommand() || pOther->IsCommand() )
{
Warning( "WARNING: unable to link %s and %s because one or more is a ConCommand.\n", variable->GetName(), pOther->GetName() );
}
else
{
// This cast is ok because we make sure they're ConVars above.
const ConVar *pChildVar = static_cast< const ConVar* >( variable );
const ConVar *pParentVar = static_cast< const ConVar* >( pOther );
// See if it's a valid linkage
if ( s_pCVarQuery->AreConVarsLinkable( pChildVar, pParentVar ) )
{
// Make sure the default values are the same (but only spew about this for FCVAR_REPLICATED)
if( pChildVar->m_pszDefaultValue && pParentVar->m_pszDefaultValue &&
pChildVar->IsFlagSet( FCVAR_REPLICATED ) && pParentVar->IsFlagSet( FCVAR_REPLICATED ) )
{
if( Q_stricmp( pChildVar->m_pszDefaultValue, pParentVar->m_pszDefaultValue ) != 0 )
{
Warning( "Parent and child ConVars with different default values! %s child: %s parent: %s (parent wins)\n",
variable->GetName(), pChildVar->m_pszDefaultValue, pParentVar->m_pszDefaultValue );
}
}
const_cast<ConVar*>( pChildVar )->m_pParent = const_cast<ConVar*>( pParentVar )->m_pParent;
// Absorb material thread related convar flags
const_cast<ConVar*>( pParentVar )->m_nFlags |= pChildVar->m_nFlags & ( FCVAR_MATERIAL_THREAD_MASK | FCVAR_ACCESSIBLE_FROM_THREADS );
// check the parent's callbacks and slam if doesn't have, warn if both have callbacks
if( pChildVar->m_fnChangeCallback )
{
if ( !pParentVar->m_fnChangeCallback )
{
const_cast<ConVar*>( pParentVar )->m_fnChangeCallback = pChildVar->m_fnChangeCallback;
}
else
{
Warning( "Convar %s has multiple different change callbacks\n", variable->GetName() );
}
}
// make sure we don't have conflicting help strings.
if ( pChildVar->m_pszHelpString && Q_strlen( pChildVar->m_pszHelpString ) != 0 )
{
if ( pParentVar->m_pszHelpString && Q_strlen( pParentVar->m_pszHelpString ) != 0 )
{
if ( Q_stricmp( pParentVar->m_pszHelpString, pChildVar->m_pszHelpString ) != 0 )
{
Warning( "Convar %s has multiple help strings:\n\tparent (wins): \"%s\"\n\tchild: \"%s\"\n",
variable->GetName(), pParentVar->m_pszHelpString, pChildVar->m_pszHelpString );
}
}
else
{
const_cast<ConVar *>( pParentVar )->m_pszHelpString = pChildVar->m_pszHelpString;
}
}
// make sure we don't have conflicting FCVAR_CHEAT flags.
if ( ( pChildVar->m_nFlags & FCVAR_CHEAT ) != ( pParentVar->m_nFlags & FCVAR_CHEAT ) )
{
Warning( "Convar %s has conflicting FCVAR_CHEAT flags (child: %s, parent: %s, parent wins)\n",
variable->GetName(), ( pChildVar->m_nFlags & FCVAR_CHEAT ) ? "FCVAR_CHEAT" : "no FCVAR_CHEAT",
( pParentVar->m_nFlags & FCVAR_CHEAT ) ? "FCVAR_CHEAT" : "no FCVAR_CHEAT" );
}
// make sure we don't have conflicting FCVAR_REPLICATED flags.
if ( ( pChildVar->m_nFlags & FCVAR_REPLICATED ) != ( pParentVar->m_nFlags & FCVAR_REPLICATED ) )
{
Warning( "Convar %s has conflicting FCVAR_REPLICATED flags (child: %s, parent: %s, parent wins)\n",
variable->GetName(), ( pChildVar->m_nFlags & FCVAR_REPLICATED ) ? "FCVAR_REPLICATED" : "no FCVAR_REPLICATED",
( pParentVar->m_nFlags & FCVAR_REPLICATED ) ? "FCVAR_REPLICATED" : "no FCVAR_REPLICATED" );
}
// make sure we don't have conflicting FCVAR_DONTRECORD flags.
if ( ( pChildVar->m_nFlags & FCVAR_DONTRECORD ) != ( pParentVar->m_nFlags & FCVAR_DONTRECORD ) )
{
Warning( "Convar %s has conflicting FCVAR_DONTRECORD flags (child: %s, parent: %s, parent wins)\n",
variable->GetName(), ( pChildVar->m_nFlags & FCVAR_DONTRECORD ) ? "FCVAR_DONTRECORD" : "no FCVAR_DONTRECORD",
( pParentVar->m_nFlags & FCVAR_DONTRECORD ) ? "FCVAR_DONTRECORD" : "no FCVAR_DONTRECORD" );
}
}
}
variable->m_pNext = NULL;
return;
}
// link the variable in
variable->m_pNext = m_pConCommandList;
m_pConCommandList = variable;
}
void CCvar::UnregisterConCommand( ConCommandBase *pCommandToRemove )
{
// Not registered? Don't bother
if ( !pCommandToRemove->IsRegistered() )
return;
pCommandToRemove->m_bRegistered = false;
// FIXME: Should we make this a doubly-linked list? Would remove faster
ConCommandBase *pPrev = NULL;
for( ConCommandBase *pCommand = m_pConCommandList; pCommand; pCommand = pCommand->m_pNext )
{
if ( pCommand != pCommandToRemove )
{
pPrev = pCommand;
continue;
}
if ( pPrev == NULL )
{
m_pConCommandList = pCommand->m_pNext;
}
else
{
pPrev->m_pNext = pCommand->m_pNext;
}
pCommand->m_pNext = NULL;
break;
}
}
// Crash here in TF2, so I'm adding some debugging stuff.
#ifdef WIN32
#pragma optimize( "", off )
#endif
void CCvar::UnregisterConCommands( CVarDLLIdentifier_t id )
{
ConCommandBase *pNewList;
ConCommandBase *pCommand, *pNext;
int iCommandsLooped = 0;
pNewList = NULL;
pCommand = m_pConCommandList;
while ( pCommand )
{
pNext = pCommand->m_pNext;
if ( pCommand->GetDLLIdentifier() != id )
{
pCommand->m_pNext = pNewList;
pNewList = pCommand;
}
else
{
// Unlink
pCommand->m_bRegistered = false;
pCommand->m_pNext = NULL;
}
pCommand = pNext;
iCommandsLooped++;
}
m_pConCommandList = pNewList;
}
#ifdef WIN32
#pragma optimize( "", on )
#endif
//-----------------------------------------------------------------------------
// Finds base commands
//-----------------------------------------------------------------------------
const ConCommandBase *CCvar::FindCommandBase( const char *name ) const
{
const ConCommandBase *cmd = GetCommands();
for ( ; cmd; cmd = cmd->GetNext() )
{
if ( !Q_stricmp( name, cmd->GetName() ) )
return cmd;
}
return NULL;
}
ConCommandBase *CCvar::FindCommandBase( const char *name )
{
ConCommandBase *cmd = GetCommands();
for ( ; cmd; cmd = cmd->GetNext() )
{
if ( !Q_stricmp( name, cmd->GetName() ) )
return cmd;
}
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose Finds ConVars
//-----------------------------------------------------------------------------
const ConVar *CCvar::FindVar( const char *var_name ) const
{
VPROF_INCREMENT_COUNTER( "CCvar::FindVar", 1 );
VPROF( "CCvar::FindVar" );
const ConCommandBase *var = FindCommandBase( var_name );
if ( !var || var->IsCommand() )
return NULL;
return static_cast<const ConVar*>(var);
}
ConVar *CCvar::FindVar( const char *var_name )
{
VPROF_INCREMENT_COUNTER( "CCvar::FindVar", 1 );
VPROF( "CCvar::FindVar" );
ConCommandBase *var = FindCommandBase( var_name );
if ( !var || var->IsCommand() )
return NULL;
return static_cast<ConVar*>( var );
}
//-----------------------------------------------------------------------------
// Purpose Finds ConCommands
//-----------------------------------------------------------------------------
const ConCommand *CCvar::FindCommand( const char *pCommandName ) const
{
const ConCommandBase *var = FindCommandBase( pCommandName );
if ( !var || !var->IsCommand() )
return NULL;
return static_cast<const ConCommand*>(var);
}
ConCommand *CCvar::FindCommand( const char *pCommandName )
{
ConCommandBase *var = FindCommandBase( pCommandName );
if ( !var || !var->IsCommand() )
return NULL;
return static_cast<ConCommand*>( var );
}
const char* CCvar::GetCommandLineValue( const char *pVariableName )
{
int nLen = Q_strlen(pVariableName);
char *pSearch = (char*)stackalloc( nLen + 2 );
pSearch[0] = '+';
memcpy( &pSearch[1], pVariableName, nLen + 1 );
return CommandLine()->ParmValue( pSearch );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
ConCommandBase *CCvar::GetCommands( void )
{
return m_pConCommandList;
}
const ConCommandBase *CCvar::GetCommands( void ) const
{
return m_pConCommandList;
}
//-----------------------------------------------------------------------------
// Install, remove global callbacks
//-----------------------------------------------------------------------------
void CCvar::InstallGlobalChangeCallback( FnChangeCallback_t callback )
{
Assert( callback && m_GlobalChangeCallbacks.Find( callback ) < 0 );
m_GlobalChangeCallbacks.AddToTail( callback );
}
void CCvar::RemoveGlobalChangeCallback( FnChangeCallback_t callback )
{
Assert( callback );
m_GlobalChangeCallbacks.FindAndRemove( callback );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CCvar::CallGlobalChangeCallbacks( ConVar *var, const char *pOldString, float flOldValue )
{
int nCallbackCount = m_GlobalChangeCallbacks.Count();
for ( int i = 0; i < nCallbackCount; ++i )
{
(*m_GlobalChangeCallbacks[i])( var, pOldString, flOldValue );
}
}
//-----------------------------------------------------------------------------
// Sets convars containing the flags to their default value
//-----------------------------------------------------------------------------
void CCvar::RevertFlaggedConVars( int nFlag )
{
for (const ConCommandBase *var= GetCommands() ; var ; var=var->GetNext())
{
if ( var->IsCommand() )
continue;
ConVar *pCvar = ( ConVar * )var;
if ( !pCvar->IsFlagSet( nFlag ) )
continue;
// It's == to the default value, don't count
if ( !Q_stricmp( pCvar->GetDefault(), pCvar->GetString() ) )
continue;
pCvar->Revert();
// DevMsg( "%s = \"%s\" (reverted)\n", cvar->GetName(), cvar->GetString() );
}
}
//-----------------------------------------------------------------------------
// Deal with queued material system convars
//-----------------------------------------------------------------------------
bool CCvar::IsMaterialThreadSetAllowed( ) const
{
Assert( ThreadInMainThread() );
return m_bMaterialSystemThreadSetAllowed;
}
void CCvar::QueueMaterialThreadSetValue( ConVar *pConVar, const char *pValue )
{
Assert( ThreadInMainThread() );
int j = m_QueuedConVarSets.AddToTail();
m_QueuedConVarSets[j].m_pConVar = pConVar;
m_QueuedConVarSets[j].m_nType = CONVAR_SET_STRING;
m_QueuedConVarSets[j].m_String = pValue;
}
void CCvar::QueueMaterialThreadSetValue( ConVar *pConVar, int nValue )
{
Assert( ThreadInMainThread() );
int j = m_QueuedConVarSets.AddToTail();
m_QueuedConVarSets[j].m_pConVar = pConVar;
m_QueuedConVarSets[j].m_nType = CONVAR_SET_INT;
m_QueuedConVarSets[j].m_nInt = nValue;
}
void CCvar::QueueMaterialThreadSetValue( ConVar *pConVar, float flValue )
{
Assert( ThreadInMainThread() );
int j = m_QueuedConVarSets.AddToTail();
m_QueuedConVarSets[j].m_pConVar = pConVar;
m_QueuedConVarSets[j].m_nType = CONVAR_SET_FLOAT;
m_QueuedConVarSets[j].m_flFloat = flValue;
}
bool CCvar::HasQueuedMaterialThreadConVarSets() const
{
Assert( ThreadInMainThread() );
return m_QueuedConVarSets.Count() > 0;
}
int CCvar::ProcessQueuedMaterialThreadConVarSets()
{
Assert( ThreadInMainThread() );
m_bMaterialSystemThreadSetAllowed = true;
int nUpdateFlags = 0;
int nCount = m_QueuedConVarSets.Count();
for ( int i = 0; i < nCount; ++i )
{
const QueuedConVarSet_t& set = m_QueuedConVarSets[i];
switch( set.m_nType )
{
case CONVAR_SET_FLOAT:
set.m_pConVar->SetValue( set.m_flFloat );
break;
case CONVAR_SET_INT:
set.m_pConVar->SetValue( set.m_nInt );
break;
case CONVAR_SET_STRING:
set.m_pConVar->SetValue( set.m_String );
break;
}
nUpdateFlags |= set.m_pConVar->GetFlags() & FCVAR_MATERIAL_THREAD_MASK;
}
m_QueuedConVarSets.RemoveAll();
m_bMaterialSystemThreadSetAllowed = false;
return nUpdateFlags;
}
//-----------------------------------------------------------------------------
// Display queued messages
//-----------------------------------------------------------------------------
void CCvar::DisplayQueuedMessages( )
{
// Display any queued up messages
if ( m_TempConsoleBuffer.TellPut() == 0 )
return;
Color clr;
int nStringLength;
while( m_TempConsoleBuffer.IsValid() )
{
int nType = m_TempConsoleBuffer.GetChar();
if ( nType == CONSOLE_COLOR_PRINT )
{
clr.SetRawColor( m_TempConsoleBuffer.GetInt() );
}
nStringLength = m_TempConsoleBuffer.PeekStringLength();
char* pTemp = (char*)stackalloc( nStringLength + 1 );
m_TempConsoleBuffer.GetStringManualCharCount( pTemp, nStringLength + 1 );
switch( nType )
{
case CONSOLE_COLOR_PRINT:
ConsoleColorPrintf( clr, pTemp );
break;
case CONSOLE_PRINT:
ConsolePrintf( pTemp );
break;
case CONSOLE_DPRINT:
ConsoleDPrintf( pTemp );
break;
}
}
m_TempConsoleBuffer.Purge();
}
//-----------------------------------------------------------------------------
// Install a console printer
//-----------------------------------------------------------------------------
void CCvar::InstallConsoleDisplayFunc( IConsoleDisplayFunc* pDisplayFunc )
{
Assert( m_DisplayFuncs.Find( pDisplayFunc ) < 0 );
m_DisplayFuncs.AddToTail( pDisplayFunc );
DisplayQueuedMessages();
}
void CCvar::RemoveConsoleDisplayFunc( IConsoleDisplayFunc* pDisplayFunc )
{
m_DisplayFuncs.FindAndRemove( pDisplayFunc );
}
void CCvar::ConsoleColorPrintf( const Color& clr, const char *pFormat, ... ) const
{
char temp[ 8192 ];
va_list argptr;
va_start( argptr, pFormat );
_vsnprintf( temp, sizeof( temp ) - 1, pFormat, argptr );
va_end( argptr );
temp[ sizeof( temp ) - 1 ] = 0;
int c = m_DisplayFuncs.Count();
if ( c == 0 )
{
m_TempConsoleBuffer.PutChar( CONSOLE_COLOR_PRINT );
m_TempConsoleBuffer.PutInt( clr.GetRawColor() );
m_TempConsoleBuffer.PutString( temp );
return;
}
for ( int i = 0 ; i < c; ++i )
{
m_DisplayFuncs[ i ]->ColorPrint( clr, temp );
}
}
void CCvar::ConsolePrintf( const char *pFormat, ... ) const
{
char temp[ 8192 ];
va_list argptr;
va_start( argptr, pFormat );
_vsnprintf( temp, sizeof( temp ) - 1, pFormat, argptr );
va_end( argptr );
temp[ sizeof( temp ) - 1 ] = 0;
int c = m_DisplayFuncs.Count();
if ( c == 0 )
{
m_TempConsoleBuffer.PutChar( CONSOLE_PRINT );
m_TempConsoleBuffer.PutString( temp );
return;
}
for ( int i = 0 ; i < c; ++i )
{
m_DisplayFuncs[ i ]->Print( temp );
}
}
void CCvar::ConsoleDPrintf( const char *pFormat, ... ) const
{
char temp[ 8192 ];
va_list argptr;
va_start( argptr, pFormat );
_vsnprintf( temp, sizeof( temp ) - 1, pFormat, argptr );
va_end( argptr );
temp[ sizeof( temp ) - 1 ] = 0;
int c = m_DisplayFuncs.Count();
if ( c == 0 )
{
m_TempConsoleBuffer.PutChar( CONSOLE_DPRINT );
m_TempConsoleBuffer.PutString( temp );
return;
}
for ( int i = 0 ; i < c; ++i )
{
m_DisplayFuncs[ i ]->DPrint( temp );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
#if defined( _X360 )
void CCvar::PublishToVXConsole()
{
const char *commands[4096];
const char *helptext[4096];
const ConCommandBase *pCur;
int numCommands = 0;
// iterate and publish commands to the remote console
for ( pCur = m_pConCommandList; pCur; pCur=pCur->GetNext() )
{
// add unregistered commands to list
if ( numCommands < sizeof(commands)/sizeof(commands[0]) )
{
commands[numCommands] = pCur->GetName();
helptext[numCommands] = pCur->GetHelpText();
numCommands++;
}
}
if ( numCommands )
{
XBX_rAddCommands( numCommands, commands, helptext );
}
}
#endif
//-----------------------------------------------------------------------------
// Console commands
//-----------------------------------------------------------------------------
void CCvar::Find( const CCommand &args )
{
const char *search;
const ConCommandBase *var;
if ( args.ArgC() != 2 )
{
ConMsg( "Usage: find <string>\n" );
return;
}
// Get substring to find
search = args[1];
// Loop through vars and print out findings
for ( var = GetCommands(); var; var=var->GetNext() )
{
if ( var->IsFlagSet(FCVAR_DEVELOPMENTONLY) || var->IsFlagSet(FCVAR_HIDDEN) )
continue;
if ( !Q_stristr( var->GetName(), search ) &&
!Q_stristr( var->GetHelpText(), search ) )
continue;
ConVar_PrintDescription( var );
}
}

View File

@ -0,0 +1,17 @@
; call cpuid with args in eax, ecx
; store eax, ebx, ecx, edx to p
PUBLIC GetStackPtr64
.CODE
GetStackPtr64 PROC FRAME
; unsigned char* GetStackPtr64(void);
.endprolog
mov rax, rsp ; get stack ptr
add rax, 8h ; account for 8-byte return value of this function
ret
GetStackPtr64 ENDP
_TEXT ENDS
END

1457
vstdlib/jobthread.cpp Normal file

File diff suppressed because it is too large Load Diff

427
vstdlib/osversion.cpp Normal file
View File

@ -0,0 +1,427 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
#include "vstdlib/osversion.h"
#include "winlite.h"
#include "strtools.h"
#include "tier0/dbg.h"
#ifdef OSX
#include <CoreServices/CoreServices.h>
#endif
//-----------------------------------------------------------------------------
// Purpose: return the OS type for this machine
//-----------------------------------------------------------------------------
EOSType GetOSType()
{
static EOSType eOSVersion = k_eOSUnknown;
#if defined( _WIN32 ) && !defined( _X360 )
if ( eOSVersion == k_eOSUnknown || eOSVersion == k_eWinUnknown )
{
eOSVersion = k_eWinUnknown;
OSVERSIONINFOEX osvi;
Q_memset( &osvi, 0x00, sizeof(osvi) );
osvi.dwOSVersionInfoSize = sizeof(osvi);
if ( GetVersionEx( (OSVERSIONINFO *) &osvi ) )
{
switch ( osvi.dwPlatformId )
{
case VER_PLATFORM_WIN32_NT:
if ( osvi.dwMajorVersion <= 4 )
{
eOSVersion = k_eWinNT;
}
else if ( osvi.dwMajorVersion == 5 )
{
switch( osvi.dwMinorVersion )
{
case 0:
eOSVersion = k_eWin2000;
break;
case 1:
eOSVersion = k_eWinXP;
break;
case 2:
eOSVersion = k_eWin2003;
break;
}
}
else if ( osvi.dwMajorVersion >= 6 )
{
if ( osvi.wProductType == VER_NT_WORKSTATION )
{
switch ( osvi.dwMinorVersion )
{
case 0:
eOSVersion = k_eWinVista;
break;
case 1:
eOSVersion = k_eWindows7;
break;
}
}
else /* ( osvi.wProductType != VER_NT_WORKSTATION ) */
{
switch ( osvi.dwMinorVersion )
{
case 0:
eOSVersion = k_eWin2008; // Windows 2008, not R2
break;
case 1:
eOSVersion = k_eWin2008; // Windows 2008 R2
break;
}
}
}
break;
case VER_PLATFORM_WIN32_WINDOWS:
switch ( osvi.dwMinorVersion )
{
case 0:
eOSVersion = k_eWin95;
break;
case 10:
eOSVersion = k_eWin98;
break;
case 90:
eOSVersion = k_eWinME;
break;
}
break;
case VER_PLATFORM_WIN32s:
eOSVersion = k_eWin311;
break;
}
}
}
#elif defined(OSX)
if ( eOSVersion == k_eOSUnknown )
{
SInt32 MajorVer = 0;
SInt32 MinorVer = 0;
SInt32 PatchVer = 0;
OSErr err = noErr;
err = Gestalt( gestaltSystemVersionMajor, &MajorVer );
if ( err != noErr )
return k_eOSUnknown;
err = Gestalt( gestaltSystemVersionMinor, &MinorVer );
if ( err != noErr )
return k_eOSUnknown;
err = Gestalt( gestaltSystemVersionBugFix, &PatchVer );
if ( err != noErr )
return k_eOSUnknown;
switch ( MajorVer )
{
case 10:
{
switch( MinorVer )
{
case 4:
eOSVersion = k_eMacOS104;
break;
case 5:
eOSVersion = k_eMacOS105;
switch ( PatchVer )
{
case 8:
eOSVersion = k_eMacOS1058;
default:
break;
}
break;
case 6:
eOSVersion = k_eMacOS106;
switch ( PatchVer )
{
case 1:
case 2:
break;
case 3:
default:
// note the default here - 10.6.4 (5,6...) >= 10.6.3, so we want to
// identify as 10.6.3 for sysreqs purposes
eOSVersion = k_eMacOS1063;
break;
}
break;
case 7:
eOSVersion = k_eMacOS107;
break;
default:
break;
}
}
default:
break;
}
}
#elif defined(LINUX)
if ( eOSVersion == k_eOSUnknown )
{
FILE *fpKernelVer = fopen( "/proc/version", "r" );
if ( !fpKernelVer )
return k_eLinuxUnknown;
char rgchVersionLine[1024];
char *pchRet = fgets( rgchVersionLine, sizeof(rgchVersionLine), fpKernelVer );
fclose( fpKernelVer );
eOSVersion = k_eLinuxUnknown;
// move past "Linux version "
const char *pchVersion = rgchVersionLine + Q_strlen( "Linux version " );
if ( pchRet && *pchVersion == '2' && *(pchVersion+1) == '.' )
{
pchVersion += 2; // move past "2."
if ( *pchVersion == '2' && *(pchVersion+1) == '.' )
eOSVersion = k_eLinux22;
else if ( *pchVersion == '4' && *(pchVersion+1) == '.' )
eOSVersion = k_eLinux24;
else if ( *pchVersion == '6' && *(pchVersion+1) == '.' )
eOSVersion = k_eLinux26;
}
}
#endif
return eOSVersion;
}
//-----------------------------------------------------------------------------
// Purpose: get platform-specific OS details (distro, on linux)
// returns a pointer to the input buffer on success (for convenience),
// NULL on failure.
//-----------------------------------------------------------------------------
const char *GetOSDetailString( char *pchOutBuf, int cchOutBuf )
{
#if defined WIN32
(void)( pchOutBuf );
(void)( cchOutBuf );
// no interesting details
return NULL;
#else
#if defined LINUX
// we're about to go poking around to see if we can figure out distribution
// looking @ any /etc file is fragile (people can change 'em),
// but since this is just hardware survey data, we're not super concerned.
// a bunch of OS-specific issue files
const char *pszIssueFile[] =
{
"/etc/redhat-release",
"/etc/fedora-release",
"/etc/slackware-release",
"/etc/debian_release",
"/etc/mandrake-release",
"/etc/yellowdog-release",
"/etc/gentoo-release",
"/etc/lsb-release",
"/etc/SUSE-release",
};
if ( !pchOutBuf )
return NULL;
for (int i = 0; i < Q_ARRAYSIZE( pszIssueFile ); i++ )
{
FILE *fdInfo = fopen( pszIssueFile[i], "r" );
if ( !fdInfo )
continue;
// prepend the buffer with the name of the file we found for easier grouping
snprintf( pchOutBuf, cchOutBuf, "%s\n", pszIssueFile[i] );
int cchIssueFile = strlen( pszIssueFile[i] ) + 1;
ssize_t cubRead = fread( (void*) (pchOutBuf + cchIssueFile) , sizeof(char), cchOutBuf - cchIssueFile, fdInfo );
fclose( fdInfo );
if ( cubRead < 0 )
return NULL;
// null terminate
pchOutBuf[ MIN( cubRead, cchOutBuf-1 ) ] = '\0';
return pchOutBuf;
}
#endif
// if all else fails, just send back uname -a
if ( !pchOutBuf )
return NULL;
FILE *fpUname = popen( "uname -mrsv", "r" );
if ( !fpUname )
return NULL;
size_t cchRead = fread( pchOutBuf, sizeof(char), cchOutBuf, fpUname );
pclose( fpUname );
pchOutBuf[ MIN( cchRead, (size_t)cchOutBuf-1 ) ] = '\0';
return pchOutBuf;
#endif
}
//-----------------------------------------------------------------------------
// Purpose: get a friendly name for an OS type
//-----------------------------------------------------------------------------
const char *GetNameFromOSType( EOSType eOSType )
{
switch ( eOSType )
{
case k_eWinUnknown:
return "Windows";
case k_eWin311:
return "Windows 3.11";
case k_eWin95:
return "Windows 95";
case k_eWin98:
return "Windows 98";
case k_eWinME:
return "Windows ME";
case k_eWinNT:
return "Windows NT";
case k_eWin2000:
return "Windows 2000";
case k_eWinXP:
return "Windows XP";
case k_eWin2003:
return "Windows 2003";
case k_eWinVista:
return "Windows Vista";
case k_eWindows7:
return "Windows 7";
case k_eWin2008:
return "Windows 2008";
#ifdef POSIX
case k_eMacOSUnknown:
return "Mac OS";
case k_eMacOS104:
return "MacOS 10.4";
case k_eMacOS105:
return "MacOS 10.5";
case k_eMacOS1058:
return "MacOS 10.5.8";
case k_eMacOS106:
return "MacOS 10.6";
case k_eMacOS1063:
return "MacOS 10.6.3";
case k_eMacOS107:
return "MacOS 10.7";
case k_eLinuxUnknown:
return "Linux";
case k_eLinux22:
return "Linux 2.2";
case k_eLinux24:
return "Linux 2.4";
case k_eLinux26:
return "Linux 2.6";
#endif
default:
case k_eOSUnknown:
return "Unknown";
}
}
// friendly name to OS type, MUST be same size as EOSType enum
struct OSTypeNameTuple
{
EOSType m_OSType;
const char *m_pchOSName;
};
const OSTypeNameTuple k_rgOSTypeToName[] =
{
{ k_eOSUnknown, "unknown" },
{ k_eMacOSUnknown, "macos" },
{ k_eMacOS104, "macos104" },
{ k_eMacOS105, "macos105" },
{ k_eMacOS1058, "macos1058" },
{ k_eMacOS106, "macos106" },
{ k_eMacOS1063, "macos1063" },
{ k_eMacOS107, "macos107" },
{ k_eLinuxUnknown, "linux" },
{ k_eLinux22, "linux22" },
{ k_eLinux24, "linux24" },
{ k_eLinux26, "linux26" },
{ k_eWinUnknown, "windows" },
{ k_eWin311, "win311" },
{ k_eWin95, "win95" },
{ k_eWin98, "win98" },
{ k_eWinME, "winME" },
{ k_eWinNT, "winNT" },
{ k_eWin2000, "win200" },
{ k_eWinXP, "winXP" },
{ k_eWin2003, "win2003" },
{ k_eWinVista, "winVista" },
{ k_eWindows7, "win7" },
{ k_eWin2008, "win2008" },
};
//-----------------------------------------------------------------------------
// Purpose: convert a friendly OS name to a eostype
//-----------------------------------------------------------------------------
EOSType GetOSTypeFromString_Deprecated( const char *pchName )
{
EOSType eOSType;
#ifdef WIN32
eOSType = k_eWinUnknown;
#else
eOSType = k_eOSUnknown;
#endif
// if this fires, make sure all OS types are in the map
Assert( Q_ARRAYSIZE( k_rgOSTypeToName ) == k_eOSTypeMax );
if ( !pchName || Q_strlen( pchName ) == 0 )
return eOSType;
for ( int iOS = 0; iOS < Q_ARRAYSIZE( k_rgOSTypeToName ) ; iOS++ )
{
if ( !Q_stricmp( k_rgOSTypeToName[iOS].m_pchOSName, pchName ) )
return k_rgOSTypeToName[iOS].m_OSType;
}
return eOSType;
}
bool OSTypesAreCompatible( EOSType eOSTypeDetected, EOSType eOSTypeRequired )
{
// check windows (on the positive side of the number line)
if ( eOSTypeRequired >= k_eWinUnknown )
return ( eOSTypeDetected >= eOSTypeRequired );
if ( eOSTypeRequired == k_eOSUnknown )
return true;
// osx
if ( eOSTypeRequired >= k_eMacOSUnknown && eOSTypeRequired < k_eOSUnknown )
return ( eOSTypeDetected >= eOSTypeRequired && eOSTypeDetected < k_eOSUnknown );
// and linux
if ( eOSTypeRequired >= k_eLinuxUnknown && eOSTypeRequired < k_eMacOSUnknown )
return ( eOSTypeDetected >= eOSTypeRequired && eOSTypeDetected < k_eMacOSUnknown );
return false;
}
// these strings "windows", "macos", "linux" are part of the
// interface, which is why they're hard-coded here, rather than using
// the strings in k_rgOSTypeToName
const char *GetPlatformName( bool *pbIs64Bit )
{
if ( pbIs64Bit )
*pbIs64Bit = Is64BitOS();
EOSType eType = GetOSType();
if ( OSTypesAreCompatible( eType, k_eWinUnknown ) )
return "windows";
if ( OSTypesAreCompatible( eType, k_eMacOSUnknown ) )
return "macos";
if ( OSTypesAreCompatible( eType, k_eLinuxUnknown ) )
return "linux";
return "unknown";
}

473
vstdlib/processutils.cpp Normal file
View File

@ -0,0 +1,473 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//===========================================================================//
#if !defined( _X360 )
#include <windows.h>
#endif
#include "vstdlib/iprocessutils.h"
#include "tier1/utllinkedlist.h"
#include "tier1/utlstring.h"
#include "tier1/utlbuffer.h"
#include "tier1/tier1.h"
//-----------------------------------------------------------------------------
// At the moment, we can only run one process at a time
//-----------------------------------------------------------------------------
class CProcessUtils : public CTier1AppSystem< IProcessUtils >
{
typedef CTier1AppSystem< IProcessUtils > BaseClass;
public:
CProcessUtils() : BaseClass( false ) {}
// Inherited from IAppSystem
virtual InitReturnVal_t Init();
virtual void Shutdown();
// Inherited from IProcessUtils
virtual ProcessHandle_t StartProcess( const char *pCommandLine, bool bConnectStdPipes );
virtual ProcessHandle_t StartProcess( int argc, const char **argv, bool bConnectStdPipes );
virtual void CloseProcess( ProcessHandle_t hProcess );
virtual void AbortProcess( ProcessHandle_t hProcess );
virtual bool IsProcessComplete( ProcessHandle_t hProcess );
virtual void WaitUntilProcessCompletes( ProcessHandle_t hProcess );
virtual int SendProcessInput( ProcessHandle_t hProcess, char *pBuf, int nBufLen );
virtual int GetProcessOutputSize( ProcessHandle_t hProcess );
virtual int GetProcessOutput( ProcessHandle_t hProcess, char *pBuf, int nBufLen );
virtual int GetProcessExitCode( ProcessHandle_t hProcess );
private:
struct ProcessInfo_t
{
HANDLE m_hChildStdinRd;
HANDLE m_hChildStdinWr;
HANDLE m_hChildStdoutRd;
HANDLE m_hChildStdoutWr;
HANDLE m_hChildStderrWr;
HANDLE m_hProcess;
CUtlString m_CommandLine;
CUtlBuffer m_ProcessOutput;
};
// Returns the last error that occurred
char *GetErrorString( char *pBuf, int nBufLen );
// creates the process, adds it to the list and writes the windows HANDLE into info.m_hProcess
ProcessHandle_t CreateProcess( ProcessInfo_t &info, bool bConnectStdPipes );
// Shuts down the process handle
void ShutdownProcess( ProcessHandle_t hProcess );
// Methods used to read output back from a process
int GetActualProcessOutputSize( ProcessHandle_t hProcess );
int GetActualProcessOutput( ProcessHandle_t hProcess, char *pBuf, int nBufLen );
CUtlFixedLinkedList< ProcessInfo_t > m_Processes;
ProcessHandle_t m_hCurrentProcess;
bool m_bInitialized;
};
//-----------------------------------------------------------------------------
// Purpose: singleton accessor
//-----------------------------------------------------------------------------
static CProcessUtils s_ProcessUtils;
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CProcessUtils, IProcessUtils, PROCESS_UTILS_INTERFACE_VERSION, s_ProcessUtils );
//-----------------------------------------------------------------------------
// Initialize, shutdown process system
//-----------------------------------------------------------------------------
InitReturnVal_t CProcessUtils::Init()
{
InitReturnVal_t nRetVal = BaseClass::Init();
if ( nRetVal != INIT_OK )
return nRetVal;
m_bInitialized = true;
m_hCurrentProcess = PROCESS_HANDLE_INVALID;
return INIT_OK;
}
void CProcessUtils::Shutdown()
{
Assert( m_bInitialized );
Assert( m_Processes.Count() == 0 );
if ( m_Processes.Count() != 0 )
{
AbortProcess( m_hCurrentProcess );
}
m_bInitialized = false;
return BaseClass::Shutdown();
}
//-----------------------------------------------------------------------------
// Returns the last error that occurred
//-----------------------------------------------------------------------------
char *CProcessUtils::GetErrorString( char *pBuf, int nBufLen )
{
FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0, pBuf, nBufLen, NULL );
char *p = strchr(pBuf, '\r'); // get rid of \r\n
if(p)
{
p[0] = 0;
}
return pBuf;
}
ProcessHandle_t CProcessUtils::CreateProcess( ProcessInfo_t &info, bool bConnectStdPipes )
{
STARTUPINFO si;
memset(&si, 0, sizeof si);
si.cb = sizeof(si);
if ( bConnectStdPipes )
{
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdInput = info.m_hChildStdinRd;
si.hStdError = info.m_hChildStderrWr;
si.hStdOutput = info.m_hChildStdoutWr;
}
PROCESS_INFORMATION pi;
if ( ::CreateProcess( NULL, info.m_CommandLine.GetForModify(), NULL, NULL, TRUE, DETACHED_PROCESS, NULL, NULL, &si, &pi ) )
{
info.m_hProcess = pi.hProcess;
m_hCurrentProcess = m_Processes.AddToTail( info );
return m_hCurrentProcess;
}
char buf[ 512 ];
Warning( "Could not execute the command:\n %s\n"
"Windows gave the error message:\n \"%s\"\n",
info.m_CommandLine.Get(), GetErrorString( buf, sizeof(buf) ) );
return PROCESS_HANDLE_INVALID;
}
//-----------------------------------------------------------------------------
// Options for compilation
//-----------------------------------------------------------------------------
ProcessHandle_t CProcessUtils::StartProcess( const char *pCommandLine, bool bConnectStdPipes )
{
Assert( m_bInitialized );
// NOTE: For the moment, we can only run one process at a time
// although in the future, I expect to have a process queue.
if ( m_hCurrentProcess != PROCESS_HANDLE_INVALID )
{
WaitUntilProcessCompletes( m_hCurrentProcess );
}
ProcessInfo_t info;
info.m_CommandLine = pCommandLine;
if ( !bConnectStdPipes )
{
info.m_hChildStderrWr = INVALID_HANDLE_VALUE;
info.m_hChildStdinRd = info.m_hChildStdinWr = INVALID_HANDLE_VALUE;
info.m_hChildStdoutRd = info.m_hChildStdoutWr = INVALID_HANDLE_VALUE;
return CreateProcess( info, false );
}
SECURITY_ATTRIBUTES saAttr;
// Set the bInheritHandle flag so pipe handles are inherited.
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
// Create a pipe for the child's STDOUT.
if ( CreatePipe( &info.m_hChildStdoutRd, &info.m_hChildStdoutWr, &saAttr, 0 ) )
{
if ( CreatePipe( &info.m_hChildStdinRd, &info.m_hChildStdinWr, &saAttr, 0 ) )
{
if ( DuplicateHandle( GetCurrentProcess(), info.m_hChildStdoutWr, GetCurrentProcess(),
&info.m_hChildStderrWr, 0, TRUE, DUPLICATE_SAME_ACCESS ) )
{
// _setmode( info.m_hChildStdoutRd, _O_TEXT );
// _setmode( info.m_hChildStdoutWr, _O_TEXT );
// _setmode( info.m_hChildStderrWr, _O_TEXT );
ProcessHandle_t hProcess = CreateProcess( info, true );
if ( hProcess != PROCESS_HANDLE_INVALID )
return hProcess;
CloseHandle( info.m_hChildStderrWr );
}
CloseHandle( info.m_hChildStdinRd );
CloseHandle( info.m_hChildStdinWr );
}
CloseHandle( info.m_hChildStdoutRd );
CloseHandle( info.m_hChildStdoutWr );
}
return PROCESS_HANDLE_INVALID;
}
//-----------------------------------------------------------------------------
// Start up a process
//-----------------------------------------------------------------------------
ProcessHandle_t CProcessUtils::StartProcess( int argc, const char **argv, bool bConnectStdPipes )
{
CUtlString commandLine;
for ( int i = 0; i < argc; ++i )
{
commandLine += argv[i];
if ( i != argc-1 )
{
commandLine += " ";
}
}
return StartProcess( commandLine.Get(), bConnectStdPipes );
}
//-----------------------------------------------------------------------------
// Shuts down the process handle
//-----------------------------------------------------------------------------
void CProcessUtils::ShutdownProcess( ProcessHandle_t hProcess )
{
ProcessInfo_t& info = m_Processes[hProcess];
CloseHandle( info.m_hChildStderrWr );
CloseHandle( info.m_hChildStdinRd );
CloseHandle( info.m_hChildStdinWr );
CloseHandle( info.m_hChildStdoutRd );
CloseHandle( info.m_hChildStdoutWr );
m_Processes.Remove( hProcess );
}
//-----------------------------------------------------------------------------
// Closes the process
//-----------------------------------------------------------------------------
void CProcessUtils::CloseProcess( ProcessHandle_t hProcess )
{
Assert( m_bInitialized );
if ( hProcess != PROCESS_HANDLE_INVALID )
{
WaitUntilProcessCompletes( hProcess );
ShutdownProcess( hProcess );
}
}
//-----------------------------------------------------------------------------
// Aborts the process
//-----------------------------------------------------------------------------
void CProcessUtils::AbortProcess( ProcessHandle_t hProcess )
{
Assert( m_bInitialized );
if ( hProcess != PROCESS_HANDLE_INVALID )
{
if ( !IsProcessComplete( hProcess ) )
{
ProcessInfo_t& info = m_Processes[hProcess];
TerminateProcess( info.m_hProcess, 1 );
}
ShutdownProcess( hProcess );
}
}
//-----------------------------------------------------------------------------
// Returns true if the process is complete
//-----------------------------------------------------------------------------
bool CProcessUtils::IsProcessComplete( ProcessHandle_t hProcess )
{
Assert( m_bInitialized );
Assert( hProcess != PROCESS_HANDLE_INVALID );
if ( m_hCurrentProcess != hProcess )
return true;
HANDLE h = m_Processes[hProcess].m_hProcess;
return ( WaitForSingleObject( h, 0 ) != WAIT_TIMEOUT );
}
//-----------------------------------------------------------------------------
// Methods used to write input into a process
//-----------------------------------------------------------------------------
int CProcessUtils::SendProcessInput( ProcessHandle_t hProcess, char *pBuf, int nBufLen )
{
// Unimplemented yet
Assert( 0 );
return 0;
}
//-----------------------------------------------------------------------------
// Methods used to read output back from a process
//-----------------------------------------------------------------------------
int CProcessUtils::GetActualProcessOutputSize( ProcessHandle_t hProcess )
{
Assert( hProcess != PROCESS_HANDLE_INVALID );
ProcessInfo_t& info = m_Processes[ hProcess ];
if ( info.m_hChildStdoutRd == INVALID_HANDLE_VALUE )
return 0;
DWORD dwCount = 0;
if ( !PeekNamedPipe( info.m_hChildStdoutRd, NULL, NULL, NULL, &dwCount, NULL ) )
{
char buf[ 512 ];
Warning( "Could not read from pipe associated with command %s\n"
"Windows gave the error message:\n \"%s\"\n",
info.m_CommandLine.Get(), GetErrorString( buf, sizeof(buf) ) );
return 0;
}
// Add 1 for auto-NULL termination
return ( dwCount > 0 ) ? (int)dwCount + 1 : 0;
}
int CProcessUtils::GetActualProcessOutput( ProcessHandle_t hProcess, char *pBuf, int nBufLen )
{
ProcessInfo_t& info = m_Processes[ hProcess ];
if ( info.m_hChildStdoutRd == INVALID_HANDLE_VALUE )
return 0;
DWORD dwCount = 0;
DWORD dwRead = 0;
// FIXME: Is there a way of making pipes be text mode so we don't get /n/rs back?
char *pTempBuf = (char*)_alloca( nBufLen );
if ( !PeekNamedPipe( info.m_hChildStdoutRd, NULL, NULL, NULL, &dwCount, NULL ) )
{
char buf[ 512 ];
Warning( "Could not read from pipe associated with command %s\n"
"Windows gave the error message:\n \"%s\"\n",
info.m_CommandLine.Get(), GetErrorString( buf, sizeof(buf) ) );
return 0;
}
dwCount = min( dwCount, (DWORD)nBufLen - 1 );
ReadFile( info.m_hChildStdoutRd, pTempBuf, dwCount, &dwRead, NULL);
// Convert /n/r -> /n
int nActualCountRead = 0;
for ( unsigned int i = 0; i < dwRead; ++i )
{
char c = pTempBuf[i];
if ( c == '\r' )
{
if ( ( i+1 < dwRead ) && ( pTempBuf[i+1] == '\n' ) )
{
pBuf[nActualCountRead++] = '\n';
++i;
continue;
}
}
pBuf[nActualCountRead++] = c;
}
return nActualCountRead;
}
int CProcessUtils::GetProcessOutputSize( ProcessHandle_t hProcess )
{
Assert( m_bInitialized );
if ( hProcess == PROCESS_HANDLE_INVALID )
return 0;
return GetActualProcessOutputSize( hProcess ) + m_Processes[hProcess].m_ProcessOutput.TellPut();
}
int CProcessUtils::GetProcessOutput( ProcessHandle_t hProcess, char *pBuf, int nBufLen )
{
Assert( m_bInitialized );
if ( hProcess == PROCESS_HANDLE_INVALID )
return 0;
ProcessInfo_t &info = m_Processes[hProcess];
int nCachedBytes = info.m_ProcessOutput.TellPut();
int nBytesRead = 0;
if ( nCachedBytes )
{
nBytesRead = min( nBufLen-1, nCachedBytes );
info.m_ProcessOutput.Get( pBuf, nBytesRead );
pBuf[ nBytesRead ] = 0;
nBufLen -= nBytesRead;
pBuf += nBytesRead;
if ( info.m_ProcessOutput.GetBytesRemaining() == 0 )
{
info.m_ProcessOutput.Purge();
}
if ( nBufLen <= 1 )
return nBytesRead;
}
// Auto-NULL terminate
int nActualCountRead = GetActualProcessOutput( hProcess, pBuf, nBufLen );
pBuf[nActualCountRead] = 0;
return nActualCountRead + nBytesRead + 1;
}
//-----------------------------------------------------------------------------
// Returns the exit code for the process. Doesn't work unless the process is complete
//-----------------------------------------------------------------------------
int CProcessUtils::GetProcessExitCode( ProcessHandle_t hProcess )
{
Assert( m_bInitialized );
ProcessInfo_t &info = m_Processes[hProcess];
DWORD nExitCode;
BOOL bOk = GetExitCodeProcess( info.m_hProcess, &nExitCode );
if ( !bOk || nExitCode == STILL_ACTIVE )
return -1;
return nExitCode;
}
//-----------------------------------------------------------------------------
// Waits until a process is complete
//-----------------------------------------------------------------------------
void CProcessUtils::WaitUntilProcessCompletes( ProcessHandle_t hProcess )
{
Assert( m_bInitialized );
// For the moment, we can only run one process at a time
if ( ( hProcess == PROCESS_HANDLE_INVALID ) || ( m_hCurrentProcess != hProcess ) )
return;
ProcessInfo_t &info = m_Processes[ hProcess ];
if ( info.m_hChildStdoutRd == INVALID_HANDLE_VALUE )
{
WaitForSingleObject( info.m_hProcess, INFINITE );
}
else
{
// NOTE: The called process can block during writes to stderr + stdout
// if the pipe buffer is empty. Therefore, waiting INFINITE is not
// possible here. We must queue up messages received to allow the
// process to continue
while ( WaitForSingleObject( info.m_hProcess, 100 ) == WAIT_TIMEOUT )
{
int nLen = GetActualProcessOutputSize( hProcess );
if ( nLen > 0 )
{
int nPut = info.m_ProcessOutput.TellPut();
info.m_ProcessOutput.EnsureCapacity( nPut + nLen );
int nBytesRead = GetActualProcessOutput( hProcess, (char*)info.m_ProcessOutput.PeekPut(), nLen );
info.m_ProcessOutput.SeekPut( CUtlBuffer::SEEK_HEAD, nPut + nBytesRead );
}
}
}
m_hCurrentProcess = PROCESS_HANDLE_INVALID;
}

265
vstdlib/random.cpp Normal file
View File

@ -0,0 +1,265 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Random number generator
//
// $Workfile: $
// $NoKeywords: $
//===========================================================================//
#include "vstdlib/random.h"
#include <math.h>
#include "dbg.h"
#include "tier0/memdbgon.h"
#define IA 16807
#define IM 2147483647
#define IQ 127773
#define IR 2836
#define NDIV (1+(IM-1)/NTAB)
#define MAX_RANDOM_RANGE 0x7FFFFFFFUL
// fran1 -- return a random floating-point number on the interval [0,1)
//
#define AM (1.0/IM)
#define EPS 1.2e-7
#define RNMX (1.0-EPS)
//-----------------------------------------------------------------------------
// globals
//-----------------------------------------------------------------------------
static CUniformRandomStream s_UniformStream;
static CGaussianRandomStream s_GaussianStream;
static IUniformRandomStream *s_pUniformStream = &s_UniformStream;
//-----------------------------------------------------------------------------
// Installs a global random number generator, which will affect the Random functions above
//-----------------------------------------------------------------------------
void InstallUniformRandomStream( IUniformRandomStream *pStream )
{
s_pUniformStream = pStream ? pStream : &s_UniformStream;
}
//-----------------------------------------------------------------------------
// A couple of convenience functions to access the library's global uniform stream
//-----------------------------------------------------------------------------
void RandomSeed( int iSeed )
{
s_pUniformStream->SetSeed( iSeed );
}
float RandomFloat( float flMinVal, float flMaxVal )
{
return s_pUniformStream->RandomFloat( flMinVal, flMaxVal );
}
float RandomFloatExp( float flMinVal, float flMaxVal, float flExponent )
{
return s_pUniformStream->RandomFloatExp( flMinVal, flMaxVal, flExponent );
}
int RandomInt( int iMinVal, int iMaxVal )
{
return s_pUniformStream->RandomInt( iMinVal, iMaxVal );
}
float RandomGaussianFloat( float flMean, float flStdDev )
{
return s_GaussianStream.RandomFloat( flMean, flStdDev );
}
//-----------------------------------------------------------------------------
//
// Implementation of the uniform random number stream
//
//-----------------------------------------------------------------------------
CUniformRandomStream::CUniformRandomStream()
{
SetSeed(0);
}
void CUniformRandomStream::SetSeed( int iSeed )
{
AUTO_LOCK( m_mutex );
m_idum = ( ( iSeed < 0 ) ? iSeed : -iSeed );
m_iy = 0;
}
int CUniformRandomStream::GenerateRandomNumber()
{
AUTO_LOCK( m_mutex );
int j;
int k;
if (m_idum <= 0 || !m_iy)
{
if (-(m_idum) < 1)
m_idum=1;
else
m_idum = -(m_idum);
for ( j=NTAB+7; j>=0; j--)
{
k = (m_idum)/IQ;
m_idum = IA*(m_idum-k*IQ)-IR*k;
if (m_idum < 0)
m_idum += IM;
if (j < NTAB)
m_iv[j] = m_idum;
}
m_iy=m_iv[0];
}
k=(m_idum)/IQ;
m_idum=IA*(m_idum-k*IQ)-IR*k;
if (m_idum < 0)
m_idum += IM;
j=m_iy/NDIV;
// We're seeing some strange memory corruption in the contents of s_pUniformStream.
// Perhaps it's being caused by something writing past the end of this array?
// Bounds-check in release to see if that's the case.
if (j >= NTAB || j < 0)
{
DebuggerBreakIfDebugging();
Warning("CUniformRandomStream had an array overrun: tried to write to element %d of 0..31. Contact Tom or Elan.\n", j);
// Ensure that NTAB is a power of two.
COMPILE_TIME_ASSERT( ( NTAB & ( NTAB - 1 ) ) == 0 );
// Clamp j.
j &= NTAB - 1;
}
m_iy=m_iv[j];
m_iv[j] = m_idum;
return m_iy;
}
float CUniformRandomStream::RandomFloat( float flLow, float flHigh )
{
// float in [0,1)
float fl = AM * GenerateRandomNumber();
if (fl > RNMX)
{
fl = RNMX;
}
return (fl * ( flHigh - flLow ) ) + flLow; // float in [low,high)
}
float CUniformRandomStream::RandomFloatExp( float flMinVal, float flMaxVal, float flExponent )
{
// float in [0,1)
float fl = AM * GenerateRandomNumber();
if (fl > RNMX)
{
fl = RNMX;
}
if ( flExponent != 1.0f )
{
fl = powf( fl, flExponent );
}
return (fl * ( flMaxVal - flMinVal ) ) + flMinVal; // float in [low,high)
}
int CUniformRandomStream::RandomInt( int iLow, int iHigh )
{
//ASSERT(lLow <= lHigh);
unsigned int maxAcceptable;
unsigned int x = iHigh-iLow+1;
unsigned int n;
// If you hit either of these assert, you're not getting back the random number that you thought you were.
Assert( x == iHigh-(int64)iLow+1 ); // Check that we didn't overflow int
Assert( x-1 <= MAX_RANDOM_RANGE ); // Check that the values provide an acceptable range
if (x <= 1 || MAX_RANDOM_RANGE < x-1)
{
Assert( iLow == iHigh ); // This is the only time it is OK to have a range containing a single number
return iLow;
}
// The following maps a uniform distribution on the interval [0,MAX_RANDOM_RANGE]
// to a smaller, client-specified range of [0,x-1] in a way that doesn't bias
// the uniform distribution unfavorably. Even for a worst case x, the loop is
// guaranteed to be taken no more than half the time, so for that worst case x,
// the average number of times through the loop is 2. For cases where x is
// much smaller than MAX_RANDOM_RANGE, the average number of times through the
// loop is very close to 1.
//
maxAcceptable = MAX_RANDOM_RANGE - ((MAX_RANDOM_RANGE+1) % x );
do
{
n = GenerateRandomNumber();
} while (n > maxAcceptable);
return iLow + (n % x);
}
//-----------------------------------------------------------------------------
//
// Implementation of the gaussian random number stream
// We're gonna use the Box-Muller method (which actually generates 2
// gaussian-distributed numbers at once)
//
//-----------------------------------------------------------------------------
CGaussianRandomStream::CGaussianRandomStream( IUniformRandomStream *pUniformStream )
{
AttachToStream( pUniformStream );
}
//-----------------------------------------------------------------------------
// Attaches to a random uniform stream
//-----------------------------------------------------------------------------
void CGaussianRandomStream::AttachToStream( IUniformRandomStream *pUniformStream )
{
AUTO_LOCK( m_mutex );
m_pUniformStream = pUniformStream;
m_bHaveValue = false;
}
//-----------------------------------------------------------------------------
// Generates random numbers
//-----------------------------------------------------------------------------
float CGaussianRandomStream::RandomFloat( float flMean, float flStdDev )
{
AUTO_LOCK( m_mutex );
IUniformRandomStream *pUniformStream = m_pUniformStream ? m_pUniformStream : s_pUniformStream;
float fac,rsq,v1,v2;
if (!m_bHaveValue)
{
// Pick 2 random #s from -1 to 1
// Make sure they lie inside the unit circle. If they don't, try again
do
{
v1 = 2.0f * pUniformStream->RandomFloat() - 1.0f;
v2 = 2.0f * pUniformStream->RandomFloat() - 1.0f;
rsq = v1*v1 + v2*v2;
} while ((rsq > 1.0f) || (rsq == 0.0f));
// The box-muller transformation to get the two gaussian numbers
fac = sqrtf( -2.0f * log(rsq) / rsq );
// Store off one value for later use
m_flRandomValue = v1 * fac;
m_bHaveValue = true;
return flStdDev * (v2 * fac) + flMean;
}
else
{
m_bHaveValue = false;
return flStdDev * m_flRandomValue + flMean;
}
}
//-----------------------------------------------------------------------------
// Creates a histogram (for testing)
//-----------------------------------------------------------------------------

9
vstdlib/vcover.cpp Normal file
View File

@ -0,0 +1,9 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================
#include "vstdlib/vcover.h"
CVCoverage g_VCoverage;

157
vstdlib/vstdlib.vpc Normal file
View File

@ -0,0 +1,157 @@
//-----------------------------------------------------------------------------
// VSTDLIB.VPC
//
// Project Script
//-----------------------------------------------------------------------------
$Macro SRCDIR ".."
$Macro OUTBINDIR "$SRCDIR\..\game\bin"
$include "$SRCDIR\vpc_scripts\source_dll_base.vpc"
$MacroRequired "PLATSUBDIR"
$Configuration
{
$General
{
// X360 version publishes to some other directory then copies here so we need to tell VPC to track this
// or else it won't know what depends on this project.
$AdditionalOutputFiles "$SRCDIR\lib\public\$(TargetName).lib" [$X360]
}
$Compiler
{
$PreprocessorDefinitions "$BASE;VSTDLIB_DLL_EXPORT"
$GCC_ExtraCompilerFlags "-U_FORTIFY_SOURCE" [$LINUXALL]
}
$Linker
{
$AdditionalDependencies "$BASE odbc32.lib odbccp32.lib" [$WINDOWS]
// pc publishes the import library directly
$ImportLibrary "$LIBPUBLIC\$(TargetName).lib" [$WINDOWS]
// 360 publishes the import library via a post build step
$ImportLibrary "$(TargetDir)\$(TargetName).lib" [$X360]
// 360 will auto generate a def file for this import library
$ModuleDefinitionFile " " [$X360]
$AdditionalOptions "$BASE /AUTODEF:xbox\xbox.def" [$X360]
// Suppress this warning using the undocumented /ignore linker switch
// tier1.lib(KeyValues.obj) : warning LNK4217: locally defined symbol _KeyValuesSystem imported in function "public: static int __cdecl KeyValues::GetSymbolForStringClassic(char const *,bool)" (?GetSymbolForStringClassic@KeyValues@@SAHPBD_N@Z)
$AdditionalOptions "$BASE /ignore:4217" [$WINDOWS]
$SystemLibraries "iconv" [$OSXALL]
$SystemFrameworks "CoreServices" [$OSXALL]
$GCC_ExtraLinkerFlags "-all_load" [$OSXALL]
$ImportLibrary "$LIBPUBLIC\$_IMPLIB_PREFIX$OUTBINNAME$_IMPLIB_EXT" [$POSIX]
$OutputFile "$(OBJ_DIR)/$_IMPLIB_PREFIX$OUTBINNAME$_IMPLIB_EXT" [$POSIX]
}
$PreLinkEvent [$WINDOWS]
{
$CommandLine "call $SRCDIR\vpc_scripts\valve_p4_edit.cmd $LIBPUBLIC\$(TargetName).lib $SRCDIR" "\n" \
"$BASE"
}
$PreLinkEvent [$X360]
{
// Run a pre-link event to clean the .def file from the last link
$CommandLine "if exist xbox\xbox.def del xbox\xbox.def" "\n" \
"$BASE"
}
$PostBuildEvent [$X360]
{
// Run a post build event to validate the .def file was correctly generated
$CommandLine "perl $SRCDIR\devtools\bin\make360def.pl -checkauto xbox\xbox.def" "\n" \
"if exist $(TargetDir)$(TargetName).lib copy $(TargetDir)$(TargetName).lib $SRCDIR\lib\public\$(TargetName).lib" "\n" \
"$BASE"
}
$General [$POSIX]
{
$GameOutputFile "$OUTBINDIR/$_IMPLIB_DLL_PREFIX$OUTBINNAME$_DLL_EXT"
}
}
$Project "vstdlib"
{
$Folder "Source Files"
{
$File "xbox\___FirstModule.cpp" [$X360]
$File "GetStackPtr64.masm" [$WIN64]
{
$Configuration
{
$CustomBuildStep
{
// General
$CommandLine "$QUOTE$(VCInstallDir)bin\x86_amd64\ml64.exe$QUOTE /nologo /c /Fo$QUOTE$(IntDir)\$(InputName).obj$QUOTE $QUOTE$(InputPath)$QUOTE"
$Description "Compiling GetStackPtr64.masm"
$Outputs "$(IntDir)\$(InputName).obj"
}
}
}
$File "coroutine_win64.masm" [$WIN64]
{
$Configuration
{
$CustomBuildStep
{
// General
$CommandLine "$QUOTE$(VCInstallDir)bin\x86_amd64\ml64.exe$QUOTE /c /Fo$QUOTE$(IntDir)\$(InputName).obj$QUOTE $QUOTE$(InputPath)$QUOTE"
$Description "Compiling coroutine_win64.masm"
$Outputs "$(IntDir)\$(InputName).obj"
}
}
}
$File "coroutine.cpp" [!$X360 && !$OSXALL]
{
$Configuration
{
$Compiler
{
$BasicRuntimeChecks "Default"
}
}
}
$File "cvar.cpp"
$File "jobthread.cpp"
$File "KeyValuesSystem.cpp"
$File "osversion.cpp"
$File "processutils.cpp" [$WINDOWS]
$File "random.cpp"
$File "vcover.cpp"
}
$Folder "Public Header Files"
{
$File "$SRCDIR\public\vstdlib\cvar.h"
$File "$SRCDIR\public\vstdlib\coroutine.h"
$File "$SRCDIR\public\vstdlib\jobthread.h"
$File "$SRCDIR\public\vstdlib\IKeyValuesSystem.h"
$File "$SRCDIR\public\vstdlib\iprocessutils.h"
$File "$SRCDIR\public\tier1\mempool.h"
$File "$SRCDIR\public\vstdlib\osversion.h"
$File "$SRCDIR\public\vstdlib\random.h"
$File "$SRCDIR\public\vstdlib\vcover.h"
$File "$SRCDIR\public\vstdlib\vstdlib.h"
}
$Folder "Link Libraries"
{
-$ImpLib vstdlib
$Lib "coroutine_osx" [$OSXALL]
}
}

View File

@ -0,0 +1,20 @@
//-----------------------------------------------------------------------------
// vstdlib_exclude.vpc
//
// Project Script
//-----------------------------------------------------------------------------
$MacroRequired "PLATSUBDIR"
$Project
{
$Folder "Link Libraries"
{
// Should match the sites that include this in base
-$Lib "$LIBPUBLIC\vstdlib" [$POSIX && !$IS_LIB_PROJECT]
-$Implib vstdlib [$POSIX]
-$File "$SRCDIR\lib\public\$_IMPLIB_PREFIXvstdlib$_IMPLIB_EXT" [$WIN32]
-$File "$SRCDIR\lib\public$PLATSUBDIR\$_IMPLIB_PREFIXvstdlib$_IMPLIB_EXT" [$WIN64]
-$File "$SRCDIR\lib\public\vstdlib_360.lib" [$X360]
}
}

View File

@ -0,0 +1,19 @@
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// MUST BE THE FIRST MODULE IN THE LINK PROCESS TO ACHIEVE @1
//
// This is a 360 specific trick to force this import library and the new 360
// link option /AUTODEF to put CreateInterface at @1 (360 lacks named exports) and
// first in sequence. Otherwise, the valve interface techique that does a
// GetProcAddress( @1 ) gets the wrong function pointer. All other exported
// functions can appear in any order, but the oridnals should be autogened sequential.
//===========================================================================//
#include "tier1/tier1.h"
// Should be the first function that the linker 'sees' as an export
void* CreateInterfaceThunk( const char *pName, int *pReturnCode )
{
// descend into the real function
return CreateInterface( pName, pReturnCode );
}