uid issue

This commit is contained in:
KittenPopo
2021-07-24 21:11:47 -07:00
commit c2130ba4e9
13850 changed files with 6241419 additions and 0 deletions

View File

@ -0,0 +1,76 @@
// ----------------------------------------- //
// File generated by VPC //
// ----------------------------------------- //
Source file: D:\Users\berta\Downloads\csgosourcecode\cstrike15_src\vstdlib\coroutine.cpp
Debug output file: D:\Users\berta\Downloads\csgosourcecode\cstrike15_src\vstdlib\coroutine.cpp
Release output file: D:\Users\berta\Downloads\csgosourcecode\cstrike15_src\vstdlib\coroutine.cpp
Containing unity file:
PCH file:
Source file: D:\Users\berta\Downloads\csgosourcecode\cstrike15_src\vstdlib\cvar.cpp
Debug output file: D:\Users\berta\Downloads\csgosourcecode\cstrike15_src\vstdlib\cvar.cpp
Release output file: D:\Users\berta\Downloads\csgosourcecode\cstrike15_src\vstdlib\cvar.cpp
Containing unity file:
PCH file:
Source file: D:\Users\berta\Downloads\csgosourcecode\cstrike15_src\common\debug_dll_check.cpp
Debug output file: D:\Users\berta\Downloads\csgosourcecode\cstrike15_src\common\debug_dll_check.cpp
Release output file: D:\Users\berta\Downloads\csgosourcecode\cstrike15_src\common\debug_dll_check.cpp
Containing unity file:
PCH file:
Source file: D:\Users\berta\Downloads\csgosourcecode\cstrike15_src\vstdlib\eventsystem.cpp
Debug output file: D:\Users\berta\Downloads\csgosourcecode\cstrike15_src\vstdlib\eventsystem.cpp
Release output file: D:\Users\berta\Downloads\csgosourcecode\cstrike15_src\vstdlib\eventsystem.cpp
Containing unity file:
PCH file:
Source file: D:\Users\berta\Downloads\csgosourcecode\cstrike15_src\vstdlib\jobthread.cpp
Debug output file: D:\Users\berta\Downloads\csgosourcecode\cstrike15_src\vstdlib\jobthread.cpp
Release output file: D:\Users\berta\Downloads\csgosourcecode\cstrike15_src\vstdlib\jobthread.cpp
Containing unity file:
PCH file:
Source file: D:\Users\berta\Downloads\csgosourcecode\cstrike15_src\vstdlib\keyvaluessystem.cpp
Debug output file: D:\Users\berta\Downloads\csgosourcecode\cstrike15_src\vstdlib\keyvaluessystem.cpp
Release output file: D:\Users\berta\Downloads\csgosourcecode\cstrike15_src\vstdlib\keyvaluessystem.cpp
Containing unity file:
PCH file:
Source file: D:\Users\berta\Downloads\csgosourcecode\cstrike15_src\public\tier0\memoverride.cpp
Debug output file: D:\Users\berta\Downloads\csgosourcecode\cstrike15_src\public\tier0\memoverride.cpp
Release output file: D:\Users\berta\Downloads\csgosourcecode\cstrike15_src\public\tier0\memoverride.cpp
Containing unity file:
PCH file:
Source file: D:\Users\berta\Downloads\csgosourcecode\cstrike15_src\vstdlib\processutils.cpp
Debug output file: D:\Users\berta\Downloads\csgosourcecode\cstrike15_src\vstdlib\processutils.cpp
Release output file: D:\Users\berta\Downloads\csgosourcecode\cstrike15_src\vstdlib\processutils.cpp
Containing unity file:
PCH file:
Source file: D:\Users\berta\Downloads\csgosourcecode\cstrike15_src\vstdlib\random.cpp
Debug output file: D:\Users\berta\Downloads\csgosourcecode\cstrike15_src\vstdlib\random.cpp
Release output file: D:\Users\berta\Downloads\csgosourcecode\cstrike15_src\vstdlib\random.cpp
Containing unity file:
PCH file:
Source file: D:\Users\berta\Downloads\csgosourcecode\cstrike15_src\vstdlib\vcover.cpp
Debug output file: D:\Users\berta\Downloads\csgosourcecode\cstrike15_src\vstdlib\vcover.cpp
Release output file: D:\Users\berta\Downloads\csgosourcecode\cstrike15_src\vstdlib\vcover.cpp
Containing unity file:
PCH file:
Source file: D:\Users\berta\Downloads\csgosourcecode\cstrike15_src\vstdlib\vstdlib_strtools.cpp
Debug output file: D:\Users\berta\Downloads\csgosourcecode\cstrike15_src\vstdlib\vstdlib_strtools.cpp
Release output file: D:\Users\berta\Downloads\csgosourcecode\cstrike15_src\vstdlib\vstdlib_strtools.cpp
Containing unity file:
PCH file:
Source file: D:\Users\berta\Downloads\csgosourcecode\cstrike15_src\vstdlib\vstrtools.cpp
Debug output file: D:\Users\berta\Downloads\csgosourcecode\cstrike15_src\vstdlib\vstrtools.cpp
Release output file: D:\Users\berta\Downloads\csgosourcecode\cstrike15_src\vstdlib\vstrtools.cpp
Containing unity file:
PCH file:

212
vstdlib/concommandhash.h Normal file
View File

@ -0,0 +1,212 @@
//===== Copyright <20> 1996-2005, Valve Corporation, All rights reserved. ======//
//
// Purpose: Special case hash table for console commands
//
// $NoKeywords: $
//
//===========================================================================//
#if !defined( CONCOMMANDHASH_H )
#define CONCOMMANDHASH_H
#ifdef _WIN32
#pragma once
#endif
#include "utllinkedlist.h"
#include "generichash.h"
// This is a hash table class very similar to the CUtlHashFast, but
// modified specifically so that we can look up ConCommandBases
// by string names without having to actually store those strings in
// the dictionary, and also iterate over all of them.
// It uses separate chaining: each key hashes to a bucket, each
// bucket is a linked list of hashed commands. We store the hash of
// the command's string name as well as its pointer, so we can do
// the linked list march part of the Find() operation more quickly.
class CConCommandHash
{
public:
typedef intp CCommandHashHandle_t;
typedef unsigned int HashKey_t;
// Constructor/Deconstructor.
CConCommandHash();
~CConCommandHash();
// Memory.
void Purge( bool bReinitialize );
// Invalid handle.
static CCommandHashHandle_t InvalidHandle( void ) { return ( CCommandHashHandle_t )~0; }
inline bool IsValidHandle( CCommandHashHandle_t hHash ) const;
/// Initialize.
void Init( void ); // bucket count is hardcoded in enum below.
/// Get hash value for a concommand
static inline HashKey_t Hash( const ConCommandBase *cmd );
// Size not available; count is meaningless for multilists.
// int Count( void ) const;
// Insertion.
CCommandHashHandle_t Insert( ConCommandBase *cmd );
CCommandHashHandle_t FastInsert( ConCommandBase *cmd );
// Removal.
void Remove( CCommandHashHandle_t hHash ) RESTRICT;
void RemoveAll( void );
// Retrieval.
inline CCommandHashHandle_t Find( const char *name ) const;
CCommandHashHandle_t Find( const ConCommandBase *cmd ) const RESTRICT;
// A convenience version of Find that skips the handle part
// and returns a pointer to a concommand, or NULL if none was found.
inline ConCommandBase * FindPtr( const char *name ) const;
inline ConCommandBase * &operator[]( CCommandHashHandle_t hHash );
inline ConCommandBase *const &operator[]( CCommandHashHandle_t hHash ) const;
#ifdef _DEBUG
// Dump a report to MSG
void Report( void );
#endif
// Iteration
struct CCommandHashIterator_t
{
int bucket;
CCommandHashHandle_t handle;
CCommandHashIterator_t(int _bucket, const CCommandHashHandle_t &_handle)
: bucket(_bucket), handle(_handle) {};
// inline operator UtlHashFastHandle_t() const { return handle; };
};
inline CCommandHashIterator_t First() const;
inline CCommandHashIterator_t Next( const CCommandHashIterator_t &hHash ) const;
inline bool IsValidIterator( const CCommandHashIterator_t &iter ) const;
inline ConCommandBase * &operator[]( const CCommandHashIterator_t &iter ) { return (*this)[iter.handle]; }
inline ConCommandBase * const &operator[]( const CCommandHashIterator_t &iter ) const { return (*this)[iter.handle]; }
private:
// a find func where we've already computed the hash for the string.
// (hidden private in case we decide to invent a custom string hash func
// for this class)
CCommandHashHandle_t Find( const char *name, HashKey_t hash) const RESTRICT;
protected:
enum
{
kNUM_BUCKETS = 256,
kBUCKETMASK = kNUM_BUCKETS - 1,
};
struct HashEntry_t
{
HashKey_t m_uiKey;
ConCommandBase *m_Data;
HashEntry_t(unsigned int _hash, ConCommandBase * _cmd)
: m_uiKey(_hash), m_Data(_cmd) {};
HashEntry_t(){};
};
typedef CUtlFixedLinkedList<HashEntry_t> datapool_t;
CUtlVector<CCommandHashHandle_t> m_aBuckets;
datapool_t m_aDataPool;
};
inline bool CConCommandHash::IsValidHandle( CCommandHashHandle_t hHash ) const
{
return m_aDataPool.IsValidIndex(hHash);
}
inline CConCommandHash::CCommandHashHandle_t CConCommandHash::Find( const char *name ) const
{
return Find( name, HashStringCaseless(name) );
}
inline ConCommandBase * &CConCommandHash::operator[]( CCommandHashHandle_t hHash )
{
return ( m_aDataPool[hHash].m_Data );
}
inline ConCommandBase *const &CConCommandHash::operator[]( CCommandHashHandle_t hHash ) const
{
return ( m_aDataPool[hHash].m_Data );
}
//-----------------------------------------------------------------------------
// Purpose: For iterating over the whole hash, return the index of the first element
//-----------------------------------------------------------------------------
CConCommandHash::CCommandHashIterator_t CConCommandHash::First() const
{
// walk through the buckets to find the first one that has some data
int bucketCount = m_aBuckets.Count();
const CCommandHashHandle_t invalidIndex = m_aDataPool.InvalidIndex();
for ( int bucket = 0 ; bucket < bucketCount ; ++bucket )
{
CCommandHashHandle_t iElement = m_aBuckets[bucket]; // get the head of the bucket
if ( iElement != invalidIndex )
return CCommandHashIterator_t( bucket, iElement );
}
// if we are down here, the list is empty
return CCommandHashIterator_t( -1, invalidIndex );
}
//-----------------------------------------------------------------------------
// Purpose: For iterating over the whole hash, return the next element after
// the param one. Or an invalid iterator.
//-----------------------------------------------------------------------------
CConCommandHash::CCommandHashIterator_t
CConCommandHash::Next( const CConCommandHash::CCommandHashIterator_t &iter ) const
{
// look for the next entry in the current bucket
CCommandHashHandle_t next = m_aDataPool.Next(iter.handle);
const CCommandHashHandle_t invalidIndex = m_aDataPool.InvalidIndex();
if ( next != invalidIndex )
{
// this bucket still has more elements in it
return CCommandHashIterator_t(iter.bucket, next);
}
// otherwise look for the next bucket with data
int bucketCount = m_aBuckets.Count();
for ( int bucket = iter.bucket+1 ; bucket < bucketCount ; ++bucket )
{
CCommandHashHandle_t next = m_aBuckets[bucket]; // get the head of the bucket
if (next != invalidIndex)
return CCommandHashIterator_t( bucket, next );
}
// if we're here, there's no more data to be had
return CCommandHashIterator_t(-1, invalidIndex);
}
bool CConCommandHash::IsValidIterator( const CCommandHashIterator_t &iter ) const
{
return ( (iter.bucket >= 0) && (m_aDataPool.IsValidIndex(iter.handle)) );
}
inline CConCommandHash::HashKey_t CConCommandHash::Hash( const ConCommandBase *cmd )
{
return HashStringCaseless( cmd->GetName() );
}
inline ConCommandBase * CConCommandHash::FindPtr( const char *name ) const
{
CCommandHashHandle_t handle = Find(name);
if (handle == InvalidHandle())
{
return NULL;
}
else
{
return (*this)[handle];
}
}
#endif

1070
vstdlib/coroutine.cpp Normal file

File diff suppressed because it is too large Load Diff

1311
vstdlib/cvar.cpp Normal file

File diff suppressed because it is too large Load Diff

503
vstdlib/eventsystem.cpp Normal file
View File

@ -0,0 +1,503 @@
//========== Copyright (c) Valve Corporation, All rights reserved. ==========//
//
// Purpose:
//
// $NoKeywords: $
//
//===========================================================================//
#include "vstdlib/ieventsystem.h"
#include "tier1/generichash.h"
#include "tier1/utllinkedlist.h"
#include "tier0/tslist.h"
#include "tier1/utltshash.h"
#include "tier1/tier1.h"
// NOTE: This has to be the last file included!
#include "tier0/memdbgon.h"
//-----------------------------------------------------------------------------
// A hash of the event name
//-----------------------------------------------------------------------------
typedef uint32 EventNameHash_t;
//-----------------------------------------------------------------------------
// An event queue is a thread-safe event queue
// NOTE: There are questions about whether we should have a secondary queue
// similar to vgui, where if you are doing event handling, you want to
// process the secondary queue prior to processing the next primary queue
// Problem is w/ threadsafety: what happens if other threads which are not
// on the thread processing the events posts an event?
//-----------------------------------------------------------------------------
class CEventQueue
{
public:
CEventQueue();
~CEventQueue();
// Posts an event
void PostEvent( CFunctorCallback *pCallback, CFunctorData *pData );
// Processes the event queue
void ProcessEvents( );
// Discards events that use a particular callback
void DiscardEvents( CFunctorCallback *pCallback );
// Refcount
int AddRef() { return ++m_nRefCount; }
int Release() { return --m_nRefCount; }
int RefCount() { return m_nRefCount; }
private:
struct QueuedEvent_t
{
void *m_pTarget;
CFunctorData *m_pData;
CFunctorCallback *m_pCallback;
};
void Cleanup();
CTSQueue< CFunctorCallback* > m_QueuedEventDiscards;
CTSQueue< QueuedEvent_t > m_Queue;
CInterlockedInt m_nRefCount;
#ifdef _DEBUG
bool m_bIsProcessingEvents;
bool m_bIsDiscardingEvents;
#endif
};
//-----------------------------------------------------------------------------
// Constructor, destructor
//-----------------------------------------------------------------------------
CEventQueue::CEventQueue()
{
#ifdef _DEBUG
m_bIsProcessingEvents = false;
m_bIsDiscardingEvents = false;
#endif
}
CEventQueue::~CEventQueue()
{
Assert( !m_bIsProcessingEvents );
Assert( !m_bIsDiscardingEvents );
Cleanup();
}
//-----------------------------------------------------------------------------
// Cleans up the event queue
//-----------------------------------------------------------------------------
void CEventQueue::Cleanup()
{
QueuedEvent_t event;
while ( m_Queue.PopItem( &event ) )
{
event.m_pCallback->Release();
event.m_pData->Release();
}
}
//-----------------------------------------------------------------------------
// Posts an event
//-----------------------------------------------------------------------------
void CEventQueue::PostEvent( CFunctorCallback *pCallback, CFunctorData *pData )
{
QueuedEvent_t event;
event.m_pCallback = pCallback;
event.m_pData = pData;
pCallback->AddRef();
pData->AddRef();
m_Queue.PushItem( event );
}
//-----------------------------------------------------------------------------
// Discards events that use a particular callback
//-----------------------------------------------------------------------------
void CEventQueue::DiscardEvents( CFunctorCallback *pCallback )
{
Assert( !m_bIsProcessingEvents );
#ifdef _DEBUG
m_bIsDiscardingEvents = true;
#endif
m_QueuedEventDiscards.PushItem( pCallback );
#ifdef _DEBUG
m_bIsDiscardingEvents = false;
#endif
}
//-----------------------------------------------------------------------------
// Processes the event queue
//-----------------------------------------------------------------------------
void CEventQueue::ProcessEvents( )
{
Assert( !m_bIsProcessingEvents );
Assert( !m_bIsDiscardingEvents );
#ifdef _DEBUG
m_bIsProcessingEvents = true;
#endif
if ( m_QueuedEventDiscards.Count() == 0 )
{
// Dispatch all events
QueuedEvent_t event;
while ( m_Queue.PopItem( &event ) )
{
(*(event.m_pCallback))( event.m_pData );
event.m_pData->Release();
event.m_pCallback->Release();
}
}
else
{
// Build list of callbacks which are stale and need to die
CUtlVector< CFunctorCallback * > callbacks( 0, m_QueuedEventDiscards.Count() );
CFunctorCallback *pCallback = NULL;
while( m_QueuedEventDiscards.PopItem( &pCallback ) )
{
callbacks.AddToTail( pCallback );
}
// Only invoke events on non-stale callbacks
int nCount = callbacks.Count();
QueuedEvent_t event;
while ( m_Queue.PopItem( &event ) )
{
bool bFound = false;
for ( int i = 0; i < nCount; ++i )
{
bFound = ( event.m_pCallback == callbacks[i] );
if ( bFound )
break;
}
if ( !bFound )
{
(*(event.m_pCallback))( event.m_pData );
}
event.m_pData->Release();
event.m_pCallback->Release();
}
}
#ifdef _DEBUG
m_bIsProcessingEvents = false;
#endif
}
//-----------------------------------------------------------------------------
// An event id maintains a list of all interested listeners
//-----------------------------------------------------------------------------
class CEventId
{
public:
// Registers/unregisters a listener of this event
void RegisterListener( CEventQueue *pEventQueue, CFunctorCallback *pCallback );
void UnregisterListener( CEventQueue *pEventQueue, CFunctorCallback *pCallback );
// Unregisters all listeners associated with a particular queue
void UnregisterAllListeners( CEventQueue *pEventQueue );
// Posts an event
void PostEvent( CEventQueue *pEventQueue, const void *pListener, CFunctorData *pData );
private:
struct SubscribedQueue_t
{
CEventQueue *m_pEventQueue;
CFunctorCallback *m_pCallback;
};
CUtlFixedLinkedList< SubscribedQueue_t > m_SubscribedQueueList;
CThreadSpinRWLock m_ListenerLock;
};
//-----------------------------------------------------------------------------
// Registers a listener of this event
//-----------------------------------------------------------------------------
void CEventId::RegisterListener( CEventQueue *pEventQueue, CFunctorCallback *pCallback )
{
// NOTE: We could probably move the write locks so they cover less code
// but I'm wanting to be cautious
m_ListenerLock.LockForWrite();
intp i;
for( i = m_SubscribedQueueList.Head(); i != m_SubscribedQueueList.InvalidIndex(); i = m_SubscribedQueueList.Next( i ) )
{
if ( ( m_SubscribedQueueList[i].m_pEventQueue == pEventQueue ) && m_SubscribedQueueList[i].m_pCallback->IsEqual( pCallback ) )
{
Warning( "Tried to install the same listener on the same event id + queue twice!\n" );
break;
}
}
if ( i == m_SubscribedQueueList.InvalidIndex() )
{
SubscribedQueue_t queue;
queue.m_pEventQueue = pEventQueue;
queue.m_pCallback = pCallback;
pCallback->AddRef();
pEventQueue->AddRef();
m_SubscribedQueueList.AddToTail( queue );
}
m_ListenerLock.UnlockWrite();
}
//-----------------------------------------------------------------------------
// Unregisters a listener
//-----------------------------------------------------------------------------
void CEventId::UnregisterListener( CEventQueue *pEventQueue, CFunctorCallback *pCallback )
{
// NOTE: We could probably move the write locks so they cover less code
// but I'm wanting to be cautious
m_ListenerLock.LockForWrite();
intp i;
for( i = m_SubscribedQueueList.Head(); i != m_SubscribedQueueList.InvalidIndex(); i = m_SubscribedQueueList.Next( i ) )
{
if ( ( m_SubscribedQueueList[i].m_pEventQueue == pEventQueue ) && m_SubscribedQueueList[i].m_pCallback->IsEqual( pCallback ) )
break;
}
if ( i != m_SubscribedQueueList.InvalidIndex() )
{
CFunctorCallback *pCachedCallback = m_SubscribedQueueList[ i ].m_pCallback;
m_SubscribedQueueList.Remove( i );
// Remove the cached callback from the queued list of events
// if it has a non-zero refcount
if ( pCachedCallback->Release() > 0 )
{
pEventQueue->DiscardEvents( pCachedCallback );
}
pEventQueue->Release();
}
m_ListenerLock.UnlockWrite();
}
//-----------------------------------------------------------------------------
// Unregisters all listeners associated with a particular queue
//-----------------------------------------------------------------------------
void CEventId::UnregisterAllListeners( CEventQueue *pEventQueue )
{
// NOTE: We could probably move the write locks so they cover less code
// but I'm wanting to be cautious
m_ListenerLock.LockForWrite();
intp j, next;
for( j = m_SubscribedQueueList.Head(); j != m_SubscribedQueueList.InvalidIndex(); j = next )
{
next = m_SubscribedQueueList.Next( j );
if ( m_SubscribedQueueList[j].m_pEventQueue != pEventQueue )
continue;
m_SubscribedQueueList[j].m_pCallback->Release();
pEventQueue->Release();
m_SubscribedQueueList.Remove( j );
}
m_ListenerLock.UnlockWrite();
}
//-----------------------------------------------------------------------------
// Posts an event
//-----------------------------------------------------------------------------
void CEventId::PostEvent( CEventQueue *pEventQueue, const void *pListener, CFunctorData *pData )
{
m_ListenerLock.LockForRead();
for( intp i = m_SubscribedQueueList.Head(); i != m_SubscribedQueueList.InvalidIndex(); i = m_SubscribedQueueList.Next( i ) )
{
SubscribedQueue_t &queue = m_SubscribedQueueList[i];
if ( pEventQueue && queue.m_pEventQueue != pEventQueue )
continue;
if ( pListener && pListener != queue.m_pCallback->GetTarget() )
continue;
queue.m_pEventQueue->PostEvent( queue.m_pCallback, pData );
}
m_ListenerLock.UnlockRead();
}
//-----------------------------------------------------------------------------
// Event system implementation
//-----------------------------------------------------------------------------
class CEventSystem : public CTier1AppSystem< IEventSystem >
{
typedef CTier1AppSystem< IEventSystem > BaseClass;
// Methods of IAppSystem
public:
// Methods of IEventSystem
public:
virtual EventQueue_t CreateEventQueue();
virtual void DestroyEventQueue( EventQueue_t hQueue );
virtual void ProcessEvents( EventQueue_t hQueue );
virtual EventId_t RegisterEvent( const char *pEventName );
virtual void PostEventInternal( EventId_t nEventId, EventQueue_t hQueue, const void *pListener, CFunctorData *pData );
virtual void RegisterListener( EventId_t nEventId, EventQueue_t hQueue, CFunctorCallback *pCallback );
virtual void UnregisterListener( EventId_t nEventId, EventQueue_t hQueue, CFunctorCallback *pCallback );
// Other public methods
public:
CEventSystem();
virtual ~CEventSystem();
private:
CUtlTSHash< CEventId, 251, EventNameHash_t, CUtlTSHashGenericHash< 251, EventNameHash_t>, 8 > m_EventIds;
};
//-----------------------------------------------------------------------------
// Globals
//-----------------------------------------------------------------------------
static CEventSystem s_EventSystem;
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CEventSystem, IEventSystem,
EVENTSYSTEM_INTERFACE_VERSION, s_EventSystem )
//-----------------------------------------------------------------------------
// constructor, destructor
//-----------------------------------------------------------------------------
CEventSystem::CEventSystem() : m_EventIds( 256 )
{
}
CEventSystem::~CEventSystem()
{
}
//-----------------------------------------------------------------------------
// Create, destroy an event queue
//-----------------------------------------------------------------------------
EventQueue_t CEventSystem::CreateEventQueue()
{
CEventQueue *pEventQueue = new CEventQueue;
return (EventQueue_t)pEventQueue;
}
void CEventSystem::DestroyEventQueue( EventQueue_t hQueue )
{
CEventQueue *pEventQueue = ( CEventQueue* )( hQueue );
if ( !pEventQueue )
return;
if ( pEventQueue->RefCount() != 0 )
{
// This means it's still sitting in a listener list
Warning( "Perf warning: Forgot to unregister listeners on event queue %p\n", hQueue );
int nCount = m_EventIds.Count();
UtlTSHashHandle_t *pHandles = (UtlTSHashHandle_t*)stackalloc( nCount * sizeof(UtlTSHashHandle_t) );
nCount = m_EventIds.GetElements( 0, nCount, pHandles );
for ( int i = 0; i < nCount; ++i )
{
m_EventIds[ pHandles[i] ].UnregisterAllListeners( pEventQueue );
}
}
delete pEventQueue;
}
//-----------------------------------------------------------------------------
// Registers an event
//-----------------------------------------------------------------------------
EventId_t CEventSystem::RegisterEvent( const char *pEventName )
{
EventNameHash_t hId = (EventNameHash_t)MurmurHash2( pEventName, Q_strlen(pEventName), 0xE1E47644 );
CDefaultTSHashConstructor< CEventId > constructor;
UtlTSHashHandle_t hList = m_EventIds.Insert( hId, &constructor );
return ( EventId_t )( &m_EventIds[ hList ] );
}
//-----------------------------------------------------------------------------
// Registers, unregisters an event listener
//-----------------------------------------------------------------------------
void CEventSystem::RegisterListener( EventId_t nEventId, EventQueue_t hQueue, CFunctorCallback *pCallback )
{
CEventQueue *pEventQueue = ( CEventQueue* )( hQueue );
if ( pEventQueue )
{
CEventId *pEventId = ( CEventId* )nEventId;
if ( pEventId )
{
pEventId->RegisterListener( pEventQueue, pCallback );
}
}
pCallback->Release();
}
void CEventSystem::UnregisterListener( EventId_t nEventId, EventQueue_t hQueue, CFunctorCallback *pCallback )
{
CEventQueue *pEventQueue = ( CEventQueue* )( hQueue );
if ( pEventQueue )
{
CEventId *pEventId = ( CEventId* )nEventId;
if ( pEventId )
{
pEventId->UnregisterListener( pEventQueue, pCallback );
}
}
pCallback->Release();
}
//-----------------------------------------------------------------------------
// Posts an event to all current listeners in a single queue
//-----------------------------------------------------------------------------
void CEventSystem::PostEventInternal( EventId_t nEventId, EventQueue_t hQueue, const void *pListener, CFunctorData *pData )
{
CEventQueue *pEventQueue = ( CEventQueue* )( hQueue );
CEventId *pEventId = (CEventId*)nEventId;
if ( pEventId )
{
pEventId->PostEvent( pEventQueue, pListener, pData );
}
pData->Release();
}
//-----------------------------------------------------------------------------
// Process queued events on a single queue
//-----------------------------------------------------------------------------
void CEventSystem::ProcessEvents( EventQueue_t hQueue )
{
CEventQueue *pEventQueue = ( CEventQueue* )( hQueue );
if ( pEventQueue )
{
pEventQueue->ProcessEvents();
}
}

120
vstdlib/getstackptr64.masm Normal file
View File

@ -0,0 +1,120 @@
; call cpuid with args in eax, ecx
; store eax, ebx, ecx, edx to p
option casemap:none
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
; 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

1614
vstdlib/jobthread.cpp Normal file

File diff suppressed because it is too large Load Diff

620
vstdlib/keyvaluessystem.cpp Normal file
View File

@ -0,0 +1,620 @@
//========= Copyright <20> 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include <vstdlib/ikeyvaluessystem.h>
#include <keyvalues.h>
#include "tier1/mempool.h"
#include "utlsymbol.h"
#include "utlmap.h"
#include "tier0/threadtools.h"
#include "tier1/memstack.h"
#include "tier1/convar.h"
#ifdef _PS3
#include "ps3/ps3_core.h"
#endif
// 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
//
// Defines platform-endian-specific macros:
// MEM_4BYTES_AS_0_AND_3BYTES : present a 4 byte uint32 as a memory
// layout where first memory byte is zero
// and the other 3 bytes represent value
// MEM_4BYTES_FROM_0_AND_3BYTES: unpack from memory with first zero byte
// and 3 value bytes the original uint32 value
//
// used for efficiently reading/writing storing 3 byte values into memory
// region immediately following a null-byte-terminated string, essentially
// sharing the null-byte-terminator with the first memory byte
//
#if defined( PLAT_LITTLE_ENDIAN )
// Number in memory has lowest-byte in front, use shifts to make it zero
#define MEM_4BYTES_AS_0_AND_3BYTES( x4bytes ) ( ( (uint32) (x4bytes) ) << 8 )
#define MEM_4BYTES_FROM_0_AND_3BYTES( x03bytes ) ( ( (uint32) (x03bytes) ) >> 8 )
#endif
#if defined( PLAT_BIG_ENDIAN )
// Number in memory has highest-byte in front, use masking to make it zero
#define MEM_4BYTES_AS_0_AND_3BYTES( x4bytes ) ( ( (uint32) (x4bytes) ) & 0x00FFFFFF )
#define MEM_4BYTES_FROM_0_AND_3BYTES( x03bytes ) ( ( (uint32) (x03bytes) ) & 0x00FFFFFF )
#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);
// set/get a value for keyvalues resolution symbol
// e.g.: SetKeyValuesExpressionSymbol( "LOWVIOLENCE", true ) - enables [$LOWVIOLENCE]
virtual void SetKeyValuesExpressionSymbol( const char *name, bool bValue );
virtual bool GetKeyValuesExpressionSymbol( const char *name );
// symbol table access from code with case-preserving requirements (used for key names)
virtual HKeySymbol GetSymbolForStringCaseSensitive( HKeySymbol &hCaseInsensitiveSymbol, const char *name, bool bCreate = true );
private:
#ifdef KEYVALUES_USE_POOL
CUtlMemoryPool *m_pMemPool;
#endif
int m_iMaxKeyValuesSize;
// string hash table
/*
Here's the way key values system data structures are laid out:
hash table with 2047 hash buckets:
[0] { hash_item_t }
[1]
[2]
...
each hash_item_t's stringIndex is an offset in m_Strings memory
at that offset we store the actual null-terminated string followed
by another 3 bytes for an alternative capitalization.
These 3 trailing bytes are set to 0 if no alternative capitalization
variants are present in the dictionary.
These trailing 3 bytes are interpreted as stringIndex into m_Strings
memory for the next alternative capitalization
Getting a string value by HKeySymbol : constant time access at the
string memory represented by stringIndex
Getting a symbol for a string value:
1) compute the hash
2) start walking the hash-bucket using special version of stricmp
until a case insensitive match is found
3a) for case-insensitive lookup return the found stringIndex
3b) for case-sensitive lookup keep walking the list of alternative
capitalizations using strcmp until exact case match is found
*/
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);
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;
CUtlMap< HKeySymbol, bool > m_KvConditionalSymbolTable;
CThreadFastMutex m_mutex;
};
// 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, CUtlMemoryPool::GROW_FAST, "CKeyValuesSystem::m_HashItemMemPool"),
m_KeyValuesTrackingList(0, 0, MemoryLeakTrackerLessFunc),
m_KvConditionalSymbolTable( DefLessFunc( HKeySymbol ) )
{
MEM_ALLOC_CREDIT();
// 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( "CKeyValuesSystem::m_Strings", 4*1024*1024, 64*1024, 0, 4 );
// Make 0 stringIndex to never be returned, by allocating
// and wasting minimal number of alignment bytes now:
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
}
//-----------------------------------------------------------------------------
// 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;
}
}
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 );
}
//-----------------------------------------------------------------------------
// 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, CUtlMemoryPool::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 );
MEM_ALLOC_CREDIT();
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;
int numStringBytes = strlen(name);
char *pString = (char *)m_Strings.Alloc( numStringBytes + 1 + 3 );
if ( !pString )
{
Error( "Out of keyvalue string space" );
return -1;
}
item->stringIndex = pString - (char *)m_Strings.GetBase();
Q_memcpy( pString, name, numStringBytes );
* reinterpret_cast< uint32 * >( pString + numStringBytes ) = 0; // string null-terminator + 3 alternative spelling bytes
return (HKeySymbol)item->stringIndex;
}
item = item->next;
}
// shouldn't be able to get here
Assert(0);
return (-1);
}
//-----------------------------------------------------------------------------
// Purpose: symbol table access (used for key names)
//-----------------------------------------------------------------------------
HKeySymbol CKeyValuesSystem::GetSymbolForStringCaseSensitive( HKeySymbol &hCaseInsensitiveSymbol, const char *name, bool bCreate )
{
if ( !name )
{
return (-1);
}
AUTO_LOCK( m_mutex );
MEM_ALLOC_CREDIT();
int hash = CaseInsensitiveHash(name, m_HashTable.Count());
int numNameStringBytes = -1;
int i = 0;
hash_item_t *item = &m_HashTable[hash];
while (1)
{
char *pCompareString = (char *)m_Strings.GetBase() + item->stringIndex;
int iResult = _V_stricmp_NegativeForUnequal( name, pCompareString );
if ( iResult == 0 )
{
// strings are exactly equal matching every letter's case
hCaseInsensitiveSymbol = (HKeySymbol)item->stringIndex;
return (HKeySymbol)item->stringIndex;
}
else if ( iResult > 0 )
{
// strings are equal in a case-insensitive compare, but have different case for some letters
// Need to walk the case-resolving chain
numNameStringBytes = Q_strlen( pCompareString );
uint32 *pnCaseResolveIndex = reinterpret_cast< uint32 * >( pCompareString + numNameStringBytes );
hCaseInsensitiveSymbol = (HKeySymbol)item->stringIndex;
while ( int nAlternativeStringIndex = MEM_4BYTES_FROM_0_AND_3BYTES( *pnCaseResolveIndex ) )
{
pCompareString = (char *)m_Strings.GetBase() + nAlternativeStringIndex;
int iResult = strcmp( name, pCompareString );
if ( !iResult )
{
// found an exact match
return (HKeySymbol)nAlternativeStringIndex;
}
// Keep traversing alternative case-resolving chain
pnCaseResolveIndex = reinterpret_cast< uint32 * >( pCompareString + numNameStringBytes );
}
// Reached the end of alternative case-resolving chain, pnCaseResolveIndex is pointing at 0 bytes
// indicating no further alternative stringIndex
if ( !bCreate )
{
// If we aren't interested in creating the actual string index,
// then return symbol with default capitalization
// NOTE: this is not correct value, but it cannot be used to create a new value anyway,
// only for locating a pre-existing value and lookups are case-insensitive
return (HKeySymbol)item->stringIndex;
}
else
{
char *pString = (char *)m_Strings.Alloc( numNameStringBytes + 1 + 3 );
if ( !pString )
{
Error( "Out of keyvalue string space" );
return -1;
}
int nNewAlternativeStringIndex = pString - (char *)m_Strings.GetBase();
Q_memcpy( pString, name, numNameStringBytes );
* reinterpret_cast< uint32 * >( pString + numNameStringBytes ) = 0; // string null-terminator + 3 alternative spelling bytes
*pnCaseResolveIndex = MEM_4BYTES_AS_0_AND_3BYTES( nNewAlternativeStringIndex ); // link previous spelling entry to the new entry
return (HKeySymbol)nNewAlternativeStringIndex;
}
}
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;
int numStringBytes = strlen(name);
char *pString = (char *)m_Strings.Alloc( numStringBytes + 1 + 3 );
if ( !pString )
{
Error( "Out of keyvalue string space" );
return -1;
}
item->stringIndex = pString - (char *)m_Strings.GetBase();
Q_memcpy( pString, name, numStringBytes );
* reinterpret_cast< uint32 * >( pString + numStringBytes ) = 0; // string null-terminator + 3 alternative spelling bytes
hCaseInsensitiveSymbol = (HKeySymbol)item->stringIndex;
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: 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: set/get a value for keyvalues resolution symbol
// e.g.: SetKeyValuesExpressionSymbol( "LOWVIOLENCE", true ) - enables [$LOWVIOLENCE]
//-----------------------------------------------------------------------------
void CKeyValuesSystem::SetKeyValuesExpressionSymbol( const char *name, bool bValue )
{
if ( !name )
return;
if ( name[0] == '$' )
++ name;
HKeySymbol hSym = GetSymbolForString( name, true ); // find or create symbol
{
AUTO_LOCK( m_mutex );
m_KvConditionalSymbolTable.InsertOrReplace( hSym, bValue );
}
}
bool CKeyValuesSystem::GetKeyValuesExpressionSymbol( const char *name )
{
if ( !name )
return false;
if ( name[0] == '$' )
++ name;
HKeySymbol hSym = GetSymbolForString( name, false ); // find or create symbol
if ( hSym != -1 )
{
AUTO_LOCK( m_mutex );
CUtlMap< HKeySymbol, bool >::IndexType_t idx = m_KvConditionalSymbolTable.Find( hSym );
if ( idx != m_KvConditionalSymbolTable.InvalidIndex() )
{
// Found the symbol value in conditional symbol table
return m_KvConditionalSymbolTable.Element( idx );
}
}
//
// Fallback conditionals
//
if ( !V_stricmp( name, "GAMECONSOLESPLITSCREEN" ) )
{
#if defined( _GAMECONSOLE )
return ( XBX_GetNumGameUsers() > 1 );
#else
return false;
#endif
}
if ( !V_stricmp( name, "GAMECONSOLEGUEST" ) )
{
#if defined( _GAMECONSOLE )
return ( XBX_GetPrimaryUserIsGuest() != 0 );
#else
return false;
#endif
}
if ( !V_stricmp( name, "ENGLISH" ) ||
!V_stricmp( name, "JAPANESE" ) ||
!V_stricmp( name, "GERMAN" ) ||
!V_stricmp( name, "FRENCH" ) ||
!V_stricmp( name, "SPANISH" ) ||
!V_stricmp( name, "ITALIAN" ) ||
!V_stricmp( name, "KOREAN" ) ||
!V_stricmp( name, "TCHINESE" ) ||
!V_stricmp( name, "PORTUGUESE" ) ||
!V_stricmp( name, "SCHINESE" ) ||
!V_stricmp( name, "POLISH" ) ||
!V_stricmp( name, "RUSSIAN" ) ||
!V_stricmp( name, "TURKISH" ) )
{
// the language symbols are true if we are in that language
// english is assumed when no language is present
const char *pLanguageString;
#ifdef _GAMECONSOLE
pLanguageString = XBX_GetLanguageString();
#else
static ConVarRef cl_language( "cl_language" );
pLanguageString = cl_language.GetString();
#endif
if ( !pLanguageString || !pLanguageString[0] )
{
pLanguageString = "english";
}
if ( !V_stricmp( name, pLanguageString ) )
{
return true;
}
else
{
return false;
}
}
// very expensive, back door for DLC updates
if ( !V_strnicmp( name, "CVAR_", 5 ) )
{
ConVarRef cvRef( name + 5 );
if ( cvRef.IsValid() )
return cvRef.GetBool();
}
// purposely warn on these to prevent syntax errors
// need to get these fixed asap, otherwise unintended false behavior
Warning( "KV Conditional: Unknown symbol %s\n", name );
return false;
}

709
vstdlib/processutils.cpp Normal file
View File

@ -0,0 +1,709 @@
//===== Copyright <20> 1996-2005, 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"
// NOTE: This has to be the last file included!
#include "tier0/memdbgon.h"
class CProcess;
class CProcessPipeRead : public IPipeRead
{
// IPipeRead overrides.
public:
virtual int GetNumBytesAvailable();
virtual void ReadAvailable( CUtlString &sStr, int nMaxBytes );
virtual void ReadAvailable( CUtlBuffer *pOutBuffer, int nMaxBytes );
virtual void Read( CUtlString &sStr, int nMaxBytes );
virtual void ReadLine( CUtlString &sStr );
public:
bool IsValid() const;
// This reads in whatever data is available in the pipe and caches it.
void CacheOutput();
// Returns true when there is data in m_hRead, false otherwise.
bool WaitForOutput();
int GetActualProcessOutputSize();
int GetProcessOutput( void *pBuf, int nBytes );
int GetActualProcessOutput( void *pBuf, int nBufLen );
public:
CProcess *m_pProcess;
HANDLE m_hRead; // Comes from ProcessInfo_t::m_hChildStdoutRd or m_hChildStderrRd.
// This is stored if they wait for the process to exit. Even though they haven't read the data yet,
// they can still call ReadAvailable() or GetNumBytesAvailable() on stdio after the process was terminated.
CUtlBuffer m_CachedOutput;
};
struct ProcessInfo_t
{
HANDLE m_hChildStdinRd;
HANDLE m_hChildStdinWr;
HANDLE m_hChildStdoutRd;
HANDLE m_hChildStdoutWr;
HANDLE m_hChildStderrRd;
HANDLE m_hChildStderrWr;
HANDLE m_hProcess;
CUtlString m_CommandLine;
int m_fFlags; // PROCESSSTART_xxx.
};
class CProcessUtils;
class CProcess : public IProcess
{
public:
CProcess( CProcessUtils *pProcessUtils, const ProcessInfo_t &info );
// IProcess overrides.
public:
virtual void Release();
virtual void Abort();
virtual bool IsComplete();
virtual int WaitUntilComplete();
virtual int WriteStdin( char *pBuf, int nBufLen );
virtual IPipeRead* GetStdout();
virtual IPipeRead* GetStderr();
virtual int GetExitCode();
public:
ProcessInfo_t m_Info;
CProcessPipeRead m_StdoutRead;
CProcessPipeRead m_StderrRead;
CProcessUtils *m_pProcessUtils;
intp m_nProcessesIndex; // Index into CProcessUtils::m_Processes.
};
//-----------------------------------------------------------------------------
// At the moment, we can only run one process at a time
//-----------------------------------------------------------------------------
class CProcessUtils : public CTier1AppSystem< IProcessUtils >
{
typedef CTier1AppSystem< IProcessUtils > BaseClass;
public:
CProcessUtils() {}
// Inherited from IAppSystem
virtual InitReturnVal_t Init();
virtual void Shutdown();
// Inherited from IProcessUtils
virtual IProcess* StartProcess( const char *pCommandLine, int fFlags, const char *pWorkingDir );
virtual IProcess* StartProcess( int argc, const char **argv, int fFlags, const char *pWorkingDir );
virtual int SimpleRunProcess( const char *pCommandLine, const char *pWorkingDir, CUtlString *pStdout );
public:
void OnProcessDelete( CProcess *pProcess );
private:
// creates the process, adds it to the list and writes the windows HANDLE into info.m_hProcess
CProcess* CreateProcess( ProcessInfo_t &info, int fFlags, const char *pWorkingDir );
CUtlFixedLinkedList< CProcess* > m_Processes;
bool m_bInitialized;
};
//-----------------------------------------------------------------------------
// Purpose: singleton accessor
//-----------------------------------------------------------------------------
static CProcessUtils s_ProcessUtils;
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CProcessUtils, IProcessUtils, PROCESS_UTILS_INTERFACE_VERSION, s_ProcessUtils );
//-----------------------------------------------------------------------------
// Returns the last error that occurred
//-----------------------------------------------------------------------------
char *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;
}
// ------------------------------------------------------------------------------------- //
// CProcessPipeRead implementation.
// ------------------------------------------------------------------------------------- //
bool CProcessPipeRead::IsValid() const
{
return m_hRead != INVALID_HANDLE_VALUE;
}
int CProcessPipeRead::GetNumBytesAvailable()
{
return GetActualProcessOutputSize() + m_CachedOutput.TellPut();
}
void CProcessPipeRead::ReadAvailable( CUtlString &sStr, int32 nMaxBytes )
{
int nBytesAvailable = GetNumBytesAvailable();
nBytesAvailable = MIN( nBytesAvailable, nMaxBytes );
sStr.SetLength( nBytesAvailable ); // If nBytesAvailable != 0, this auto allocates an extra byte for the null terminator.
if ( nBytesAvailable > 0 )
{
char *pOut = sStr.Get();
int nBytesGotten = GetProcessOutput( pOut, nBytesAvailable );
Assert( nBytesGotten == nBytesAvailable );
// Null-terminate it.
pOut[nBytesGotten] = 0;
}
}
void CProcessPipeRead::ReadAvailable( CUtlBuffer *pOutBuffer, int nMaxBytes )
{
int nBytesAvailable = GetNumBytesAvailable();
nBytesAvailable = MIN( nBytesAvailable, nMaxBytes );
if ( nBytesAvailable > 0 )
{
char *pOut = (char*)pOutBuffer->AccessForDirectRead(nBytesAvailable+1);
int nBytesGotten = GetProcessOutput( pOut, nBytesAvailable );
Assert( nBytesGotten == nBytesAvailable );
// Null-terminate it.
pOut[nBytesGotten] = 0;
}
}
void CProcessPipeRead::Read( CUtlString &sStr, int32 nBytes )
{
sStr.SetLength( 0 );
int nBytesLeftToRead = nBytes;
while ( nBytesLeftToRead > 0 )
{
int nAvail = GetNumBytesAvailable();
if ( nAvail == 0 )
{
if ( m_pProcess->IsComplete() )
{
return;
}
else
{
WaitForOutput();
continue;
}
}
int nToRead = MIN( nBytesLeftToRead, nAvail );
// Read as much as we need and add it to the string.
CUtlString sTemp;
ReadAvailable( sTemp, nToRead );
Assert( sTemp.Length() == nToRead );
sStr += sTemp;
nBytesLeftToRead -= nToRead;
}
}
void CProcessPipeRead::ReadLine( CUtlString &sStr )
{
sStr.SetLength( 0 );
while ( 1 )
{
// Wait for output if there's nothing left in our cached output.
if ( m_CachedOutput.GetBytesRemaining() == 0 && !WaitForOutput() )
return;
CacheOutput();
char *pStr = (char*)m_CachedOutput.PeekGet();
int nBytes = m_CachedOutput.GetBytesRemaining();
// No more stuff available and the process is dead?
if ( nBytes == 0 && m_pProcess->IsComplete() )
break;
int i;
for ( i=0; i < nBytes; i++ )
{
if ( pStr[i] == '\n' )
break;
}
if ( i < nBytes )
{
// We hit a line ending.
int nBytesToRead = i;
if ( nBytesToRead > 0 && pStr[nBytesToRead-1] == '\r' )
--nBytesToRead;
CUtlString sTemp;
sTemp.SetDirect( pStr, nBytesToRead );
sStr += sTemp;
m_CachedOutput.SeekGet( CUtlBuffer::SEEK_CURRENT, i+1 ); // Use i here because it'll be at the \n, not the \r\n.
if ( m_CachedOutput.GetBytesRemaining() == 0 )
m_CachedOutput.Purge();
return;
}
}
}
bool CProcessPipeRead::WaitForOutput()
{
if ( m_hRead == INVALID_HANDLE_VALUE )
{
Assert( false );
return false;
}
while ( GetActualProcessOutputSize() == 0 )
{
if ( m_pProcess->IsComplete() )
return false;
else
ThreadSleep( 1 );
}
return true;
}
void CProcessPipeRead::CacheOutput()
{
int nBytes = GetActualProcessOutputSize();
if ( nBytes == 0 )
return;
int nPut = m_CachedOutput.TellPut();
m_CachedOutput.EnsureCapacity( nPut + nBytes );
int nBytesRead = GetActualProcessOutput( (char*)m_CachedOutput.PeekPut(), nBytes );
Assert( nBytesRead == nBytes );
m_CachedOutput.SeekPut( CUtlBuffer::SEEK_HEAD, nPut + nBytesRead );
}
//-----------------------------------------------------------------------------
// Methods used to read output back from a process
//-----------------------------------------------------------------------------
int CProcessPipeRead::GetActualProcessOutputSize()
{
if ( m_hRead == INVALID_HANDLE_VALUE )
{
Assert( false );
return 0;
}
DWORD dwCount = 0;
if ( !PeekNamedPipe( m_hRead, 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",
m_pProcess->m_Info.m_CommandLine.Get(), GetErrorString( buf, sizeof(buf) ) );
return 0;
}
return (int)dwCount;
}
int CProcessPipeRead::GetProcessOutput( void *pBuf, int nBytes )
{
int nCachedBytes = MIN( nBytes, m_CachedOutput.TellPut() );
int nPipeBytes = nBytes - nCachedBytes;
// Read from the cached buffer.
m_CachedOutput.Get( pBuf, nCachedBytes );
if ( m_CachedOutput.GetBytesRemaining() == 0 )
{
m_CachedOutput.Purge();
}
// Read from the pipe.
int nPipedBytesRead = GetActualProcessOutput( (char*)pBuf + nCachedBytes, nPipeBytes );
return nCachedBytes + nPipedBytesRead;
}
int CProcessPipeRead::GetActualProcessOutput( void *pBuf, int nBufLen )
{
if ( m_hRead == INVALID_HANDLE_VALUE )
{
Assert( false );
return 0;
}
// ReadFile can deadlock in a blaze of awesomeness if you ask for 0 bytes.
if ( nBufLen == 0 )
return 0;
DWORD nBytesRead = 0;
BOOL bSuccess = ReadFile( m_hRead, pBuf, nBufLen, &nBytesRead, NULL );
if ( bSuccess )
{
// ReadFile -should- block until it gets the number of bytes requested OR until
// the process is complete. So if it didn't return the # of bytes requested,
// then make sure the process is complete.
if ( (int)nBytesRead != nBufLen )
{
Assert( m_pProcess->IsComplete() );
}
}
else
{
Assert( false );
}
return (int)nBytesRead;
}
// ------------------------------------------------------------------------------------- //
// CProcess implementation.
// ------------------------------------------------------------------------------------- //
CProcess::CProcess( CProcessUtils *pProcessUtils, const ProcessInfo_t &info )
{
m_pProcessUtils = pProcessUtils;
m_Info = info;
m_StdoutRead.m_pProcess = this;
m_StdoutRead.m_hRead = info.m_hChildStdoutRd;
m_StderrRead.m_pProcess = this;
m_StderrRead.m_hRead = info.m_hChildStderrRd;
}
void CProcess::Release()
{
if ( !( m_Info.m_fFlags & STARTPROCESS_NOAUTOKILL ) )
{
Abort();
}
ProcessInfo_t& info = m_Info;
CloseHandle( info.m_hChildStderrRd );
CloseHandle( info.m_hChildStderrWr );
CloseHandle( info.m_hChildStdinRd );
CloseHandle( info.m_hChildStdinWr );
CloseHandle( info.m_hChildStdoutRd );
CloseHandle( info.m_hChildStdoutWr );
m_pProcessUtils->OnProcessDelete( this );
delete this;
}
void CProcess::Abort()
{
if ( !IsComplete() )
{
TerminateProcess( m_Info.m_hProcess, 1 );
}
}
bool CProcess::IsComplete()
{
HANDLE h = m_Info.m_hProcess;
return ( WaitForSingleObject( h, 0 ) != WAIT_TIMEOUT );
}
int CProcess::WaitUntilComplete()
{
ProcessInfo_t &info = m_Info;
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, 50 ) == WAIT_TIMEOUT )
{
m_StdoutRead.CacheOutput();
if ( m_StderrRead.IsValid() )
m_StderrRead.CacheOutput();
}
}
return GetExitCode();
}
int CProcess::WriteStdin( char *pBuf, int nBufLen )
{
ProcessInfo_t& info = m_Info;
if ( info.m_hChildStdinWr == INVALID_HANDLE_VALUE )
{
Assert( false );
return 0;
}
DWORD nBytesWritten = 0;
if ( WriteFile( info.m_hChildStdinWr, pBuf, nBufLen, &nBytesWritten, NULL ) )
return (int)nBytesWritten;
else
return 0;
}
IPipeRead* CProcess::GetStdout()
{
return &m_StdoutRead;
}
IPipeRead* CProcess::GetStderr()
{
return &m_StderrRead;
}
int CProcess::GetExitCode()
{
ProcessInfo_t &info = m_Info;
DWORD nExitCode;
BOOL bOk = GetExitCodeProcess( info.m_hProcess, &nExitCode );
if ( !bOk || nExitCode == STILL_ACTIVE )
return -1;
return nExitCode;
}
//-----------------------------------------------------------------------------
// Initialize, shutdown process system
//-----------------------------------------------------------------------------
InitReturnVal_t CProcessUtils::Init()
{
InitReturnVal_t nRetVal = BaseClass::Init();
if ( nRetVal != INIT_OK )
return nRetVal;
m_bInitialized = true;
return INIT_OK;
}
void CProcessUtils::Shutdown()
{
Assert( m_bInitialized );
Assert( m_Processes.Count() == 0 );
// Delete any lingering processes.
while ( m_Processes.Count() > 0 )
{
m_Processes[ m_Processes.Head() ]->Release();
}
m_bInitialized = false;
return BaseClass::Shutdown();
}
void CProcessUtils::OnProcessDelete( CProcess *pProcess )
{
m_Processes.Remove( pProcess->m_nProcessesIndex );
}
CProcess *CProcessUtils::CreateProcess( ProcessInfo_t &info, int fFlags, const char *pWorkingDir )
{
STARTUPINFO si;
memset(&si, 0, sizeof si);
si.cb = sizeof(si);
if ( fFlags & STARTPROCESS_CONNECTSTDPIPES )
{
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdInput = info.m_hChildStdinRd;
si.hStdError = info.m_hChildStderrWr;
si.hStdOutput = info.m_hChildStdoutWr;
}
DWORD dwCreateProcessFlags = 0;
if ( !( fFlags & STARTPROCESS_SHARE_CONSOLE ) )
{
dwCreateProcessFlags |= DETACHED_PROCESS;
}
PROCESS_INFORMATION pi;
if ( ::CreateProcess( NULL, info.m_CommandLine.Get(), NULL, NULL, TRUE, dwCreateProcessFlags, NULL, pWorkingDir, &si, &pi ) )
{
info.m_hProcess = pi.hProcess;
CProcess *pProcess = new CProcess( this, info );
pProcess->m_nProcessesIndex = m_Processes.AddToTail( pProcess );
return pProcess;
}
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 NULL;
}
//-----------------------------------------------------------------------------
// Options for compilation
//-----------------------------------------------------------------------------
IProcess* CProcessUtils::StartProcess( const char *pCommandLine, int fFlags, const char *pWorkingDir )
{
Assert( m_bInitialized );
ProcessInfo_t info;
info.m_CommandLine = pCommandLine;
if ( !(fFlags & STARTPROCESS_CONNECTSTDPIPES) )
{
info.m_hChildStderrRd = 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, fFlags, pWorkingDir );
}
SECURITY_ATTRIBUTES saAttr;
// Set the bInheritHandle flag so pipe handles are inherited.
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
DWORD nPipeSize = 0; // default size
if ( fFlags & STARTPROCESS_FATPIPES )
{
nPipeSize = 1024*1024;
}
// Create a pipe for the child's STDOUT.
if ( CreatePipe( &info.m_hChildStdoutRd, &info.m_hChildStdoutWr, &saAttr, nPipeSize ) )
{
if ( CreatePipe( &info.m_hChildStdinRd, &info.m_hChildStdinWr, &saAttr, nPipeSize ) )
{
BOOL bSuccess = false;
if ( fFlags & STARTPROCESS_SEPARATE_STDERR )
{
bSuccess = CreatePipe( &info.m_hChildStderrRd, &info.m_hChildStderrWr, &saAttr, nPipeSize );
}
else
{
bSuccess = DuplicateHandle( GetCurrentProcess(), info.m_hChildStdoutWr, GetCurrentProcess(),
&info.m_hChildStderrWr, 0, TRUE, DUPLICATE_SAME_ACCESS );
info.m_hChildStderrRd = INVALID_HANDLE_VALUE;
}
if ( bSuccess )
{
IProcess *pProcess = CreateProcess( info, fFlags, pWorkingDir );
if ( pProcess )
return pProcess;
CloseHandle( info.m_hChildStderrRd );
CloseHandle( info.m_hChildStderrWr );
}
CloseHandle( info.m_hChildStdinRd );
CloseHandle( info.m_hChildStdinWr );
}
CloseHandle( info.m_hChildStdoutRd );
CloseHandle( info.m_hChildStdoutWr );
}
return NULL;
}
//-----------------------------------------------------------------------------
// Start up a process
//-----------------------------------------------------------------------------
IProcess* CProcessUtils::StartProcess( int argc, const char **argv, int fFlags, const char *pWorkingDir )
{
CUtlString commandLine;
for ( int i = 0; i < argc; ++i )
{
commandLine += argv[i];
if ( i != argc-1 )
{
commandLine += " ";
}
}
return StartProcess( commandLine.Get(), fFlags, pWorkingDir );
}
int CProcessUtils::SimpleRunProcess( const char *pCommandLine, const char *pWorkingDir, CUtlString *pStdout )
{
int nFlags = 0;
if ( pStdout )
nFlags |= STARTPROCESS_CONNECTSTDPIPES;
IProcess *pProcess = StartProcess( pCommandLine, nFlags, pWorkingDir );
if ( !pProcess )
return -1;
int nExitCode = pProcess->WaitUntilComplete();
if ( pStdout )
{
pProcess->GetStdout()->Read( *pStdout );
}
pProcess->Release();
return nExitCode;
}
// Translate '\r\n' to '\n'.
void TranslateLinefeedsToUnix( char *pBuffer )
{
char *pOut = pBuffer;
while ( *pBuffer )
{
if ( pBuffer[0] == '\r' && pBuffer[1] == '\n' )
{
*pOut = '\n';
++pBuffer;
}
else
{
*pOut = *pBuffer;
}
++pBuffer;
++pOut;
}
*pOut = 0;
}

71
vstdlib/prx.cpp Normal file
View File

@ -0,0 +1,71 @@
#include <cellstatus.h>
#include <sys/prx.h>
#include <sys/exportcplusplus.h>
#include <stdio.h>
#include "tier0/threadtools.h"
#include "vstdlib/random.h"
#include "ps3/ps3_helpers.h"
#ifdef _DEBUG
#define vstdlib_ps3 vstdlib_dbg
#else
#define vstdlib_ps3 vstdlib_rel
#endif
// macro to redirect name substitution
PS3_PRX_SYS_MODULE_INFO_FULLMACROREPLACEMENTHELPER( vstdlib );
SYS_MODULE_START( _vstdlib_ps3_prx_entry );
SYS_LIB_DECLARE( vstdlib_ps3, SYS_LIB_AUTO_EXPORT | SYS_LIB_WEAK_IMPORT );
SYS_LIB_EXPORT( RandomSeed, vstdlib_ps3 );
SYS_LIB_EXPORT( RandomFloat, vstdlib_ps3 );
SYS_LIB_EXPORT( RandomFloatExp, vstdlib_ps3 );
SYS_LIB_EXPORT( RandomInt, vstdlib_ps3 );
SYS_LIB_EXPORT( RandomGaussianFloat, vstdlib_ps3 );
SYS_LIB_EXPORT( InstallUniformRandomStream, vstdlib_ps3 );
SYS_LIB_EXPORT( KeyValuesSystem, vstdlib_ps3 );
SYS_LIB_EXPORT( VStdLib_GetICVarFactory, vstdlib_ps3 );
SYS_LIB_EXPORT( RunThreadPoolTests, vstdlib_ps3 );
SYS_LIB_EXPORT( CreateNewThreadPool, vstdlib_ps3 );
SYS_LIB_EXPORT( DestroyThreadPool, vstdlib_ps3 );
SYS_LIB_EXPORT_VAR( g_pThreadPool, vstdlib_ps3 );
SYS_LIB_EXPORT( V_UTF8ToUnicode, vstdlib_ps3 );
SYS_LIB_EXPORT( V_UnicodeToUTF8, vstdlib_ps3 );
SYS_LIB_EXPORT( V_UCS2ToUnicode, vstdlib_ps3 );
SYS_LIB_EXPORT( V_UCS2ToUTF8, vstdlib_ps3 );
SYS_LIB_EXPORT( V_UnicodeToUCS2, vstdlib_ps3 );
SYS_LIB_EXPORT( V_UTF8ToUCS2, vstdlib_ps3 );
SYS_LIB_EXPORT( V_UTF8_strncpy, vstdlib_ps3 );
SYS_LIB_EXPORT( Coroutine_Create, vstdlib_ps3 );
SYS_LIB_EXPORT( Coroutine_Continue, vstdlib_ps3 );
SYS_LIB_EXPORT( Coroutine_YieldToMain, vstdlib_ps3 );
SYS_LIB_EXPORT( Coroutine_DebugAssert, vstdlib_ps3 );
SYS_LIB_EXPORT( Coroutine_DebugBreak, vstdlib_ps3 );
// BEGIN PICKUP
// You must run build_prxexport_snc.bat AFTER building vstdlib to regenerate prxexport.c, and THEN rebuild vstdlib again for the changes to take effect.
// This is required to get C++ class exports working
SYS_LIB_EXPORTPICKUP_CLASS( "CUniformRandomStream@", vstdlib_ps3 );
SYS_LIB_EXPORTPICKUP_CLASS( "CGaussianRandomStream@", vstdlib_ps3 );
// END PICKUP
extern "C" int _vstdlib_ps3_prx_entry( unsigned int args, void *pArg )
{
return SYS_PRX_RESIDENT;
}
void _vstdlib_ps3_prx_required_for_linking()
{
}

28
vstdlib/prxexport.cpp Normal file
View File

@ -0,0 +1,28 @@
#include "tier0/platform.h"
#ifndef _PS3
#error "Error: _PS3 not defined in PS3-specific file"
#endif // _PS3
#ifdef _DEBUG
#define vstdlib_ps3 vstdlib_dbg
#else
#define vstdlib_ps3 vstdlib_rel
#endif
#ifdef _DEBUG
#include "Debug_PS3/prxexport.inl"
#else
#include "Release_PS3/prxexport.inl"
#endif
extern void _vstdlib_ps3_prx_required_for_linking();
void _vstdlib_ps3_prx_required_for_linking_prx()
{
_vstdlib_ps3_prx_required_for_linking();
}

259
vstdlib/random.cpp Normal file
View File

@ -0,0 +1,259 @@
//===== Copyright <20> 1996-2005, 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 (x <= 1 || MAX_RANDOM_RANGE < x-1)
{
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)
//-----------------------------------------------------------------------------

13
vstdlib/vcover.cpp Normal file
View File

@ -0,0 +1,13 @@
//========== Copyright <20> 2005, Valve Corporation, All rights reserved. ========
//
// Purpose:
//
//=============================================================================
#include "vstdlib/vcover.h"
// NOTE: This has to be the last file included!
#include "tier0/memdbgon.h"
CVCoverage g_VCoverage;

1
vstdlib/vsi.nul Normal file
View File

@ -0,0 +1 @@
IMPORTANT: Do not remove the custom build step for this file

63
vstdlib/vstdlib.inc Normal file
View File

@ -0,0 +1,63 @@
//-----------------------------------------------------------------------------
// VSTDLIB.INC
//
// Project Script
//-----------------------------------------------------------------------------
$Project
{
$Folder "Source Files"
{
$File "xbox\___FirstModule.cpp" [$X360]
$File "concommandhash.h"
$File "coroutine.cpp" [!$X360]
{
$Configuration
{
$Compiler [!$PS3]
{
$BasicRuntimeChecks "Default"
}
}
}
$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 "cvar.cpp"
$File "jobthread.cpp"
$File "keyvaluessystem.cpp"
$File "processutils.cpp" [$WINDOWS]
$File "random.cpp"
$File "vcover.cpp"
$File "eventsystem.cpp"
$File "prx.cpp" [$PS3]
$File "prxexport.cpp" [$PS3]
$File "vstrtools.cpp"
$File "vstdlib_strtools.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\random.h"
$File "$SRCDIR\public\vstdlib\vcover.h"
$File "$SRCDIR\public\vstdlib\vstdlib.h"
$File "$SRCDIR\public\vstdlib\ieventsystem.h"
$File "$SRCDIR\public\vstdlib\vstrtools.h"
}
}

152
vstdlib/vstdlib.vpc Normal file
View File

@ -0,0 +1,152 @@
//-----------------------------------------------------------------------------
// VSTDLIB.VPC
//
// Project Script
//-----------------------------------------------------------------------------
$Macro SRCDIR ".."
$Macro OUTBINDIR "$SRCDIR\..\game\bin"
$Macro NOSCHEMACOMPILER "1"
$include "$SRCDIR\vpc_scripts\source_dll_base.vpc"
$Configuration
{
$General [$X360]
{
// 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 "$LIBPUBLIC\$(TargetName).lib" [$X360]
$GameOutputFile "$OUTBINDIR/$_IMPLIB_DLL_PREFIX$OUTBINNAME$_DLL_EXT"
}
$Compiler
{
$PreprocessorDefinitions "$BASE;VSTDLIB_DLL_EXPORT"
$GCC_ExtraCompilerFlags "-fno-stack-protector" [$OSXALL]
$GCC_ExtraCompilerFlags "-U_FORTIFY_SOURCE -fno-stack-protector" [$LINUXALL]
}
$Linker [!$PS3]
{
$AdditionalDependencies "$BASE odbc32.lib odbccp32.lib" [$WINDOWS]
// 360 publishes the import library via a post build step
$ImportLibrary "$(TargetDir)\$(TargetName).lib" [$X360]
// Everyone but 360 build right to the location.
$ImportLibrary "$LIBPUBLIC\$_IMPLIB_PREFIX$OUTBINNAME$_IMPLIB_EXT" [!$X360]
// 360 will auto generate a def file for this import library
$ModuleDefinitionFile " " [$X360]
$AdditionalOptions "$BASE /AUTODEF:xbox\xbox.def" [$X360]
$SystemFrameworks "CoreServices"
$SystemLibraries "iconv" [$OSXALL]
}
$Linker [$PS3||$POSIX]
{
$ImportLibrary "$LIBPUBLIC\$_IMPLIB_PREFIX$OUTBINNAME$_IMPLIB_EXT"
$AdditionalDependencies "$BASE libl10n_stub.a" [$PS3]
}
$PreLinkEvent [!$POSIX]
{
$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"
}
$PreLinkEvent [$PS3]
{
$CommandLine "$BASE"
$Description "NOTE - If PRX linking fails, make sure your vstdlib_staticlib is building in same solution configuration (debug/release) as vstdlib prx."
}
$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 $LIBPUBLIC\$(TargetName).lib" "\n" \
"$BASE"
}
$PostBuildEvent [$PS3]
{
// Publish the import lib
$CommandLine "if exist $(TargetName)_stub.a move $(TargetName)_stub.a $LIBPUBLIC\$(TargetName).lib" "\n" \
"if exist $(TargetName)_verlog.txt del $(TargetName)_verlog.txt" "\n" \
"$BASE" "\n"
}
$General [$POSIX]
{
$GameOutputFile "$OUTBINDIR/$_IMPLIB_DLL_PREFIX$OUTBINNAME$_DLL_EXT"
}
$General [$PS3]
{
$AdditionalProjectDependencies "$BASE;vstdlib_staticlib"
}
}
$Configuration "Release"
{
$PreBuildEvent [$PS3]
{
// Clear potentially stale verlog files
$CommandLine "ppu-lv2-prx-exportpickup -o Release_PS3/prxexport.inl Release_vstdlibstaticlib_PS3/prx.obj Release_vstdlibstaticlib_PS3/random.obj Release_vstdlibstaticlib_PS3/coroutine.obj" "\n" \
"if exist vstdlib_rel_verlog.txt del vstdlib_rel_verlog.txt" "\n" \
"$BASE" "\n"
}
$Linker [$PS3]
{
$AdditionalDependencies "$BASE &quot;Release_vstdlibstaticlib_PS3/vstdlib_staticlib_ps3.lib&quot;"
}
}
$Configuration "Debug"
{
$PreBuildEvent [$PS3]
{
// Clear potentially stale verlog files
$CommandLine "ppu-lv2-prx-exportpickup -o Debug_PS3/prxexport.inl Debug_vstdlibstaticlib_PS3/prx.obj Debug_vstdlibstaticlib_PS3/random.obj Debug_vstdlibstaticlib_PS3/coroutine.obj" "\n" \
"if exist vstdlib_dbg_verlog.txt del vstdlib_dbg_verlog.txt" "\n" \
"$BASE" "\n"
}
$Linker [$PS3]
{
$AdditionalDependencies "$BASE &quot;Debug_vstdlibstaticlib_PS3/vstdlib_staticlib_ps3.lib&quot;"
}
}
$Include "vstdlib.inc"
$Project
{
$Folder "PS3 Files" [$PS3]
{
$File "prxexport.cpp"
}
$Folder "Source Files"
{
-$File "$SRCDIR\common\ps3\prx.cpp" [$PS3]
}
$folder "Link Libraries"
{
-$ImpLib "$LIBPUBLIC\vstdlib"
}
}
$Project "vstdlib"
{
}

View File

@ -0,0 +1,13 @@
"vpc_cache"
{
"CacheVersion" "1"
"win32"
{
"CRCFile" "vstdlib.vcxproj.vpc_crc"
"OutputFiles"
{
"0" "vstdlib.vcxproj"
"1" "vstdlib.vcxproj.filters"
}
}
}

View File

@ -0,0 +1,13 @@
//-----------------------------------------------------------------------------
// vstdlib_exclude.vpc
//
// Project Script
//-----------------------------------------------------------------------------
$Project
{
$Folder "Link Libraries"
{
-$Implib vstdlib [!$IS_LIB_PROJECT || $OSXALL]
}
}

View File

@ -0,0 +1,56 @@
//-----------------------------------------------------------------------------
// VSTDLIB_STATICLIB.VPC
//
// Project Script
//-----------------------------------------------------------------------------
$Macro SRCDIR ".."
$Macro OUTLIBNAME "vstdlib_staticlib"
$Macro NOSCHEMACOMPILER "1"
$include "$SRCDIR\vpc_scripts\source_lib_base.vpc"
$Configuration
{
$Compiler
{
$PreprocessorDefinitions "$BASE;VSTDLIB_DLL_EXPORT"
}
}
$Configuration "Release"
{
$Librarian
{
$OutputFile "Release_vstdlibstaticlib_PS3\$OUTLIBNAME_ps3.lib"
}
$General
{
$OutputDirectory ".\Release_vstdlibstaticlib_PS3" [$PS3]
$IntermediateDirectory ".\Release_vstdlibstaticlib_PS3" [$PS3]
}
}
$Configuration "Debug"
{
$Librarian
{
$OutputFile "Debug_vstdlibstaticlib_PS3\$OUTLIBNAME_ps3.lib"
}
$General
{
$OutputDirectory ".\Debug_vstdlibstaticlib_PS3" [$PS3]
$IntermediateDirectory ".\Debug_vstdlibstaticlib_PS3" [$PS3]
}
}
$Include "vstdlib.inc"
$Project "vstdlib_staticlib"
{
$Folder "Source Files"
{
-$File "prxexport.cpp" [$PS3]
}
}

View File

@ -0,0 +1,96 @@
//===================== Copyright (c) Valve Corporation. All Rights Reserved. ======================
//
// C runtime and standard library wrappers / equivalents / upgrades.
// Allows centralization of CRT and CRT-like code and consistent behavior across platforms.
//
//==================================================================================================
#include "tier1/strtools.h"
#include "tier1/utlbuffer.h"
#include "tier1/utlstring.h"
#include "tier1/utlvector.h"
#include "tier1/fmtstr.h"
#include "tier1/characterset.h"
// Prints out a memory dump where stuff that's ascii is human readable, etc.
void V_LogMultiline(bool input, char const *label, const char *data, size_t len, CUtlString &output);
// If the string above is really long, you can't pass it into varargs print routines, so this splits everything out into smaller chunks
void V_LogMultilineToArray( bool input, char const *label, const char *data, size_t len, CUtlVector< CUtlString, CUtlMemory<CUtlString, int> > &output )
{
static const char HEX[] = "0123456789abcdef";
const char * direction = (input ? " << " : " >> ");
const size_t LINE_SIZE = 24;
char hex_line[LINE_SIZE * 9 / 4 + 2], asc_line[LINE_SIZE + 1];
while (len > 0)
{
V_memset(asc_line, ' ', sizeof(asc_line));
V_memset(hex_line, ' ', sizeof(hex_line));
size_t line_len = MIN(len, LINE_SIZE);
for (size_t i=0; i<line_len; ++i) {
unsigned char ch = static_cast<unsigned char>(data[i]);
asc_line[i] = ( V_isprint(ch) && !V_iscntrl(ch) ) ? data[i] : '.';
hex_line[i*2 + i/4] = HEX[ch >> 4];
hex_line[i*2 + i/4 + 1] = HEX[ch & 0xf];
}
asc_line[sizeof(asc_line)-1] = 0;
hex_line[sizeof(hex_line)-1] = 0;
CUtlString s;
s.Format( "%s %s %s %s\n", label, direction, asc_line, hex_line );
output.AddToTail( s );
data += line_len;
len -= line_len;
}
}
char* AllocString(const char *pStr, int nMaxChars);
void V_SplitString2InPlace( char *pString, const char **pSeparators, int nSeparators, CUtlVector<const char *> &outStrings )
{
// We must pass in an empty outStrings buffer or call outStrings.PurgeAndDeleteElements between
// calls.
Assert( outStrings.Count() == 0 );
// This will make outStrings empty but it will not free any memory that the elements were pointing to.
outStrings.Purge();
char *pCurPos = pString;
while ( 1 )
{
int iFirstSeparator = -1;
char *pFirstSeparator = 0;
for ( int i=0; i < nSeparators; i++ )
{
char *pTest = V_stristr_fast( pCurPos, pSeparators[i] );
if ( pTest && (!pFirstSeparator || pTest < pFirstSeparator) )
{
iFirstSeparator = i;
pFirstSeparator = pTest;
}
}
if ( pFirstSeparator )
{
// Split on this separator and continue on.
int separatorLen = V_strlen( pSeparators[iFirstSeparator] );
V_memset( pFirstSeparator, 0, separatorLen );
outStrings.AddToTail( pCurPos );
pCurPos = pFirstSeparator + separatorLen;
}
else
{
// Copy the rest of the string
if ( *pCurPos != '\0' )
{
outStrings.AddToTail( pCurPos );
}
return;
}
}
}
void V_SplitStringInPlace( IN_Z char *pString, IN_Z const char *pSeparator, CUtlVector<const char *> &outStrings )
{
V_SplitString2InPlace( pString, &pSeparator, 1, outStrings );
}

319
vstdlib/vstrtools.cpp Normal file
View File

@ -0,0 +1,319 @@
#include "tier0/dbg.h"
#include "vstdlib/vstrtools.h"
#if defined( _WIN32 ) && !defined( _X360 )
#include <windows.h>
#endif
#if defined(POSIX) && !defined(_PS3)
#include <iconv.h>
#endif
#ifdef _PS3
#include <cell/sysmodule.h>
#include <cell/l10n.h>
class DummyInitL10N
{
public:
DummyInitL10N()
{
int ret = cellSysmoduleLoadModule( CELL_SYSMODULE_L10N );
if( ret != CELL_OK )
{
Warning( "Cannot initialize l10n, unicode services will not work. Error %d\n", ret );
}
}
~DummyInitL10N()
{
cellSysmoduleUnloadModule( CELL_SYSMODULE_L10N );
}
}s_dummyInitL10N;
#endif
//-----------------------------------------------------------------------------
// Purpose: Converts a UTF8 string into a unicode string
//-----------------------------------------------------------------------------
int V_UTF8ToUnicode( const char *pUTF8, wchar_t *pwchDest, int cubDestSizeInBytes )
{
if ( !pUTF8 )
return 0;
AssertValidStringPtr(pUTF8);
AssertValidWritePtr(pwchDest);
pwchDest[0] = 0;
#ifdef _WIN32
int cchResult = MultiByteToWideChar( CP_UTF8, 0, pUTF8, -1, pwchDest, cubDestSizeInBytes / sizeof(wchar_t) );
#elif defined( _PS3 )
size_t cchResult = cubDestSizeInBytes / sizeof( uint16 ), cchSrc = V_strlen( pUTF8 ) + 1;
L10nResult result = UTF8stoUCS2s( ( const uint8 *) pUTF8, &cchSrc, ( uint16 * ) pwchDest, &cchResult );
Assert( result == ConversionOK );
cchResult *= sizeof( uint16 );
#elif POSIX
iconv_t conv_t = iconv_open( "UTF-32LE", "UTF-8" );
int cchResult = -1;
size_t nLenUnicde = cubDestSizeInBytes;
size_t nMaxUTF8 = strlen(pUTF8) + 1;
char *pIn = (char *)pUTF8;
char *pOut = (char *)pwchDest;
if ( conv_t > 0 )
{
cchResult = 0;
size_t nInputCharCount = nMaxUTF8;
cchResult = iconv( conv_t, &pIn, &nMaxUTF8, &pOut, &nLenUnicde );
iconv_close( conv_t );
if ( (int)cchResult < 0 )
cchResult = 0;
else
cchResult = nInputCharCount - nMaxUTF8; // nMaxUTF8 is decremented for each converted character. We want to return the count of conversions to match windows.
}
#endif
pwchDest[(cubDestSizeInBytes / sizeof(wchar_t)) - 1] = 0;
return cchResult;
}
//-----------------------------------------------------------------------------
// Purpose: Converts a unicode string into a UTF8 (standard) string
//-----------------------------------------------------------------------------
int V_UnicodeToUTF8( const wchar_t *pUnicode, char *pUTF8, int cubDestSizeInBytes )
{
AssertValidStringPtr(pUTF8, cubDestSizeInBytes);
AssertValidReadPtr(pUnicode);
if ( cubDestSizeInBytes > 0 )
{
pUTF8[0] = 0;
}
#ifdef _WIN32
int cchResult = WideCharToMultiByte( CP_UTF8, 0, pUnicode, -1, pUTF8, cubDestSizeInBytes, NULL, NULL );
#elif defined( _PS3 )
size_t cchResult = cubDestSizeInBytes, cchSrc = V_wcslen( pUnicode ) + 1;
L10nResult result = UCS2stoUTF8s( ( const uint16 *) pUnicode, &cchSrc, ( uint8 * ) pUTF8, &cchResult );
Assert( result == ConversionOK );
#elif POSIX
int cchResult = 0;
if ( pUnicode && pUTF8 )
{
iconv_t conv_t = iconv_open( "UTF-8", "UTF-32LE" );
size_t nLenUnicde = ( wcslen(pUnicode) + 1 ) * sizeof(wchar_t); // 4 bytes per wchar vs. 1 byte for utf8 for simple english
size_t nMaxUTF8 = cubDestSizeInBytes;
char *pIn = (char *)pUnicode;
char *pOut = (char *)pUTF8;
if ( conv_t > 0 )
{
cchResult = iconv( conv_t, &pIn, &nLenUnicde, &pOut, &nMaxUTF8 );
iconv_close( conv_t );
if ( (int)cchResult < 0 )
cchResult = 0;
else
cchResult = nMaxUTF8;
}
}
#endif
if ( cubDestSizeInBytes > 0 )
{
pUTF8[cubDestSizeInBytes - 1] = 0;
}
return cchResult;
}
//-----------------------------------------------------------------------------
// Purpose: Converts a ucs2 string to a unicode (wchar_t) one, no-op on win32
//-----------------------------------------------------------------------------
int V_UCS2ToUnicode( const ucs2 *pUCS2, wchar_t *pUnicode, int cubDestSizeInBytes )
{
AssertValidWritePtr(pUnicode);
AssertValidReadPtr(pUCS2);
pUnicode[0] = 0;
#if defined( _WIN32 ) || defined( _PS3 )
int lenUCS2 = V_wcslen( pUCS2 );
int cchResult = MIN( (lenUCS2+1)*( int )sizeof(ucs2), cubDestSizeInBytes );
V_wcsncpy( (wchar_t*)pUCS2, pUnicode, cchResult );
#else
iconv_t conv_t = iconv_open( "UCS-4LE", "UCS-2LE" );
int cchResult = -1;
size_t nLenUnicde = cubDestSizeInBytes;
size_t nMaxUTF8 = cubDestSizeInBytes;
char *pIn = (char *)pUCS2;
char *pOut = (char *)pUnicode;
if ( conv_t > 0 )
{
cchResult = 0;
cchResult = iconv( conv_t, &pIn, &nLenUnicde, &pOut, &nMaxUTF8 );
iconv_close( conv_t );
if ( (int)cchResult < 0 )
cchResult = 0;
else
cchResult = nMaxUTF8;
}
#endif
pUnicode[(cubDestSizeInBytes / sizeof(wchar_t)) - 1] = 0;
return cchResult;
}
//-----------------------------------------------------------------------------
// Purpose: Converts a wchar_t string into a UCS2 string -noop on windows
//-----------------------------------------------------------------------------
int V_UnicodeToUCS2( const wchar_t *pUnicode, int cubSrcInBytes, char *pUCS2, int cubDestSizeInBytes )
{
// TODO: MACMERGE: Figure out how to convert from 2-byte Win32 wchars to platform wchar_t type that can be 4 bytes
#if defined( _WIN32 ) || defined( _PS3 )
int cchResult = MIN( cubSrcInBytes, cubDestSizeInBytes );
V_wcsncpy( (wchar_t*)pUCS2, pUnicode, cchResult );
#elif defined (POSIX)
iconv_t conv_t = iconv_open( "UCS-2LE", "UTF-32LE" );
size_t cchResult = -1;
size_t nLenUnicde = cubSrcInBytes;
size_t nMaxUCS2 = cubDestSizeInBytes;
char *pIn = (char*)pUnicode;
char *pOut = pUCS2;
if ( conv_t > 0 )
{
cchResult = 0;
cchResult = iconv( conv_t, &pIn, &nLenUnicde, &pOut, &nMaxUCS2 );
iconv_close( conv_t );
if ( (int)cchResult < 0 )
cchResult = 0;
else
cchResult = cubSrcInBytes / sizeof( wchar_t );
}
#endif
return cchResult;
}
// UTF-8 encodes each character (code point) in 1 to 4 octets (8-bit bytes).
// The first 128 characters of the Unicode character set (which correspond directly to the ASCII) use a single octet with the same binary value as in ASCII.
// url:http://en.wikipedia.org/wiki/UTF-8
#define MAX_UTF8_CHARACTER_BYTES 4
//-----------------------------------------------------------------------------
// Purpose: Converts a ucs-2 (windows wchar_t) string into a UTF8 (standard) string
//-----------------------------------------------------------------------------
VSTRTOOLS_INTERFACE int V_UCS2ToUTF8( const ucs2 *pUCS2, char *pUTF8, int cubDestSizeInBytes )
{
AssertValidStringPtr(pUTF8, cubDestSizeInBytes);
AssertValidReadPtr(pUCS2);
Assert( cubDestSizeInBytes >= 1 ); // must have at least 1 byte to write the terminator character
pUTF8[0] = '\0';
#ifdef _WIN32
// under win32 wchar_t == ucs2, sigh
int cchResult = WideCharToMultiByte( CP_UTF8, 0, pUCS2, -1, pUTF8, cubDestSizeInBytes, NULL, NULL );
#elif defined( _PS3 )
size_t cchResult = cubDestSizeInBytes, cchSrc = V_wcslen( pUCS2 ) + 1;
L10nResult result = UCS2stoUTF8s( ( const uint16 *) pUCS2, &cchSrc, ( uint8 * ) pUTF8, &cchResult );
Assert( result == ConversionOK );
#elif defined(POSIX)
iconv_t conv_t = iconv_open( "UTF-8", "UCS-2LE" );
size_t cchResult = -1;
size_t nLenUnicde = cubDestSizeInBytes;
size_t nMaxUTF8 = cubDestSizeInBytes;
char *pIn = (char *)pUCS2;
char *pOut = (char *)pUTF8;
if ( conv_t > 0 )
{
cchResult = 0;
cchResult = iconv( conv_t, &pIn, &nLenUnicde, &pOut, &nMaxUTF8 );
iconv_close( conv_t );
if ( (int)cchResult < 0 )
cchResult = 0;
else
cchResult = nMaxUTF8;
}
#endif
pUTF8[cubDestSizeInBytes - 1] = '\0';
return cchResult;
}
//-----------------------------------------------------------------------------
// Purpose: Converts a UTF8 to ucs-2 (windows wchar_t)
//-----------------------------------------------------------------------------
VSTRTOOLS_INTERFACE int V_UTF8ToUCS2( const char *pUTF8, int cubSrcInBytes, ucs2 *pUCS2, int cubDestSizeInBytes )
{
AssertValidStringPtr(pUTF8, cubDestSizeInBytes);
AssertValidReadPtr(pUCS2);
pUCS2[0] = 0;
#ifdef _WIN32
// under win32 wchar_t == ucs2, sigh
int cchResult = MultiByteToWideChar( CP_UTF8, 0, pUTF8, -1, pUCS2, cubDestSizeInBytes / sizeof(wchar_t) );
#elif defined( _PS3 )
size_t cchResult = cubDestSizeInBytes / sizeof( uint16 ), cchSrc = cubSrcInBytes;
L10nResult result = UTF8stoUCS2s( ( const uint8 *) pUTF8, &cchSrc, ( uint16 * ) pUCS2, &cchResult );
Assert( result == ConversionOK );
cchResult *= sizeof( uint16 );
#elif defined(POSIX)
iconv_t conv_t = iconv_open( "UCS-2LE", "UTF-8" );
size_t cchResult = -1;
size_t nLenUnicde = cubSrcInBytes;
size_t nMaxUTF8 = cubDestSizeInBytes;
char *pIn = (char *)pUTF8;
char *pOut = (char *)pUCS2;
if ( conv_t > 0 )
{
cchResult = 0;
cchResult = iconv( conv_t, &pIn, &nLenUnicde, &pOut, &nMaxUTF8 );
iconv_close( conv_t );
if ( (int)cchResult < 0 )
cchResult = 0;
else
cchResult = cubSrcInBytes;
}
#endif
pUCS2[ (cubDestSizeInBytes/sizeof(ucs2)) - 1] = 0;
return cchResult;
}
//-----------------------------------------------------------------------------
// Purpose: copies at most nMaxBytes of the UTF-8 input data into the destination,
// ensuring that a trailing multi-byte sequence isn't truncated.
//-----------------------------------------------------------------------------
VSTRTOOLS_INTERFACE void * V_UTF8_strncpy( char *pDest, const char *pSrc, size_t nMaxBytes )
{
strncpy( pDest, pSrc, nMaxBytes );
// http://en.wikipedia.org/wiki/UTF-8
int end = nMaxBytes-1;
pDest[end] = 0;
// walk backwards, ignoring nulls
while ( end > 0 && pDest[end] == 0 )
--end;
// found a non-null - see if it's part of a multi-byte sequence
int nBytesSeen = 0;
while ( end >= 0 && ( pDest[end] & 0xC0 ) == 0x80 ) // utf8 multi-byte trailing characters begin with 10xxxxxx
{
nBytesSeen++;
--end;
}
int nBytesExpected = 0;
if ( ( pDest[end] & 0xC0 ) == 0xC0 ) // utf8 multi-byte character sequences begin with 11xxxxxx
{
for ( int i = 6; i > 1; --i )
{
if ( (char)( pDest[end] >> i ) & 0x1 )
++nBytesExpected;
else
break;
}
}
if ( nBytesExpected != nBytesSeen )
pDest[end] = 0;
return pDest;
}

View File

@ -0,0 +1,22 @@
//===== Copyright <20> 1996-2006, 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.
//===========================================================================//
// explicit definition, including the headers prevents this from occurring @1
#define DLL_EXPORT extern "C" __declspec( dllexport )
DLL_EXPORT void* CreateInterface(const char *pName, int *pReturnCode);
DLL_EXPORT void *CreateInterfaceThunk( const char *pName, int *pReturnCode );
// 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 );
}