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.

2562 lines
76 KiB

  1. //========= Copyright � 1996-2010, Valve LLC, All rights reserved. ============
  2. //
  3. // Purpose: Implementation for CWebAPIResponse objects
  4. //
  5. //=============================================================================
  6. #include "stdafx.h"
  7. #include "thirdparty/JSON_parser/JSON_parser.h"
  8. using namespace GCSDK;
  9. #include "tier0/memdbgoff.h"
  10. // !FIXME! DOTAMERGE
  11. //IMPLEMENT_CLASS_MEMPOOL_MT( CWebAPIValues, 1000, UTLMEMORYPOOL_GROW_SLOW );
  12. // memdbgon must be the last include file in a .cpp file!!!
  13. #include "tier0/memdbgon.h"
  14. //-----------------------------------------------------------------------------
  15. // Purpose: Helper for emitting properly escaped json string values
  16. //-----------------------------------------------------------------------------
  17. void EmitJSONString( CUtlBuffer &outputBuffer, const char *pchValue )
  18. {
  19. outputBuffer.PutChar( '"' );
  20. if ( pchValue )
  21. {
  22. int i = 0;
  23. while( pchValue[i] )
  24. {
  25. switch ( pchValue[i] )
  26. {
  27. case '"':
  28. outputBuffer.Put( "\\\"", 2 );
  29. break;
  30. case '\\':
  31. outputBuffer.Put( "\\\\", 2 );
  32. break;
  33. case '\n':
  34. outputBuffer.Put( "\\n", 2 );
  35. break;
  36. case '\r':
  37. outputBuffer.Put( "\\r", 2 );
  38. break;
  39. case '\t':
  40. outputBuffer.Put( "\\t", 2 );
  41. break;
  42. default:
  43. if ( (uint8) pchValue[i] < 32 )
  44. {
  45. outputBuffer.Put( "\\u00", 4 );
  46. outputBuffer.PutChar( ( pchValue[i] & 16 ) ? '1' : '0' );
  47. outputBuffer.PutChar( "0123456789abcdef"[ pchValue[i] & 0xF ] );
  48. }
  49. else
  50. {
  51. outputBuffer.PutChar( pchValue[i] );
  52. }
  53. }
  54. ++i;
  55. }
  56. }
  57. outputBuffer.PutChar( '"' );
  58. }
  59. //-----------------------------------------------------------------------------
  60. // Purpose: Helper for emitting properly escaped XML string values, we always use UTF8,
  61. // so we only really need to encode & ' " < >
  62. //-----------------------------------------------------------------------------
  63. void EmitXMLString( CUtlBuffer &outputBuffer, const char *pchValue )
  64. {
  65. if ( pchValue )
  66. {
  67. int i = 0;
  68. while( pchValue[i] )
  69. {
  70. switch ( pchValue[i] )
  71. {
  72. case '&':
  73. outputBuffer.Put( "&amp;", 5 );
  74. break;
  75. case '\'':
  76. outputBuffer.Put( "&apos;", 6 );
  77. break;
  78. case '"':
  79. outputBuffer.Put( "&quot;", 6 );
  80. break;
  81. case '<':
  82. outputBuffer.Put( "&lt;", 4 );
  83. break;
  84. case '>':
  85. outputBuffer.Put( "&gt;", 4 );
  86. break;
  87. default:
  88. outputBuffer.PutChar( pchValue[i] );
  89. }
  90. ++i;
  91. }
  92. }
  93. }
  94. //-----------------------------------------------------------------------------
  95. // Purpose: Helper for emitting properly escaped VDF string values, we always use UTF8,
  96. // and we escape only " and \
  97. //-----------------------------------------------------------------------------
  98. void EmitVDFString( CUtlBuffer &outputBuffer, const char *pchValue )
  99. {
  100. outputBuffer.PutChar( '"' );
  101. if ( pchValue )
  102. {
  103. int i = 0;
  104. while( pchValue[i] )
  105. {
  106. switch ( pchValue[i] )
  107. {
  108. case '\\':
  109. outputBuffer.Put( "\\\\", 2 );
  110. break;
  111. case '"':
  112. outputBuffer.Put( "\\\"", 2 );
  113. break;
  114. default:
  115. outputBuffer.PutChar( pchValue[i] );
  116. }
  117. ++i;
  118. }
  119. }
  120. outputBuffer.PutChar( '"' );
  121. }
  122. namespace GCSDK
  123. {
  124. enum { k_LineBreakEveryNGroups = 18 }; // line break every 18 groups of 4 characters (every 72 characters)
  125. uint32 Base64EncodeMaxOutput( const uint32 cubData, const char *pszLineBreak )
  126. {
  127. // terminating null + 4 chars per 3-byte group + line break after every 18 groups (72 output chars) + final line break
  128. uint32 nGroups = (cubData+2)/3;
  129. uint32 cchRequired = 1 + nGroups*4 + ( pszLineBreak ? Q_strlen(pszLineBreak)*(1+(nGroups-1)/k_LineBreakEveryNGroups) : 0 );
  130. return cchRequired;
  131. }
  132. bool Base64Encode( const uint8 *pubData, uint32 cubData, char *pchEncodedData, uint32 *pcchEncodedData, const char *pszLineBreak )
  133. {
  134. if ( pchEncodedData == NULL )
  135. {
  136. AssertMsg( *pcchEncodedData == 0, "NULL output buffer with non-zero size passed to Base64Encode" );
  137. *pcchEncodedData = Base64EncodeMaxOutput( cubData, pszLineBreak );
  138. return true;
  139. }
  140. const uint8 *pubDataEnd = pubData + cubData;
  141. char *pchEncodedDataStart = pchEncodedData;
  142. uint32 unLineBreakLen = pszLineBreak ? Q_strlen( pszLineBreak ) : 0;
  143. int nNextLineBreak = unLineBreakLen ? k_LineBreakEveryNGroups : INT_MAX;
  144. const char * const pszBase64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  145. uint32 cchEncodedData = *pcchEncodedData;
  146. if ( cchEncodedData == 0 )
  147. goto out_of_space;
  148. --cchEncodedData; // pre-decrement for the terminating null so we don't forget about it
  149. // input 3 x 8-bit, output 4 x 6-bit
  150. while ( pubDataEnd - pubData >= 3 )
  151. {
  152. if ( cchEncodedData < 4 + unLineBreakLen )
  153. goto out_of_space;
  154. if ( nNextLineBreak == 0 )
  155. {
  156. memcpy( pchEncodedData, pszLineBreak, unLineBreakLen );
  157. pchEncodedData += unLineBreakLen;
  158. cchEncodedData -= unLineBreakLen;
  159. nNextLineBreak = k_LineBreakEveryNGroups;
  160. }
  161. uint32 un24BitsData;
  162. un24BitsData = (uint32) pubData[0] << 16;
  163. un24BitsData |= (uint32) pubData[1] << 8;
  164. un24BitsData |= (uint32) pubData[2];
  165. pubData += 3;
  166. pchEncodedData[0] = pszBase64Chars[ (un24BitsData >> 18) & 63 ];
  167. pchEncodedData[1] = pszBase64Chars[ (un24BitsData >> 12) & 63 ];
  168. pchEncodedData[2] = pszBase64Chars[ (un24BitsData >> 6) & 63 ];
  169. pchEncodedData[3] = pszBase64Chars[ (un24BitsData ) & 63 ];
  170. pchEncodedData += 4;
  171. cchEncodedData -= 4;
  172. --nNextLineBreak;
  173. }
  174. // Clean up remaining 1 or 2 bytes of input, pad output with '='
  175. if ( pubData != pubDataEnd )
  176. {
  177. if ( cchEncodedData < 4 + unLineBreakLen )
  178. goto out_of_space;
  179. if ( nNextLineBreak == 0 )
  180. {
  181. memcpy( pchEncodedData, pszLineBreak, unLineBreakLen );
  182. pchEncodedData += unLineBreakLen;
  183. cchEncodedData -= unLineBreakLen;
  184. }
  185. uint32 un24BitsData;
  186. un24BitsData = (uint32) pubData[0] << 16;
  187. if ( pubData+1 != pubDataEnd )
  188. {
  189. un24BitsData |= (uint32) pubData[1] << 8;
  190. }
  191. pchEncodedData[0] = pszBase64Chars[ (un24BitsData >> 18) & 63 ];
  192. pchEncodedData[1] = pszBase64Chars[ (un24BitsData >> 12) & 63 ];
  193. pchEncodedData[2] = pubData+1 != pubDataEnd ? pszBase64Chars[ (un24BitsData >> 6) & 63 ] : '=';
  194. pchEncodedData[3] = '=';
  195. pchEncodedData += 4;
  196. cchEncodedData -= 4;
  197. }
  198. if ( unLineBreakLen )
  199. {
  200. if ( cchEncodedData < unLineBreakLen )
  201. goto out_of_space;
  202. memcpy( pchEncodedData, pszLineBreak, unLineBreakLen );
  203. pchEncodedData += unLineBreakLen;
  204. cchEncodedData -= unLineBreakLen;
  205. }
  206. *pchEncodedData = 0;
  207. *pcchEncodedData = pchEncodedData - pchEncodedDataStart;
  208. return true;
  209. out_of_space:
  210. *pchEncodedData = 0;
  211. *pcchEncodedData = Base64EncodeMaxOutput( cubData, pszLineBreak );
  212. AssertMsg( false, "CCrypto::Base64Encode: insufficient output buffer (up to n*4/3+5 bytes required, plus linebreaks)" );
  213. return false;
  214. }
  215. bool Base64Decode( const char *pchData, uint32 cchDataMax, uint8 *pubDecodedData, uint32 *pcubDecodedData, bool bIgnoreInvalidCharacters )
  216. {
  217. uint32 cubDecodedData = *pcubDecodedData;
  218. uint32 cubDecodedDataOrig = cubDecodedData;
  219. if ( pubDecodedData == NULL )
  220. {
  221. AssertMsg( *pcubDecodedData == 0, "NULL output buffer with non-zero size passed to Base64Decode" );
  222. cubDecodedDataOrig = cubDecodedData = ~0u;
  223. }
  224. // valid base64 character range: '+' (0x2B) to 'z' (0x7A)
  225. // table entries are 0-63, -1 for invalid entries, -2 for '='
  226. static const char rgchInvBase64[] = {
  227. 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
  228. -1, -1, -1, -2, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7,
  229. 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
  230. 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31,
  231. 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
  232. 47, 48, 49, 50, 51
  233. };
  234. COMPILE_TIME_ASSERT( Q_ARRAYSIZE(rgchInvBase64) == 0x7A - 0x2B + 1 );
  235. uint32 un24BitsWithSentinel = 1;
  236. while ( cchDataMax-- > 0 )
  237. {
  238. char c = *pchData++;
  239. if ( (uint8)(c - 0x2B) >= Q_ARRAYSIZE( rgchInvBase64 ) )
  240. {
  241. if ( c == '\0' )
  242. break;
  243. if ( !bIgnoreInvalidCharacters && !( c == '\r' || c == '\n' || c == '\t' || c == ' ' ) )
  244. goto decode_failed;
  245. else
  246. continue;
  247. }
  248. c = rgchInvBase64[(uint8)(c - 0x2B)];
  249. if ( c < 0 )
  250. {
  251. if ( c == -2 ) // -2 -> terminating '='
  252. break;
  253. if ( !bIgnoreInvalidCharacters )
  254. goto decode_failed;
  255. else
  256. continue;
  257. }
  258. un24BitsWithSentinel <<= 6;
  259. un24BitsWithSentinel |= c;
  260. if ( un24BitsWithSentinel & (1<<24) )
  261. {
  262. if ( cubDecodedData < 3 ) // out of space? go to final write logic
  263. break;
  264. if ( pubDecodedData )
  265. {
  266. pubDecodedData[0] = (uint8)( un24BitsWithSentinel >> 16 );
  267. pubDecodedData[1] = (uint8)( un24BitsWithSentinel >> 8);
  268. pubDecodedData[2] = (uint8)( un24BitsWithSentinel );
  269. pubDecodedData += 3;
  270. }
  271. cubDecodedData -= 3;
  272. un24BitsWithSentinel = 1;
  273. }
  274. }
  275. // If un24BitsWithSentinel contains data, output the remaining full bytes
  276. if ( un24BitsWithSentinel >= (1<<6) )
  277. {
  278. // Possibilities are 3, 2, 1, or 0 full output bytes.
  279. int nWriteBytes = 3;
  280. while ( un24BitsWithSentinel < (1<<24) )
  281. {
  282. nWriteBytes--;
  283. un24BitsWithSentinel <<= 6;
  284. }
  285. // Write completed bytes to output
  286. while ( nWriteBytes-- > 0 )
  287. {
  288. if ( cubDecodedData == 0 )
  289. {
  290. AssertMsg( false, "CCrypto::Base64Decode: insufficient output buffer (up to n*3/4+2 bytes required)" );
  291. goto decode_failed;
  292. }
  293. if ( pubDecodedData )
  294. {
  295. *pubDecodedData++ = (uint8)(un24BitsWithSentinel >> 16);
  296. }
  297. --cubDecodedData;
  298. un24BitsWithSentinel <<= 8;
  299. }
  300. }
  301. *pcubDecodedData = cubDecodedDataOrig - cubDecodedData;
  302. return true;
  303. decode_failed:
  304. *pcubDecodedData = cubDecodedDataOrig - cubDecodedData;
  305. return false;
  306. }
  307. }
  308. //-----------------------------------------------------------------------------
  309. // Purpose: Constructor
  310. //-----------------------------------------------------------------------------
  311. CWebAPIResponse::CWebAPIResponse()
  312. {
  313. m_pValues = NULL;
  314. m_unExpirationSeconds = 0;
  315. m_rtLastModified = 0;
  316. m_bExtendedArrays = false;
  317. m_bJSONAnonymousRootNode = false;
  318. m_eStatusCode = k_EHTTPStatusCode500InternalServerError;
  319. }
  320. //-----------------------------------------------------------------------------
  321. // Purpose: Destructor
  322. //-----------------------------------------------------------------------------
  323. CWebAPIResponse::~CWebAPIResponse()
  324. {
  325. if ( m_pValues)
  326. delete m_pValues;
  327. m_pValues = NULL;
  328. }
  329. //-----------------------------------------------------------------------------
  330. // Purpose: Outputs formatted data to buffer
  331. //-----------------------------------------------------------------------------
  332. bool CWebAPIResponse::BEmitFormattedOutput( EWebAPIOutputFormat eFormat, CUtlBuffer &outputBuffer, size_t unMaxResultSize )
  333. {
  334. VPROF_BUDGET( "CWebAPIResponse::BEmitFormattedOutput", VPROF_BUDGETGROUP_STEAM );
  335. outputBuffer.Clear();
  336. switch( eFormat )
  337. {
  338. case k_EWebAPIOutputFormat_JSON:
  339. return BEmitJSON( outputBuffer, unMaxResultSize );
  340. case k_EWebAPIOutputFormat_XML:
  341. return BEmitXML( outputBuffer, unMaxResultSize );
  342. case k_EWebAPIOutputFormat_VDF:
  343. return BEmitVDF( outputBuffer, unMaxResultSize );
  344. case k_EWebAPIOutputFormat_ParameterEncoding:
  345. return BEmitParameterEncoding( outputBuffer );
  346. default:
  347. return false;
  348. }
  349. }
  350. //-----------------------------------------------------------------------------
  351. // Purpose: Emits JSON formatted representation of response
  352. //-----------------------------------------------------------------------------
  353. bool CWebAPIResponse::BEmitJSON( CUtlBuffer &outputBuffer, size_t unMaxResultSize )
  354. {
  355. outputBuffer.PutChar( '{' );
  356. outputBuffer.PutChar( '\n' );
  357. CWebAPIValues *pValues = m_pValues;
  358. //if we have an anonymous root, get the first child instead of the root itself
  359. if ( m_bJSONAnonymousRootNode && m_pValues )
  360. {
  361. pValues = m_pValues->GetFirstChild();
  362. }
  363. if ( pValues )
  364. {
  365. if( !CWebAPIValues::BEmitJSONRecursive( pValues, outputBuffer, 1, unMaxResultSize, m_bExtendedArrays ) )
  366. return false;
  367. }
  368. outputBuffer.PutChar( '\n' );
  369. outputBuffer.PutChar( '}' );
  370. if ( !outputBuffer.IsValid() )
  371. return false;
  372. return true;
  373. }
  374. //-----------------------------------------------------------------------------
  375. // Purpose: Emits KeyValues .vdf style formatted representation of response
  376. //-----------------------------------------------------------------------------
  377. bool CWebAPIResponse::BEmitVDF( CUtlBuffer &outputBuffer, size_t unMaxResultSize )
  378. {
  379. if ( m_pValues )
  380. if( !CWebAPIValues::BEmitVDFRecursive( m_pValues, outputBuffer, 0, 0, unMaxResultSize, m_bExtendedArrays ) )
  381. return false;
  382. return true;
  383. }
  384. //-----------------------------------------------------------------------------
  385. // Purpose: Emits XML formatted representation of response
  386. //-----------------------------------------------------------------------------
  387. bool CWebAPIResponse::BEmitXML( CUtlBuffer &outputBuffer, size_t unMaxResultSize )
  388. {
  389. const char *pchProlog = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
  390. outputBuffer.Put( pchProlog, Q_strlen( pchProlog ) );
  391. outputBuffer.Put( "<!DOCTYPE ", Q_strlen( "<!DOCTYPE " ) );
  392. if ( m_pValues && m_pValues->GetName() )
  393. EmitXMLString( outputBuffer, m_pValues->GetName() );
  394. outputBuffer.PutChar('>');
  395. outputBuffer.PutChar('\n');
  396. if ( m_pValues )
  397. if( !CWebAPIValues::BEmitXMLRecursive( m_pValues, outputBuffer, 0, unMaxResultSize ) )
  398. return false;
  399. if ( !outputBuffer.IsValid() )
  400. return false;
  401. return true;
  402. }
  403. //-----------------------------------------------------------------------------
  404. // Purpose: Emits Parameter Encoding formatted representation of response
  405. //-----------------------------------------------------------------------------
  406. bool CWebAPIResponse::BEmitParameterEncoding( CUtlBuffer &outputBuffer )
  407. {
  408. if ( !m_pValues )
  409. return true;
  410. CWebAPIValues *pValue = m_pValues->GetFirstChild();
  411. while ( pValue != NULL )
  412. {
  413. outputBuffer.Put( pValue->GetName(), Q_strlen( pValue->GetName() ) );
  414. outputBuffer.Put( "=", 1 );
  415. CUtlString sValue;
  416. switch ( pValue->GetType() )
  417. {
  418. case k_EWebAPIValueType_Object:
  419. Assert( false );
  420. return false; // no cursive values
  421. case k_EWebAPIValueType_NumericArray:
  422. Assert( false );
  423. return false; // no arrays
  424. case k_EWebAPIValueType_BinaryBlob:
  425. Assert( false );
  426. return false; // no binary
  427. case k_EWebAPIValueType_Int32:
  428. sValue = CNumStr( pValue->GetInt32Value() );
  429. break;
  430. case k_EWebAPIValueType_Int64:
  431. sValue = CNumStr( pValue->GetInt64Value() );
  432. break;
  433. case k_EWebAPIValueType_UInt32:
  434. sValue = CNumStr( pValue->GetUInt32Value() );
  435. break;
  436. case k_EWebAPIValueType_UInt64:
  437. sValue = CNumStr( pValue->GetUInt64Value() );
  438. break;
  439. case k_EWebAPIValueType_Double:
  440. sValue = CNumStr( pValue->GetDoubleValue() );
  441. break;
  442. case k_EWebAPIValueType_String:
  443. pValue->GetStringValue( sValue );
  444. break;
  445. case k_EWebAPIValueType_Bool:
  446. sValue = CNumStr( pValue->GetBoolValue() );
  447. break;
  448. }
  449. outputBuffer.Put( sValue, sValue.Length() );
  450. pValue = pValue->GetNextChild();
  451. if ( pValue )
  452. outputBuffer.Put( "&", 1 );
  453. }
  454. return true;
  455. }
  456. //-----------------------------------------------------------------------------
  457. // Purpose: Resets the response to be empty
  458. //-----------------------------------------------------------------------------
  459. void CWebAPIResponse::Clear()
  460. {
  461. if ( m_pValues )
  462. delete m_pValues;
  463. m_pValues = NULL;
  464. }
  465. //-----------------------------------------------------------------------------
  466. // Purpose: Access the root value element in the response
  467. //-----------------------------------------------------------------------------
  468. CWebAPIValues *CWebAPIResponse::CreateRootValue( const char *pchName )
  469. {
  470. if ( m_pValues )
  471. {
  472. AssertMsg( false, "CWwebAPIResponse::CreateRootValue called while root already existed." );
  473. Clear();
  474. }
  475. m_pValues = new CWebAPIValues( pchName );
  476. return m_pValues;
  477. }
  478. //----------------------------------------------------------------------------
  479. // Purpose: Gets a singleton buffer pool for webapi values
  480. //----------------------------------------------------------------------------
  481. #ifdef GC
  482. static GCConVar webapi_values_max_pool_size_mb( "webapi_values_max_pool_size_mb", "10", "Maximum size in bytes of the WebAPIValues buffer pool" );
  483. static GCConVar webapi_values_init_buffer_size( "webapi_values_init_buffer_size", "65536", "Initial buffer size for buffers in the WebAPIValues buffer pool" );
  484. /*static*/ CBufferPoolMT &CWebAPIValues::GetBufferPool()
  485. {
  486. static CBufferPoolMT s_bufferPool( "WebAPIValues", webapi_values_max_pool_size_mb, webapi_values_init_buffer_size );
  487. return s_bufferPool;
  488. }
  489. #endif
  490. //-----------------------------------------------------------------------------
  491. // Purpose: Constructor
  492. //-----------------------------------------------------------------------------
  493. CWebAPIValues::CWebAPIValues( CWebAPIValues *pParent, const char *pchName, EWebAPIValueType eValueType, const char *pchArrayElementNames )
  494. {
  495. InitInternal( pParent, -1, eValueType, pchArrayElementNames );
  496. SetName( pchName );
  497. }
  498. //-----------------------------------------------------------------------------
  499. // Purpose: Constructor
  500. //-----------------------------------------------------------------------------
  501. CWebAPIValues::CWebAPIValues( const char *pchName )
  502. {
  503. InitInternal( NULL, -1, k_EWebAPIValueType_Object, NULL );
  504. SetName( pchName );
  505. }
  506. //-----------------------------------------------------------------------------
  507. // Purpose: Constructor
  508. //-----------------------------------------------------------------------------
  509. CWebAPIValues::CWebAPIValues( const char *pchName, const char *pchArrayElementNames )
  510. {
  511. InitInternal( NULL, -1, k_EWebAPIValueType_NumericArray, pchArrayElementNames );
  512. SetName( pchName );
  513. }
  514. //-----------------------------------------------------------------------------
  515. // Purpose: Constructor
  516. //-----------------------------------------------------------------------------
  517. CWebAPIValues::CWebAPIValues( CWebAPIValues *pParent, int nNamePos, EWebAPIValueType eValueType, const char *pchArrayElementNames )
  518. {
  519. InitInternal( pParent, nNamePos, eValueType, pchArrayElementNames );
  520. }
  521. //-----------------------------------------------------------------------------
  522. // Purpose: Constructor
  523. //-----------------------------------------------------------------------------
  524. void CWebAPIValues::InitInternal( CWebAPIValues *pParent, int nNamePos, EWebAPIValueType eValueType, const char *pchArrayElementNames )
  525. {
  526. if ( NULL == pParent )
  527. {
  528. #ifdef GC
  529. m_pStringBuffer = GetBufferPool().GetBuffer();
  530. #else
  531. m_pStringBuffer = new CUtlBuffer;
  532. #endif
  533. }
  534. else
  535. {
  536. m_pStringBuffer = pParent->m_pStringBuffer;
  537. }
  538. m_nNamePos = nNamePos;
  539. m_eValueType = eValueType;
  540. if ( m_eValueType == k_EWebAPIValueType_NumericArray )
  541. {
  542. Assert( pchArrayElementNames );
  543. m_nArrayChildElementNamePos = m_pStringBuffer->TellPut();
  544. m_pStringBuffer->PutString( pchArrayElementNames );
  545. }
  546. m_pFirstChild = NULL;
  547. m_pLastChild = NULL;
  548. m_pNextPeer = NULL;
  549. m_pParent = pParent;
  550. }
  551. //-----------------------------------------------------------------------------
  552. // Purpose: Destructor
  553. //-----------------------------------------------------------------------------
  554. CWebAPIValues::~CWebAPIValues()
  555. {
  556. ClearValue();
  557. CWebAPIValues *pChild = m_pFirstChild;
  558. while( pChild )
  559. {
  560. CWebAPIValues *pDelete = pChild;
  561. pChild = pChild->m_pNextPeer;
  562. delete pDelete;
  563. }
  564. m_pFirstChild = NULL;
  565. m_pNextPeer = NULL;
  566. if ( NULL == m_pParent )
  567. {
  568. #ifdef GC
  569. GetBufferPool().ReturnBuffer( m_pStringBuffer );
  570. #else
  571. delete m_pStringBuffer;
  572. #endif
  573. }
  574. // This two ptrs are just for optimized traversal at runtime, deleting just
  575. // our first child and next peer will lead to the full tree being deleted correctly.
  576. m_pLastChild = NULL;
  577. m_pParent = NULL;
  578. }
  579. //-----------------------------------------------------------------------------
  580. // Purpose: Sets the name of the values node
  581. //-----------------------------------------------------------------------------
  582. void CWebAPIValues::SetName( const char * pchName )
  583. {
  584. if ( pchName == NULL )
  585. {
  586. AssertMsg( false, "CWebAPIValues constructed with NULL name, breaks some output serialization. Shouldn't do this." );
  587. m_nNamePos = -1;
  588. }
  589. else
  590. {
  591. // Shouldn't use ', ", &, <, or > in names since they can't output well in XML. : is no good since it implies namespacing in XML.
  592. // Assert about it so we don't end up with responses that are badly formed in XML output.
  593. int unLen = 0;
  594. while ( pchName[unLen] != 0 )
  595. {
  596. if ( pchName[unLen] == '\'' || pchName[unLen] == '"' || pchName[unLen] == '&'
  597. || pchName[unLen] == '>' || pchName[unLen] == '>' || pchName[unLen] == ':' )
  598. {
  599. AssertMsg( false, "Shouldn't use any of '\"&<>: in CWebAPIValues node names, you used %s", pchName );
  600. break;
  601. }
  602. ++unLen;
  603. }
  604. m_nNamePos = m_pStringBuffer->TellPut();
  605. m_pStringBuffer->PutString( pchName );
  606. }
  607. }
  608. //-----------------------------------------------------------------------------
  609. // Purpose: Assert that we don't have any child nodes, this is used when setting a
  610. // native type value. We don't support having both our own value and children. You
  611. // are either an array of more values, or you are a value yourself.
  612. //-----------------------------------------------------------------------------
  613. void CWebAPIValues::AssertNoChildren()
  614. {
  615. AssertMsg( m_pFirstChild == NULL, "CWebAPIValues has child nodes, but you are trying to set a direct value for it. Can't have both children and your own value." );
  616. }
  617. //-----------------------------------------------------------------------------
  618. // Purpose: Clears any existing value, freeing memory if needed
  619. //-----------------------------------------------------------------------------
  620. void CWebAPIValues::ClearValue()
  621. {
  622. m_eValueType = k_EWebAPIValueType_Object;
  623. }
  624. //-----------------------------------------------------------------------------
  625. // Purpose: Setter
  626. //-----------------------------------------------------------------------------
  627. void CWebAPIValues::SetStringValue( const char *pchValue )
  628. {
  629. ClearValue();
  630. AssertNoChildren();
  631. m_eValueType = k_EWebAPIValueType_String;
  632. if ( pchValue == NULL )
  633. {
  634. m_nStrValuePos = -1;
  635. }
  636. else
  637. {
  638. m_nStrValuePos = m_pStringBuffer->TellPut();
  639. m_pStringBuffer->PutString( pchValue );
  640. }
  641. }
  642. //-----------------------------------------------------------------------------
  643. // Purpose: Setter
  644. //-----------------------------------------------------------------------------
  645. void CWebAPIValues::SetInt32Value( int32 nValue )
  646. {
  647. ClearValue();
  648. AssertNoChildren();
  649. m_eValueType = k_EWebAPIValueType_Int32;
  650. m_nValue = nValue;
  651. }
  652. //-----------------------------------------------------------------------------
  653. // Purpose: Setter
  654. //-----------------------------------------------------------------------------
  655. void CWebAPIValues::SetUInt32Value( uint32 unValue )
  656. {
  657. ClearValue();
  658. AssertNoChildren();
  659. m_eValueType = k_EWebAPIValueType_UInt32;
  660. m_unValue = unValue;
  661. }
  662. //-----------------------------------------------------------------------------
  663. // Purpose: Setter
  664. //-----------------------------------------------------------------------------
  665. void CWebAPIValues::SetInt64Value ( int64 lValue )
  666. {
  667. ClearValue();
  668. AssertNoChildren();
  669. m_eValueType = k_EWebAPIValueType_Int64;
  670. m_lValue = lValue;
  671. }
  672. //-----------------------------------------------------------------------------
  673. // Purpose: Setter
  674. //-----------------------------------------------------------------------------
  675. void CWebAPIValues::SetUInt64Value( uint64 ulValue )
  676. {
  677. ClearValue();
  678. AssertNoChildren();
  679. m_eValueType = k_EWebAPIValueType_UInt64;
  680. m_ulValue = ulValue;
  681. }
  682. //-----------------------------------------------------------------------------
  683. // Purpose: Setter
  684. //-----------------------------------------------------------------------------
  685. void CWebAPIValues::SetDoubleValue( double flValue )
  686. {
  687. ClearValue();
  688. AssertNoChildren();
  689. m_eValueType = k_EWebAPIValueType_Double;
  690. m_flValue = flValue;
  691. }
  692. //-----------------------------------------------------------------------------
  693. // Purpose: Setter
  694. //-----------------------------------------------------------------------------
  695. void CWebAPIValues::SetBoolValue( bool bValue )
  696. {
  697. ClearValue();
  698. AssertNoChildren();
  699. m_eValueType = k_EWebAPIValueType_Bool;
  700. m_bValue = bValue;
  701. }
  702. //-----------------------------------------------------------------------------
  703. // Purpose: Setter
  704. //-----------------------------------------------------------------------------
  705. void CWebAPIValues::SetNullValue()
  706. {
  707. ClearValue();
  708. AssertNoChildren();
  709. m_eValueType = k_EWebAPIValueType_Null;
  710. }
  711. //-----------------------------------------------------------------------------
  712. // Purpose: Setter
  713. //-----------------------------------------------------------------------------
  714. void CWebAPIValues::SetBinaryValue( const uint8 *pValue, uint32 unBytes )
  715. {
  716. ClearValue();
  717. AssertNoChildren();
  718. m_eValueType = k_EWebAPIValueType_BinaryBlob;
  719. if ( pValue == NULL || unBytes < 1 )
  720. {
  721. m_BinaryValue.m_nDataPos = 0;
  722. m_BinaryValue.m_unBytes = 0;
  723. }
  724. else
  725. {
  726. m_BinaryValue.m_unBytes = unBytes;
  727. m_BinaryValue.m_nDataPos = m_pStringBuffer->TellPut();
  728. m_pStringBuffer->Put( pValue, unBytes );
  729. }
  730. }
  731. //-----------------------------------------------------------------------------
  732. // Purpose: Get the type currently held by the node
  733. //-----------------------------------------------------------------------------
  734. EWebAPIValueType CWebAPIValues::GetType() const
  735. {
  736. return m_eValueType;
  737. }
  738. //-----------------------------------------------------------------------------
  739. // Purpose: Get int32 value
  740. //-----------------------------------------------------------------------------
  741. int32 CWebAPIValues::GetInt32Value() const
  742. {
  743. // we can read uint64 values this way too
  744. switch ( m_eValueType )
  745. {
  746. case k_EWebAPIValueType_Int32:
  747. return m_nValue;
  748. case k_EWebAPIValueType_UInt32: // because we can't tell the type of an int when we parse this node might have different type
  749. case k_EWebAPIValueType_UInt64:
  750. case k_EWebAPIValueType_String:
  751. {
  752. uint32 uiVal = GetUInt32Value();
  753. AssertMsg( uiVal < INT_MAX, "GetInt32Value called on node with %u, which is too big to fit in an Int32", uiVal );
  754. return (int32)uiVal;
  755. }
  756. }
  757. AssertMsg( false, "Shouldn't call CWebAPIValues::GetInt32Value unless value type is int32, uint32, uint64, or string. %d does not.", m_eValueType );
  758. return 0;
  759. }
  760. //-----------------------------------------------------------------------------
  761. // Purpose: Get uint32 value
  762. //-----------------------------------------------------------------------------
  763. uint32 CWebAPIValues::GetUInt32Value() const
  764. {
  765. // we can read uint64 values this way too
  766. switch ( m_eValueType )
  767. {
  768. case k_EWebAPIValueType_UInt32:
  769. return m_unValue;
  770. case k_EWebAPIValueType_UInt64:
  771. case k_EWebAPIValueType_String:
  772. {
  773. uint64 uiVal = GetUInt64Value();
  774. AssertMsg( uiVal <= UINT_MAX, "GetUInt32Value called on node with %llu, which is too big to fit in an UInt32", uiVal );
  775. return (uint32)uiVal;
  776. }
  777. }
  778. AssertMsg( false, "Shouldn't call CWebAPIValues::GetUInt32Value unless value type is uint32, uint64, or string. %d does not", m_eValueType );
  779. return 0;
  780. }
  781. //-----------------------------------------------------------------------------
  782. // Purpose: Get int64 value
  783. //-----------------------------------------------------------------------------
  784. int64 CWebAPIValues::GetInt64Value() const
  785. {
  786. // we can read int32 values this way too
  787. switch ( m_eValueType )
  788. {
  789. case k_EWebAPIValueType_Int32:
  790. return GetInt32Value();
  791. case k_EWebAPIValueType_Int64:
  792. return m_lValue;
  793. case k_EWebAPIValueType_String:
  794. if ( m_nStrValuePos < 0 )
  795. {
  796. return 0ull;
  797. }
  798. else
  799. {
  800. #if defined(_PS3) || defined(POSIX)
  801. return strtoll( (const char *)m_pStringBuffer->Base() + m_nStrValuePos, NULL, 10);
  802. #else
  803. return _strtoi64( (const char *)m_pStringBuffer->Base() + m_nStrValuePos, NULL, 10);
  804. #endif
  805. }
  806. default:
  807. AssertMsg1( false, "Shouldn't call CWebAPIValues::GetInt64Value unless value type matches. %d does not", m_eValueType );
  808. return 0;
  809. }
  810. }
  811. //-----------------------------------------------------------------------------
  812. // Purpose: Get uint64 value
  813. //-----------------------------------------------------------------------------
  814. uint64 CWebAPIValues::GetUInt64Value() const
  815. {
  816. // we can read uint32 values this way too
  817. switch ( m_eValueType )
  818. {
  819. case k_EWebAPIValueType_UInt32:
  820. return GetUInt32Value();
  821. case k_EWebAPIValueType_UInt64:
  822. return m_ulValue;
  823. case k_EWebAPIValueType_String:
  824. if ( m_nStrValuePos < 0 )
  825. {
  826. return 0ull;
  827. }
  828. else
  829. {
  830. #if defined(_PS3) || defined(POSIX)
  831. return strtoull( (const char *)m_pStringBuffer->Base() + m_nStrValuePos, NULL, 10);
  832. #else
  833. return _strtoui64( (const char *)m_pStringBuffer->Base() + m_nStrValuePos, NULL, 10);
  834. #endif
  835. }
  836. default:
  837. AssertMsg1( false, "Shouldn't call CWebAPIValues::GetUInt64Value unless value type matches. %d does not", m_eValueType );
  838. return 0;
  839. }
  840. }
  841. //-----------------------------------------------------------------------------
  842. // Purpose: Get double value
  843. //-----------------------------------------------------------------------------
  844. double CWebAPIValues::GetDoubleValue() const
  845. {
  846. switch ( m_eValueType )
  847. {
  848. case k_EWebAPIValueType_Int32:
  849. return (double)m_nValue;
  850. case k_EWebAPIValueType_UInt32:
  851. return (double)m_unValue;
  852. case k_EWebAPIValueType_Int64:
  853. return (double)m_lValue;
  854. case k_EWebAPIValueType_UInt64:
  855. return (double)m_ulValue;
  856. case k_EWebAPIValueType_Double:
  857. return m_flValue;
  858. default:
  859. AssertMsg1( false, "Shouldn't call CWebAPIValues::GetDoubleValue unless value type matches. %d does not", m_eValueType );
  860. return 0.0f;
  861. }
  862. }
  863. //-----------------------------------------------------------------------------
  864. // Purpose: Get bool value
  865. //-----------------------------------------------------------------------------
  866. bool CWebAPIValues::GetBoolValue() const
  867. {
  868. if ( m_eValueType != k_EWebAPIValueType_Bool )
  869. {
  870. AssertMsg( false, "Shouldn't call CWebAPIValues::GetBoolValue unless value type matches" );
  871. return false;
  872. }
  873. return m_bValue;
  874. }
  875. //-----------------------------------------------------------------------------
  876. // Purpose: Get string value
  877. //-----------------------------------------------------------------------------
  878. void CWebAPIValues::GetStringValue( CUtlString &stringOut ) const
  879. {
  880. switch ( m_eValueType )
  881. {
  882. case k_EWebAPIValueType_String:
  883. if ( m_nStrValuePos < 0 )
  884. {
  885. stringOut.Clear();
  886. }
  887. else
  888. {
  889. stringOut = (const char *)m_pStringBuffer->Base() + m_nStrValuePos;
  890. }
  891. return;
  892. case k_EWebAPIValueType_Int32:
  893. stringOut = CNumStr( m_nValue ).String();
  894. return;
  895. case k_EWebAPIValueType_Int64:
  896. stringOut = CNumStr( m_lValue ).String();
  897. return;
  898. case k_EWebAPIValueType_UInt32:
  899. stringOut = CNumStr( m_unValue ).String();
  900. return;
  901. case k_EWebAPIValueType_UInt64:
  902. stringOut = CNumStr( m_ulValue ).String();
  903. return;
  904. case k_EWebAPIValueType_Double:
  905. stringOut = CNumStr( m_flValue ).String();
  906. return;
  907. case k_EWebAPIValueType_Bool:
  908. stringOut = m_bValue ? "true" : "false";
  909. return;
  910. default:
  911. AssertMsg1( false, "CWebAPIValues::GetStringValue(), unable to convert data type %d to string", m_eValueType );
  912. stringOut = "";
  913. return;
  914. }
  915. }
  916. //-----------------------------------------------------------------------------
  917. // Purpose: Get binary blob value
  918. //-----------------------------------------------------------------------------
  919. void CWebAPIValues::GetBinaryValue( CUtlBuffer &bufferOut ) const
  920. {
  921. if ( m_eValueType != k_EWebAPIValueType_BinaryBlob )
  922. {
  923. AssertMsg( false, "Shouldn't call CWebAPIValues::GetBinaryValue unless value type matches" );
  924. bufferOut.Clear();
  925. return;
  926. }
  927. bufferOut.Clear();
  928. bufferOut.EnsureCapacity( m_BinaryValue.m_unBytes );
  929. if ( m_BinaryValue.m_unBytes )
  930. bufferOut.Put( (byte*)m_pStringBuffer->Base() + m_BinaryValue.m_nDataPos, m_BinaryValue.m_unBytes );
  931. return;
  932. }
  933. //-----------------------------------------------------------------------------
  934. // Purpose: Create a child array of this node. Note that array nodes can only
  935. // have un-named children, in XML the pchArrayElementNames value will be used
  936. // as the element name for each of the children of the array, in JSON it will simply
  937. // be a numerically indexed [] array.
  938. //-----------------------------------------------------------------------------
  939. CWebAPIValues * CWebAPIValues::CreateChildArray( const char *pchName, const char *pchArrayElementNames )
  940. {
  941. return CreateChildInternal( pchName, k_EWebAPIValueType_NumericArray, pchArrayElementNames );
  942. }
  943. //-----------------------------------------------------------------------------
  944. // Purpose: Create a child of this node. Note, it's possible to create multiple,
  945. // children with the same name, but you really don't want to. We'll assert about it
  946. // in debug builds to detect, but not in release. If you do create duplicates you'll
  947. // have broken JSON output.
  948. //-----------------------------------------------------------------------------
  949. CWebAPIValues * CWebAPIValues::CreateChildObject( const char *pchName )
  950. {
  951. return CreateChildInternal( pchName, k_EWebAPIValueType_Object );
  952. }
  953. //-----------------------------------------------------------------------------
  954. // Purpose: Return an existing child object - otherwise create one and return that.
  955. //-----------------------------------------------------------------------------
  956. CWebAPIValues *CWebAPIValues::FindOrCreateChildObject( const char *pchName )
  957. {
  958. CWebAPIValues *pChild = FindChild( pchName );
  959. if ( pChild )
  960. {
  961. return pChild;
  962. }
  963. return CreateChildObject( pchName );
  964. }
  965. //-----------------------------------------------------------------------------
  966. // Purpose: Add a child object to the array, this should only be called on objects that are of the array type
  967. //-----------------------------------------------------------------------------
  968. CWebAPIValues * CWebAPIValues::AddChildObjectToArray()
  969. {
  970. if ( m_eValueType != k_EWebAPIValueType_NumericArray )
  971. {
  972. AssertMsg( m_eValueType == k_EWebAPIValueType_NumericArray, "Can't add array elements to CWebAPIVAlues unless type is of numeric array." );
  973. return NULL;
  974. }
  975. // Use child element array name as name of all children of arrays
  976. return CreateChildInternal( NULL, k_EWebAPIValueType_Object );
  977. }
  978. //-----------------------------------------------------------------------------
  979. // Purpose: Add a child array to the array, this should only be called on objects that are of the array type
  980. //-----------------------------------------------------------------------------
  981. CWebAPIValues * CWebAPIValues::AddChildArrayToArray( const char * pchArrayElementNames )
  982. {
  983. if ( m_eValueType != k_EWebAPIValueType_NumericArray )
  984. {
  985. AssertMsg( m_eValueType == k_EWebAPIValueType_NumericArray, "Can't add array elements to CWebAPIVAlues unless type is of numeric array." );
  986. return NULL;
  987. }
  988. // Use child element array name as name of all children of arrays
  989. return CreateChildInternal( NULL, k_EWebAPIValueType_NumericArray, pchArrayElementNames );
  990. }
  991. //-----------------------------------------------------------------------------
  992. // Purpose: Internal helper for creating children
  993. //-----------------------------------------------------------------------------
  994. CWebAPIValues * CWebAPIValues::CreateChildInternal( const char *pchName, EWebAPIValueType eValueType, const char *pchArrayElementNames )
  995. {
  996. // Shouldn't create children if you have a direct value. You are either an object or array of children,
  997. // or a native value type. Not both.
  998. AssertMsg( m_eValueType == k_EWebAPIValueType_Object || m_eValueType == k_EWebAPIValueType_NumericArray, "You are trying to create a child node of a CWebAPIValues object, but it has a direct value already. Can't have children and a value." );
  999. if ( m_eValueType != k_EWebAPIValueType_Object && m_eValueType != k_EWebAPIValueType_NumericArray )
  1000. ClearValue();
  1001. // Shouldn't create named children if you are a numeric array
  1002. CWebAPIValues *pNewNode;
  1003. if ( m_eValueType == k_EWebAPIValueType_NumericArray )
  1004. {
  1005. if ( pchName )
  1006. {
  1007. AssertMsg( false, "Can't create named child of CWebAPIValues object of type NumericArray. Should call AddArrayElement instead of CreateChild*." );
  1008. }
  1009. // Force name to match what all items in the array should use
  1010. pNewNode = new CWebAPIValues( this, m_nArrayChildElementNamePos, eValueType, pchArrayElementNames );
  1011. }
  1012. else
  1013. {
  1014. pNewNode = new CWebAPIValues( this, pchName, eValueType, pchArrayElementNames );
  1015. }
  1016. if ( eValueType == k_EWebAPIValueType_NumericArray )
  1017. {
  1018. Assert( pchArrayElementNames );
  1019. }
  1020. if ( !m_pFirstChild )
  1021. {
  1022. m_pLastChild = m_pFirstChild = pNewNode;
  1023. return m_pFirstChild;
  1024. }
  1025. else
  1026. {
  1027. CWebAPIValues *pCurLastChild = m_pLastChild;
  1028. #ifdef _DEBUG
  1029. // In debug, traverse all children so we can check for duplicate names, which will break JSON output!
  1030. pCurLastChild = m_pFirstChild;
  1031. if ( m_eValueType != k_EWebAPIValueType_NumericArray )
  1032. {
  1033. if ( Q_stricmp( pCurLastChild->GetName(), pchName ) == 0 )
  1034. {
  1035. AssertMsg( false, "Trying to create CWebAPIValues child with name %s that conflicts with existing child. Breaks JSON output!", pchName );
  1036. }
  1037. }
  1038. while ( pCurLastChild->m_pNextPeer )
  1039. {
  1040. pCurLastChild = pCurLastChild->m_pNextPeer;
  1041. if ( m_eValueType != k_EWebAPIValueType_NumericArray )
  1042. {
  1043. if ( Q_stricmp( pCurLastChild->GetName(), pchName ) == 0 )
  1044. {
  1045. AssertMsg( false, "Trying to create CWebAPIValues child with name %s that conflicts with existing child. Breaks JSON output!", pchName );
  1046. }
  1047. }
  1048. }
  1049. // Also, in debug assert last child ptr looks correct
  1050. Assert( m_pLastChild == pCurLastChild );
  1051. #endif
  1052. m_pLastChild = pCurLastChild->m_pNextPeer = pNewNode;
  1053. return m_pLastChild;
  1054. }
  1055. }
  1056. //-----------------------------------------------------------------------------
  1057. // Purpose: Set a child node's string value
  1058. //-----------------------------------------------------------------------------
  1059. void CWebAPIValues::SetChildStringValue( const char *pchChildName, const char *pchValue )
  1060. {
  1061. CreateChildObject( pchChildName )->SetStringValue( pchValue );
  1062. }
  1063. //-----------------------------------------------------------------------------
  1064. // Purpose: Set a child node's int32 value
  1065. //-----------------------------------------------------------------------------
  1066. void CWebAPIValues::SetChildInt32Value( const char *pchChildName, int32 nValue )
  1067. {
  1068. CreateChildObject( pchChildName )->SetInt32Value( nValue );
  1069. }
  1070. //-----------------------------------------------------------------------------
  1071. // Purpose: Set a child node's uint32 value
  1072. //-----------------------------------------------------------------------------
  1073. void CWebAPIValues::SetChildUInt32Value( const char *pchChildName, uint32 unValue )
  1074. {
  1075. CreateChildObject( pchChildName )->SetUInt32Value( unValue );
  1076. }
  1077. //-----------------------------------------------------------------------------
  1078. // Purpose: Set a child node's int64 value
  1079. //-----------------------------------------------------------------------------
  1080. void CWebAPIValues::SetChildInt64Value ( const char *pchChildName, int64 lValue )
  1081. {
  1082. CreateChildObject( pchChildName )->SetInt64Value( lValue );
  1083. }
  1084. //-----------------------------------------------------------------------------
  1085. // Purpose: Set a child node's uint64 value
  1086. //-----------------------------------------------------------------------------
  1087. void CWebAPIValues::SetChildUInt64Value( const char *pchChildName, uint64 ulValue )
  1088. {
  1089. CreateChildObject( pchChildName )->SetUInt64Value( ulValue );
  1090. }
  1091. //-----------------------------------------------------------------------------
  1092. // Purpose: Set a child node's double value
  1093. //-----------------------------------------------------------------------------
  1094. void CWebAPIValues::SetChildDoubleValue( const char *pchChildName, double flValue )
  1095. {
  1096. CreateChildObject( pchChildName )->SetDoubleValue( flValue );
  1097. }
  1098. //-----------------------------------------------------------------------------
  1099. // Purpose: Set a child node's binary blob value
  1100. //-----------------------------------------------------------------------------
  1101. void CWebAPIValues::SetChildBinaryValue( const char *pchChildName, const uint8 *pValue, uint32 unBytes )
  1102. {
  1103. CreateChildObject( pchChildName )->SetBinaryValue( pValue, unBytes );
  1104. }
  1105. //-----------------------------------------------------------------------------
  1106. // Purpose: Set a child node's boolean value
  1107. //-----------------------------------------------------------------------------
  1108. void CWebAPIValues::SetChildBoolValue( const char *pchChildName, bool bValue )
  1109. {
  1110. CreateChildObject( pchChildName )->SetBoolValue( bValue );
  1111. }
  1112. //-----------------------------------------------------------------------------
  1113. // Purpose: Set a child node's boolean value
  1114. //-----------------------------------------------------------------------------
  1115. void CWebAPIValues::SetChildNullValue( const char *pchChildName )
  1116. {
  1117. CreateChildObject( pchChildName )->SetNullValue();
  1118. }
  1119. //-----------------------------------------------------------------------------
  1120. // Purpose: Get a child node's int32 value or return the default if the node doesn't exist
  1121. //-----------------------------------------------------------------------------
  1122. int32 CWebAPIValues::GetChildInt32Value( const char *pchChildName, int32 nDefault ) const
  1123. {
  1124. const CWebAPIValues *pChild = FindChild( pchChildName );
  1125. if( pChild )
  1126. return pChild->GetInt32Value();
  1127. else
  1128. return nDefault;
  1129. }
  1130. //-----------------------------------------------------------------------------
  1131. // Purpose: Get a child node's uint32 value or return the default if the node doesn't exist
  1132. //-----------------------------------------------------------------------------
  1133. uint32 CWebAPIValues::GetChildUInt32Value( const char *pchChildName, uint32 unDefault ) const
  1134. {
  1135. const CWebAPIValues *pChild = FindChild( pchChildName );
  1136. if( pChild )
  1137. return pChild->GetUInt32Value();
  1138. else
  1139. return unDefault;
  1140. }
  1141. //-----------------------------------------------------------------------------
  1142. // Purpose: Get a child node's int64 value or return the default if the node doesn't exist
  1143. //-----------------------------------------------------------------------------
  1144. int64 CWebAPIValues::GetChildInt64Value( const char *pchChildName, int64 lDefault ) const
  1145. {
  1146. const CWebAPIValues *pChild = FindChild( pchChildName );
  1147. if( pChild )
  1148. return pChild->GetInt64Value();
  1149. else
  1150. return lDefault;
  1151. }
  1152. //-----------------------------------------------------------------------------
  1153. // Purpose: Get a child node's uint64 value or return the default if the node doesn't exist
  1154. //-----------------------------------------------------------------------------
  1155. uint64 CWebAPIValues::GetChildUInt64Value( const char *pchChildName, uint64 ulDefault ) const
  1156. {
  1157. const CWebAPIValues *pChild = FindChild( pchChildName );
  1158. if( pChild )
  1159. return pChild->GetUInt64Value();
  1160. else
  1161. return ulDefault;
  1162. }
  1163. //-----------------------------------------------------------------------------
  1164. // Purpose: Get a child node's double value or return the default if the node doesn't exist
  1165. //-----------------------------------------------------------------------------
  1166. double CWebAPIValues::GetChildDoubleValue( const char *pchChildName, double flDefault ) const
  1167. {
  1168. const CWebAPIValues *pChild = FindChild( pchChildName );
  1169. if( pChild )
  1170. return pChild->GetDoubleValue();
  1171. else
  1172. return flDefault;
  1173. }
  1174. //-----------------------------------------------------------------------------
  1175. // Purpose: Get a child node's string value or return the default if the node doesn't exist
  1176. //-----------------------------------------------------------------------------
  1177. void CWebAPIValues::GetChildStringValue( CUtlString &stringOut, const char *pchChildName, const char *pchDefault ) const
  1178. {
  1179. const CWebAPIValues *pChild = FindChild( pchChildName );
  1180. if( pChild )
  1181. {
  1182. pChild->GetStringValue( stringOut );
  1183. }
  1184. else
  1185. {
  1186. stringOut = pchDefault;
  1187. }
  1188. }
  1189. //-----------------------------------------------------------------------------
  1190. // Purpose: Get a child node's binary blob value (returns false if the child wasn't found)
  1191. //-----------------------------------------------------------------------------
  1192. bool CWebAPIValues::BGetChildBinaryValue( CUtlBuffer &bufferOut, const char *pchChildName ) const
  1193. {
  1194. const CWebAPIValues *pChild = FindChild( pchChildName );
  1195. if( pChild )
  1196. {
  1197. pChild->GetBinaryValue( bufferOut );
  1198. return true;
  1199. }
  1200. else
  1201. {
  1202. return false;
  1203. }
  1204. }
  1205. //-----------------------------------------------------------------------------
  1206. // Purpose: Get a child node's binary blob value (returns false if the child wasn't found)
  1207. //-----------------------------------------------------------------------------
  1208. bool CWebAPIValues::IsChildNullValue( const char *pchChildName ) const
  1209. {
  1210. const CWebAPIValues *pChild = FindChild( pchChildName );
  1211. if( pChild )
  1212. return pChild->IsNullValue();
  1213. else
  1214. return false;
  1215. }
  1216. //-----------------------------------------------------------------------------
  1217. // Purpose: Get a child node's bool value or return the default if the node doesn't exist
  1218. //-----------------------------------------------------------------------------
  1219. bool CWebAPIValues::GetChildBoolValue( const char *pchChildName, bool bDefault ) const
  1220. {
  1221. const CWebAPIValues *pChild = FindChild( pchChildName );
  1222. if( pChild )
  1223. return pChild->GetBoolValue();
  1224. else
  1225. return bDefault;
  1226. }
  1227. //-----------------------------------------------------------------------------
  1228. // Purpose: Find first matching child by name, O(N) on number of children, this class isn't designed for searching
  1229. //-----------------------------------------------------------------------------
  1230. CWebAPIValues * CWebAPIValues::FindChild( const char *pchName )
  1231. {
  1232. CWebAPIValues *pCurLastChild = m_pFirstChild;
  1233. while ( pCurLastChild )
  1234. {
  1235. if ( Q_stricmp( pCurLastChild->GetName(), pchName ) == 0 )
  1236. return pCurLastChild;
  1237. pCurLastChild = pCurLastChild->m_pNextPeer;
  1238. }
  1239. return NULL;
  1240. }
  1241. //-----------------------------------------------------------------------------
  1242. // Purpose: Get the first child of this node
  1243. //-----------------------------------------------------------------------------
  1244. CWebAPIValues * CWebAPIValues::GetFirstChild()
  1245. {
  1246. return m_pFirstChild;
  1247. }
  1248. //-----------------------------------------------------------------------------
  1249. // Purpose: Call this on the returned value from GetFirstChild() or a previous GetNextChild() call to
  1250. // proceed to the next child of the parent GetFirstChild() was originally called on.
  1251. //-----------------------------------------------------------------------------
  1252. CWebAPIValues * CWebAPIValues::GetNextChild()
  1253. {
  1254. return m_pNextPeer;
  1255. }
  1256. //-----------------------------------------------------------------------------
  1257. // Purpose: Call this on any node to return the parent of that node
  1258. //-----------------------------------------------------------------------------
  1259. CWebAPIValues * CWebAPIValues::GetParent()
  1260. {
  1261. return m_pParent;
  1262. }
  1263. //-----------------------------------------------------------------------------
  1264. // Purpose: Deletes a child node by name
  1265. //-----------------------------------------------------------------------------
  1266. void CWebAPIValues::DeleteChild( const char *pchName )
  1267. {
  1268. CWebAPIValues *pChild = NULL; // child we're examining, could be NULL at exit if we don't find it
  1269. CWebAPIValues *pPrev = NULL; // previous sibling, or NULL
  1270. for ( pChild = m_pFirstChild; pChild != NULL; pPrev = pChild, pChild = pChild->m_pNextPeer )
  1271. {
  1272. if ( !Q_stricmp( pChild->GetName(), pchName ) )
  1273. {
  1274. if ( pChild == m_pFirstChild )
  1275. {
  1276. // first child, fixup parent's pointer to take pChild out
  1277. Assert( pPrev == NULL );
  1278. m_pFirstChild = pChild->m_pNextPeer;
  1279. }
  1280. else
  1281. {
  1282. // not first child, fixup sibling's pointer to take pChild out
  1283. Assert( pPrev != NULL );
  1284. pPrev->m_pNextPeer = pChild->m_pNextPeer;
  1285. }
  1286. // clean up next ptr on child so we don't double free
  1287. pChild->m_pNextPeer = NULL;
  1288. // fixup last child pointer if pChild is the last child
  1289. if ( pChild == m_pLastChild )
  1290. {
  1291. m_pLastChild = pPrev;
  1292. }
  1293. break;
  1294. }
  1295. }
  1296. // debug only, check that there is no child by the specified name any more and that it was excised OK
  1297. AssertMsg( FindChild( pchName ) == NULL, "cwebapivalues deleted child is still lurking" );
  1298. Assert( pChild == NULL || pChild->m_pNextPeer == NULL );
  1299. // now take the removed child out of the heap
  1300. delete pChild;
  1301. }
  1302. //-----------------------------------------------------------------------------
  1303. // Purpose: Emits JSON formatted representation of values
  1304. //
  1305. // when bEmitOldStyleArrays is true, arrays are emitted as a child of a singlet object, and any empty arrays
  1306. // will be emitted as having a single null member.
  1307. //
  1308. // when bEmitOldStyleArrays is false, arrays are emitted bare (no subobject) and empty arrays
  1309. // are emitted empty.
  1310. //-----------------------------------------------------------------------------
  1311. bool CWebAPIValues::BEmitJSONRecursive( const CWebAPIValues *pCurrent, CUtlBuffer &outputBuffer, int nTabLevel, size_t unMaxResultSize, bool bEmitOldStyleArrays )
  1312. {
  1313. bool bSuccess = true;
  1314. while( pCurrent )
  1315. {
  1316. // don't let the buffer grow until it consumes all available memory
  1317. if( unMaxResultSize && (size_t)outputBuffer.TellMaxPut() > unMaxResultSize )
  1318. {
  1319. return false;
  1320. }
  1321. // Can't emit nameless nodes in JSON. Nodes should always have a name.
  1322. Assert( pCurrent->GetName() );
  1323. if ( pCurrent->GetName() )
  1324. {
  1325. for( int i=0; i < nTabLevel; ++i )
  1326. outputBuffer.PutChar ( '\t' );
  1327. if ( !pCurrent->m_pParent || pCurrent->m_pParent->GetType() != k_EWebAPIValueType_NumericArray )
  1328. {
  1329. EmitJSONString( outputBuffer, pCurrent->GetName() );
  1330. outputBuffer.PutChar( ':' );
  1331. outputBuffer.PutChar( ' ' );
  1332. }
  1333. if ( pCurrent->m_eValueType == k_EWebAPIValueType_Object || pCurrent->m_eValueType == k_EWebAPIValueType_NumericArray )
  1334. {
  1335. if( bEmitOldStyleArrays || pCurrent->m_eValueType == k_EWebAPIValueType_Object )
  1336. {
  1337. outputBuffer.PutChar( '{' );
  1338. outputBuffer.PutChar( '\n' );
  1339. }
  1340. if ( pCurrent->m_eValueType == k_EWebAPIValueType_NumericArray )
  1341. {
  1342. if( bEmitOldStyleArrays )
  1343. {
  1344. for( int i=0; i < nTabLevel+1; ++i )
  1345. outputBuffer.PutChar ( '\t' );
  1346. EmitJSONString( outputBuffer, (const char *)pCurrent->m_pStringBuffer->Base() + pCurrent->m_nArrayChildElementNamePos );
  1347. outputBuffer.PutChar( ':' );
  1348. outputBuffer.PutChar( ' ' );
  1349. }
  1350. outputBuffer.PutChar( '[' );
  1351. outputBuffer.PutChar( '\n' );
  1352. }
  1353. // First add any children
  1354. if ( pCurrent->m_pFirstChild )
  1355. {
  1356. int nChildTabLevel = nTabLevel+1;
  1357. if ( pCurrent->m_eValueType == k_EWebAPIValueType_NumericArray && bEmitOldStyleArrays )
  1358. ++nChildTabLevel;
  1359. bSuccess = BEmitJSONRecursive( pCurrent->m_pFirstChild, outputBuffer, nChildTabLevel, unMaxResultSize, bEmitOldStyleArrays );
  1360. if ( !bSuccess )
  1361. return false;
  1362. }
  1363. else if ( bEmitOldStyleArrays )
  1364. {
  1365. for( int i=0; i < nTabLevel + 1; ++i )
  1366. outputBuffer.PutChar ( '\t' );
  1367. outputBuffer.Put( "null", 4 );
  1368. }
  1369. outputBuffer.PutChar( '\n' );
  1370. for( int i=0; i < nTabLevel; ++i )
  1371. outputBuffer.PutChar ( '\t' );
  1372. if ( pCurrent->m_eValueType == k_EWebAPIValueType_NumericArray )
  1373. {
  1374. if( bEmitOldStyleArrays )
  1375. outputBuffer.PutChar( '\t' );
  1376. outputBuffer.PutChar( ']' );
  1377. outputBuffer.PutChar( '\n' );
  1378. for( int i=0; i < nTabLevel; ++i )
  1379. outputBuffer.PutChar ( '\t' );
  1380. }
  1381. if( bEmitOldStyleArrays || pCurrent->m_eValueType == k_EWebAPIValueType_Object )
  1382. {
  1383. outputBuffer.PutChar( '}' );
  1384. }
  1385. }
  1386. else
  1387. {
  1388. switch ( pCurrent->m_eValueType )
  1389. {
  1390. case k_EWebAPIValueType_Int32:
  1391. {
  1392. CNumStr numStr( pCurrent->m_nValue );
  1393. outputBuffer.Put( numStr.String(), Q_strlen( numStr.String() ) );
  1394. }
  1395. break;
  1396. case k_EWebAPIValueType_Int64:
  1397. {
  1398. CNumStr numStr( pCurrent->m_lValue );
  1399. outputBuffer.Put( numStr.String(), Q_strlen( numStr.String() ) );
  1400. }
  1401. break;
  1402. case k_EWebAPIValueType_UInt32:
  1403. {
  1404. CNumStr numStr( pCurrent->m_unValue );
  1405. outputBuffer.Put( numStr.String(), Q_strlen( numStr.String() ) );
  1406. }
  1407. break;
  1408. case k_EWebAPIValueType_UInt64:
  1409. {
  1410. CNumStr numStr( pCurrent->m_ulValue );
  1411. outputBuffer.Put( numStr.String(), Q_strlen( numStr.String() ) );
  1412. }
  1413. break;
  1414. case k_EWebAPIValueType_Double:
  1415. {
  1416. CNumStr numStr( pCurrent->m_flValue );
  1417. outputBuffer.Put( numStr.String(), Q_strlen( numStr.String() ) );
  1418. }
  1419. break;
  1420. case k_EWebAPIValueType_String:
  1421. {
  1422. if ( pCurrent->m_nStrValuePos < 0 )
  1423. outputBuffer.Put( "null", 4 );
  1424. else
  1425. EmitJSONString( outputBuffer, (const char *)pCurrent->m_pStringBuffer->Base() + pCurrent->m_nStrValuePos );
  1426. }
  1427. break;
  1428. case k_EWebAPIValueType_Bool:
  1429. {
  1430. if ( !pCurrent->m_bValue )
  1431. outputBuffer.Put( "false", 5 );
  1432. else
  1433. outputBuffer.Put( "true", 4 );
  1434. }
  1435. break;
  1436. case k_EWebAPIValueType_Null:
  1437. {
  1438. outputBuffer.Put( "null", 4 );
  1439. }
  1440. break;
  1441. case k_EWebAPIValueType_BinaryBlob:
  1442. {
  1443. if ( pCurrent->m_BinaryValue.m_unBytes == 0 )
  1444. outputBuffer.Put( "null", 4 );
  1445. else
  1446. {
  1447. CUtlMemory<char> buffEncoded;
  1448. DbgVerify( Base64EncodeIntoUTLMemory( (const uint8 *)pCurrent->m_pStringBuffer->Base() + pCurrent->m_BinaryValue.m_nDataPos, pCurrent->m_BinaryValue.m_unBytes, buffEncoded ) );
  1449. EmitJSONString( outputBuffer, buffEncoded.Base() );
  1450. }
  1451. }
  1452. break;
  1453. default:
  1454. break;
  1455. }
  1456. }
  1457. }
  1458. // Now, check for any peers
  1459. if ( bSuccess && pCurrent->m_pNextPeer )
  1460. {
  1461. outputBuffer.PutChar( ',' );
  1462. outputBuffer.PutChar( '\n' );
  1463. pCurrent = pCurrent->m_pNextPeer;
  1464. }
  1465. else
  1466. {
  1467. // We're done, or failing early
  1468. pCurrent = NULL;
  1469. }
  1470. }
  1471. return bSuccess && outputBuffer.IsValid();
  1472. }
  1473. //-----------------------------------------------------------------------------
  1474. // Purpose: Emits KeyValues .vdf style formatted representation of values
  1475. //-----------------------------------------------------------------------------
  1476. bool CWebAPIValues::BEmitVDFRecursive( const CWebAPIValues *pCurrent, CUtlBuffer &outputBuffer, int nTabLevel, uint32 nArrayElement, size_t unMaxResultSize, bool bIncludeArrayElementName )
  1477. {
  1478. bool bSuccess = true;
  1479. // We can have lots of peers, so this is an optimization to avoid tail recursion and resulting stack-overflows!
  1480. while( pCurrent )
  1481. {
  1482. // don't let the buffer grow until it consumes all available memory
  1483. if( unMaxResultSize && (size_t)outputBuffer.TellMaxPut() > unMaxResultSize )
  1484. {
  1485. return false;
  1486. }
  1487. if ( pCurrent->GetName() )
  1488. {
  1489. for( int i=0; i < nTabLevel; ++i )
  1490. outputBuffer.PutChar ( '\t' );
  1491. // Open node, special naming when inside arrays
  1492. if ( pCurrent->m_pParent && pCurrent->m_pParent->GetType() == k_EWebAPIValueType_NumericArray )
  1493. {
  1494. CNumStr numStr( nArrayElement );
  1495. numStr.AddQuotes();
  1496. outputBuffer.Put( numStr.String(), Q_strlen( numStr.String() ) );
  1497. }
  1498. else
  1499. {
  1500. EmitVDFString( outputBuffer, pCurrent->GetName() );
  1501. }
  1502. if ( pCurrent->m_eValueType == k_EWebAPIValueType_Object || pCurrent->m_eValueType == k_EWebAPIValueType_NumericArray )
  1503. {
  1504. outputBuffer.PutChar( '\n' );
  1505. for( int i=0; i < nTabLevel; ++i )
  1506. outputBuffer.PutChar ( '\t' );
  1507. outputBuffer.PutChar( '{' );
  1508. outputBuffer.PutChar( '\n' );
  1509. if ( pCurrent->m_eValueType == k_EWebAPIValueType_NumericArray && bIncludeArrayElementName )
  1510. {
  1511. for( int i=0; i < nTabLevel+1; ++i )
  1512. outputBuffer.PutChar ( '\t' );
  1513. EmitVDFString( outputBuffer, (const char *)pCurrent->m_pStringBuffer->Base() + pCurrent->m_nArrayChildElementNamePos );
  1514. outputBuffer.PutChar( '\n' );
  1515. for( int i=0; i < nTabLevel+1; ++i )
  1516. outputBuffer.PutChar ( '\t' );
  1517. outputBuffer.PutChar( '{' );
  1518. outputBuffer.PutChar( '\n' );
  1519. }
  1520. if ( pCurrent->m_pFirstChild )
  1521. {
  1522. int nChildTabLevel = nTabLevel+1;
  1523. if ( pCurrent->m_eValueType == k_EWebAPIValueType_NumericArray && bIncludeArrayElementName )
  1524. ++nChildTabLevel;
  1525. bSuccess = BEmitVDFRecursive( pCurrent->m_pFirstChild, outputBuffer, nChildTabLevel, 0, unMaxResultSize, bIncludeArrayElementName );
  1526. if ( !bSuccess )
  1527. return false;
  1528. }
  1529. if ( pCurrent->m_eValueType == k_EWebAPIValueType_NumericArray && bIncludeArrayElementName )
  1530. {
  1531. outputBuffer.PutChar( '\n' );
  1532. for( int i=0; i < nTabLevel+1; ++i )
  1533. outputBuffer.PutChar ( '\t' );
  1534. outputBuffer.PutChar( '}' );
  1535. }
  1536. outputBuffer.PutChar( '\n' );
  1537. for( int i=0; i < nTabLevel; ++i )
  1538. outputBuffer.PutChar ( '\t' );
  1539. outputBuffer.PutChar( '}' );
  1540. }
  1541. else
  1542. {
  1543. outputBuffer.PutChar( '\t' );
  1544. switch ( pCurrent->m_eValueType )
  1545. {
  1546. case k_EWebAPIValueType_Int32:
  1547. {
  1548. CNumStr numStr( pCurrent->m_nValue );
  1549. numStr.AddQuotes();
  1550. outputBuffer.Put( numStr.String(), Q_strlen( numStr.String() ) );
  1551. }
  1552. break;
  1553. case k_EWebAPIValueType_Int64:
  1554. {
  1555. CNumStr numStr( pCurrent->m_lValue );
  1556. numStr.AddQuotes();
  1557. outputBuffer.Put( numStr.String(), Q_strlen( numStr.String() ) );
  1558. }
  1559. break;
  1560. case k_EWebAPIValueType_UInt32:
  1561. {
  1562. CNumStr numStr( pCurrent->m_unValue );
  1563. numStr.AddQuotes();
  1564. outputBuffer.Put( numStr.String(), Q_strlen( numStr.String() ) );
  1565. }
  1566. break;
  1567. case k_EWebAPIValueType_UInt64:
  1568. {
  1569. CNumStr numStr( pCurrent->m_ulValue );
  1570. numStr.AddQuotes();
  1571. outputBuffer.Put( numStr.String(), Q_strlen( numStr.String() ) );
  1572. }
  1573. break;
  1574. case k_EWebAPIValueType_Double:
  1575. {
  1576. CNumStr numStr( pCurrent->m_flValue );
  1577. numStr.AddQuotes();
  1578. outputBuffer.Put( numStr.String(), Q_strlen( numStr.String() ) );
  1579. }
  1580. break;
  1581. case k_EWebAPIValueType_String:
  1582. {
  1583. EmitVDFString( outputBuffer, pCurrent->m_nStrValuePos >= 0 ? ( (const char *)pCurrent->m_pStringBuffer->Base() + pCurrent->m_nStrValuePos ) : NULL );
  1584. }
  1585. break;
  1586. case k_EWebAPIValueType_Bool:
  1587. {
  1588. if ( !pCurrent->m_bValue )
  1589. outputBuffer.Put( "\"0\"", 3 );
  1590. else
  1591. outputBuffer.Put( "\"1\"", 3 );
  1592. }
  1593. break;
  1594. case k_EWebAPIValueType_Null:
  1595. {
  1596. outputBuffer.Put ("\"\"", 2 );
  1597. }
  1598. break;
  1599. case k_EWebAPIValueType_BinaryBlob:
  1600. {
  1601. if ( pCurrent->m_BinaryValue.m_unBytes == 0 )
  1602. outputBuffer.Put( "\"\"", 2 );
  1603. else
  1604. {
  1605. CUtlMemory<char> buffEncoded;
  1606. DbgVerify( Base64EncodeIntoUTLMemory( (const uint8 *)pCurrent->m_pStringBuffer->Base() + pCurrent->m_BinaryValue.m_nDataPos, pCurrent->m_BinaryValue.m_unBytes, buffEncoded ) );
  1607. EmitVDFString( outputBuffer, buffEncoded.Base() );
  1608. }
  1609. }
  1610. break;
  1611. default:
  1612. break;
  1613. }
  1614. }
  1615. }
  1616. // Now, check for any peers
  1617. if ( bSuccess && pCurrent->m_pNextPeer )
  1618. {
  1619. outputBuffer.PutChar( '\n' );
  1620. pCurrent = pCurrent->m_pNextPeer;
  1621. nArrayElement += 1;
  1622. }
  1623. else
  1624. {
  1625. // We're done, or failing early
  1626. pCurrent = NULL;
  1627. }
  1628. }
  1629. return bSuccess && outputBuffer.IsValid();
  1630. }
  1631. //-----------------------------------------------------------------------------
  1632. // Purpose: Emits XML formatted representation of values
  1633. //-----------------------------------------------------------------------------
  1634. bool CWebAPIValues::BEmitXMLRecursive( const CWebAPIValues *pCurrent, CUtlBuffer &outputBuffer, int nTabLevel, size_t unMaxResultSize )
  1635. {
  1636. bool bSuccess = true;
  1637. while( pCurrent )
  1638. {
  1639. // don't let the buffer grow until it consumes all available memory
  1640. if( unMaxResultSize && (size_t)outputBuffer.TellMaxPut() > unMaxResultSize )
  1641. {
  1642. return false;
  1643. }
  1644. // Can't emit nameless nodes in XML. Nodes should always have a name.
  1645. Assert( pCurrent->GetName() );
  1646. if ( pCurrent->GetName() )
  1647. {
  1648. for( int i=0; i < nTabLevel; ++i )
  1649. outputBuffer.PutChar ( '\t' );
  1650. // Open node
  1651. outputBuffer.PutChar( '<' );
  1652. EmitXMLString( outputBuffer, pCurrent->GetName() );
  1653. outputBuffer.PutChar( '>' );
  1654. if ( pCurrent->m_eValueType == k_EWebAPIValueType_Object || pCurrent->m_eValueType == k_EWebAPIValueType_NumericArray )
  1655. {
  1656. // First add any children
  1657. if ( pCurrent->m_pFirstChild )
  1658. {
  1659. outputBuffer.PutChar( '\n' );
  1660. bSuccess = BEmitXMLRecursive( pCurrent->m_pFirstChild, outputBuffer, nTabLevel+1, unMaxResultSize );
  1661. if ( !bSuccess )
  1662. return false;
  1663. outputBuffer.PutChar( '\n' );
  1664. // Return to correct tab level, for when we close element below
  1665. for( int i=0; i < nTabLevel; ++i )
  1666. outputBuffer.PutChar ( '\t' );
  1667. }
  1668. }
  1669. else
  1670. {
  1671. switch ( pCurrent->m_eValueType )
  1672. {
  1673. case k_EWebAPIValueType_Int32:
  1674. {
  1675. CNumStr numStr( pCurrent->m_nValue );
  1676. outputBuffer.Put( numStr.String(), Q_strlen( numStr.String() ) );
  1677. }
  1678. break;
  1679. case k_EWebAPIValueType_Int64:
  1680. {
  1681. CNumStr numStr( pCurrent->m_lValue );
  1682. outputBuffer.Put( numStr.String(), Q_strlen( numStr.String() ) );
  1683. }
  1684. break;
  1685. case k_EWebAPIValueType_UInt32:
  1686. {
  1687. CNumStr numStr( pCurrent->m_unValue );
  1688. outputBuffer.Put( numStr.String(), Q_strlen( numStr.String() ) );
  1689. }
  1690. break;
  1691. case k_EWebAPIValueType_UInt64:
  1692. {
  1693. CNumStr numStr( pCurrent->m_ulValue );
  1694. outputBuffer.Put( numStr.String(), Q_strlen( numStr.String() ) );
  1695. }
  1696. break;
  1697. case k_EWebAPIValueType_Double:
  1698. {
  1699. CNumStr numStr( pCurrent->m_flValue );
  1700. outputBuffer.Put( numStr.String(), Q_strlen( numStr.String() ) );
  1701. }
  1702. break;
  1703. case k_EWebAPIValueType_String:
  1704. {
  1705. if ( pCurrent->m_nStrValuePos < 0 )
  1706. outputBuffer.Put( "null", 4 );
  1707. else
  1708. EmitXMLString( outputBuffer, (const char *)pCurrent->m_pStringBuffer->Base() + pCurrent->m_nStrValuePos );
  1709. }
  1710. break;
  1711. case k_EWebAPIValueType_Bool:
  1712. {
  1713. if ( !pCurrent->m_bValue )
  1714. outputBuffer.Put( "false", 5 );
  1715. else
  1716. outputBuffer.Put( "true", 4 );
  1717. }
  1718. break;
  1719. case k_EWebAPIValueType_Null:
  1720. {
  1721. outputBuffer.Put ("null", 4 );
  1722. }
  1723. break;
  1724. case k_EWebAPIValueType_BinaryBlob:
  1725. {
  1726. if ( pCurrent->m_BinaryValue.m_unBytes == 0 )
  1727. outputBuffer.Put( "null", 4 );
  1728. else
  1729. {
  1730. CUtlMemory<char> buffEncoded;
  1731. DbgVerify( Base64EncodeIntoUTLMemory( (const uint8 *)pCurrent->m_pStringBuffer->Base() + pCurrent->m_BinaryValue.m_nDataPos, pCurrent->m_BinaryValue.m_unBytes, buffEncoded ) );
  1732. EmitXMLString( outputBuffer, buffEncoded.Base() );
  1733. }
  1734. }
  1735. break;
  1736. default:
  1737. break;
  1738. }
  1739. }
  1740. }
  1741. // Close element
  1742. outputBuffer.PutChar( '<' );
  1743. outputBuffer.PutChar( '/' );
  1744. EmitXMLString( outputBuffer, pCurrent->GetName() );
  1745. outputBuffer.PutChar( '>' );
  1746. // Now, check for any peers
  1747. if ( bSuccess && pCurrent->m_pNextPeer )
  1748. {
  1749. outputBuffer.PutChar( '\n' );
  1750. pCurrent = pCurrent->m_pNextPeer;
  1751. }
  1752. else
  1753. {
  1754. // We're done, or failing early
  1755. pCurrent = NULL;
  1756. }
  1757. }
  1758. return bSuccess && outputBuffer.IsValid();
  1759. }
  1760. struct JSONParserContext_t
  1761. {
  1762. CWebAPIValues *m_pCurrentNode;
  1763. CWebAPIValues *m_pRootNode;
  1764. CUtlString m_sChildName;
  1765. bool m_bIsKey;
  1766. };
  1767. static int JSONParserCallback(void* void_ctx, int type, const JSON_value* value)
  1768. {
  1769. JSONParserContext_t *ctx = (JSONParserContext_t *)void_ctx;
  1770. CWebAPIValues *pCreatedNode = NULL;
  1771. switch(type)
  1772. {
  1773. case JSON_T_ARRAY_BEGIN:
  1774. // handle the root node
  1775. if( !ctx->m_pRootNode )
  1776. {
  1777. Assert( !ctx->m_pCurrentNode );
  1778. ctx->m_pRootNode = ctx->m_pCurrentNode = new CWebAPIValues( ctx->m_sChildName, "e" );
  1779. break;
  1780. }
  1781. if( ctx->m_pCurrentNode->IsArray() )
  1782. {
  1783. Assert( ctx->m_sChildName.IsEmpty() );
  1784. ctx->m_pCurrentNode = ctx->m_pCurrentNode->AddChildArrayToArray( "e" );
  1785. }
  1786. else
  1787. {
  1788. Assert( !ctx->m_sChildName.IsEmpty() );
  1789. ctx->m_pCurrentNode = ctx->m_pCurrentNode->CreateChildArray( ctx->m_sChildName, "e" );
  1790. ctx->m_sChildName.Clear();
  1791. }
  1792. break;
  1793. case JSON_T_ARRAY_END:
  1794. ctx->m_pCurrentNode = ctx->m_pCurrentNode->GetParent();
  1795. break;
  1796. case JSON_T_OBJECT_BEGIN:
  1797. // this might be the start of the root object itself
  1798. if( !ctx->m_pRootNode )
  1799. {
  1800. ctx->m_pRootNode = ctx->m_pCurrentNode = new CWebAPIValues( ctx->m_sChildName );
  1801. break;
  1802. }
  1803. if( ctx->m_pCurrentNode->IsArray() )
  1804. {
  1805. Assert( ctx->m_sChildName.IsEmpty() );
  1806. ctx->m_pCurrentNode = ctx->m_pCurrentNode->AddChildObjectToArray();
  1807. }
  1808. else
  1809. {
  1810. Assert( !ctx->m_sChildName.IsEmpty() );
  1811. ctx->m_pCurrentNode = ctx->m_pCurrentNode->CreateChildObject( ctx->m_sChildName );
  1812. ctx->m_sChildName.Clear();
  1813. }
  1814. break;
  1815. case JSON_T_OBJECT_END:
  1816. ctx->m_pCurrentNode = ctx->m_pCurrentNode->GetParent();
  1817. break;
  1818. case JSON_T_KEY:
  1819. ctx->m_sChildName = value->vu.str.value;
  1820. break;
  1821. case JSON_T_INTEGER:
  1822. case JSON_T_FLOAT:
  1823. case JSON_T_NULL:
  1824. case JSON_T_TRUE:
  1825. case JSON_T_FALSE:
  1826. case JSON_T_STRING:
  1827. // create the new node for this value
  1828. if( ctx->m_pCurrentNode->IsArray() )
  1829. {
  1830. pCreatedNode = ctx->m_pCurrentNode->AddChildObjectToArray();
  1831. }
  1832. else
  1833. {
  1834. pCreatedNode = ctx->m_pCurrentNode->CreateChildObject( ctx->m_sChildName );
  1835. ctx->m_sChildName.Clear();
  1836. }
  1837. // set the actual value
  1838. switch( type )
  1839. {
  1840. case JSON_T_INTEGER:
  1841. // try to figure out what type to use
  1842. if( value->vu.integer_value >= 0 )
  1843. {
  1844. // unsigned
  1845. if( value->vu.integer_value <= UINT_MAX )
  1846. {
  1847. pCreatedNode->SetUInt32Value( (uint32)value->vu.integer_value );
  1848. }
  1849. else
  1850. {
  1851. pCreatedNode->SetUInt64Value( (uint64)value->vu.integer_value );
  1852. }
  1853. }
  1854. else
  1855. {
  1856. // signed
  1857. if( value->vu.integer_value >= INT_MIN )
  1858. {
  1859. pCreatedNode->SetInt32Value( (int32)value->vu.integer_value );
  1860. }
  1861. else
  1862. {
  1863. pCreatedNode->SetInt64Value( value->vu.integer_value );
  1864. }
  1865. }
  1866. break;
  1867. case JSON_T_FLOAT:
  1868. pCreatedNode->SetDoubleValue( value->vu.float_value );
  1869. break;
  1870. case JSON_T_NULL:
  1871. pCreatedNode->SetNullValue();
  1872. break;
  1873. case JSON_T_TRUE:
  1874. pCreatedNode->SetBoolValue( true );
  1875. break;
  1876. case JSON_T_FALSE:
  1877. pCreatedNode->SetBoolValue( false );
  1878. break;
  1879. case JSON_T_STRING:
  1880. pCreatedNode->SetStringValue( value->vu.str.value );
  1881. break;
  1882. }
  1883. break;
  1884. default:
  1885. Assert( false );
  1886. break;
  1887. }
  1888. return 1;
  1889. }
  1890. // parses JSON into a tree of CWebAPIValues nodes with this as the root
  1891. CWebAPIValues *CWebAPIValues::ParseJSON( CUtlBuffer &inputBuffer )
  1892. {
  1893. //
  1894. // if there's nothing to parse, just early out
  1895. inputBuffer.EatWhiteSpace();
  1896. if( inputBuffer.GetBytesRemaining() == 0 )
  1897. return NULL;
  1898. // if the first character is the start of a string,
  1899. // wrap the whole thing in an object so we can parse it.
  1900. // We'll unwrap it at the end
  1901. char cFirst = *(char *)inputBuffer.PeekGet();
  1902. bool bWrapContent = cFirst == '\"';
  1903. JSON_config config;
  1904. struct JSON_parser_struct* jc = NULL;
  1905. init_JSON_config(&config);
  1906. JSONParserContext_t context;
  1907. context.m_pCurrentNode = NULL;
  1908. context.m_pRootNode = NULL;
  1909. context.m_bIsKey = false;
  1910. config.depth = 19;
  1911. config.callback = &JSONParserCallback;
  1912. config.allow_comments = 1;
  1913. config.handle_floats_manually = 0;
  1914. config.callback_ctx = &context;
  1915. config.malloc = malloc;
  1916. config.free = free;
  1917. jc = new_JSON_parser(&config);
  1918. bool bSuccess = true;
  1919. if( bWrapContent )
  1920. JSON_parser_char( jc, '{' );
  1921. while( inputBuffer.GetBytesRemaining( ) > 0 )
  1922. {
  1923. if( !JSON_parser_char( jc, (unsigned char)inputBuffer.GetChar() ) )
  1924. {
  1925. bSuccess = false;
  1926. break;
  1927. }
  1928. }
  1929. if( bWrapContent )
  1930. {
  1931. JSON_parser_char( jc, '}' );
  1932. }
  1933. if( bSuccess )
  1934. {
  1935. if (!JSON_parser_done(jc))
  1936. {
  1937. bSuccess = false;
  1938. }
  1939. }
  1940. delete_JSON_parser(jc);
  1941. // unwrap the root node
  1942. if( bWrapContent && bSuccess )
  1943. {
  1944. CWebAPIValues *pWrapRoot = context.m_pRootNode;
  1945. if( !pWrapRoot )
  1946. {
  1947. bSuccess = false;
  1948. }
  1949. else
  1950. {
  1951. CWebAPIValues *pRealRoot = pWrapRoot->GetFirstChild();
  1952. if( !pRealRoot )
  1953. {
  1954. bSuccess = false;
  1955. }
  1956. else
  1957. {
  1958. if( pRealRoot->GetNextChild() )
  1959. {
  1960. bSuccess = false;
  1961. }
  1962. else
  1963. {
  1964. pWrapRoot->m_pFirstChild = NULL;
  1965. pRealRoot->m_pParent = NULL;
  1966. context.m_pRootNode = pRealRoot;
  1967. delete pWrapRoot;
  1968. }
  1969. }
  1970. }
  1971. }
  1972. if( bSuccess )
  1973. {
  1974. return context.m_pRootNode;
  1975. }
  1976. else
  1977. {
  1978. delete context.m_pRootNode;
  1979. return NULL;
  1980. }
  1981. }
  1982. CWebAPIValues *CWebAPIValues::ParseJSON( const char *pchJSONString )
  1983. {
  1984. CUtlBuffer bufJSON( pchJSONString, Q_strlen( pchJSONString), CUtlBuffer::TEXT_BUFFER | CUtlBuffer::READ_ONLY );
  1985. return ParseJSON( bufJSON );
  1986. }
  1987. void CWebAPIValues::CopyFrom( const CWebAPIValues *pSource )
  1988. {
  1989. switch( pSource->GetType() )
  1990. {
  1991. case k_EWebAPIValueType_Int32:
  1992. SetInt32Value( pSource->GetInt32Value() );
  1993. break;
  1994. case k_EWebAPIValueType_Int64:
  1995. SetInt64Value( pSource->GetInt64Value() );
  1996. break;
  1997. case k_EWebAPIValueType_UInt32:
  1998. SetUInt32Value( pSource->GetUInt32Value() );
  1999. break;
  2000. case k_EWebAPIValueType_UInt64:
  2001. SetUInt64Value( pSource->GetUInt64Value() );
  2002. break;
  2003. case k_EWebAPIValueType_Double:
  2004. SetDoubleValue( pSource->GetDoubleValue() );
  2005. break;
  2006. case k_EWebAPIValueType_String:
  2007. {
  2008. CUtlString sValue;
  2009. pSource->GetStringValue( sValue );
  2010. SetStringValue( sValue );
  2011. }
  2012. break;
  2013. case k_EWebAPIValueType_Bool:
  2014. SetBoolValue( pSource->GetBoolValue() );
  2015. break;
  2016. case k_EWebAPIValueType_Null:
  2017. SetNullValue( );
  2018. break;
  2019. case k_EWebAPIValueType_BinaryBlob:
  2020. {
  2021. CUtlBuffer bufValue;
  2022. pSource->GetBinaryValue( bufValue );
  2023. SetBinaryValue( (uint8 *)bufValue.Base(), bufValue.TellMaxPut() );
  2024. }
  2025. break;
  2026. case k_EWebAPIValueType_Object:
  2027. {
  2028. ClearValue();
  2029. m_eValueType = k_EWebAPIValueType_Object;
  2030. const CWebAPIValues *pSourceChild = pSource->GetFirstChild();
  2031. while( pSourceChild )
  2032. {
  2033. CWebAPIValues *pChild;
  2034. if( pSourceChild->IsArray() )
  2035. pChild = CreateChildArray( pSourceChild->GetName(), pSourceChild->GetElementName() );
  2036. else
  2037. pChild = CreateChildObject( pSourceChild->GetName() );
  2038. pChild->CopyFrom( pSourceChild );
  2039. pSourceChild = pSourceChild->GetNextChild();
  2040. }
  2041. }
  2042. break;
  2043. case k_EWebAPIValueType_NumericArray:
  2044. {
  2045. // the type should have been set when this node was first created
  2046. Assert( m_eValueType == k_EWebAPIValueType_NumericArray );
  2047. m_eValueType = k_EWebAPIValueType_NumericArray;
  2048. const CWebAPIValues *pSourceChild = pSource->GetFirstChild();
  2049. while( pSourceChild )
  2050. {
  2051. CWebAPIValues *pChild;
  2052. if( pSourceChild->IsArray() )
  2053. pChild = AddChildArrayToArray( pSourceChild->GetElementName() );
  2054. else
  2055. pChild = AddChildObjectToArray( );
  2056. pChild->CopyFrom( pSourceChild );
  2057. pSourceChild = pSourceChild->GetNextChild();
  2058. }
  2059. }
  2060. break;
  2061. default:
  2062. AssertMsg( false, "Unknown type in CWebAPIValues::CopyFrom" );
  2063. break;
  2064. }
  2065. }
  2066. //-----------------------------------------------------------------------------
  2067. // Purpose: Fills a WebAPI key values from a corresponding protobuf message.
  2068. //-----------------------------------------------------------------------------
  2069. bool ProtoBufHelper::RecursiveAddProtoBufToWebAPIValues( CWebAPIValues *pWebAPIRoot, const ::google::protobuf::Message & msg )
  2070. {
  2071. using ::google::protobuf::FieldDescriptor;
  2072. const ::google::protobuf::Descriptor *pDescriptor = msg.GetDescriptor();
  2073. const ::google::protobuf::Reflection *pReflection = msg.GetReflection();
  2074. for ( int iField = 0; iField < pDescriptor->field_count(); iField++ )
  2075. {
  2076. const ::google::protobuf::FieldDescriptor *pField = pDescriptor->field( iField );
  2077. const char *pFieldName = pField->name().c_str();
  2078. if ( pField->is_repeated() )
  2079. {
  2080. if ( pReflection->FieldSize( msg, pField ) == 0 )
  2081. {
  2082. continue; // No need to create the array if it is empty
  2083. // If the field has been disabled externally, it has been cleared already (and thus will be skipped)
  2084. }
  2085. // bugbug: Is there a way to get this from the google API?
  2086. const char *pchTypeName = "unknown_type";
  2087. switch ( pField->cpp_type() )
  2088. {
  2089. case FieldDescriptor::CPPTYPE_INT32: pchTypeName = "int32"; break;
  2090. case FieldDescriptor::CPPTYPE_INT64: pchTypeName = "int64"; break;
  2091. case FieldDescriptor::CPPTYPE_UINT32: pchTypeName = "uint32"; break;
  2092. case FieldDescriptor::CPPTYPE_DOUBLE: pchTypeName = "double"; break;
  2093. case FieldDescriptor::CPPTYPE_FLOAT: pchTypeName = "float"; break;
  2094. case FieldDescriptor::CPPTYPE_BOOL: pchTypeName = "bool"; break;
  2095. case FieldDescriptor::CPPTYPE_ENUM: pchTypeName = "enum"; break;
  2096. case FieldDescriptor::CPPTYPE_STRING:
  2097. {
  2098. if ( pField->type() == FieldDescriptor::TYPE_STRING )
  2099. {
  2100. pchTypeName = "string";
  2101. }
  2102. else
  2103. {
  2104. AssertMsg1( pField->type() == FieldDescriptor::TYPE_BYTES, "Unrecognized field type: %d", pField->type() );
  2105. pchTypeName = "bytes";
  2106. }
  2107. break;
  2108. }
  2109. case FieldDescriptor::CPPTYPE_MESSAGE: pchTypeName = "message"; break;
  2110. }
  2111. CWebAPIValues *pContainer = pWebAPIRoot->CreateChildArray( pFieldName, pchTypeName );
  2112. for ( int iRepeated = 0; iRepeated < pReflection->FieldSize( msg, pField ); iRepeated++ )
  2113. {
  2114. switch ( pField->cpp_type() )
  2115. {
  2116. case FieldDescriptor::CPPTYPE_INT32: pContainer->SetChildInt32Value( NULL, pReflection->GetRepeatedInt32( msg, pField, iRepeated ) ); break;
  2117. case FieldDescriptor::CPPTYPE_INT64: pContainer->SetChildInt64Value( NULL, pReflection->GetRepeatedInt64( msg, pField, iRepeated ) ); break;
  2118. case FieldDescriptor::CPPTYPE_UINT32: pContainer->SetChildUInt32Value( NULL, pReflection->GetRepeatedUInt32( msg, pField, iRepeated ) ); break;
  2119. case FieldDescriptor::CPPTYPE_UINT64: pContainer->SetChildUInt64Value( NULL, pReflection->GetRepeatedUInt64( msg, pField, iRepeated ) ); break;
  2120. case FieldDescriptor::CPPTYPE_DOUBLE: pContainer->SetChildDoubleValue( NULL, pReflection->GetRepeatedDouble( msg, pField, iRepeated ) ); break;
  2121. case FieldDescriptor::CPPTYPE_FLOAT: pContainer->SetChildDoubleValue( NULL, pReflection->GetRepeatedFloat( msg, pField, iRepeated ) ); break;
  2122. case FieldDescriptor::CPPTYPE_BOOL: pContainer->SetChildBoolValue( NULL, pReflection->GetRepeatedBool( msg, pField, iRepeated ) ); break;
  2123. case FieldDescriptor::CPPTYPE_ENUM: pContainer->SetChildInt32Value( NULL, pReflection->GetRepeatedEnum( msg, pField, iRepeated )->number() ); break;
  2124. case FieldDescriptor::CPPTYPE_STRING:
  2125. {
  2126. const std::string &strValue = pReflection->GetRepeatedString( msg, pField, iRepeated );
  2127. if ( pField->type() == FieldDescriptor::TYPE_STRING )
  2128. {
  2129. pContainer->SetChildStringValue( NULL, strValue.c_str() );
  2130. }
  2131. else
  2132. {
  2133. // Binary blobs are automatically encoded in Base64 when converted to string by Web request
  2134. pContainer->SetChildBinaryValue( NULL, (const uint8 *)strValue.c_str(), strValue.size() );
  2135. }
  2136. break;
  2137. }
  2138. case FieldDescriptor::CPPTYPE_MESSAGE:
  2139. {
  2140. const ::google::protobuf::Message &subMsg = pReflection->GetRepeatedMessage( msg, pField, iRepeated );
  2141. CWebAPIValues *pChild = pContainer->CreateChildObject( NULL );
  2142. if ( RecursiveAddProtoBufToWebAPIValues( pChild, subMsg ) == false )
  2143. {
  2144. return false;
  2145. }
  2146. break;
  2147. }
  2148. default:
  2149. AssertMsg1( false, "Unknown cpp_type %d", pField->cpp_type() );
  2150. return false;
  2151. }
  2152. }
  2153. }
  2154. else
  2155. {
  2156. if ( ( ( pReflection->HasField( msg, pField ) == false ) && ( pField->has_default_value() == false ) )
  2157. // || pField->options().GetExtension( is_field_disabled_externally ) // Steam vesion supports this
  2158. )
  2159. {
  2160. continue; // If no field set and no default value set, there is no need to send the field
  2161. // Or it is externally disabled (it has been cleared already but there still may be a default value set)
  2162. }
  2163. switch ( pField->cpp_type() )
  2164. {
  2165. case FieldDescriptor::CPPTYPE_INT32: pWebAPIRoot->SetChildInt32Value( pFieldName, pReflection->GetInt32( msg, pField ) ); break;
  2166. case FieldDescriptor::CPPTYPE_INT64: pWebAPIRoot->SetChildInt64Value( pFieldName, pReflection->GetInt64( msg, pField ) ); break;
  2167. case FieldDescriptor::CPPTYPE_UINT32: pWebAPIRoot->SetChildUInt32Value( pFieldName, pReflection->GetUInt32( msg, pField ) ); break;
  2168. case FieldDescriptor::CPPTYPE_UINT64: pWebAPIRoot->SetChildUInt64Value( pFieldName, pReflection->GetUInt64( msg, pField ) ); break;
  2169. case FieldDescriptor::CPPTYPE_DOUBLE: pWebAPIRoot->SetChildDoubleValue( pFieldName, pReflection->GetDouble( msg, pField ) ); break;
  2170. case FieldDescriptor::CPPTYPE_FLOAT: pWebAPIRoot->SetChildDoubleValue( pFieldName, pReflection->GetFloat( msg, pField ) ); break;
  2171. case FieldDescriptor::CPPTYPE_BOOL: pWebAPIRoot->SetChildBoolValue( pFieldName, pReflection->GetBool( msg, pField ) ); break;
  2172. case FieldDescriptor::CPPTYPE_ENUM: pWebAPIRoot->SetChildInt32Value( pFieldName, pReflection->GetEnum( msg, pField )->number() ); break;
  2173. case FieldDescriptor::CPPTYPE_STRING:
  2174. {
  2175. const std::string &strValue = pReflection->GetString( msg, pField );
  2176. if ( pField->type() == FieldDescriptor::TYPE_STRING )
  2177. {
  2178. pWebAPIRoot->SetChildStringValue( pFieldName, strValue.c_str() );
  2179. }
  2180. else
  2181. {
  2182. AssertMsg1( pField->type() == FieldDescriptor::TYPE_BYTES, "Unrecognized field type: %d", pField->type() );
  2183. // Binary blobs are automatically encoded in Base64 when converted to string by Web request
  2184. pWebAPIRoot->SetChildBinaryValue( pFieldName, (const uint8 *)strValue.c_str(), strValue.size() );
  2185. }
  2186. break;
  2187. }
  2188. case FieldDescriptor::CPPTYPE_MESSAGE:
  2189. {
  2190. CWebAPIValues *pChild = pWebAPIRoot->CreateChildObject( pFieldName );
  2191. #undef GetMessage // Work around unfortunate Microsoft macro
  2192. const ::google::protobuf::Message &subMsg = pReflection->GetMessage( msg, pField );
  2193. #ifdef UNICODE
  2194. #define GetMessage GetMessageW
  2195. #else
  2196. #define GetMessage GetMessageA
  2197. #endif // !UNICODE
  2198. if ( RecursiveAddProtoBufToWebAPIValues( pChild, subMsg ) == false )
  2199. {
  2200. return false;
  2201. }
  2202. break;
  2203. }
  2204. default:
  2205. AssertMsg1( false, "Unknown cpp_type %d", pField->cpp_type() );
  2206. return false;
  2207. }
  2208. }
  2209. }
  2210. return true;
  2211. }