Counter Strike : Global Offensive Source Code
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.

1139 lines
30 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "stdafx.h"
  8. #include "tier1/keyvalues.h"
  9. #include "vgui/ILocalize.h"
  10. #include <vstdlib/vstrtools.h>
  11. #include <vstdlib/ikeyvaluessystem.h>
  12. // memdbgon must be the last include file in a .cpp file!!!
  13. #include <tier0/memdbgon.h>
  14. #define HTML_BUFFER_CHARS 4086
  15. #define MAX_KEYWORD_BUFFER_SIZE 128
  16. // Undefining this for the Pro-Player demo:
  17. // #define FORCE_BUTTON_GLYPHS
  18. /*******************************************************************
  19. * how button glyphs work:
  20. *
  21. 1. in the translation key, the code looks for an @ sign with a number following ( withoutspaces ) and assumes the number is
  22. the font size. So in Flash, you might enter #SFUI_textkey@15 in text with a 15 point font and #SFUI_textkey@25 in text
  23. with a 25 point font.
  24. 2. in the translated text, the code looks for keywords of the form ${keyword} and looks those keywords up in the
  25. g_buttonFunctionKeywords array below. The position of the keyword in the array is interpreted as an
  26. IScaleformUI::ControllerButton::Enum, is used to place the matching button glyph into the string. For example we can produce
  27. the glyph for the button normally used to cancel ( the B button on xbox ) by using the string ${cancel} in a translated string.
  28. 3. if no button was found to match the keyword, they keyword is matched against the functions currently bound to the buttons
  29. with the preceing '+' stripped. That way you can use any of the bindable commands like "attack" or "forward"
  30. 4. if the controller button is currently not mapped, then we'll attempt to translate the keyword ( or failing that
  31. the keyword itself ) and output that as text.
  32. 5. if ControllerButton was found, then the button value is then used to lookup a string in the g_controllerButtonNames array.
  33. This string corresponds to the name of the button image in the sharedlib.swf flash file.
  34. */
  35. // global buffer used to hold the string after translation and insertion of html codes
  36. wchar_t g_htmlBuffer[HTML_BUFFER_CHARS + 1];
  37. // global buffer used to return wchar_t results when the caller called with char
  38. wchar_t g_wcharBuffer[HTML_BUFFER_CHARS + 1];
  39. // html string used to replace the button mnemonic ( @ will get replaced with the button name )
  40. #if defined( _PS3 ) // Wide strings are %ls on ps3
  41. const wchar_t* g_htmlReplacementString = L"<img src='%ls' align='baseline' width='%d' height='%d' vspace='%d'/>";
  42. #elif defined( POSIX )
  43. const wchar_t* g_htmlReplacementString = L"<img src='%lls' align='baseline' width='%d' height='%d' vspace='%d'/>";
  44. #else
  45. const wchar_t* g_htmlReplacementString = L"<img src='%s' align='baseline' width='%d' height='%d' vspace='%d'/>";
  46. #endif
  47. const int g_defaultGlyphSize=23;
  48. const int g_defaultGlyphOffset=-6;
  49. // these are the names of the button images in sharedlib.swf
  50. // these should be in the same order as IScaleformUI::ControllerButtons
  51. // this will need to be changed for PS3
  52. const wchar_t* g_controllerButtonImageNames[] = {
  53. L"XBoxA.png",
  54. L"XBoxB.png",
  55. L"XBoxX.png",
  56. L"XBoxY.png",
  57. L"XBoxLShoulder.png",
  58. L"XBoxRShoulder.png",
  59. L"XBoxLTrigger.png",
  60. L"XBoxRTrigger.png",
  61. L"XBoxDPadUp.png",
  62. L"XBoxDPadDown.png",
  63. L"XBoxDPadLeft.png",
  64. L"XBoxDPadRight.png",
  65. L"XBoxDPad.png",
  66. L"XBoxLStickUp.png",
  67. L"XBoxLStickDown.png",
  68. L"XBoxLStickLeft.png",
  69. L"XBoxLStickRight.png",
  70. L"XBoxLStickButton.png",
  71. L"XBoxLStick.png",
  72. L"XBoxRStickUp.png",
  73. L"XBoxRStickDown.png",
  74. L"XBoxRStickLeft.png",
  75. L"XBoxRStickRight.png",
  76. L"XBoxRStickButton.png",
  77. L"XBoxRStick.png",
  78. L"XBoxStart.png",
  79. L"XBoxBack.png",
  80. L"Undefined"
  81. };
  82. const wchar_t* g_controllerButtonImageNamesSony[] = {
  83. L"x.png",
  84. L"circle.png",
  85. L"square.png",
  86. L"triangle.png",
  87. L"left-1-shoulder.png",
  88. L"right-1-shoulder.png",
  89. L"left-2-shoulder.png",
  90. L"right-2-shoulder.png",
  91. L"d-pad-up.png",
  92. L"d-pad-down.png",
  93. L"d-pad-left.png",
  94. L"d-pad-right.png",
  95. L"d-pad.png",
  96. L"left-stick-up.png",
  97. L"left-stick-down.png",
  98. L"left-stick-left.png",
  99. L"left-stick-right.png",
  100. L"left-stick-button.png",
  101. L"left-stick.png",
  102. L"right-stick-up.png",
  103. L"right-stick-down.png",
  104. L"right-stick-left.png",
  105. L"right-stick-right.png",
  106. L"right-stick-button.png",
  107. L"right-stick.png",
  108. L"start.png",
  109. L"select.png",
  110. L"Undefined"
  111. };
  112. // names that will be shown in place of the button glyphs
  113. const wchar_t* g_controllerButtonPCNames[] = {
  114. L" [SPACE] ",
  115. L" [ESC] ",
  116. NULL,
  117. NULL,
  118. NULL,
  119. NULL,
  120. NULL,
  121. NULL,
  122. L" [UP] ",
  123. L" [DOWN] ",
  124. L" [LEFT] ",
  125. L" [RIGHT] ",
  126. NULL,
  127. L" [UP] ",
  128. L" [DOWN] ",
  129. L" [LEFT] ",
  130. L" [RIGHT] ",
  131. NULL,
  132. NULL,
  133. NULL,
  134. NULL,
  135. NULL,
  136. NULL,
  137. NULL,
  138. NULL,
  139. NULL,
  140. NULL,
  141. };
  142. // these are the names to embed in strings. ie. ${dpad}
  143. const wchar_t* g_buttonFunctionKeywords[] = {
  144. // ** these should be in the order of the IScaleformUI::ControllerButton::Enum
  145. // and the length of the array should be an even multiple of IScaleformUI::ControllerButton::NumButtons
  146. // because the location of the keyword in this array is %'d by NumButtons to get a valid IScaleformUI::ControllerButton value
  147. // these are the generic names
  148. L"confirm",
  149. L"cancel",
  150. L"west",
  151. L"north",
  152. L"lshoulder",
  153. L"rshoulder",
  154. L"ltrigger",
  155. L"rtrigger",
  156. L"dpadup",
  157. L"dpaddown",
  158. L"dpadleft",
  159. L"dpadright",
  160. L"dpad",
  161. L"lstickup",
  162. L"lstickdown",
  163. L"lstickleft",
  164. L"lstickright",
  165. L"lstickbutton",
  166. L"lstick",
  167. L"rstickup",
  168. L"rstickdown",
  169. L"rstickleft",
  170. L"rstickright",
  171. L"rstickbutton",
  172. L"rstick",
  173. L"start",
  174. L"altstart",
  175. // these are the xbox names
  176. L"xboxa", // Confirm,
  177. L"xboxb", // Cancel,
  178. L"xboxx", // West,
  179. L"xboxy", // North,
  180. L"xboxlb", // LShoulder,
  181. L"xboxrb", // RShoulder,
  182. L"xboxlt", // LTrigger,
  183. L"xboxrt", // RTrigger,
  184. L"", // DPadUp,
  185. L"", // DPadDown,
  186. L"", // DPadLeft,
  187. L"", // DPadRight,
  188. L"", // DPad,
  189. L"", // LStickUp,
  190. L"", // LStickDown,
  191. L"", // LStickLeft,
  192. L"", // LStickRight,
  193. L"", // LStickButton,
  194. L"", // LStick,
  195. L"", // RStickUp,
  196. L"", // RStickDown,
  197. L"", // RStickLeft,
  198. L"", // RStickRight,
  199. L"", // RStickButton,
  200. L"", // RStick,
  201. L"xboxstart", // Start,
  202. L"xboxback", // AltStart,
  203. // and these are the ps3 names
  204. L"ps3x", // Confirm,
  205. L"ps3circle", // Cancel,
  206. L"ps3square", // West,
  207. L"ps3triangle", // North,
  208. L"ps3l1", // LShoulder,
  209. L"ps3r1", // RShoulder,
  210. L"ps3l2", // LTrigger,
  211. L"ps3r2", // RTrigger,
  212. L"", // DPadUp,
  213. L"", // DPadDown,
  214. L"", // DPadLeft,
  215. L"", // DPadRight,
  216. L"", // DPad,
  217. L"", // LStickUp,
  218. L"", // LStickDown,
  219. L"", // LStickLeft,
  220. L"", // LStickRight,
  221. L"ps3l3", // LStickButton,
  222. L"", // LStick,
  223. L"", // RStickUp,
  224. L"", // RStickDown,
  225. L"", // RStickLeft,
  226. L"", // RStickRight,
  227. L"ps3r3", // RStickButton,
  228. L"", // RStick,
  229. L"ps3start", // Start,
  230. L"ps3select", // AltStart,
  231. NULL, //this is what "undefined" keywords will return
  232. };
  233. // this table maps from the ControllerButton enum to button code defines
  234. int g_controllerButtonToButtonCodeLookup[] =
  235. {
  236. KEY_XBUTTON_A,
  237. KEY_XBUTTON_B,
  238. KEY_XBUTTON_X,
  239. KEY_XBUTTON_Y,
  240. KEY_XBUTTON_LEFT_SHOULDER,
  241. KEY_XBUTTON_RIGHT_SHOULDER,
  242. KEY_XBUTTON_LTRIGGER, // ZAXIS POSITIVE
  243. KEY_XBUTTON_RTRIGGER, // ZAXIS NEGATIVE
  244. KEY_XBUTTON_UP, // POV buttons
  245. KEY_XBUTTON_DOWN,
  246. KEY_XBUTTON_LEFT,
  247. KEY_XBUTTON_RIGHT,
  248. -1, //dpad
  249. KEY_XSTICK1_UP, // YAXIS NEGATIVE
  250. KEY_XSTICK1_DOWN, // YAXIS POSITIVE
  251. KEY_XSTICK1_LEFT, // XAXIS NEGATIVE
  252. KEY_XSTICK1_RIGHT, // XAXIS POSITIVE
  253. KEY_XBUTTON_STICK1,
  254. -1, //lstick
  255. KEY_XSTICK2_UP, // YAXIS NEGATIVE
  256. KEY_XSTICK2_DOWN, // YAXIS POSITIVE
  257. KEY_XSTICK2_LEFT, // XAXIS NEGATIVE
  258. KEY_XSTICK2_RIGHT, // XAXIS POSITIVE
  259. KEY_XBUTTON_STICK2,
  260. -1, //rstick
  261. KEY_XBUTTON_START,
  262. KEY_XBUTTON_BACK,
  263. };
  264. /************************
  265. * locally defined functions
  266. */
  267. // looks for keyword in g_buttonFunctionKeywords and returns -1 if not found
  268. int MapKeywordToTable( const wchar_t* keyword, const wchar_t** keywordTable )
  269. {
  270. int result = -1;
  271. int i = 0;
  272. while ( *keywordTable )
  273. {
  274. if ( **keywordTable && ( V_wcscmp( keyword, *keywordTable ) == 0 ) )
  275. {
  276. result = i;
  277. break;
  278. }
  279. keywordTable++;
  280. i++;
  281. }
  282. return result;
  283. }
  284. int CopyKeyword( wchar_t*dest, const wchar_t*src, int destSize )
  285. {
  286. int copied = 0;
  287. while ( destSize && *src && *src != L'}' )
  288. {
  289. *dest++ = *src++;
  290. copied++;
  291. destSize--;
  292. }
  293. if ( destSize && copied )
  294. {
  295. *dest = 0;
  296. }
  297. return copied;
  298. }
  299. int PlaceStringInOutput( const wchar_t* theString, int outindex, bool lengthPreChecked )
  300. {
  301. if ( theString && ( lengthPreChecked || V_wcslen( theString ) < ( HTML_BUFFER_CHARS - outindex ) ) )
  302. {
  303. while ( *theString )
  304. {
  305. g_htmlBuffer[outindex++] = *theString++;
  306. }
  307. }
  308. return outindex;
  309. }
  310. int PlaceGlyphHTMLIntoOutput( const wchar_t* buttonName, int fontSize, int outindex )
  311. {
  312. int imageSize = g_defaultGlyphSize;
  313. int imageOffset = g_defaultGlyphOffset;
  314. if ( fontSize )
  315. {
  316. imageSize = fontSize + ( fontSize >> 2 );
  317. imageOffset = -fontSize >> 2;
  318. }
  319. int res = outindex + V_snwprintf( &g_htmlBuffer[outindex], HTML_BUFFER_CHARS - outindex, g_htmlReplacementString, buttonName, imageSize, imageSize, imageOffset );
  320. return res;
  321. }
  322. const wchar_t *GetSonyGlyph( int buttonIndex )
  323. {
  324. if ( buttonIndex == 0 || buttonIndex == 1 )
  325. {
  326. // This key's meaning changes depending on the territory: X Button is "confirm/accept/advance" in North America;
  327. // CIRCLE button is "confirm/accept/advance" in SCEJ and Asia
  328. // xcontroller.cpp has already checked this setting on init, and written it into the keyvalues system:
  329. if ( KeyValuesSystem()->GetKeyValuesExpressionSymbol( "INPUTSWAPAB" ) )
  330. {
  331. return g_controllerButtonImageNamesSony[ ( buttonIndex == 0 ) ? 1 : 0 ];
  332. }
  333. }
  334. return g_controllerButtonImageNamesSony[buttonIndex];
  335. }
  336. // englyphonate
  337. const wchar_t* ScaleformUIImpl::ReplaceGlyphKeywordsWithHTML( const wchar_t* pin, int fontSize, bool bForceControllerGlyph )
  338. {
  339. if ( !pin )
  340. return NULL;
  341. // see if there is an instance of ${ in the string. If there is, it means
  342. // we need to replace a keyword with the html to display the button glyph
  343. const wchar_t* pinwalk = pin;
  344. bool needsReplacement = false;
  345. while ( *pinwalk )
  346. {
  347. if ( ( *pinwalk == L'$' ) && ( pinwalk[1] == L'{' ) )
  348. {
  349. needsReplacement = true;
  350. break;
  351. }
  352. pinwalk++;
  353. }
  354. // if there aren't any keywords in this string we can simply return the original string
  355. if ( !needsReplacement )
  356. {
  357. return pin;
  358. }
  359. wchar_t keywordBuffer[MAX_KEYWORD_BUFFER_SIZE];
  360. pinwalk = pin;
  361. int outindex = 0;
  362. bool bEscaping = false;
  363. // we're going to copy the pin string into g_htmlBuffer, but when we find a keyword, we're going to replace
  364. // the keyword with the html in g_htmlReplacementString ( and in that string we'll insert the correct glyph image name
  365. // and size
  366. while ( *pinwalk && outindex < HTML_BUFFER_CHARS )
  367. {
  368. // check for the escapement character ( a backslash, just like c )
  369. if ( !bEscaping && *pinwalk == L'\\' )
  370. {
  371. bEscaping = true;
  372. pinwalk++;
  373. }
  374. else if ( bEscaping || *pinwalk != L'$' )
  375. {
  376. // the keyword begins with ${, so we can skip it if this char isn't $
  377. g_htmlBuffer[outindex++] = *pinwalk++;
  378. bEscaping = false;
  379. }
  380. else
  381. {
  382. // likewise skip if the character after a '$' is not '{'
  383. pinwalk++;
  384. if ( *pinwalk == L'{' )
  385. {
  386. pinwalk++;
  387. // we've found the start of a keyword '${' now we'll copy the keyword
  388. // out of pin and into keywordBuffer so we can make it lowercase and look it up
  389. int charsCopied = CopyKeyword( keywordBuffer, pinwalk, MAX_KEYWORD_BUFFER_SIZE );
  390. // if this keyword is too big ( forgotten } maybe ) or there is no keyword, then cancel replacement
  391. // and pass back the input string, since the user may want to see that something is wrong
  392. if ( charsCopied == MAX_KEYWORD_BUFFER_SIZE || !charsCopied )
  393. {
  394. return pin;
  395. }
  396. else
  397. {
  398. // otherwise advance our position in the input string
  399. pinwalk += charsCopied;
  400. // the '}' is not copied, so we have to advance past that too
  401. // Because of how CopyKeyword works, we may be at the end of the string
  402. // so don't just blindly add 1
  403. if ( *pinwalk == L'}' )
  404. pinwalk++;
  405. }
  406. if ( keywordBuffer[0] == L'*' )
  407. {
  408. // this isn't a keyword, it's actually the name of the glyph
  409. // so we don't have to do all the lookup and everything
  410. outindex = PlaceGlyphHTMLIntoOutput( &keywordBuffer[1], fontSize, outindex );
  411. }
  412. else
  413. {
  414. V_wcslower( keywordBuffer );
  415. const wchar_t *pSteamControllerGlyph = NULL;
  416. if ( g_pInputSystem->GetCurrentInputDevice() == INPUT_DEVICE_STEAM_CONTROLLER && steamapicontext != NULL && steamapicontext->SteamController() != NULL )
  417. {
  418. char strKeyword[MAX_KEYWORD_BUFFER_SIZE];
  419. V_wcstostr( keywordBuffer, MAX_KEYWORD_BUFFER_SIZE, strKeyword, MAX_KEYWORD_BUFFER_SIZE );
  420. const char *pKeyword = strKeyword;
  421. if ( *pKeyword == '+' )
  422. {
  423. ++pKeyword;
  424. }
  425. if ( V_stricmp( pKeyword, "cancel" ) == 0 )
  426. {
  427. pKeyword = "menu_cancel";
  428. }
  429. else if ( V_stricmp( pKeyword, "confirm" ) == 0 )
  430. {
  431. pKeyword = "menu_select";
  432. }
  433. ControllerDigitalActionHandle_t actionHandle = steamapicontext->SteamController()->GetDigitalActionHandle( pKeyword );
  434. if ( actionHandle != 0 )
  435. {
  436. ControllerHandle_t handles[MAX_STEAM_CONTROLLERS];
  437. int nControllers = steamapicontext->SteamController()->GetConnectedControllers( handles );
  438. for ( int i = 0; i < nControllers; ++i )
  439. {
  440. ControllerDigitalActionData_t data = steamapicontext->SteamController()->GetDigitalActionData( handles[ i ], actionHandle );
  441. EControllerActionOrigin handleOrigins[ STEAM_CONTROLLER_MAX_ORIGINS ];
  442. int nOrigins = steamapicontext->SteamController()->GetDigitalActionOrigins( handles[ i ], steamapicontext->SteamController()->GetCurrentActionSet( handles[ i ] ), actionHandle, handleOrigins );
  443. for ( int j = 0; j < nOrigins; ++j )
  444. {
  445. if ( handleOrigins[ j ] != k_EControllerActionOrigin_None )
  446. {
  447. switch( handleOrigins[ j ] )
  448. {
  449. case k_EControllerActionOrigin_A: pSteamControllerGlyph = L"XBoxA.png"; break;
  450. case k_EControllerActionOrigin_B: pSteamControllerGlyph = L"XBoxB.png"; break;
  451. case k_EControllerActionOrigin_X: pSteamControllerGlyph = L"XBoxX.png"; break;
  452. case k_EControllerActionOrigin_Y: pSteamControllerGlyph = L"XBoxY.png"; break;
  453. case k_EControllerActionOrigin_LeftBumper: pSteamControllerGlyph = L"XBoxLShoulder.png"; break;
  454. case k_EControllerActionOrigin_RightBumper: pSteamControllerGlyph = L"XBoxRShoulder.png"; break;
  455. case k_EControllerActionOrigin_LeftTrigger_Click: pSteamControllerGlyph = L"XBoxLTrigger.png"; break;
  456. case k_EControllerActionOrigin_RightTrigger_Click: pSteamControllerGlyph = L"XBoxRTrigger.png"; break;
  457. case k_EControllerActionOrigin_LeftGrip: pSteamControllerGlyph = L"XBoxLShoulder.png"; break;
  458. case k_EControllerActionOrigin_RightGrip: pSteamControllerGlyph = L"XBoxRShoulder.png"; break;
  459. case k_EControllerActionOrigin_Start: pSteamControllerGlyph = L"XBoxStart.png"; break;
  460. }
  461. }
  462. }
  463. }
  464. }
  465. }
  466. // look up the keyword in the g_buttonFunctionKeyword array.
  467. IScaleformUI::ControllerButton::Enum bt;
  468. ButtonCode_t buttonCode;
  469. int functionIndex = MapKeywordToTable( keywordBuffer, g_buttonFunctionKeywords );
  470. if ( functionIndex != -1 )
  471. {
  472. // if the functionIndex is not -1, it means the keyword was the name of a controller button
  473. // and we have to mod it because there are three different sets of names in that table
  474. // a generic, and xbox, and a ps3, so there are more names than buttons. Modding brings the
  475. // number into the correct range
  476. bt = ( IScaleformUI::ControllerButton::Enum ) ( functionIndex % IScaleformUI::ControllerButton::NumButtons );
  477. if ( bt != IScaleformUI::ControllerButton::Undefined )
  478. {
  479. buttonCode = ( ButtonCode_t )g_controllerButtonToButtonCodeLookup[bt];
  480. }
  481. else
  482. {
  483. buttonCode = BUTTON_CODE_INVALID;
  484. }
  485. }
  486. else
  487. {
  488. // if the keyword is not a button name, then see if it is the name
  489. // of a command that's currently bound to one of the controller buttons
  490. // this will return ControllerButton::Undefined if the command is not mapped
  491. // to a controller button
  492. buttonCode = LookupButtonFromBinding( keywordBuffer, bForceControllerGlyph );
  493. bt = ValveButtonToControllerButton( buttonCode );
  494. }
  495. if ( pSteamControllerGlyph != NULL )
  496. {
  497. outindex = PlaceGlyphHTMLIntoOutput( pSteamControllerGlyph, fontSize, outindex );
  498. }
  499. else if ( bt == IScaleformUI::ControllerButton::Undefined )
  500. {
  501. const wchar_t* translated = NULL;
  502. wchar_t keyboardKeyName[256];
  503. if ( buttonCode == BUTTON_CODE_INVALID )
  504. {
  505. // if the button is actually undefined ( maybe no button is bound to the function for example )
  506. // then just insert the localized function name instead
  507. if (m_bShowActionNameIfUnbound)
  508. {
  509. wchar_t commandInBrackets[256];
  510. V_snwprintf( commandInBrackets, ARRAYSIZE( commandInBrackets ), L"[%s]", LocalizeCommand( keywordBuffer ));
  511. translated = commandInBrackets;
  512. }
  513. else
  514. {
  515. translated = L"";
  516. }
  517. }
  518. else
  519. {
  520. const char* keyName = g_pInputSystem->ButtonCodeToString( buttonCode );
  521. if ( keyName && *keyName )
  522. {
  523. char keyboardNameBuffer[256];
  524. V_snprintf(keyboardNameBuffer, sizeof(keyboardNameBuffer), "[%s]", keyName);
  525. V_strtowcs( keyboardNameBuffer, -1, keyboardKeyName, sizeof( keyboardKeyName ) );
  526. V_wcsupr( keyboardKeyName );
  527. translated = keyboardKeyName;
  528. }
  529. }
  530. if ( translated )
  531. {
  532. outindex = PlaceStringInOutput( translated, outindex, false );
  533. }
  534. else
  535. {
  536. outindex = PlaceStringInOutput( keywordBuffer, outindex, false );
  537. }
  538. }
  539. else if ( !IsSetToControllerUI( SF_SS_SLOT( m_pEngine->GetActiveSplitScreenPlayerSlot() ) ) &&
  540. !bForceControllerGlyph &&
  541. g_controllerButtonPCNames[bt] != NULL )
  542. {
  543. outindex = PlaceStringInOutput( g_controllerButtonPCNames[bt], outindex, false );
  544. }
  545. else
  546. {
  547. // otherwise construct the correct HTML to display the button glyph and put that into the output buffer
  548. outindex = PlaceGlyphHTMLIntoOutput( ( IsPS3() || SFINST.GetForcePS3() ) ? GetSonyGlyph(bt) : g_controllerButtonImageNames[bt], fontSize, outindex );
  549. }
  550. }
  551. }
  552. else
  553. {
  554. // if we found a '$' but it was not followed by a '{', we still
  555. // need to insert the '$'
  556. g_htmlBuffer[outindex++] = L'$';
  557. }
  558. }
  559. }
  560. g_htmlBuffer[outindex] = 0;
  561. return &g_htmlBuffer[0];
  562. }
  563. /********************************
  564. * scaleformuiimpl methods
  565. */
  566. void ScaleformUIImpl::InitTranslationImpl( void )
  567. {
  568. m_bShowActionNameIfUnbound = true;
  569. RemoveKeyBindings();
  570. // some commands ( like +attack ) have localized, friendly names. We're going to
  571. // load up the localization keys for those commands but looking in the file that's
  572. // used to set up the button binding screen
  573. KeyValues* pkeyValueFile = new KeyValues( "options" );
  574. // Load the config data
  575. if ( pkeyValueFile )
  576. {
  577. pkeyValueFile->LoadFromFile( g_pFullFileSystem, "scripts/controller_options.txt", "game" );
  578. for ( KeyValues* piter = pkeyValueFile->GetFirstTrueSubKey(); piter; piter = piter->GetNextTrueSubKey() )
  579. {
  580. const char* command = piter->GetString( "command", "" );
  581. if ( command && *command )
  582. {
  583. const char* name = piter->GetString( "name", "" );
  584. if ( name && *name )
  585. {
  586. if ( *command == '+' )
  587. {
  588. command++;
  589. }
  590. int length = V_strlen( command ) + 1;
  591. wchar_t* commandString = new wchar_t[length];
  592. V_UTF8ToUnicode( command, commandString, sizeof( wchar_t ) * length );
  593. V_wcslower( commandString );
  594. m_LocalizableCommandNames.AddToTail( commandString );
  595. length = V_strlen( name ) + 1;
  596. char* keyString = new char[length];
  597. V_strcpy( keyString, name );
  598. m_LocalizableCommandKeys.AddToTail( keyString );
  599. }
  600. }
  601. }
  602. pkeyValueFile->deleteThis();
  603. }
  604. }
  605. void ScaleformUIImpl::ShowActionNameWhenActionIsNotBound( bool value )
  606. {
  607. m_bShowActionNameIfUnbound = value;
  608. }
  609. void ScaleformUIImpl::ShutdownTranslationImpl( void )
  610. {
  611. int i = m_LocalizableCommandNames.Count();
  612. while ( i-- )
  613. {
  614. delete[] m_LocalizableCommandNames[i];
  615. delete[] m_LocalizableCommandKeys[i];
  616. }
  617. m_LocalizableCommandNames.Purge();
  618. m_LocalizableCommandKeys.Purge();
  619. }
  620. void ScaleformUIImpl::RemoveKeyBindings( void )
  621. {
  622. V_memset( &m_wcControllerButtonToBindingTable[0][0], 0, sizeof( m_wcControllerButtonToBindingTable ) );
  623. }
  624. void ScaleformUIImpl::BindCommandToControllerButton( ButtonCode_t code, const char* pbinding )
  625. {
  626. if ( pbinding && *pbinding == '+' )
  627. {
  628. pbinding++;
  629. }
  630. if ( pbinding && *pbinding )
  631. {
  632. V_UTF8ToUnicode( pbinding, &m_wcControllerButtonToBindingTable[code][0], sizeof( wchar_t ) * MAX_BOUND_COMMAND_LENGTH );
  633. V_wcslower( &m_wcControllerButtonToBindingTable[code][0] );
  634. }
  635. else
  636. {
  637. m_wcControllerButtonToBindingTable[code][0] = 0;
  638. }
  639. }
  640. void ScaleformUIImpl::RefreshKeyBindings( void )
  641. {
  642. // clear our bindings
  643. RemoveKeyBindings();
  644. // if we haven't connected to gameui, then just exit
  645. if ( !m_pGameUIFuncs )
  646. {
  647. return;
  648. }
  649. // go through each of the buttons on the controller
  650. for ( int buttonCode = KEY_FIRST; buttonCode < BUTTON_CODE_LAST; buttonCode++ )
  651. {
  652. // now the we have a ButtonCode, copy the name of the command ( if there is one ) into our table
  653. BindCommandToControllerButton( ( ButtonCode_t )buttonCode, m_pGameUIFuncs->GetBindingForButtonCode( ( ButtonCode_t ) buttonCode ) );
  654. }
  655. }
  656. IScaleformUI::ControllerButton::Enum ScaleformUIImpl::ValveButtonToControllerButton( ButtonCode_t b )
  657. {
  658. if ( b == KEY_NONE || b == BUTTON_CODE_INVALID )
  659. return IScaleformUI::ControllerButton::Undefined;
  660. for ( int i = 0; i < IScaleformUI::ControllerButton::NumButtons; i++ )
  661. {
  662. if ( g_controllerButtonToButtonCodeLookup[i] == b )
  663. return ( IScaleformUI::ControllerButton::Enum )i;
  664. }
  665. return IScaleformUI::ControllerButton::Undefined;
  666. }
  667. void ScaleformUIImpl::UpdateBindingForButton( ButtonCode_t bt, const char* pbinding )
  668. {
  669. BindCommandToControllerButton( bt, pbinding );
  670. }
  671. const wchar_t* ScaleformUIImpl::LocalizeCommand( const wchar_t* command )
  672. {
  673. int i = 0;
  674. int len = ( int ) m_LocalizableCommandNames.Count();
  675. while ( i < len )
  676. {
  677. if ( !V_wcscmp( command, m_LocalizableCommandNames[i] ) )
  678. {
  679. return g_pVGuiLocalize->Find( m_LocalizableCommandKeys[i] );
  680. }
  681. i++;
  682. }
  683. return command;
  684. }
  685. ButtonCode_t ScaleformUIImpl::LookupButtonFromBinding( const wchar_t* binding, bool bForceControllerLookup )
  686. {
  687. if ( *binding == L'+' )
  688. binding++;
  689. int slot = m_pEngine->GetActiveSplitScreenPlayerSlot();
  690. int firstKey;
  691. int lastKey;
  692. bool isController = false;
  693. if ( bForceControllerLookup || IsSetToControllerUI( SF_SS_SLOT( slot ) ) )
  694. {
  695. isController = true;
  696. firstKey = ButtonCodeToJoystickButtonCode( JOYSTICK_FIRST, slot );
  697. lastKey = ButtonCodeToJoystickButtonCode( KEY_XSTICK2_UP, slot );
  698. }
  699. else
  700. {
  701. firstKey = KEY_FIRST;
  702. lastKey = MOUSE_LAST;
  703. }
  704. for ( int i = firstKey; i <= lastKey; i++ )
  705. {
  706. if ( !V_wcscmp( binding, &m_wcControllerButtonToBindingTable[i][0] ) )
  707. {
  708. return GetBaseButtonCode( ( ButtonCode_t )i );
  709. }
  710. }
  711. if ( !V_wcscmp( binding, L"buymenu" ) && isController )
  712. {
  713. return LookupButtonFromBinding( L"use" );
  714. }
  715. else
  716. {
  717. return BUTTON_CODE_INVALID;
  718. }
  719. }
  720. /*********************************************
  721. * This function currently uses a global static buffer to make calling it simpler.
  722. * This means, of course, that the result must be used or copied immediately, and that
  723. * it is not thread safe.
  724. *
  725. * To solve this problem, all these functions should probably be changed to accept a
  726. * buffer from the caller.
  727. */
  728. const wchar_t* ScaleformUIImpl::ReplaceGlyphKeywordsWithHTML( const char* text, int fontSize, bool bForceControllerGlyph )
  729. {
  730. int stringLength = ( V_strlen( text ) + 1 ) * sizeof( wchar_t );
  731. if ( stringLength > HTML_BUFFER_CHARS )
  732. stringLength = HTML_BUFFER_CHARS;
  733. V_UTF8ToUnicode( text, g_wcharBuffer, stringLength );
  734. return ReplaceGlyphKeywordsWithHTML( g_wcharBuffer, fontSize, bForceControllerGlyph );
  735. }
  736. const wchar_t* ScaleformUIImpl::Translate( const char *key, bool* pIsHTML )
  737. {
  738. // first strip out any font size hints
  739. const wchar_t* replaced = NULL;
  740. const wchar_t* translated = NULL;
  741. int fontSize = 0;
  742. const char* actualKey = key;
  743. if ( pIsHTML )
  744. {
  745. *pIsHTML = false;
  746. }
  747. // if the key doesn't start with '#' then the g_pVGuiLocalize->Find function
  748. // is going to return null anyway, so don't bother doing any work
  749. if ( key[0] == '#' )
  750. {
  751. // if this is a translation key, then look to see if it has a '@' sign
  752. // with a following point size. If it does, then we'll make all the glyphs
  753. // in this string that size
  754. int len = V_strlen( key );
  755. if ( V_strnchr( key, '@', len + 1 ) )
  756. {
  757. char *asciiString = ( char * ) stackalloc( len + 1 );
  758. char *pdwalk;
  759. const char* pswalk;
  760. bool bEscaping = false;
  761. // this is a little weird, but we only want to escape the '@' sign here.
  762. // we don't want to remove the escapements for other things because
  763. // that will lead to inconsistent behavior ( since we'd only remove the escapements
  764. // if the string started with # and had a @ in it )
  765. pswalk = key;
  766. pdwalk = asciiString;
  767. while ( *pswalk )
  768. {
  769. if ( !bEscaping && *pswalk == '\\' )
  770. {
  771. bEscaping = true;
  772. pswalk++;
  773. }
  774. else if ( *pswalk != '@' )
  775. {
  776. if ( bEscaping )
  777. *pdwalk++ = '\\';
  778. *pdwalk++ = *pswalk++;
  779. bEscaping = false;
  780. }
  781. else if ( bEscaping )
  782. {
  783. *pdwalk++ = *pswalk++;
  784. bEscaping = false;
  785. }
  786. else
  787. {
  788. break;
  789. }
  790. }
  791. if ( bEscaping )
  792. {
  793. *pdwalk++ = '\\';
  794. }
  795. *pdwalk = 0;
  796. actualKey = asciiString;
  797. if ( *pswalk == '@' )
  798. {
  799. pswalk++;
  800. }
  801. if ( *pswalk )
  802. {
  803. fontSize = atoi( pswalk );
  804. if ( fontSize < 0 )
  805. fontSize = 0;
  806. }
  807. }
  808. translated = g_pVGuiLocalize->Find( actualKey );
  809. }
  810. bool bShowConsoleButtonIcons = false;
  811. // we only show console bindings on PS3 when you have a MKB hooked up because most of the nav UI is either bad or unfinished for MKB
  812. if ( IsPS3() && g_pInputSystem->IsDeviceReadingInput( INPUT_DEVICE_KEYBOARD_MOUSE ) )
  813. {
  814. bShowConsoleButtonIcons = true;
  815. }
  816. if ( translated )
  817. {
  818. replaced = ReplaceGlyphKeywordsWithHTML( translated, fontSize, bShowConsoleButtonIcons );
  819. if ( pIsHTML )
  820. {
  821. *pIsHTML = ( bool ) ( replaced != translated );
  822. }
  823. }
  824. else
  825. {
  826. replaced = ReplaceGlyphKeywordsWithHTML( actualKey, fontSize, bShowConsoleButtonIcons );
  827. if ( pIsHTML )
  828. {
  829. *pIsHTML = true;
  830. }
  831. }
  832. return replaced;
  833. }
  834. static const wchar_t *g_pCharsToBeReplaced = L"\\<>&\'\"$#";
  835. static const wchar_t *g_pReplacementStrings[] = {
  836. L"&#92;",
  837. L"&lt;",
  838. L"&gt;",
  839. L"&amp;",
  840. L"&apos;",
  841. L"&quot;",
  842. L"&#36;",
  843. L"&#35;"
  844. };
  845. void ScaleformUIImpl::MakeStringSafe( const wchar_t* oldName, wchar_t* newName, int destBufferSize )
  846. {
  847. const wchar_t* pfound;
  848. wchar_t* pout = newName;
  849. int charsLeft = ( destBufferSize / sizeof(wchar_t) ) - 1;
  850. bool bEncounteredIllegalCharacters = false;
  851. // throw a zero at the end just in case
  852. newName[charsLeft] = L'\0';
  853. for( const wchar_t *p=oldName; *p != 0 && charsLeft > 0; p++ )
  854. {
  855. // If we are about to write first character and it is a hash then skip it because it is reserved for localization
  856. if ( ( pout == newName ) && ( *p == L'#' ) )
  857. {
  858. bEncounteredIllegalCharacters = true;
  859. continue;
  860. }
  861. // Check the character for replacement sequences
  862. pfound = wcschr( g_pCharsToBeReplaced, *p );
  863. if ( pfound )
  864. {
  865. int index = pfound - g_pCharsToBeReplaced;
  866. int replacementLength = wcslen( g_pReplacementStrings[index] );
  867. if ( replacementLength <= charsLeft )
  868. {
  869. V_wcsncpy( pout, g_pReplacementStrings[index], charsLeft * sizeof( wchar_t ) );
  870. charsLeft -= replacementLength;
  871. pout += replacementLength;
  872. }
  873. else
  874. break;
  875. }
  876. else
  877. {
  878. #if 1
  879. if ( *p )
  880. #else
  881. if ( iswprint( *p ) || ( *p == L'\t' ) )
  882. #endif
  883. {
  884. *pout++ = *p;
  885. charsLeft--;
  886. }
  887. else
  888. {
  889. bEncounteredIllegalCharacters = true;
  890. }
  891. }
  892. }
  893. // If we didn't write any safe characters, but encountered illegal characters then write at least a question-mark
  894. if ( ( pout == newName ) && ( charsLeft > 0 ) && bEncounteredIllegalCharacters )
  895. {
  896. *pout++ = L'?';
  897. charsLeft--;
  898. }
  899. if ( charsLeft >= 0 ) // 0 is okay because we started charsLeft off as one too small
  900. *pout = 0;
  901. }
  902. void ScaleformUIImpl::DecodeButtonandSlotFromButtonCode( ButtonCode_t inCode, ButtonCode_t &outCode, int &outSlot )
  903. {
  904. outCode = GetBaseButtonCode( inCode );
  905. if ( !IsJoystickCode( inCode ) )
  906. {
  907. outSlot = m_iKeyboardSlot;
  908. }
  909. else
  910. {
  911. outSlot = GetJoystickForCode( inCode );
  912. }
  913. }