uid issue
This commit is contained in:
76
vstdlib/_vpc_/manifest_vstdlib/win32/manifest.txt
Normal file
76
vstdlib/_vpc_/manifest_vstdlib/win32/manifest.txt
Normal 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
212
vstdlib/concommandhash.h
Normal 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
1070
vstdlib/coroutine.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1311
vstdlib/cvar.cpp
Normal file
1311
vstdlib/cvar.cpp
Normal file
File diff suppressed because it is too large
Load Diff
503
vstdlib/eventsystem.cpp
Normal file
503
vstdlib/eventsystem.cpp
Normal 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
120
vstdlib/getstackptr64.masm
Normal 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
1614
vstdlib/jobthread.cpp
Normal file
File diff suppressed because it is too large
Load Diff
620
vstdlib/keyvaluessystem.cpp
Normal file
620
vstdlib/keyvaluessystem.cpp
Normal 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
709
vstdlib/processutils.cpp
Normal 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
71
vstdlib/prx.cpp
Normal 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
28
vstdlib/prxexport.cpp
Normal 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
259
vstdlib/random.cpp
Normal 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
13
vstdlib/vcover.cpp
Normal 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
1
vstdlib/vsi.nul
Normal file
@ -0,0 +1 @@
|
||||
IMPORTANT: Do not remove the custom build step for this file
|
63
vstdlib/vstdlib.inc
Normal file
63
vstdlib/vstdlib.inc
Normal 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
152
vstdlib/vstdlib.vpc
Normal 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 "Release_vstdlibstaticlib_PS3/vstdlib_staticlib_ps3.lib""
|
||||
}
|
||||
}
|
||||
|
||||
$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 "Debug_vstdlibstaticlib_PS3/vstdlib_staticlib_ps3.lib""
|
||||
}
|
||||
}
|
||||
|
||||
$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"
|
||||
{
|
||||
}
|
13
vstdlib/vstdlib.vpc.vpc_cache
Normal file
13
vstdlib/vstdlib.vpc.vpc_cache
Normal file
@ -0,0 +1,13 @@
|
||||
"vpc_cache"
|
||||
{
|
||||
"CacheVersion" "1"
|
||||
"win32"
|
||||
{
|
||||
"CRCFile" "vstdlib.vcxproj.vpc_crc"
|
||||
"OutputFiles"
|
||||
{
|
||||
"0" "vstdlib.vcxproj"
|
||||
"1" "vstdlib.vcxproj.filters"
|
||||
}
|
||||
}
|
||||
}
|
13
vstdlib/vstdlib_exclude.vpc
Normal file
13
vstdlib/vstdlib_exclude.vpc
Normal file
@ -0,0 +1,13 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// vstdlib_exclude.vpc
|
||||
//
|
||||
// Project Script
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
$Project
|
||||
{
|
||||
$Folder "Link Libraries"
|
||||
{
|
||||
-$Implib vstdlib [!$IS_LIB_PROJECT || $OSXALL]
|
||||
}
|
||||
}
|
56
vstdlib/vstdlib_staticlib.vpc
Normal file
56
vstdlib/vstdlib_staticlib.vpc
Normal 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]
|
||||
}
|
||||
}
|
96
vstdlib/vstdlib_strtools.cpp
Normal file
96
vstdlib/vstdlib_strtools.cpp
Normal 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
319
vstdlib/vstrtools.cpp
Normal 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;
|
||||
}
|
22
vstdlib/xbox/___FirstModule.cpp
Normal file
22
vstdlib/xbox/___FirstModule.cpp
Normal 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 );
|
||||
}
|
Reference in New Issue
Block a user