1
This commit is contained in:
416
vstdlib/KeyValuesSystem.cpp
Normal file
416
vstdlib/KeyValuesSystem.cpp
Normal 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
1157
vstdlib/coroutine.cpp
Normal file
File diff suppressed because it is too large
Load Diff
31
vstdlib/coroutine_osx.vpc
Normal file
31
vstdlib/coroutine_osx.vpc
Normal 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"
|
||||
}
|
||||
}
|
175
vstdlib/coroutine_win64.masm
Normal file
175
vstdlib/coroutine_win64.masm
Normal 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
899
vstdlib/cvar.cpp
Normal 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 );
|
||||
}
|
||||
}
|
||||
|
||||
|
17
vstdlib/getstackptr64.masm
Normal file
17
vstdlib/getstackptr64.masm
Normal 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
1457
vstdlib/jobthread.cpp
Normal file
File diff suppressed because it is too large
Load Diff
427
vstdlib/osversion.cpp
Normal file
427
vstdlib/osversion.cpp
Normal 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
473
vstdlib/processutils.cpp
Normal 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
265
vstdlib/random.cpp
Normal 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
9
vstdlib/vcover.cpp
Normal 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
157
vstdlib/vstdlib.vpc
Normal 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]
|
||||
}
|
||||
}
|
||||
|
20
vstdlib/vstdlib_exclude.vpc
Normal file
20
vstdlib/vstdlib_exclude.vpc
Normal 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]
|
||||
}
|
||||
}
|
19
vstdlib/xbox/___FirstModule.cpp
Normal file
19
vstdlib/xbox/___FirstModule.cpp
Normal 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 );
|
||||
}
|
Reference in New Issue
Block a user