Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1595 lines
34 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: LCD support
  4. //
  5. //=====================================================================================//
  6. #if defined( WIN32 ) && !defined( _X360 )
  7. #include <windows.h>
  8. #endif
  9. #include "cbase.h"
  10. #ifdef POSIX
  11. #define HICON int
  12. const int DT_LEFT = 1;
  13. const int DT_CENTER = 2;
  14. const int DT_RIGHT = 3;
  15. #endif
  16. #include "hud_lcd.h"
  17. #include "vgui_controls/Controls.h"
  18. #include "vgui_controls/SectionedListPanel.h"
  19. #include "vgui/ISurface.h"
  20. #include "vgui/IScheme.h"
  21. #include "vgui/IVGui.h"
  22. #include "vgui/ILocalize.h"
  23. #include "vgui/IPanel.h"
  24. #include "c_team.h"
  25. #include "c_playerresource.h"
  26. #include "filesystem.h"
  27. #include "g15/ig15.h"
  28. #include "tier0/icommandline.h"
  29. #if defined( _X360 )
  30. #include "xbox/xbox_win32stubs.h"
  31. #endif
  32. // memdbgon must be the last include file in a .cpp file!!!
  33. #include "tier0/memdbgon.h"
  34. #define G15_RESOURCE_FILE "resource/g15.res"
  35. #define G15_MODULE_NAME "bin/g15.dll"
  36. #define SMALL_ITEM_HEIGHT 10
  37. #define G15_DEFAULT_MAX_CHAT_HISTORY 4
  38. CLCD gLCD;
  39. IHudLCD *hudlcd = &gLCD;
  40. CON_COMMAND( g15_reload, "Reloads the Logitech G-15 Keyboard configs." )
  41. {
  42. if ( !CommandLine()->FindParm( "-g15" ) )
  43. {
  44. Msg( "Must run with -g15 to enable support for the LCD Keyboard\n" );
  45. return;
  46. }
  47. gLCD.Reload();
  48. }
  49. CON_COMMAND( g15_dumpplayer, "Spew player data." )
  50. {
  51. if ( !CommandLine()->FindParm( "-g15" ) )
  52. {
  53. Msg( "Must run with -g15 to enable support for the LCD Keyboard\n" );
  54. return;
  55. }
  56. gLCD.DumpPlayer();
  57. }
  58. static ConVar g15_update_msec( "g15_update_msec", "250", FCVAR_ARCHIVE, "Logitech G-15 Keyboard update interval." );
  59. void CLCDItem::Wipe( IG15 *lcd )
  60. {
  61. for ( int i = 0; i < m_Children.Count(); ++i )
  62. {
  63. if ( m_Children[ i ]->m_Handle )
  64. {
  65. lcd->RemoveAndDestroyObject( m_Children[ i ]->m_Handle );
  66. }
  67. m_Children[ i ]->Wipe( lcd );
  68. delete m_Children[ i ];
  69. }
  70. m_Children.Purge();
  71. }
  72. void CLCDItemAggregate::Create( IG15 *lcd )
  73. {
  74. // Nothing
  75. }
  76. void CLCDItemAggregate::Wipe( IG15 *lcd )
  77. {
  78. BaseClass::Wipe( lcd );
  79. for ( int i = 0; i < m_Definition.Count(); ++i )
  80. {
  81. m_Definition[ i ]->Wipe( lcd );
  82. delete m_Definition[ i ];
  83. }
  84. m_Definition.Purge();
  85. }
  86. void CLCDItemAggregate::WipeChildrenOnly( IG15 *lcd )
  87. {
  88. BaseClass::Wipe( lcd );
  89. }
  90. void CLCDItemIcon::Create( IG15 *lcd )
  91. {
  92. #ifdef WIN32
  93. m_Handle = lcd->AddIcon( (HICON)m_icon, w, h );
  94. #else
  95. m_Handle = lcd->AddIcon( (void *)m_icon, w, h );
  96. #endif
  97. lcd->SetOrigin( m_Handle, x, y );
  98. lcd->SetVisible( m_Handle, false );
  99. }
  100. void CLCDItemText::Create( IG15 *lcd )
  101. {
  102. m_Handle = lcd->AddText( G15_STATIC_TEXT, (G15TextSize)m_iSize, m_iAlign, w );
  103. lcd->SetOrigin( m_Handle, x, y );
  104. lcd->SetText( m_Handle, m_OriginalText );
  105. lcd->SetVisible( m_Handle, false );
  106. }
  107. ///-----------------------------------------------------------------------------
  108. /// Constructor
  109. ///-----------------------------------------------------------------------------
  110. CLCD::CLCD( void )
  111. : m_lcd( NULL ),
  112. m_nCurrentPage( 0 ),
  113. m_nSubPage( 0 ),
  114. m_bHadPlayer( false ),
  115. m_dwNextUpdateTime( 0u ),
  116. m_nMaxChatHistory( G15_DEFAULT_MAX_CHAT_HISTORY ),
  117. m_pG15Module( 0 ),
  118. m_G15Factory( 0 )
  119. {
  120. m_Size[ 0 ] = m_Size[ 1 ] = 0;
  121. }
  122. ///-----------------------------------------------------------------------------
  123. /// Destructor
  124. ///-----------------------------------------------------------------------------
  125. CLCD::~CLCD( void )
  126. {
  127. }
  128. void CLCD::Reload()
  129. {
  130. Shutdown();
  131. Msg( "Reloading G15 config\n" );
  132. Init();
  133. }
  134. ///------------------------------------------------------------------------------
  135. /// Initializes the LCD device, and sets up the text handles
  136. ///------------------------------------------------------------------------------
  137. void CLCD::Init( void )
  138. {
  139. if ( !CommandLine()->FindParm( "-g15" ) )
  140. return;
  141. if ( m_lcd )
  142. return;
  143. m_pG15Module = Sys_LoadModule( G15_MODULE_NAME );
  144. if ( !m_pG15Module )
  145. {
  146. return;
  147. }
  148. m_G15Factory = Sys_GetFactory( m_pG15Module );
  149. if ( !m_G15Factory )
  150. {
  151. Shutdown();
  152. return;
  153. }
  154. m_lcd = reinterpret_cast< IG15 * >( m_G15Factory( G15_INTERFACE_VERSION, NULL ) );
  155. if ( !m_lcd )
  156. {
  157. Shutdown();
  158. return;
  159. }
  160. m_lcd->GetLCDSize( m_Size[ 0 ], m_Size[ 1 ] );
  161. m_nCurrentPage = 0;
  162. m_nSubPage = 0;
  163. m_TextSizes.Insert( "small", G15_SMALL );
  164. m_TextSizes.Insert( "medium", G15_MEDIUM );
  165. m_TextSizes.Insert( "big", G15_BIG );
  166. m_TextAlignments.Insert( "left", DT_LEFT );
  167. m_TextAlignments.Insert( "center", DT_CENTER );
  168. m_TextAlignments.Insert( "right", DT_RIGHT );
  169. KeyValues *kv = new KeyValues( "G15" );
  170. if ( kv->LoadFromFile( filesystem, G15_RESOURCE_FILE, "MOD" ) )
  171. {
  172. char const *title = kv->GetString( "game", "Source Engine" );
  173. m_nMaxChatHistory = clamp( 1, kv->GetInt( "chatlines", m_nMaxChatHistory ), 64 );
  174. Assert( title );
  175. m_Title = title;
  176. m_lcd->Init( m_Title.String() );
  177. for ( KeyValues *sub = kv->GetFirstSubKey(); sub; sub = sub->GetNextKey() )
  178. {
  179. char const *keyName = sub->GetName();
  180. if ( !Q_stricmp( keyName, "game" ) )
  181. {
  182. // Handled above!!!
  183. }
  184. else if ( !Q_stricmp( keyName, "icons" ) )
  185. {
  186. ParseIconMappings( sub );
  187. }
  188. else if ( !Q_stricmp( keyName, "replace" ) )
  189. {
  190. ParseReplacements( sub );
  191. }
  192. else if ( !Q_stricmp( keyName, "page" ) )
  193. {
  194. ParsePage( sub );
  195. }
  196. }
  197. }
  198. kv->deleteThis();
  199. UpdateChat();
  200. Msg( "Logitech LCD Keyboard initialized\n" );
  201. }
  202. ///--------------------------------------------------------------------------
  203. /// Destroys the local EZ LCD Object
  204. ///--------------------------------------------------------------------------
  205. void CLCD::Shutdown( void )
  206. {
  207. for ( int i = 0; i < m_Pages.Count(); ++i )
  208. {
  209. CLCDPage *page = m_Pages[ i ];
  210. page->Wipe( m_lcd );
  211. delete page;
  212. }
  213. m_Pages.Purge();
  214. if ( m_lcd )
  215. {
  216. m_lcd->Shutdown();
  217. m_lcd = NULL;
  218. }
  219. m_TextSizes.Purge();
  220. m_TextAlignments.Purge();
  221. m_GlobalStats.Purge();
  222. m_G15Factory = 0;
  223. if ( m_pG15Module )
  224. {
  225. Sys_UnloadModule( m_pG15Module );
  226. m_pG15Module = 0;
  227. }
  228. }
  229. int CLCD::FindTitlePage()
  230. {
  231. for ( int i = 0; i < m_Pages.Count(); ++i )
  232. {
  233. if ( m_Pages[ i ]->m_bTitlePage )
  234. return i;
  235. }
  236. return -1;
  237. }
  238. bool CLCD::IsPageValid( int currentPage, C_BasePlayer *player )
  239. {
  240. if ( m_Pages[ currentPage ]->m_bTitlePage && player )
  241. return false;
  242. if ( m_Pages[ currentPage ]->m_bRequiresPlayer && !player )
  243. return false;
  244. return true;
  245. }
  246. ///---------------------------------------------------------------------
  247. /// Update routine
  248. ///---------------------------------------------------------------------
  249. void CLCD::Update( void )
  250. {
  251. if ( !m_lcd )
  252. return ;
  253. C_BasePlayer *player = C_BasePlayer::GetLocalPlayer();
  254. bool hasplayer = player ? true : false;
  255. bool changed = hasplayer != m_bHadPlayer;
  256. m_bHadPlayer = hasplayer;
  257. int pageCount = m_Pages.Count();
  258. int prevPage = m_nCurrentPage;
  259. if ( pageCount > 0 )
  260. {
  261. bool force = false;
  262. if ( changed && hasplayer )
  263. {
  264. force = true;
  265. m_nCurrentPage = 0;
  266. m_nSubPage = 0;
  267. }
  268. if ( !IsPageValid( m_nCurrentPage, player ) )
  269. {
  270. force = true;
  271. }
  272. if ( m_lcd->ButtonTriggered( G15_BUTTON_1 ) || force )
  273. {
  274. m_nSubPage = 0;
  275. for ( int i = 0; i < pageCount; ++i )
  276. {
  277. m_nCurrentPage = ( m_nCurrentPage + 1 ) % pageCount;
  278. if ( !IsPageValid( m_nCurrentPage, player ) )
  279. continue;
  280. break;
  281. }
  282. }
  283. if ( m_lcd->ButtonTriggered( G15_BUTTON_2 ) )
  284. {
  285. int pc = m_Pages[ m_nCurrentPage ]->m_nSubPageCount;
  286. m_nSubPage = ( m_nSubPage + 1 ) % pc;
  287. }
  288. }
  289. else
  290. {
  291. m_nCurrentPage = -1;
  292. m_nSubPage = 0;
  293. }
  294. bool pageChanged = prevPage != m_nCurrentPage;
  295. unsigned int dwCurTime = (unsigned int)( 1000.0 * gpGlobals->realtime );
  296. if ( m_lcd->IsConnected() )
  297. {
  298. if ( dwCurTime >= m_dwNextUpdateTime || pageChanged )
  299. {
  300. m_dwNextUpdateTime = dwCurTime + g15_update_msec.GetInt();
  301. DisplayCurrentPage( dwCurTime );
  302. }
  303. }
  304. m_lcd->UpdateLCD( dwCurTime );
  305. }
  306. ///--------------------------------------------------------------------------
  307. ///
  308. ///--------------------------------------------------------------------------
  309. bool CLCD::IsConnected( void ) const
  310. {
  311. return m_lcd ? m_lcd->IsConnected() : false;
  312. }
  313. void CLCD::ShowItems_R( CLCDPage *page, unsigned int dwCurTime, CUtlVector< CLCDItem * >& list, bool bShowItems )
  314. {
  315. int itemCount = list.Count();
  316. for ( int j = 0; j < itemCount; ++j )
  317. {
  318. CLCDItem *item = list[ j ];
  319. if ( !item->m_bActive )
  320. continue;
  321. if ( bShowItems )
  322. {
  323. switch ( item->m_Type )
  324. {
  325. default:
  326. break;
  327. case LCDITEM_TEXT:
  328. {
  329. CLCDItemText *txt = static_cast< CLCDItemText * >( item );
  330. if ( txt )
  331. {
  332. // Need to build updated text
  333. CUtlString updated;
  334. CUtlString str = txt->m_OriginalText;
  335. BuildUpdatedText( str.String(), updated );
  336. DoGlobalReplacements( updated );
  337. ReduceParentheses( updated );
  338. m_lcd->SetText( item->m_Handle, updated.String() );
  339. }
  340. }
  341. break;
  342. case LCDITEM_ICON:
  343. {
  344. CLCDItemIcon *icon = static_cast< CLCDItemIcon * >( item );
  345. if ( icon )
  346. {
  347. // Need to build updated text
  348. CUtlString updated;
  349. CUtlString str = icon->m_IconName;
  350. BuildUpdatedText( str.String(), updated );
  351. DoGlobalReplacements( updated );
  352. ReduceParentheses( updated );
  353. int idx = m_Icons.Find( updated.String() );
  354. if ( idx != m_Icons.InvalidIndex() )
  355. {
  356. icon->m_icon = (void *)m_Icons[ idx ].m_handle;
  357. }
  358. // Recreate
  359. if ( icon->m_Handle )
  360. {
  361. m_lcd->RemoveAndDestroyObject( icon->m_Handle );
  362. icon->m_Handle = 0;
  363. }
  364. icon->Create( m_lcd );
  365. }
  366. }
  367. break;
  368. case LCDITEM_AGGREGATE:
  369. {
  370. CLCDItemAggregate *ag = static_cast< CLCDItemAggregate * >( item );
  371. if ( ag->m_dwNextUpdateTime > dwCurTime )
  372. break;
  373. // FIXME: encode update interval in text file
  374. ag->m_dwNextUpdateTime = dwCurTime + 1000;
  375. // Blow away current data
  376. ag->WipeChildrenOnly( m_lcd );
  377. CUtlVector< int > validIndices;
  378. char prefix[ 256 ];
  379. char altprefix[ 256 ];
  380. prefix[ 0 ] = 0;
  381. altprefix[ 0 ] = 0;
  382. int curx = ag->x;
  383. int cury = ag->y;
  384. switch ( ag->m_AggType )
  385. {
  386. default:
  387. Assert( 0 );
  388. break;
  389. case AGGTYPE_PERPLAYER:
  390. // Add all players into list
  391. {
  392. for ( int pl = 1; pl <= gpGlobals->maxClients; ++pl )
  393. {
  394. if ( g_PR && g_PR->IsConnected( pl ) )
  395. {
  396. validIndices.AddToTail( pl );
  397. }
  398. }
  399. Q_strncpy( prefix, "(playerindex)", sizeof( prefix ) );
  400. Q_strncpy( altprefix, "(playerindexplusone)", sizeof( altprefix ) );
  401. }
  402. break;
  403. case AGGTYPE_PERTEAM:
  404. {
  405. C_BasePlayer *local = C_BasePlayer::GetLocalPlayer();
  406. if ( local )
  407. {
  408. for ( int pl = 1; pl <= gpGlobals->maxClients; ++pl )
  409. {
  410. if ( g_PR && g_PR->IsConnected( pl ) && local->GetTeamNumber() == g_PR->GetTeam( pl ) )
  411. {
  412. validIndices.AddToTail( pl );
  413. }
  414. }
  415. }
  416. Q_strncpy( prefix, "(playerindex)", sizeof( prefix ) );
  417. Q_strncpy( altprefix, "(playerindexplusone)", sizeof( altprefix ) );
  418. }
  419. break;
  420. }
  421. int subPage = 0;
  422. int spItems = 0;
  423. int ecount = validIndices.Count();
  424. for ( int e = 0; e < ecount; ++e )
  425. {
  426. // Now fixup any strings
  427. int index = validIndices[ e ];
  428. char s1[ 512 ], s2[ 512 ];
  429. Q_snprintf( s1, sizeof( s1 ), "%d", index );
  430. Q_snprintf( s2, sizeof( s2 ), "%d", index + 1 );
  431. // Now replace "playerindex" with the index as needed
  432. for( int r = 0; r < ag->m_Definition.Count(); ++r )
  433. {
  434. CLCDItem *newItem = NULL;
  435. CLCDItem *itemDefn = ag->m_Definition[ r ];
  436. switch ( itemDefn->m_Type )
  437. {
  438. default:
  439. break;
  440. case LCDITEM_TEXT:
  441. {
  442. CLCDItemText *text = static_cast< CLCDItemText * >(itemDefn);
  443. CUtlString s;
  444. s = text->m_OriginalText;
  445. Replace( s, prefix, s1 );
  446. Replace( s, altprefix, s2 );
  447. char itemNumber[ 32 ];
  448. Q_snprintf( itemNumber, sizeof( itemNumber ), "%d", e +1 );
  449. Replace( s, "(itemnumber)", itemNumber );
  450. DoGlobalReplacements( s );
  451. // ReduceParentheses( s );
  452. // text->m_OriginalText = s;
  453. CLCDItemText *copy = static_cast< CLCDItemText * >( page->Alloc( itemDefn->m_Type ) );
  454. *copy = *text;
  455. copy->m_bActive = true;
  456. copy->m_OriginalText = s;
  457. copy->Create( m_lcd );
  458. m_lcd->SetOrigin( copy->m_Handle, curx + copy->x, cury + copy->y );
  459. newItem = copy;
  460. }
  461. break;
  462. case LCDITEM_ICON:
  463. {
  464. CLCDItemIcon *icon = static_cast< CLCDItemIcon * >(itemDefn);
  465. CLCDItemIcon *copy = static_cast< CLCDItemIcon * >( page->Alloc( itemDefn->m_Type ) );
  466. *copy = *icon;
  467. copy->m_bActive = true;
  468. copy->Create( m_lcd );
  469. m_lcd->SetOrigin( copy->m_Handle, curx + copy->x, cury + copy->y );
  470. newItem = copy;
  471. }
  472. break;
  473. }
  474. if ( newItem )
  475. {
  476. ++spItems;
  477. newItem->m_nSubPage = subPage;
  478. ag->m_Children.AddToTail( newItem );
  479. }
  480. }
  481. cury += ag->m_yincrement;
  482. if ( cury + SMALL_ITEM_HEIGHT > m_Size[ 1 ] )
  483. {
  484. spItems = 0;
  485. ++subPage;
  486. cury = ag->y;
  487. }
  488. }
  489. if ( spItems > 0 )
  490. {
  491. page->m_nSubPageCount = subPage + 1;
  492. }
  493. else
  494. {
  495. // We thought we needed a new page, but didn't actually use it
  496. page->m_nSubPageCount = subPage;
  497. }
  498. }
  499. }
  500. }
  501. m_lcd->SetVisible( item->m_Handle, bShowItems && ( ( item->m_nSubPage == -1 ) || item->m_nSubPage == m_nSubPage ) );
  502. ShowItems_R( page, dwCurTime, item->m_Children, bShowItems );
  503. }
  504. }
  505. void CLCD::DisplayCurrentPage( unsigned int dwCurTime )
  506. {
  507. int pageCount = m_Pages.Count();
  508. for ( int i = 0; i < pageCount; ++i )
  509. {
  510. bool bShowItems = ( i == m_nCurrentPage ) ? true : false;
  511. CLCDPage* page = m_Pages[ i ];
  512. ShowItems_R( page, dwCurTime, page->m_Children, bShowItems );
  513. }
  514. }
  515. CLCDItemIcon *CLCD::ParseItemIcon( CLCDPage *page, bool bCreateHandles, KeyValues *sub )
  516. {
  517. CLCDItemIcon *item = static_cast< CLCDItemIcon * >( page->Alloc( LCDITEM_ICON ) );
  518. item->m_IconName = sub->GetString( "name", "" );
  519. item->m_nSubPage = sub->GetInt( "header", 0 ) ? -1 : page->m_nSubPageCount - 1;
  520. item->w = sub->GetInt( "w", 24 );
  521. item->h = sub->GetInt( "h", 24 );
  522. item->x = sub->GetInt( "x", 0 );
  523. item->y = sub->GetInt( "y", 0 );
  524. int idx = m_Icons.Find( item->m_IconName.String() );
  525. item->m_icon = 0;
  526. if ( idx != m_Icons.InvalidIndex() )
  527. {
  528. item->m_icon = (void *)m_Icons[ idx ].m_handle;
  529. }
  530. if ( bCreateHandles )
  531. {
  532. item->Create( m_lcd );
  533. }
  534. return item;
  535. }
  536. CLCDItemText *CLCD::ParseItemText( CLCDPage *page, bool bCreateHandles, KeyValues *sub )
  537. {
  538. CLCDItemText *item = static_cast< CLCDItemText * >( page->Alloc( LCDITEM_TEXT ) );
  539. const char *initialText = sub->GetString( "text", "" );
  540. item->m_bHasWildcard = Q_strstr( initialText, "%" ) ? true : false;
  541. item->m_nSubPage = sub->GetInt( "header", 0 ) ? -1 : page->m_nSubPageCount - 1;
  542. item->w = sub->GetInt( "w", 150 );
  543. item->x = sub->GetInt( "x", 0 );
  544. item->y = sub->GetInt( "y", 0 );
  545. const char *sizeStr = sub->GetString( "size", "small" );
  546. item->m_iSize = G15_SMALL;
  547. int iFound = m_TextSizes.Find( sizeStr );
  548. if ( iFound != m_TextSizes.InvalidIndex() )
  549. {
  550. item->m_iSize = m_TextSizes[ iFound ];
  551. }
  552. const char *alignStr = sub->GetString( "align", "left" );
  553. item->m_iAlign = DT_LEFT;
  554. iFound = m_TextAlignments.Find( alignStr );
  555. if ( iFound != m_TextAlignments.InvalidIndex() )
  556. {
  557. item->m_iAlign = m_TextAlignments[ iFound ];
  558. }
  559. item->m_OriginalText = initialText;
  560. if ( bCreateHandles )
  561. {
  562. item->Create( m_lcd );
  563. }
  564. return item;
  565. }
  566. void CLCD::ParseItems_R( CLCDPage *page, bool bCreateHandles, KeyValues *kv, CUtlVector< CLCDItem * >& list )
  567. {
  568. for ( KeyValues *sub = kv->GetFirstSubKey(); sub; sub = sub->GetNextKey() )
  569. {
  570. char const *keyName = sub->GetName();
  571. if ( !Q_stricmp( keyName, "iterate_players" ) ||
  572. !Q_stricmp( keyName, "iterate_team" ) )
  573. {
  574. int aggType = AGGTYPE_UNKNOWN;
  575. if ( !Q_stricmp( keyName, "iterate_players" ) )
  576. {
  577. aggType = AGGTYPE_PERPLAYER;
  578. }
  579. else if ( !Q_stricmp( keyName, "iterate_team" ) )
  580. {
  581. aggType = AGGTYPE_PERTEAM;
  582. }
  583. // Now we parse the items out as we generally would
  584. CLCDItemAggregate *item = static_cast< CLCDItemAggregate * >( page->Alloc( LCDITEM_AGGREGATE ) );
  585. item->m_AggType = aggType;
  586. item->x = sub->GetInt( "x", 0 );
  587. item->y = sub->GetInt( "y", 0 );
  588. item->m_yincrement = sub->GetInt( "y_increment", 10 );
  589. // Parse the definition
  590. ParseItems_R( page, false, sub, item->m_Definition );
  591. // Add the definition items as "inactive" items to the scene (so they get destroyed)
  592. for ( int i = 0; i < item->m_Definition.Count(); ++i )
  593. {
  594. CLCDItem *pItem = item->m_Definition[ i ];
  595. pItem->m_bActive = false;
  596. }
  597. list.AddToTail( item );
  598. }
  599. else if ( !Q_stricmp( keyName, "static_icon" ) )
  600. {
  601. CLCDItemIcon *item = ParseItemIcon( page, true, sub );
  602. Assert( item );
  603. list.AddToTail(item );
  604. }
  605. else if ( !Q_stricmp( keyName, "static_text" ) )
  606. {
  607. CLCDItemText *item = ParseItemText( page, true, sub );
  608. Assert( item );
  609. list.AddToTail( item );
  610. }
  611. else if ( !Q_stricmp( keyName, "newsubpage" ) )
  612. {
  613. // Add to new subpage
  614. ++page->m_nSubPageCount;
  615. }
  616. else
  617. {
  618. // Skip unknown stuff
  619. continue;
  620. }
  621. }
  622. }
  623. void CLCD::ParsePage( KeyValues *kv )
  624. {
  625. CLCDPage *newPage = new CLCDPage();
  626. m_Pages.AddToTail( newPage );
  627. newPage->m_bTitlePage = kv->GetInt( "titlepage", 0 ) ? true : false;
  628. newPage->m_bRequiresPlayer = kv->GetInt( "requiresplayer", 0 ) ? true : false;
  629. ParseItems_R( newPage, true, kv, newPage->m_Children );
  630. }
  631. void CLCD::ParseIconMappings( KeyValues *kv )
  632. {
  633. for ( KeyValues *icon = kv->GetFirstSubKey(); icon; icon = icon->GetNextKey() )
  634. {
  635. IconInfo_t info;
  636. HICON hIcon = 0;
  637. char const *name = icon->GetName();
  638. char fullpath[ 512 ];
  639. filesystem->RelativePathToFullPath( icon->GetString(), "GAME", fullpath, sizeof( fullpath ) );
  640. #ifdef WIN32
  641. hIcon = (HICON)::LoadImageA( NULL, fullpath, IMAGE_ICON, 32, 32, LR_LOADFROMFILE );
  642. #else
  643. hIcon = 0;
  644. #endif
  645. info.m_handle = (void *)hIcon;
  646. m_Icons.Insert( name, info );
  647. }
  648. }
  649. //-----------------------------------------------------------------------------
  650. // Purpose: Simply dumps all data fields in object
  651. //-----------------------------------------------------------------------------
  652. class CDescribeData
  653. {
  654. public:
  655. CDescribeData( void const *src );
  656. void DescribeShort( const short *invalue, int count );
  657. void DescribeInt( const int *invalue, int count );
  658. void DescribeBool( const bool *invalue, int count );
  659. void DescribeFloat( const float *invalue, int count );
  660. void DescribeSimpleString( const char *indata, int length );
  661. void DescribeString( const string_t *instring, int count );
  662. void DescribeVector( const Vector *inValue, int count );
  663. void DescribeColor( const Color *invalue, int count );
  664. void DumpDescription( datamap_t *pMap );
  665. void GetValueForField( char const *fieldName, char *buf, size_t bufsize );
  666. private:
  667. void DescribeFields_R( int chain_count, datamap_t *pMap, typedescription_t *pFields, int fieldCount );
  668. bool BuildFieldPath( CUtlString& path );
  669. void const *m_pSrc;
  670. int m_nSrcOffsetIndex;
  671. void Describe( const char *fmt, ... );
  672. typedescription_t *m_pCurrentField;
  673. char const *m_pCurrentClassName;
  674. datamap_t *m_pCurrentMap;
  675. CUtlVector< CUtlString > m_FieldPath;
  676. };
  677. CDescribeData::CDescribeData( void const *src )
  678. {
  679. m_pSrc = src;
  680. m_nSrcOffsetIndex = TD_OFFSET_NORMAL;
  681. m_pCurrentField = NULL;
  682. m_pCurrentMap = NULL;
  683. m_pCurrentClassName = NULL;
  684. }
  685. typedescription_t *FindFieldByName( datamap_t *pMap, char const *fn )
  686. {
  687. while ( pMap )
  688. {
  689. for ( int i = 0; i < pMap->dataNumFields; i++ )
  690. {
  691. typedescription_t *current = &pMap->dataDesc[ i ];
  692. if ( !current->fieldName )
  693. continue;
  694. if ( !Q_stricmp( current->fieldName, fn ) )
  695. return current;
  696. }
  697. pMap = pMap->baseMap;
  698. }
  699. return NULL;
  700. }
  701. typedescription_t *FindField( datamap_t *pMap, char const *relativePath )
  702. {
  703. if ( !Q_strstr( relativePath, "." ) )
  704. {
  705. // Simple case, just look up field name
  706. return FindFieldByName( pMap, relativePath );
  707. }
  708. // Complex case
  709. Assert( 0 );
  710. return NULL;
  711. }
  712. bool CDescribeData::BuildFieldPath( CUtlString& path )
  713. {
  714. int c = m_FieldPath.Count();
  715. if ( c == 0 )
  716. return false;
  717. for ( int i = 0; i < c; ++i )
  718. {
  719. CUtlString& s = m_FieldPath[ i ];
  720. if ( i != 0 )
  721. {
  722. path += ".";
  723. }
  724. path += s;
  725. }
  726. return true;
  727. }
  728. //-----------------------------------------------------------------------------
  729. // Purpose:
  730. // Input : *fmt -
  731. // ... -
  732. //-----------------------------------------------------------------------------
  733. void CDescribeData::Describe( const char *fmt, ... )
  734. {
  735. Assert( m_pCurrentMap );
  736. Assert( m_pCurrentClassName );
  737. const char *fieldname = "empty";
  738. if ( m_pCurrentField )
  739. {
  740. fieldname = m_pCurrentField->fieldName ? m_pCurrentField->fieldName : "NULL";
  741. }
  742. va_list argptr;
  743. char data[ 4096 ];
  744. int len;
  745. va_start(argptr, fmt);
  746. len = Q_vsnprintf(data, sizeof( data ), fmt, argptr);
  747. va_end(argptr);
  748. CUtlString fp;
  749. if ( BuildFieldPath( fp ) )
  750. {
  751. Msg( "%s.%s%s",
  752. fp.String(),
  753. fieldname,
  754. data );
  755. }
  756. else
  757. {
  758. Msg( "%s%s",
  759. fieldname,
  760. data );
  761. }
  762. }
  763. //-----------------------------------------------------------------------------
  764. // Purpose:
  765. // Input : size -
  766. // *outdata -
  767. // *indata -
  768. //-----------------------------------------------------------------------------
  769. void CDescribeData::DescribeSimpleString( char const *invalue, int count )
  770. {
  771. Describe( "%s\n", invalue ? invalue : "" );
  772. }
  773. void CDescribeData::DescribeShort( const short *invalue, int count )
  774. {
  775. for ( int i = 0; i < count; ++i )
  776. {
  777. if ( count == 1 )
  778. {
  779. Describe( " short (%i)\n", (int)(invalue[i]) );
  780. }
  781. else
  782. {
  783. Describe( "[%i] short (%i)\n", i, (int)(invalue[i]) );
  784. }
  785. }
  786. }
  787. void CDescribeData::DescribeInt( const int *invalue, int count )
  788. {
  789. for ( int i = 0; i < count; ++i )
  790. {
  791. if ( count == 1 )
  792. {
  793. Describe( " integer (%i)\n", invalue[i] );
  794. }
  795. else
  796. {
  797. Describe( "[%i] integer (%i)\n", i, invalue[i] );
  798. }
  799. }
  800. }
  801. void CDescribeData::DescribeBool( const bool *invalue, int count )
  802. {
  803. for ( int i = 0; i < count; ++i )
  804. {
  805. if ( count == 1 )
  806. {
  807. Describe( " bool (%s)\n", (invalue[i]) ? "true" : "false" );
  808. }
  809. else
  810. {
  811. Describe( "[%i] bool (%s)\n", i, (invalue[i]) ? "true" : "false" );
  812. }
  813. }
  814. }
  815. void CDescribeData::DescribeFloat( const float *invalue, int count )
  816. {
  817. for ( int i = 0; i < count; ++i )
  818. {
  819. if ( count == 1 )
  820. {
  821. Describe( " float (%f)\n", invalue[ i ] );
  822. }
  823. else
  824. {
  825. Describe( "[%i] float (%f)\n", i, invalue[ i ] );
  826. }
  827. }
  828. }
  829. void CDescribeData::DescribeString( const string_t *instring, int count )
  830. {
  831. for ( int i = 0; i < count; ++i )
  832. {
  833. if ( count == 1 )
  834. {
  835. Describe( " string (%s)\n", instring[ i ] ? instring[ i ] : "" );
  836. }
  837. else
  838. {
  839. Describe( "[%i] string (%s)\n", i, instring[ i ] ? instring[ i ] : "" );
  840. }
  841. }
  842. }
  843. void CDescribeData::DescribeColor( const Color *invalue, int count )
  844. {
  845. for ( int i = 0; i < count; ++i )
  846. {
  847. if ( count == 1 )
  848. {
  849. Describe( " color (%i %i %i %i)\n", invalue[ i ].r(), invalue[ i ].g(), invalue[ i ].b(), invalue[ i ].a() );
  850. }
  851. else
  852. {
  853. Describe( "[%i] color (%i %i %i %i)\n", i, invalue[ i ].r(), invalue[ i ].g(), invalue[ i ].b(), invalue[ i ].a() );
  854. }
  855. }
  856. }
  857. void CDescribeData::DescribeVector( const Vector *inValue, int count )
  858. {
  859. for ( int i = 0; i < count; ++i )
  860. {
  861. if ( count == 1 )
  862. {
  863. Describe( " vector (%f %f %f)\n",
  864. inValue[i].x, inValue[i].y, inValue[i].z );
  865. }
  866. else
  867. {
  868. Describe( "[%i] vector (%f %f %f)\n",
  869. i,
  870. inValue[i].x, inValue[i].y, inValue[i].z );
  871. }
  872. }
  873. }
  874. void CDescribeData::DescribeFields_R( int chain_count, datamap_t *pRootMap, typedescription_t *pFields, int fieldCount )
  875. {
  876. int i;
  877. int flags;
  878. int fieldOffsetSrc;
  879. int fieldSize;
  880. m_pCurrentMap = pRootMap;
  881. if ( !m_pCurrentClassName )
  882. {
  883. m_pCurrentClassName = pRootMap->dataClassName;
  884. }
  885. for ( i = 0; i < fieldCount; i++ )
  886. {
  887. m_pCurrentField = &pFields[ i ];
  888. flags = m_pCurrentField->flags;
  889. // Skip this field
  890. if ( flags & FTYPEDESC_VIEW_NEVER )
  891. continue;
  892. // Mark any subchains first
  893. if ( m_pCurrentField->override_field != NULL )
  894. {
  895. m_pCurrentField->override_field->override_count = chain_count;
  896. }
  897. // Skip this field?
  898. if ( m_pCurrentField->override_count == chain_count )
  899. {
  900. continue;
  901. }
  902. void const *pInputData;
  903. fieldOffsetSrc = m_pCurrentField->fieldOffset[ m_nSrcOffsetIndex ];
  904. fieldSize = m_pCurrentField->fieldSize;
  905. pInputData = (void const *)((char *)m_pSrc + fieldOffsetSrc );
  906. switch( m_pCurrentField->fieldType )
  907. {
  908. default:
  909. break;
  910. case FIELD_EMBEDDED:
  911. {
  912. typedescription_t *save = m_pCurrentField;
  913. void const *saveSrc = m_pSrc;
  914. const char *saveName = m_pCurrentClassName;
  915. m_pCurrentClassName = m_pCurrentField->td->dataClassName;
  916. CUtlString str;
  917. str = m_pCurrentField->fieldName;
  918. m_FieldPath.AddToTail( str );
  919. m_pSrc = pInputData;
  920. if ( ( flags & FTYPEDESC_PTR ) && (m_nSrcOffsetIndex == PC_DATA_NORMAL) )
  921. {
  922. m_pSrc = *((void**)m_pSrc);
  923. }
  924. DescribeFields_R( chain_count, pRootMap, m_pCurrentField->td->dataDesc, m_pCurrentField->td->dataNumFields );
  925. m_FieldPath.Remove( m_FieldPath.Count() - 1 );
  926. m_pCurrentClassName = saveName;
  927. m_pCurrentField = save;
  928. m_pSrc = saveSrc;
  929. }
  930. break;
  931. case FIELD_FLOAT:
  932. DescribeFloat( (float const *)pInputData, fieldSize );
  933. break;
  934. case FIELD_STRING:
  935. DescribeString( (const string_t*)pInputData, fieldSize );
  936. break;
  937. case FIELD_VECTOR:
  938. DescribeVector( (const Vector *)pInputData, fieldSize );
  939. break;
  940. case FIELD_COLOR32:
  941. DescribeColor( (const Color *)pInputData, fieldSize );
  942. break;
  943. case FIELD_BOOLEAN:
  944. DescribeBool( (bool const *)pInputData, fieldSize );
  945. break;
  946. case FIELD_INTEGER:
  947. DescribeInt( (int const *)pInputData, fieldSize );
  948. break;
  949. case FIELD_SHORT:
  950. DescribeShort( (short const *)pInputData, fieldSize );
  951. break;
  952. case FIELD_CHARACTER:
  953. DescribeSimpleString( (const char *)pInputData, fieldSize );
  954. break;
  955. }
  956. }
  957. m_pCurrentClassName = NULL;
  958. }
  959. static int g_nChainCount = 1;
  960. extern void ValidateChains_R( datamap_t *dmap );
  961. void CDescribeData::DumpDescription( datamap_t *pMap )
  962. {
  963. ++g_nChainCount;
  964. if ( !pMap->chains_validated )
  965. {
  966. ValidateChains_R( pMap );
  967. }
  968. while ( pMap )
  969. {
  970. DescribeFields_R( g_nChainCount, pMap, pMap->dataDesc, pMap->dataNumFields );
  971. pMap = pMap->baseMap;
  972. }
  973. }
  974. void CLCD::DumpPlayer()
  975. {
  976. C_BasePlayer *player = C_BasePlayer::GetLocalPlayer();
  977. if ( !player )
  978. return;
  979. Msg( "(localplayer)\n\n" );
  980. {
  981. CDescribeData helper( player );
  982. helper.DumpDescription( player->GetPredDescMap() );
  983. }
  984. Msg( "(localteam)\n\n" );
  985. C_Team *team = player->GetTeam();
  986. if ( team )
  987. {
  988. CDescribeData helper( team );
  989. helper.DumpDescription( team->GetPredDescMap() );
  990. }
  991. Msg( "(playerresource)\n\n" );
  992. if ( g_PR )
  993. {
  994. CDescribeData helper( g_PR );
  995. helper.DumpDescription( g_PR->GetPredDescMap() );
  996. }
  997. Msg( "(localplayerweapon)\n\n" );
  998. // Get the player's weapons, too
  999. C_BaseCombatWeapon *active = player->GetActiveWeapon();
  1000. if ( active )
  1001. {
  1002. CDescribeData helper( active );
  1003. helper.DumpDescription( active->GetPredDescMap() );
  1004. }
  1005. Msg( "Other replacements:\n\n" );
  1006. // Global replacements
  1007. for( int i = m_GlobalStats.First() ; i != m_GlobalStats.InvalidIndex(); i = m_GlobalStats.Next( i ) )
  1008. {
  1009. CUtlString& r = m_GlobalStats[ i ];
  1010. char const *pReplace = r.String();
  1011. char ansi[ 512 ];
  1012. ansi[ 0 ] = 0;
  1013. if ( pReplace[ 0 ] == '#' )
  1014. {
  1015. const wchar_t *pWString = g_pVGuiLocalize->Find( pReplace );
  1016. if ( pWString )
  1017. {
  1018. g_pVGuiLocalize->ConvertUnicodeToANSI( pWString, ansi, sizeof( ansi ) );
  1019. pReplace = ansi;
  1020. }
  1021. }
  1022. Msg( "'%s' = '%s'\n", m_GlobalStats.GetElementName( i ), pReplace );
  1023. }
  1024. }
  1025. bool CLCD::ExtractArrayIndex( char *str, size_t bufsize, int *index )
  1026. {
  1027. Assert( index );
  1028. *index = 0;
  1029. char s[ 2048 ];
  1030. Q_strncpy( s, str, sizeof( s ) );
  1031. char *pos = Q_strstr( s, "[" );
  1032. if ( !pos )
  1033. return false;
  1034. char *pos2 = Q_strstr( s, "]" );
  1035. if ( !pos2 )
  1036. return false;
  1037. char num[ 32 ];
  1038. Q_strncpy( num, pos + 1, pos2 - pos );
  1039. *index = Q_atoi( num );
  1040. int left = pos - s + 1;
  1041. char o[ 2048 ];
  1042. Q_strncpy( o, s, left );
  1043. Q_strncat( o, pos2 + 1, sizeof( o ), COPY_ALL_CHARACTERS );
  1044. Q_strncpy( str, o, bufsize );
  1045. return true;
  1046. }
  1047. void CLCD::LookupToken( char const *in, CUtlString& value )
  1048. {
  1049. value = "";
  1050. C_BasePlayer *player = C_BasePlayer::GetLocalPlayer();
  1051. if ( !player )
  1052. {
  1053. return;
  1054. }
  1055. C_BaseEntity *ref = NULL;
  1056. char outbuf[ 1024 ];
  1057. char *o = outbuf;
  1058. char const *i = in;
  1059. while ( *i )
  1060. {
  1061. if ( *i == '(' )
  1062. {
  1063. char token[ 512 ];
  1064. char *to = token;
  1065. // swallow everything until matching '%' character
  1066. ++i;
  1067. while ( *i && *i != ')' )
  1068. {
  1069. *to++ = *i++;
  1070. }
  1071. if ( *i )
  1072. ++i;
  1073. *to = 0;
  1074. if ( !Q_stricmp( token, "localplayer" ) )
  1075. {
  1076. ref = player;
  1077. }
  1078. else if ( !Q_stricmp( token, "localteam" ) )
  1079. {
  1080. ref = player->GetTeam();
  1081. }
  1082. else if ( !Q_stricmp( token, "localplayerweapon" ) )
  1083. {
  1084. ref = player->GetActiveWeapon();
  1085. }
  1086. else if ( !Q_stricmp( token, "playerresource" ) )
  1087. {
  1088. ref = g_PR;
  1089. }
  1090. }
  1091. else
  1092. {
  1093. *o++ = *i++;
  1094. }
  1095. }
  1096. *o = '\0';
  1097. // Fixme, also need to get array reference removed from end and do the . field searching stuff
  1098. // Outbuf now has the actual field
  1099. if ( !ref )
  1100. {
  1101. return;
  1102. }
  1103. int iIndex = 0;
  1104. ExtractArrayIndex( outbuf, sizeof( outbuf ), &iIndex );
  1105. typedescription_t *td = FindField( ref->GetPredDescMap(), outbuf );
  1106. if ( !td )
  1107. {
  1108. return;
  1109. }
  1110. // Not allowed to see this one
  1111. if ( td->flags & FTYPEDESC_VIEW_NEVER )
  1112. {
  1113. return;
  1114. }
  1115. int fieldOffsetSrc = td->fieldOffset[ TD_OFFSET_NORMAL ];
  1116. // int fieldSize = td->fieldSize;
  1117. void const *pInputData = (void const *)((char *)ref + fieldOffsetSrc );
  1118. char sz[ 256 ];
  1119. sz[ 0 ] = 0;
  1120. // Found it, now get the value
  1121. switch ( td->fieldType )
  1122. {
  1123. case FIELD_FLOAT:
  1124. Q_snprintf( sz, sizeof( sz ), "%.2f", *((float *)pInputData + iIndex ) );
  1125. break;
  1126. case FIELD_STRING:
  1127. {
  1128. string_t *pString = (string_t *)((string_t *)pInputData + iIndex );
  1129. Q_snprintf( sz, sizeof( sz ), "%s", pString ? STRING( *pString ) : "" );
  1130. }
  1131. break;
  1132. case FIELD_VECTOR:
  1133. {
  1134. Vector v = *((Vector *)pInputData + iIndex );
  1135. Q_snprintf( sz, sizeof( sz ), "%.2f %.2f %.2f", v.x, v.y, v.z );
  1136. }
  1137. break;
  1138. case FIELD_COLOR32:
  1139. {
  1140. Color c = *(( Color * )pInputData + iIndex );
  1141. Q_snprintf( sz, sizeof( sz ), "%d %d %d %d", c.r(), c.g(), c.b(), c.a() );
  1142. }
  1143. break;
  1144. case FIELD_BOOLEAN:
  1145. Q_snprintf( sz, sizeof( sz ), "%s", *( ( bool *)pInputData + iIndex ) ? "true" : "false" );
  1146. break;
  1147. case FIELD_INTEGER:
  1148. Q_snprintf( sz, sizeof( sz ), "%i", *( (int *)pInputData + iIndex ));
  1149. break;
  1150. case FIELD_SHORT:
  1151. Q_snprintf( sz, sizeof( sz ), "%i", *( (short *)pInputData + iIndex ) );
  1152. break;
  1153. case FIELD_CHARACTER:
  1154. Q_snprintf( sz, sizeof( sz ), "%s", ((const char *)pInputData + iIndex ) );
  1155. break;
  1156. }
  1157. value = sz;
  1158. }
  1159. void CLCD::BuildUpdatedText( char const *in, CUtlString& out )
  1160. {
  1161. char outbuf[ 1024 ];
  1162. char *o = outbuf;
  1163. char const *i = in;
  1164. while ( *i )
  1165. {
  1166. if ( *i == '%' )
  1167. {
  1168. char token[ 512 ];
  1169. char *to = token;
  1170. // swallow everything until matching '%' character
  1171. ++i;
  1172. while ( *i && *i != '%' )
  1173. {
  1174. *to++ = *i++;
  1175. }
  1176. if ( *i )
  1177. ++i;
  1178. *to = 0;
  1179. // Now we have the token, do the lookup
  1180. CUtlString value;
  1181. LookupToken( token, value );
  1182. to = (char *)value.String();
  1183. while ( *to )
  1184. {
  1185. *o++ = *to++;
  1186. }
  1187. }
  1188. else
  1189. {
  1190. *o++ = *i++;
  1191. }
  1192. }
  1193. *o = '\0';
  1194. out = outbuf;
  1195. }
  1196. bool CLCD::Replace( CUtlString& str, char const *search, char const *replace )
  1197. {
  1198. // If search string is part of replacement, this is a bad thing!!!
  1199. Assert( !*replace || !Q_strstr( replace, search ) );
  1200. bool changed = false;
  1201. if ( !Q_strstr( str.String(), search ) )
  1202. return false;
  1203. char s[ 2048 ];
  1204. Q_strncpy( s, str.String(), sizeof( s ) );
  1205. int searchlen = Q_strlen( search );
  1206. while ( true )
  1207. {
  1208. char *pos = Q_strstr( s, search );
  1209. if ( !pos )
  1210. break;
  1211. char temp[ 4096 ];
  1212. // Found an instance
  1213. int left = pos - s + 1;
  1214. Assert( left < sizeof( temp ) );
  1215. Q_strncpy( temp, s, left );
  1216. Q_strncat( temp, replace, sizeof( temp ), COPY_ALL_CHARACTERS );
  1217. int rightofs = left + searchlen - 1;
  1218. Q_strncat( temp, &s[ rightofs ], sizeof( temp ), COPY_ALL_CHARACTERS );
  1219. // Replace entire string
  1220. Q_strncpy( s, temp, sizeof( s ) );
  1221. changed = true;
  1222. }
  1223. str = s;
  1224. return changed;
  1225. }
  1226. void CLCD::SetGlobalStat( char const *name, char const *value )
  1227. {
  1228. if ( !m_lcd )
  1229. return;
  1230. int idx = m_GlobalStats.Find( name );
  1231. if ( idx == m_GlobalStats.InvalidIndex() )
  1232. {
  1233. idx = m_GlobalStats.Insert( name );
  1234. }
  1235. m_GlobalStats[ idx ] = value;
  1236. }
  1237. void CLCD::AddChatLine( char const *txt )
  1238. {
  1239. if ( !m_lcd )
  1240. return;
  1241. while ( m_ChatHistory.Count() >= m_nMaxChatHistory )
  1242. {
  1243. m_ChatHistory.Remove( 0 );
  1244. }
  1245. m_ChatHistory.AddToTail( CUtlString( txt ) );
  1246. UpdateChat();
  1247. }
  1248. void CLCD::UpdateChat()
  1249. {
  1250. for ( int i = 0; i < m_nMaxChatHistory; ++i )
  1251. {
  1252. char name[ 32 ];
  1253. Q_snprintf( name, sizeof( name ), "chat_%d", i + 1 );
  1254. SetGlobalStat( name, i < m_ChatHistory.Count() ? m_ChatHistory[ i ].String() : " " );
  1255. }
  1256. }
  1257. void CLCD::DoGlobalReplacements( CUtlString& str )
  1258. {
  1259. // Put some limit to avoid infinite recursion
  1260. int maxChanges = 16;
  1261. bool changed = false;
  1262. do
  1263. {
  1264. changed = false;
  1265. for ( int i = m_GlobalStats.First(); i != m_GlobalStats.InvalidIndex(); i = m_GlobalStats.Next( i ) )
  1266. {
  1267. CUtlString &r = m_GlobalStats[ i ];
  1268. char const *pReplace = r.String();
  1269. char ansi[ 512 ];
  1270. ansi[ 0 ] = 0;
  1271. if ( pReplace[ 0 ] == '#' )
  1272. {
  1273. const wchar_t *pWString = g_pVGuiLocalize->Find( pReplace );
  1274. if ( pWString )
  1275. {
  1276. g_pVGuiLocalize->ConvertUnicodeToANSI( pWString, ansi, sizeof( ansi ) );
  1277. pReplace = ansi;
  1278. }
  1279. }
  1280. if ( Replace( str, m_GlobalStats.GetElementName( i ), pReplace ) )
  1281. {
  1282. changed = true;
  1283. }
  1284. }
  1285. } while ( changed && --maxChanges >= 0 );
  1286. }
  1287. void CLCD::ReduceParentheses( CUtlString& str )
  1288. {
  1289. char s[ 2048 ];
  1290. Q_strncpy( s, str.String(), sizeof( s ) );
  1291. while ( true )
  1292. {
  1293. char *pos = Q_strstr( s, "(" );
  1294. if ( !pos )
  1295. break;
  1296. char *end = Q_strstr( pos, ")" );
  1297. if ( !end )
  1298. break;
  1299. char temp[ 4096 ];
  1300. // Found an instance
  1301. int left = pos - s + 1;
  1302. Assert( left < sizeof( temp ) );
  1303. Q_strncpy( temp, s, left );
  1304. int rightofs = end - s + 1;
  1305. Q_strncat( temp, &s[ rightofs ], sizeof( temp ), COPY_ALL_CHARACTERS );
  1306. // Replace entire string
  1307. Q_strncpy( s, temp, sizeof( s ) );
  1308. }
  1309. str = s;
  1310. }
  1311. void CLCD::ParseReplacements( KeyValues *kv )
  1312. {
  1313. for ( KeyValues *sub = kv->GetFirstSubKey(); sub; sub = sub->GetNextKey() )
  1314. {
  1315. char const *key = sub->GetName();
  1316. char const *value = sub->GetString();
  1317. SetGlobalStat( key, value );
  1318. }
  1319. }