|
|
//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: LCD support
//
//=====================================================================================//
#if defined( WIN32 ) && !defined( _GAMECONSOLE )
#include <windows.h>
#endif
#include "cbase.h"
#ifdef POSIX
#define HICON int
const int DT_LEFT = 1; const int DT_CENTER = 2; const int DT_RIGHT = 3; #endif
#include "hud_lcd.h"
#include "vgui_controls/Controls.h"
#include "vgui_controls/SectionedListPanel.h"
#include "vgui/ISurface.h"
#include "vgui/IScheme.h"
#include "vgui/IVGui.h"
#include "vgui/ILocalize.h"
#include "vgui/IPanel.h"
#include "c_team.h"
#include "c_playerresource.h"
#include "filesystem.h"
#include "g15/ig15.h"
#include "tier0/icommandline.h"
#if defined( _X360 )
#include "xbox/xbox_win32stubs.h"
#endif
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#define G15_RESOURCE_FILE "resource/g15.res"
#define G15_MODULE_NAME "bin/g15.dll"
#define SMALL_ITEM_HEIGHT 10
#define G15_DEFAULT_MAX_CHAT_HISTORY 4
CLCD gLCD; IHudLCD *hudlcd = IsGameConsole() ? NULL : &gLCD;
CON_COMMAND( g15_reload, "Reloads the Logitech G-15 Keyboard configs." ) { if ( !CommandLine()->FindParm( "-g15" ) ) { Msg( "Must run with -g15 to enable support for the LCD Keyboard\n" ); return; }
gLCD.Reload(); }
CON_COMMAND( g15_dumpplayer, "Spew player data." ) { if ( !CommandLine()->FindParm( "-g15" ) ) { Msg( "Must run with -g15 to enable support for the LCD Keyboard\n" ); return; }
gLCD.DumpPlayer(); }
static ConVar g15_update_msec( "g15_update_msec", "250", FCVAR_ARCHIVE, "Logitech G-15 Keyboard update interval." );
void CLCDItem::Wipe( IG15 *lcd ) { for ( int i = 0; i < m_Children.Count(); ++i ) { if ( m_Children[ i ]->m_Handle ) { lcd->RemoveAndDestroyObject( m_Children[ i ]->m_Handle ); }
m_Children[ i ]->Wipe( lcd );
delete m_Children[ i ]; }
m_Children.Purge();
} void CLCDItemAggregate::Create( IG15 *lcd ) { // Nothing
}
void CLCDItemAggregate::Wipe( IG15 *lcd ) { BaseClass::Wipe( lcd );
for ( int i = 0; i < m_Definition.Count(); ++i ) { m_Definition[ i ]->Wipe( lcd );
delete m_Definition[ i ]; }
m_Definition.Purge(); }
void CLCDItemAggregate::WipeChildrenOnly( IG15 *lcd ) { BaseClass::Wipe( lcd ); }
void CLCDItemIcon::Create( IG15 *lcd ) { #ifdef WIN32
m_Handle = lcd->AddIcon( (HICON)m_icon, w, h ); #else
m_Handle = lcd->AddIcon( (void *)m_icon, w, h ); #endif
lcd->SetOrigin( m_Handle, x, y ); lcd->SetVisible( m_Handle, false ); }
void CLCDItemText::Create( IG15 *lcd ) { m_Handle = lcd->AddText( G15_STATIC_TEXT, (G15TextSize)m_iSize, m_iAlign, w ); lcd->SetOrigin( m_Handle, x, y ); lcd->SetText( m_Handle, m_OriginalText ); lcd->SetVisible( m_Handle, false ); }
///-----------------------------------------------------------------------------
/// Constructor
///-----------------------------------------------------------------------------
CLCD::CLCD( void ) : m_lcd( NULL ), m_nCurrentPage( 0 ), m_nSubPage( 0 ), m_bHadPlayer( false ), m_dwNextUpdateTime( 0u ), m_nMaxChatHistory( G15_DEFAULT_MAX_CHAT_HISTORY ), m_pG15Module( 0 ), m_G15Factory( 0 ) { m_Size[ 0 ] = m_Size[ 1 ] = 0; }
///-----------------------------------------------------------------------------
/// Destructor
///-----------------------------------------------------------------------------
CLCD::~CLCD( void ) { }
void CLCD::Reload() { Shutdown();
Msg( "Reloading G15 config\n" );
Init(); }
///------------------------------------------------------------------------------
/// Initializes the LCD device, and sets up the text handles
///------------------------------------------------------------------------------
void CLCD::Init( void ) { if ( !CommandLine()->FindParm( "-g15" ) ) return;
if ( m_lcd ) return;
m_pG15Module = Sys_LoadModule( G15_MODULE_NAME ); if ( !m_pG15Module ) { return; }
m_G15Factory = Sys_GetFactory( m_pG15Module ); if ( !m_G15Factory ) { Shutdown(); return; }
m_lcd = reinterpret_cast< IG15 * >( m_G15Factory( G15_INTERFACE_VERSION, NULL ) ); if ( !m_lcd ) { Shutdown(); return; } m_lcd->GetLCDSize( m_Size[ 0 ], m_Size[ 1 ] ); m_nCurrentPage = 0; m_nSubPage = 0;
m_TextSizes.Insert( "small", G15_SMALL ); m_TextSizes.Insert( "medium", G15_MEDIUM ); m_TextSizes.Insert( "big", G15_BIG );
m_TextAlignments.Insert( "left", DT_LEFT ); m_TextAlignments.Insert( "center", DT_CENTER ); m_TextAlignments.Insert( "right", DT_RIGHT );
KeyValues *kv = new KeyValues( "G15" ); if ( kv->LoadFromFile( filesystem, G15_RESOURCE_FILE, "MOD" ) ) { char const *title = kv->GetString( "game", "Source Engine" ); m_nMaxChatHistory = clamp( 1, kv->GetInt( "chatlines", m_nMaxChatHistory ), 64 ); Assert( title ); m_Title = title; m_lcd->Init( m_Title.String() );
for ( KeyValues *sub = kv->GetFirstSubKey(); sub; sub = sub->GetNextKey() ) { char const *keyName = sub->GetName(); if ( !Q_stricmp( keyName, "game" ) ) { // Handled above!!!
} else if ( !Q_stricmp( keyName, "icons" ) ) { ParseIconMappings( sub ); } else if ( !Q_stricmp( keyName, "replace" ) ) { ParseReplacements( sub ); } else if ( !Q_stricmp( keyName, "page" ) ) { ParsePage( sub ); } } } kv->deleteThis();
UpdateChat();
Msg( "Logitech LCD Keyboard initialized\n" ); }
///--------------------------------------------------------------------------
/// Destroys the local EZ LCD Object
///--------------------------------------------------------------------------
void CLCD::Shutdown( void ) { for ( int i = 0; i < m_Pages.Count(); ++i ) { CLCDPage *page = m_Pages[ i ]; page->Wipe( m_lcd ); delete page; }
m_Pages.Purge();
if ( m_lcd ) { m_lcd->Shutdown(); m_lcd = NULL; }
m_TextSizes.Purge(); m_TextAlignments.Purge(); m_GlobalStats.Purge();
m_G15Factory = 0; if ( m_pG15Module ) { Sys_UnloadModule( m_pG15Module ); m_pG15Module = 0; } }
int CLCD::FindTitlePage() { for ( int i = 0; i < m_Pages.Count(); ++i ) { if ( m_Pages[ i ]->m_bTitlePage ) return i; }
return -1; }
bool CLCD::IsPageValid( int currentPage, C_BasePlayer *player ) { if ( m_Pages[ currentPage ]->m_bTitlePage && player ) return false;
if ( m_Pages[ currentPage ]->m_bRequiresPlayer && !player ) return false;
return true; }
///---------------------------------------------------------------------
/// Update routine
///---------------------------------------------------------------------
void CLCD::Update( void ) { if ( !m_lcd ) return ;
C_BasePlayer *player = C_BasePlayer::GetLocalPlayer(); bool hasplayer = player ? true : false;
bool changed = hasplayer != m_bHadPlayer; m_bHadPlayer = hasplayer;
int pageCount = m_Pages.Count();
int prevPage = m_nCurrentPage;
if ( pageCount > 0 ) { bool force = false; if ( changed && hasplayer ) { force = true; m_nCurrentPage = 0; m_nSubPage = 0; }
if ( !IsPageValid( m_nCurrentPage, player ) ) { force = true; } if ( m_lcd->ButtonTriggered( G15_BUTTON_1 ) || force ) { m_nSubPage = 0;
for ( int i = 0; i < pageCount; ++i ) { m_nCurrentPage = ( m_nCurrentPage + 1 ) % pageCount; if ( !IsPageValid( m_nCurrentPage, player ) ) continue;
break; } }
if ( m_lcd->ButtonTriggered( G15_BUTTON_2 ) ) { int pc = m_Pages[ m_nCurrentPage ]->m_nSubPageCount;
m_nSubPage = ( m_nSubPage + 1 ) % pc; } } else { m_nCurrentPage = -1; m_nSubPage = 0; }
bool pageChanged = prevPage != m_nCurrentPage;
unsigned int dwCurTime = (unsigned int)( 1000.0 * gpGlobals->realtime );
if ( m_lcd->IsConnected() ) { if ( dwCurTime >= m_dwNextUpdateTime || pageChanged ) { m_dwNextUpdateTime = dwCurTime + g15_update_msec.GetInt(); DisplayCurrentPage( dwCurTime ); } }
m_lcd->UpdateLCD( dwCurTime ); }
///--------------------------------------------------------------------------
///
///--------------------------------------------------------------------------
bool CLCD::IsConnected( void ) const { return m_lcd ? m_lcd->IsConnected() : false; }
void CLCD::ShowItems_R( CLCDPage *page, unsigned int dwCurTime, CUtlVector< CLCDItem * >& list, bool bShowItems ) { int itemCount = list.Count(); for ( int j = 0; j < itemCount; ++j ) { CLCDItem *item = list[ j ]; if ( !item->m_bActive ) continue;
if ( bShowItems ) { switch ( item->m_Type ) { default: break; case LCDITEM_TEXT: { CLCDItemText *txt = static_cast< CLCDItemText * >( item ); if ( txt ) { // Need to build updated text
CUtlString updated; CUtlString str = txt->m_OriginalText; BuildUpdatedText( str.String(), updated ); DoGlobalReplacements( updated ); ReduceParentheses( updated );
m_lcd->SetText( item->m_Handle, updated.String() ); } } break; case LCDITEM_ICON: { CLCDItemIcon *icon = static_cast< CLCDItemIcon * >( item ); if ( icon ) { // Need to build updated text
CUtlString updated; CUtlString str = icon->m_IconName; BuildUpdatedText( str.String(), updated ); DoGlobalReplacements( updated ); ReduceParentheses( updated );
int idx = m_Icons.Find( updated.String() ); if ( idx != m_Icons.InvalidIndex() ) { icon->m_icon = (void *)m_Icons[ idx ].m_handle; }
// Recreate
if ( icon->m_Handle ) { m_lcd->RemoveAndDestroyObject( icon->m_Handle ); icon->m_Handle = 0; } icon->Create( m_lcd ); } } break; case LCDITEM_AGGREGATE: { CLCDItemAggregate *ag = static_cast< CLCDItemAggregate * >( item ); if ( ag->m_dwNextUpdateTime > dwCurTime ) break;
// FIXME: encode update interval in text file
ag->m_dwNextUpdateTime = dwCurTime + 1000;
// Blow away current data
ag->WipeChildrenOnly( m_lcd ); CUtlVector< int > validIndices; char prefix[ 256 ]; char altprefix[ 256 ]; prefix[ 0 ] = 0; altprefix[ 0 ] = 0;
int curx = ag->x; int cury = ag->y;
switch ( ag->m_AggType ) { default: Assert( 0 ); break; case AGGTYPE_PERPLAYER: // Add all players into list
{ for ( int pl = 1; pl <= gpGlobals->maxClients; ++pl ) { if ( g_PR && g_PR->IsConnected( pl ) ) { validIndices.AddToTail( pl ); } }
Q_strncpy( prefix, "(playerindex)", sizeof( prefix ) ); Q_strncpy( altprefix, "(playerindexplusone)", sizeof( altprefix ) ); } break; case AGGTYPE_PERTEAM: { C_BasePlayer *local = C_BasePlayer::GetLocalPlayer();
if ( local ) { for ( int pl = 1; pl <= gpGlobals->maxClients; ++pl ) { if ( g_PR && g_PR->IsConnected( pl ) && local->GetTeamNumber() == g_PR->GetTeam( pl ) ) { validIndices.AddToTail( pl ); } } }
Q_strncpy( prefix, "(playerindex)", sizeof( prefix ) ); Q_strncpy( altprefix, "(playerindexplusone)", sizeof( altprefix ) ); } break; }
int subPage = 0; int spItems = 0;
int ecount = validIndices.Count(); for ( int e = 0; e < ecount; ++e ) { // Now fixup any strings
int index = validIndices[ e ];
char s1[ 512 ], s2[ 512 ]; Q_snprintf( s1, sizeof( s1 ), "%d", index ); Q_snprintf( s2, sizeof( s2 ), "%d", index + 1 ); // Now replace "playerindex" with the index as needed
for( int r = 0; r < ag->m_Definition.Count(); ++r ) { CLCDItem *newItem = NULL;
CLCDItem *item = ag->m_Definition[ r ]; switch ( item->m_Type ) { default: break;
case LCDITEM_TEXT: { CLCDItemText *text = static_cast< CLCDItemText * >( item ); CUtlString s; s = text->m_OriginalText; Replace( s, prefix, s1 ); Replace( s, altprefix, s2 ); char itemNumber[ 32 ]; Q_snprintf( itemNumber, sizeof( itemNumber ), "%d", e +1 );
Replace( s, "(itemnumber)", itemNumber );
DoGlobalReplacements( s ); // ReduceParentheses( s );
// text->m_OriginalText = s;
CLCDItemText *copy = static_cast< CLCDItemText * >( page->Alloc( item->m_Type ) ); *copy = *text; copy->m_bActive = true; copy->m_OriginalText = s; copy->Create( m_lcd );
m_lcd->SetOrigin( copy->m_Handle, curx + copy->x, cury + copy->y );
newItem = copy; } break; case LCDITEM_ICON: { CLCDItemIcon *icon = static_cast< CLCDItemIcon * >( item ); CLCDItemIcon *copy = static_cast< CLCDItemIcon * >( page->Alloc( item->m_Type ) ); *copy = *icon; copy->m_bActive = true; copy->Create( m_lcd );
m_lcd->SetOrigin( copy->m_Handle, curx + copy->x, cury + copy->y );
newItem = copy; } break; }
if ( newItem ) { ++spItems; newItem->m_nSubPage = subPage; ag->m_Children.AddToTail( newItem ); } }
cury += ag->m_yincrement;
if ( cury + SMALL_ITEM_HEIGHT > m_Size[ 1 ] ) { spItems = 0; ++subPage; cury = ag->y; } }
if ( spItems > 0 ) { page->m_nSubPageCount = subPage + 1; } else { // We thought we needed a new page, but didn't actually use it
page->m_nSubPageCount = subPage; } } } }
m_lcd->SetVisible( item->m_Handle, bShowItems && ( ( item->m_nSubPage == -1 ) || item->m_nSubPage == m_nSubPage ) ); ShowItems_R( page, dwCurTime, item->m_Children, bShowItems ); } }
void CLCD::DisplayCurrentPage( unsigned int dwCurTime ) { int pageCount = m_Pages.Count(); for ( int i = 0; i < pageCount; ++i ) { bool bShowItems = ( i == m_nCurrentPage ) ? true : false;
CLCDPage* page = m_Pages[ i ]; ShowItems_R( page, dwCurTime, page->m_Children, bShowItems ); } }
CLCDItemIcon *CLCD::ParseItemIcon( CLCDPage *page, bool bCreateHandles, KeyValues *sub ) { CLCDItemIcon *item = static_cast< CLCDItemIcon * >( page->Alloc( LCDITEM_ICON ) );
item->m_IconName = sub->GetString( "name", "" );
item->m_nSubPage = sub->GetInt( "header", 0 ) ? -1 : page->m_nSubPageCount - 1; item->w = sub->GetInt( "w", 24 ); item->h = sub->GetInt( "h", 24 ); item->x = sub->GetInt( "x", 0 ); item->y = sub->GetInt( "y", 0 );
int idx = m_Icons.Find( item->m_IconName.String() ); item->m_icon = 0; if ( idx != m_Icons.InvalidIndex() ) { item->m_icon = (void *)m_Icons[ idx ].m_handle; }
if ( bCreateHandles ) { item->Create( m_lcd ); }
return item; }
CLCDItemText *CLCD::ParseItemText( CLCDPage *page, bool bCreateHandles, KeyValues *sub ) { CLCDItemText *item = static_cast< CLCDItemText * >( page->Alloc( LCDITEM_TEXT ) );
const char *initialText = sub->GetString( "text", "" ); item->m_bHasWildcard = Q_strstr( initialText, "%" ) ? true : false;
item->m_nSubPage = sub->GetInt( "header", 0 ) ? -1 : page->m_nSubPageCount - 1; item->w = sub->GetInt( "w", 150 ); item->x = sub->GetInt( "x", 0 ); item->y = sub->GetInt( "y", 0 );
const char *sizeStr = sub->GetString( "size", "small" ); item->m_iSize = G15_SMALL; int iFound = m_TextSizes.Find( sizeStr ); if ( iFound != m_TextSizes.InvalidIndex() ) { item->m_iSize = m_TextSizes[ iFound ]; }
const char *alignStr = sub->GetString( "align", "left" ); item->m_iAlign = DT_LEFT; iFound = m_TextAlignments.Find( alignStr ); if ( iFound != m_TextAlignments.InvalidIndex() ) { item->m_iAlign = m_TextAlignments[ iFound ]; }
item->m_OriginalText = initialText; if ( bCreateHandles ) { item->Create( m_lcd ); }
return item; }
void CLCD::ParseItems_R( CLCDPage *page, bool bCreateHandles, KeyValues *kv, CUtlVector< CLCDItem * >& list ) { for ( KeyValues *sub = kv->GetFirstSubKey(); sub; sub = sub->GetNextKey() ) { char const *keyName = sub->GetName(); if ( !Q_stricmp( keyName, "iterate_players" ) || !Q_stricmp( keyName, "iterate_team" ) ) { int aggType = AGGTYPE_UNKNOWN; if ( !Q_stricmp( keyName, "iterate_players" ) ) { aggType = AGGTYPE_PERPLAYER; } else if ( !Q_stricmp( keyName, "iterate_team" ) ) { aggType = AGGTYPE_PERTEAM; }
// Now we parse the items out as we generally would
CLCDItemAggregate *item = static_cast< CLCDItemAggregate * >( page->Alloc( LCDITEM_AGGREGATE ) ); item->m_AggType = aggType;
item->x = sub->GetInt( "x", 0 ); item->y = sub->GetInt( "y", 0 ); item->m_yincrement = sub->GetInt( "y_increment", 10 );
// Parse the definition
ParseItems_R( page, false, sub, item->m_Definition );
// Add the definition items as "inactive" items to the scene (so they get destroyed)
for ( int i = 0; i < item->m_Definition.Count(); ++i ) { CLCDItem *pItem = item->m_Definition[ i ]; pItem->m_bActive = false; }
list.AddToTail( item ); } else if ( !Q_stricmp( keyName, "static_icon" ) ) { CLCDItemIcon *item = ParseItemIcon( page, true, sub ); Assert( item ); list.AddToTail(item ); } else if ( !Q_stricmp( keyName, "static_text" ) ) { CLCDItemText *item = ParseItemText( page, true, sub ); Assert( item ); list.AddToTail( item ); } else if ( !Q_stricmp( keyName, "newsubpage" ) ) { // Add to new subpage
++page->m_nSubPageCount; } else { // Skip unknown stuff
continue; } } }
void CLCD::ParsePage( KeyValues *kv ) { CLCDPage *newPage = new CLCDPage(); m_Pages.AddToTail( newPage );
newPage->m_bTitlePage = kv->GetBool( "titlepage", false ); newPage->m_bRequiresPlayer = kv->GetBool( "requiresplayer", false );
ParseItems_R( newPage, true, kv, newPage->m_Children ); }
void CLCD::ParseIconMappings( KeyValues *kv ) { for ( KeyValues *icon = kv->GetFirstSubKey(); icon; icon = icon->GetNextKey() ) { IconInfo_t info; HICON hIcon = 0; char const *name = icon->GetName(); char fullpath[ 512 ]; filesystem->RelativePathToFullPath( icon->GetString(), "GAME", fullpath, sizeof( fullpath ) ); #ifdef WIN32
hIcon = (HICON)::LoadImageA( NULL, fullpath, IMAGE_ICON, 32, 32, LR_LOADFROMFILE ); #else
hIcon = 0; #endif
info.m_handle = (void *)hIcon; m_Icons.Insert( name, info ); } }
//-----------------------------------------------------------------------------
// Purpose: Simply dumps all data fields in object
//-----------------------------------------------------------------------------
class CDescribeData { public: explicit CDescribeData( const byte *src );
void DescribeShort( const datamap_t *dmap, const typedescription_t *pField, const short *invalue, int count ); void DescribeInt( const datamap_t *dmap, const typedescription_t *pField, const int *invalue, int count ); void DescribeBool( const datamap_t *dmap, const typedescription_t *pField, const bool *invalue, int count ); void DescribeFloat( const datamap_t *dmap, const typedescription_t *pField, const float *invalue, int count ); void DescribeSimpleString( const datamap_t *dmap, const typedescription_t *pField, const char *indata, int length ); void DescribeString( const datamap_t *dmap, const typedescription_t *pField, const string_t *instring, int count ); void DescribeVector( const datamap_t *dmap, const typedescription_t *pField, const Vector *inValue, int count ); void DescribeColor( const datamap_t *dmap, const typedescription_t *pField, const Color *invalue, int count ); void DumpDescription( datamap_t *pMap );
private: void DescribeFields( const datamap_t *pMap, int nPredictionCopyType ); const byte *m_pSrc; int m_nSrcOffsetIndex;
void Describe( const datamap_t *dmap, const typedescription_t *pField, const char *fmt, ... ); };
CDescribeData::CDescribeData( const byte *src ) { m_pSrc = src; m_nSrcOffsetIndex = TD_OFFSET_NORMAL; }
static typedescription_t *FindByName( datamap_t *pMap, char const *fn ) { while ( pMap ) { for ( int i = 0; i < pMap->dataNumFields; i++ ) { typedescription_t *current = &pMap->dataDesc[ i ]; if ( !current->fieldName ) continue;
if ( !Q_stricmp( current->fieldName, fn ) ) return current; }
pMap = pMap->baseMap; }
return NULL; }
static typedescription_t *FindField( datamap_t *pMap, char const *relativePath ) { if ( !Q_strstr( relativePath, "." ) ) { // Simple case, just look up field name
return FindByName( pMap, relativePath ); }
// Complex case
Assert( 0 ); return NULL; }
//-----------------------------------------------------------------------------
// Purpose:
// Input : *fmt -
// ... -
//-----------------------------------------------------------------------------
void CDescribeData::Describe( const datamap_t *dmap, const typedescription_t *pField, const char *fmt, ... ) { const char *fieldname; fieldname = pField->fieldName ? pField->fieldName : "null";
va_list argptr; char data[ 4096 ]; int len; va_start(argptr, fmt); len = Q_vsnprintf(data, sizeof( data ), fmt, argptr); va_end(argptr);
Msg( "%s::%s%s", dmap->dataClassName, fieldname, data ); }
//-----------------------------------------------------------------------------
// Purpose:
// Input : size -
// *outdata -
// *indata -
//-----------------------------------------------------------------------------
void CDescribeData::DescribeSimpleString( const datamap_t *dmap, const typedescription_t *pField, char const *invalue, int count ) { Describe( dmap, pField, "%s\n", invalue ? invalue : "" ); }
void CDescribeData::DescribeShort( const datamap_t *dmap, const typedescription_t *pField, const short *invalue, int count ) { for ( int i = 0; i < count; ++i ) { if ( count == 1 ) { Describe( dmap, pField, " short (%i)\n", (int)(invalue[i]) ); } else { Describe( dmap, pField, "[%i] short (%i)\n", i, (int)(invalue[i]) ); } } }
void CDescribeData::DescribeInt( const datamap_t *dmap, const typedescription_t *pField, const int *invalue, int count ) { for ( int i = 0; i < count; ++i ) { if ( count == 1 ) { Describe( dmap, pField, " integer (%i)\n", invalue[i] ); } else { Describe( dmap, pField, "[%i] integer (%i)\n", i, invalue[i] ); } } }
void CDescribeData::DescribeBool( const datamap_t *dmap, const typedescription_t *pField, const bool *invalue, int count ) { for ( int i = 0; i < count; ++i ) { if ( count == 1 ) { Describe( dmap, pField, " bool (%s)\n", (invalue[i]) ? "true" : "false" ); } else { Describe( dmap, pField, "[%i] bool (%s)\n", i, (invalue[i]) ? "true" : "false" ); } } }
void CDescribeData::DescribeFloat( const datamap_t *dmap, const typedescription_t *pField, const float *invalue, int count ) { for ( int i = 0; i < count; ++i ) { if ( count == 1 ) { Describe( dmap, pField, " float (%f)\n", invalue[ i ] ); } else { Describe( dmap, pField, "[%i] float (%f)\n", i, invalue[ i ] ); } } }
void CDescribeData::DescribeString( const datamap_t *dmap, const typedescription_t *pField, const string_t *instring, int count ) { for ( int i = 0; i < count; ++i ) { if ( count == 1 ) { Describe( dmap, pField, " string (%s)\n", instring[ i ] ? instring[ i ] : "" ); } else { Describe( dmap, pField, "[%i] string (%s)\n", i, instring[ i ] ? instring[ i ] : "" ); } } }
void CDescribeData::DescribeColor( const datamap_t *dmap, const typedescription_t *pField, const Color *invalue, int count ) { for ( int i = 0; i < count; ++i ) { if ( count == 1 ) { Describe( dmap, pField, " color (%i %i %i %i)\n", invalue[ i ].r(), invalue[ i ].g(), invalue[ i ].b(), invalue[ i ].a() ); } else { Describe( dmap, pField, "[%i] color (%i %i %i %i)\n", i, invalue[ i ].r(), invalue[ i ].g(), invalue[ i ].b(), invalue[ i ].a() ); } } }
void CDescribeData::DescribeVector( const datamap_t *dmap, const typedescription_t *pField, const Vector *inValue, int count ) { for ( int i = 0; i < count; ++i ) { if ( count == 1 ) { Describe( dmap, pField, " vector (%f %f %f)\n", inValue[i].x, inValue[i].y, inValue[i].z ); } else { Describe( dmap, pField, "[%i] vector (%f %f %f)\n", i, inValue[i].x, inValue[i].y, inValue[i].z ); } } }
void CDescribeData::DescribeFields( const datamap_t *pRootMap, int nPredictionCopyType ) { int i; int flags; int fieldOffsetSrc; int fieldSize;
const flattenedoffsets_t &flat = pRootMap->m_pOptimizedDataMap->m_Info[ nPredictionCopyType ].m_Flat; int fieldCount = flat.m_Flattened.Count();
for ( i = 0; i < fieldCount; i++ ) { const typedescription_t *pField = &flat.m_Flattened[ i ]; flags = pField->flags;
// Skip this field
if ( flags & FTYPEDESC_VIEW_NEVER ) continue;
fieldSize = pField->fieldSize;
fieldOffsetSrc = pField->flatOffset[ TD_OFFSET_NORMAL ]; const byte *pInputData = m_pSrc + fieldOffsetSrc; switch( pField->fieldType ) { default: break; case FIELD_EMBEDDED: { Error( "FIELD_EMBEDDED in flattened field list!" ); } break; case FIELD_FLOAT: DescribeFloat( pRootMap, pField, (float const *)pInputData, fieldSize ); break; case FIELD_STRING: DescribeString( pRootMap, pField, (const string_t*)pInputData, fieldSize ); break; case FIELD_VECTOR: DescribeVector( pRootMap, pField, (const Vector *)pInputData, fieldSize ); break; case FIELD_COLOR32: DescribeColor( pRootMap, pField, (const Color *)pInputData, fieldSize ); break; case FIELD_BOOLEAN: DescribeBool( pRootMap, pField, (bool const *)pInputData, fieldSize ); break; case FIELD_INTEGER: DescribeInt( pRootMap, pField, (int const *)pInputData, fieldSize ); break; case FIELD_SHORT: DescribeShort( pRootMap, pField, (short const *)pInputData, fieldSize ); break; case FIELD_CHARACTER: DescribeSimpleString( pRootMap, pField, (const char *)pInputData, fieldSize ); break; } } }
void CDescribeData::DumpDescription( datamap_t *pMap ) { CPredictionCopy::PrepareDataMap( pMap ); for ( int pc = 0; pc < PC_COPYTYPE_COUNT; ++pc ) { DescribeFields( pMap, pc ); } }
void CLCD::DumpPlayer() { C_BasePlayer *player = C_BasePlayer::GetLocalPlayer(); if ( !player ) return;
Msg( "(localplayer)\n\n" );
CDescribeData helper( (const byte *)player ); helper.DumpDescription( player->GetPredDescMap() );
Msg( "(localteam)\n\n" );
C_Team *team = player->GetTeam(); if ( team ) { CDescribeData helper( (const byte *)team ); helper.DumpDescription( team->GetPredDescMap() ); }
Msg( "(playerresource)\n\n" );
if ( g_PR ) { CDescribeData helper( (const byte *)g_PR ); helper.DumpDescription( g_PR->GetPredDescMap() ); }
Msg( "(localplayerweapon)\n\n" ); // Get the player's weapons, too
C_BaseCombatWeapon *active = player->GetActiveWeapon(); if ( active ) { CDescribeData helper( (const byte *)active ); helper.DumpDescription( active->GetPredDescMap() ); }
Msg( "Other replacements:\n\n" );
// Global replacements
for( int i = m_GlobalStats.First() ; i != m_GlobalStats.InvalidIndex(); i = m_GlobalStats.Next( i ) ) { CUtlString& r = m_GlobalStats[ i ];
char const *pReplace = r.String(); char ansi[ 512 ]; ansi[ 0 ] = 0;
if ( pReplace[ 0 ] == '#' ) { const wchar_t *pWString = g_pVGuiLocalize->Find( pReplace ); if ( pWString ) { g_pVGuiLocalize->ConvertUnicodeToANSI( pWString, ansi, sizeof( ansi ) ); pReplace = ansi; } }
Msg( "'%s' = '%s'\n", m_GlobalStats.GetElementName( i ), pReplace ); } }
bool CLCD::ExtractArrayIndex( char *str, size_t bufsize, int *index ) { Assert( index ); *index = 0;
char s[ 2048 ]; Q_strncpy( s, str, sizeof( s ) );
char *pos = Q_strstr( s, "[" ); if ( !pos ) return false;
char *pos2 = Q_strstr( s, "]" ); if ( !pos2 ) return false;
char num[ 32 ]; Q_strncpy( num, pos + 1, pos2 - pos ); *index = Q_atoi( num );
int left = pos - s + 1; char o[ 2048 ]; Q_strncpy( o, s, left ); Q_strncat( o, pos2 + 1, sizeof( o ), COPY_ALL_CHARACTERS );
Q_strncpy( str, o, bufsize ); return true; }
void CLCD::LookupToken( char const *in, CUtlString& value ) { value = "";
C_BasePlayer *player = C_BasePlayer::GetLocalPlayer(); if ( !player ) { return; }
C_BaseEntity *ref = NULL;
char outbuf[ 1024 ]; char *o = outbuf; char const *i = in; while ( *i ) { if ( *i == '(' ) { char token[ 512 ]; char *to = token; // swallow everything until matching '%' character
++i; while ( *i && *i != ')' ) { *to++ = *i++; }
if ( *i ) ++i;
*to = 0;
if ( !Q_stricmp( token, "localplayer" ) ) { ref = player; } else if ( !Q_stricmp( token, "localteam" ) ) { ref = player->GetTeam(); } else if ( !Q_stricmp( token, "localplayerweapon" ) ) { ref = player->GetActiveWeapon(); } else if ( !Q_stricmp( token, "playerresource" ) ) { ref = g_PR; } } else { *o++ = *i++; } }
*o = '\0';
// Fixme, also need to get array reference removed from end and do the . field searching stuff
// Outbuf now has the actual field
if ( !ref ) { return; }
int iIndex = 0; ExtractArrayIndex( outbuf, sizeof( outbuf ), &iIndex ); typedescription_t *td = FindField( ref->GetPredDescMap(), outbuf ); if ( !td ) { return; }
// Not allowed to see this one
if ( td->flags & FTYPEDESC_VIEW_NEVER ) { return; }
int fieldOffsetSrc = td->fieldOffset; // int fieldSize = td->fieldSize;
void const *pInputData = (void const *)((char *)ref + fieldOffsetSrc );
char sz[ 256 ]; sz[ 0 ] = 0; // Found it, now get the value
switch ( td->fieldType ) { case FIELD_FLOAT: Q_snprintf( sz, sizeof( sz ), "%.2f", *((float *)pInputData + iIndex ) ); break; case FIELD_STRING: { string_t *pString = (string_t *)((string_t *)pInputData + iIndex ); Q_snprintf( sz, sizeof( sz ), "%s", pString ? STRING( *pString ) : "" ); } break; case FIELD_VECTOR: { Vector v = *((Vector *)pInputData + iIndex ); Q_snprintf( sz, sizeof( sz ), "%.2f %.2f %.2f", v.x, v.y, v.z ); } break; case FIELD_COLOR32: { Color c = *(( Color * )pInputData + iIndex ); Q_snprintf( sz, sizeof( sz ), "%d %d %d %d", c.r(), c.g(), c.b(), c.a() ); } break; case FIELD_BOOLEAN: Q_snprintf( sz, sizeof( sz ), "%s", *( ( bool *)pInputData + iIndex ) ? "true" : "false" ); break; case FIELD_INTEGER: Q_snprintf( sz, sizeof( sz ), "%i", *( (int *)pInputData + iIndex )); break; case FIELD_SHORT: Q_snprintf( sz, sizeof( sz ), "%i", *( (short *)pInputData + iIndex ) ); break; case FIELD_CHARACTER: Q_snprintf( sz, sizeof( sz ), "%s", ((const char *)pInputData + iIndex ) ); break; }
value = sz; }
void CLCD::BuildUpdatedText( char const *in, CUtlString& out ) { char outbuf[ 1024 ]; char *o = outbuf; char const *i = in; while ( *i ) { if ( *i == '%' ) { char token[ 512 ]; char *to = token; // swallow everything until matching '%' character
++i; while ( *i && *i != '%' ) { *to++ = *i++; }
if ( *i ) ++i;
*to = 0;
// Now we have the token, do the lookup
CUtlString value; LookupToken( token, value );
to = (char *)value.String(); while ( *to ) { *o++ = *to++; } } else { *o++ = *i++; } }
*o = '\0';
out = outbuf; }
bool CLCD::Replace( CUtlString& str, char const *search, char const *replace ) { // If search string is part of replacement, this is a bad thing!!!
Assert( !*replace || !Q_strstr( replace, search ) );
bool changed = false; if ( !Q_strstr( str.String(), search ) ) return false;
char s[ 2048 ]; Q_strncpy( s, str.String(), sizeof( s ) );
int searchlen = Q_strlen( search ); while ( true ) { char *pos = Q_strstr( s, search ); if ( !pos ) break;
char temp[ 4096 ]; // Found an instance
int left = pos - s + 1; Assert( left < sizeof( temp ) ); Q_strncpy( temp, s, left ); Q_strncat( temp, replace, sizeof( temp ), COPY_ALL_CHARACTERS ); int rightofs = left + searchlen - 1; Q_strncat( temp, &s[ rightofs ], sizeof( temp ), COPY_ALL_CHARACTERS );
// Replace entire string
Q_strncpy( s, temp, sizeof( s ) ); changed = true; }
str = s;
return changed; }
void CLCD::SetGlobalStat( char const *name, char const *value ) { if ( !m_lcd ) return;
int idx = m_GlobalStats.Find( name ); if ( idx == m_GlobalStats.InvalidIndex() ) { idx = m_GlobalStats.Insert( name ); }
m_GlobalStats[ idx ] = value; }
void CLCD::AddChatLine( char const *txt ) { if ( !m_lcd ) return;
while ( m_ChatHistory.Count() >= m_nMaxChatHistory ) { m_ChatHistory.Remove( 0 ); }
m_ChatHistory.AddToTail( CUtlString( txt ) );
UpdateChat(); }
void CLCD::UpdateChat() { for ( int i = 0; i < m_nMaxChatHistory; ++i ) { char name[ 32 ]; Q_snprintf( name, sizeof( name ), "chat_%d", i + 1 );
SetGlobalStat( name, i < m_ChatHistory.Count() ? m_ChatHistory[ i ].String() : " " ); } }
void CLCD::DoGlobalReplacements( CUtlString& str ) { // Put some limit to avoid infinite recursion
int maxChanges = 16;
bool changed = false; do { changed = false; for ( int i = m_GlobalStats.First(); i != m_GlobalStats.InvalidIndex(); i = m_GlobalStats.Next( i ) ) { CUtlString &r = m_GlobalStats[ i ];
char const *pReplace = r.String(); char ansi[ 512 ]; ansi[ 0 ] = 0;
if ( pReplace[ 0 ] == '#' ) { const wchar_t *pWString = g_pVGuiLocalize->Find( pReplace ); if ( pWString ) { g_pVGuiLocalize->ConvertUnicodeToANSI( pWString, ansi, sizeof( ansi ) ); pReplace = ansi; } }
if ( Replace( str, m_GlobalStats.GetElementName( i ), pReplace ) ) { changed = true; } } } while ( changed && --maxChanges >= 0 ); }
void CLCD::ReduceParentheses( CUtlString& str ) { char s[ 2048 ]; Q_strncpy( s, str.String(), sizeof( s ) );
while ( true ) { char *pos = Q_strstr( s, "(" ); if ( !pos ) break;
char *end = Q_strstr( pos, ")" ); if ( !end ) break;
char temp[ 4096 ]; // Found an instance
int left = pos - s + 1; Assert( left < sizeof( temp ) ); Q_strncpy( temp, s, left ); int rightofs = end - s + 1; Q_strncat( temp, &s[ rightofs ], sizeof( temp ), COPY_ALL_CHARACTERS );
// Replace entire string
Q_strncpy( s, temp, sizeof( s ) ); }
str = s; }
void CLCD::ParseReplacements( KeyValues *kv ) { for ( KeyValues *sub = kv->GetFirstSubKey(); sub; sub = sub->GetNextKey() ) { char const *key = sub->GetName(); char const *value = sub->GetString();
SetGlobalStat( key, value ); } }
CON_COMMAND_F( cl_dumpplayer, "Dumps info about a player", FCVAR_CHEAT ) { if ( args.ArgC() != 2 ) return;
int n = Q_atoi( args[1] ); C_BasePlayer *pPlayer = ToBasePlayer( C_BaseEntity::Instance( n ) ); if ( !pPlayer ) return;
Vector absMins, absMaxs; pPlayer->GetRenderBoundsWorldspace( absMins, absMaxs ); Msg( "Effects: %x\n", pPlayer->GetEffects() ); Msg( "Dormant: %d\n", pPlayer->IsDormant() ); Msg( "Worldspace Render Bounds: [%.2f %.2f %.2f] -> [%.2f %.2f %.2f]\n", absMins.x, absMins.y, absMins.z, absMaxs.x, absMaxs.y, absMaxs.z );
CDescribeData helper( (const byte *)pPlayer ); helper.DumpDescription( pPlayer->GetPredDescMap() ); }
|