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.

1454 lines
42 KiB

  1. //====== Copyright � 1996-2004, Valve Corporation, All rights reserved. =======
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "dmxloader/dmxelement.h"
  7. #include <ctype.h>
  8. #include "tier1/utlbuffer.h"
  9. #include "tier1/utlbufferutil.h"
  10. #include <limits.h>
  11. #include "dmxserializationdictionary.h"
  12. //-----------------------------------------------------------------------------
  13. // Forward declarations
  14. //-----------------------------------------------------------------------------
  15. class CUtlBuffer;
  16. //-----------------------------------------------------------------------------
  17. // a simple class to keep track of a stack of valid parsed symbols
  18. //-----------------------------------------------------------------------------
  19. class CDmxKeyValues2ErrorStack
  20. {
  21. public:
  22. CDmxKeyValues2ErrorStack();
  23. // Sets the filename to report with errors; sets the line number to 0
  24. void SetFilename( const char *pFilename );
  25. // Current line control
  26. void IncrementCurrentLine();
  27. void SetCurrentLine( int nLine );
  28. int GetCurrentLine() const;
  29. // entering a new keyvalues block, save state for errors
  30. // Not save symbols instead of pointers because the pointers can move!
  31. int Push( CUtlSymbolLarge symName );
  32. // exiting block, error isn't in this block, remove.
  33. void Pop();
  34. // Allows you to keep the same stack level, but change the name as you parse peers
  35. void Reset( int stackLevel, CUtlSymbolLarge symName );
  36. // Hit an error, report it and the parsing stack for context
  37. void ReportError( const char *pError, ... );
  38. static CUtlSymbolTableLarge& GetSymbolTable() { return m_ErrorSymbolTable; }
  39. private:
  40. enum
  41. {
  42. MAX_ERROR_STACK = 64
  43. };
  44. CUtlSymbolLarge m_errorStack[MAX_ERROR_STACK];
  45. const char *m_pFilename;
  46. int m_nFileLine;
  47. int m_errorIndex;
  48. int m_maxErrorIndex;
  49. static CUtlSymbolTableLarge m_ErrorSymbolTable;
  50. };
  51. CUtlSymbolTableLarge CDmxKeyValues2ErrorStack::m_ErrorSymbolTable;
  52. //-----------------------------------------------------------------------------
  53. // Singleton instance
  54. //-----------------------------------------------------------------------------
  55. static CDmxKeyValues2ErrorStack g_KeyValues2ErrorStack;
  56. //-----------------------------------------------------------------------------
  57. // Constructor
  58. //-----------------------------------------------------------------------------
  59. CDmxKeyValues2ErrorStack::CDmxKeyValues2ErrorStack() :
  60. m_pFilename("NULL"), m_errorIndex(0), m_maxErrorIndex(0), m_nFileLine(1)
  61. {
  62. }
  63. //-----------------------------------------------------------------------------
  64. // Sets the filename
  65. //-----------------------------------------------------------------------------
  66. void CDmxKeyValues2ErrorStack::SetFilename( const char *pFilename )
  67. {
  68. m_pFilename = pFilename;
  69. m_maxErrorIndex = 0;
  70. m_nFileLine = 1;
  71. }
  72. //-----------------------------------------------------------------------------
  73. // Current line control
  74. //-----------------------------------------------------------------------------
  75. void CDmxKeyValues2ErrorStack::IncrementCurrentLine()
  76. {
  77. ++m_nFileLine;
  78. }
  79. void CDmxKeyValues2ErrorStack::SetCurrentLine( int nLine )
  80. {
  81. m_nFileLine = nLine;
  82. }
  83. int CDmxKeyValues2ErrorStack::GetCurrentLine() const
  84. {
  85. return m_nFileLine;
  86. }
  87. //-----------------------------------------------------------------------------
  88. // entering a new keyvalues block, save state for errors
  89. // Not save symbols instead of pointers because the pointers can move!
  90. //-----------------------------------------------------------------------------
  91. int CDmxKeyValues2ErrorStack::Push( CUtlSymbolLarge symName )
  92. {
  93. if ( m_errorIndex < MAX_ERROR_STACK )
  94. {
  95. m_errorStack[m_errorIndex] = symName;
  96. }
  97. m_errorIndex++;
  98. m_maxErrorIndex = MAX( m_maxErrorIndex, (m_errorIndex-1) );
  99. return m_errorIndex-1;
  100. }
  101. //-----------------------------------------------------------------------------
  102. // exiting block, error isn't in this block, remove.
  103. //-----------------------------------------------------------------------------
  104. void CDmxKeyValues2ErrorStack::Pop()
  105. {
  106. m_errorIndex--;
  107. Assert(m_errorIndex>=0);
  108. }
  109. //-----------------------------------------------------------------------------
  110. // Allows you to keep the same stack level, but change the name as you parse peers
  111. //-----------------------------------------------------------------------------
  112. void CDmxKeyValues2ErrorStack::Reset( int stackLevel, CUtlSymbolLarge symName )
  113. {
  114. Assert( stackLevel >= 0 && stackLevel < m_errorIndex );
  115. m_errorStack[stackLevel] = symName;
  116. }
  117. //-----------------------------------------------------------------------------
  118. // Hit an error, report it and the parsing stack for context
  119. //-----------------------------------------------------------------------------
  120. void CDmxKeyValues2ErrorStack::ReportError( const char *pFmt, ... )
  121. {
  122. char temp[2048];
  123. va_list args;
  124. va_start( args, pFmt );
  125. Q_vsnprintf( temp, sizeof( temp ), pFmt, args );
  126. va_end( args );
  127. Warning( "%s(%d) : %s\n", m_pFilename, m_nFileLine, temp );
  128. for ( int i = 0; i < m_maxErrorIndex; i++ )
  129. {
  130. if ( !m_errorStack[i].IsValid() )
  131. continue;
  132. if ( i < m_errorIndex )
  133. {
  134. Warning( "%s, ", m_errorStack[i].String() );
  135. }
  136. else
  137. {
  138. Warning( "(*%s*), ", m_errorStack[i].String() );
  139. }
  140. }
  141. Warning( "\n" );
  142. }
  143. //-----------------------------------------------------------------------------
  144. // a simple helper that creates stack entries as it goes in & out of scope
  145. //-----------------------------------------------------------------------------
  146. class CKeyValues2ErrorContext
  147. {
  148. public:
  149. CKeyValues2ErrorContext( const char *pSymName )
  150. {
  151. Init( CDmxKeyValues2ErrorStack::GetSymbolTable().AddString( pSymName ) );
  152. }
  153. CKeyValues2ErrorContext( CUtlSymbolLarge symName )
  154. {
  155. Init( symName );
  156. }
  157. ~CKeyValues2ErrorContext()
  158. {
  159. g_KeyValues2ErrorStack.Pop();
  160. }
  161. void Reset( CUtlSymbolLarge symName )
  162. {
  163. g_KeyValues2ErrorStack.Reset( m_stackLevel, symName );
  164. }
  165. private:
  166. void Init( CUtlSymbolLarge symName )
  167. {
  168. m_stackLevel = g_KeyValues2ErrorStack.Push( symName );
  169. }
  170. int m_stackLevel;
  171. };
  172. //-----------------------------------------------------------------------------
  173. // Element dictionary used in unserialization
  174. //-----------------------------------------------------------------------------
  175. typedef int DmxElementDictHandle_t;
  176. enum
  177. {
  178. ELEMENT_DICT_HANDLE_INVALID = (DmxElementDictHandle_t)~0
  179. };
  180. class CDmxElementDictionary
  181. {
  182. public:
  183. CDmxElementDictionary();
  184. DmxElementDictHandle_t InsertElement( CDmxElement *pElement );
  185. CDmxElement *GetElement( DmxElementDictHandle_t handle );
  186. void AddAttribute( CDmxAttribute *pAttribute, const DmObjectId_t &pElementId );
  187. void AddArrayAttribute( CDmxAttribute *pAttribute, DmxElementDictHandle_t hChild );
  188. void AddArrayAttribute( CDmxAttribute *pAttribute, const DmObjectId_t &pElementId );
  189. // Finds an element into the table
  190. DmxElementDictHandle_t FindElement( CDmxElement *pElement );
  191. DmxElementDictHandle_t FindElement( const DmObjectId_t &objectId );
  192. // Sets the element id for an element
  193. void SetElementId( DmxElementDictHandle_t hElement, const DmObjectId_t &objectId );
  194. // Hook up all element references (which were unserialized as object ids)
  195. void HookUpElementReferences();
  196. // Clears the dictionary
  197. void Clear();
  198. // iteration through elements
  199. DmxElementDictHandle_t FirstElement() { return 0; }
  200. DmxElementDictHandle_t NextElement( DmxElementDictHandle_t h )
  201. {
  202. return m_Dict.IsValidIndex( h+1 ) ? h+1 : ELEMENT_DICT_HANDLE_INVALID;
  203. }
  204. private:
  205. struct DictInfo_t
  206. {
  207. CDmxElement *m_pElement;
  208. DmObjectId_t m_Id;
  209. };
  210. struct AttributeInfo_t
  211. {
  212. CDmxAttribute *m_pAttribute;
  213. bool m_bObjectId;
  214. union
  215. {
  216. DmxElementDictHandle_t m_hElement;
  217. DmObjectId_t m_ObjectId;
  218. };
  219. };
  220. typedef CUtlVector<AttributeInfo_t> AttributeList_t;
  221. // Hook up all element references (which were unserialized as object ids)
  222. void HookUpElementAttributes();
  223. void HookUpElementArrayAttributes();
  224. CUtlVector< DictInfo_t > m_Dict;
  225. AttributeList_t m_Attributes;
  226. AttributeList_t m_ArrayAttributes;
  227. };
  228. //-----------------------------------------------------------------------------
  229. // Constructor
  230. //-----------------------------------------------------------------------------
  231. CDmxElementDictionary::CDmxElementDictionary()
  232. {
  233. }
  234. //-----------------------------------------------------------------------------
  235. // Clears the dictionary
  236. //-----------------------------------------------------------------------------
  237. void CDmxElementDictionary::Clear()
  238. {
  239. m_Dict.Purge();
  240. m_Attributes.Purge();
  241. m_ArrayAttributes.Purge();
  242. }
  243. //-----------------------------------------------------------------------------
  244. // Inserts an element into the table
  245. //-----------------------------------------------------------------------------
  246. DmxElementDictHandle_t CDmxElementDictionary::InsertElement( CDmxElement *pElement )
  247. {
  248. // Insert it into the reconnection table
  249. DmxElementDictHandle_t h = m_Dict.AddToTail( );
  250. m_Dict[h].m_pElement = pElement;
  251. InvalidateUniqueId( &m_Dict[h].m_Id );
  252. return h;
  253. }
  254. //-----------------------------------------------------------------------------
  255. // Sets the element id for an element
  256. //-----------------------------------------------------------------------------
  257. void CDmxElementDictionary::SetElementId( DmxElementDictHandle_t hElement, const DmObjectId_t &objectId )
  258. {
  259. Assert( hElement != ELEMENT_DICT_HANDLE_INVALID );
  260. CopyUniqueId( objectId, &m_Dict[hElement].m_Id );
  261. }
  262. //-----------------------------------------------------------------------------
  263. // Returns a particular element
  264. //-----------------------------------------------------------------------------
  265. CDmxElement *CDmxElementDictionary::GetElement( DmxElementDictHandle_t handle )
  266. {
  267. if ( handle == ELEMENT_DICT_HANDLE_INVALID )
  268. return NULL;
  269. return m_Dict[ handle ].m_pElement;
  270. }
  271. //-----------------------------------------------------------------------------
  272. // Adds an attribute to the fixup list
  273. //-----------------------------------------------------------------------------
  274. void CDmxElementDictionary::AddAttribute( CDmxAttribute *pAttribute, const DmObjectId_t &objectId )
  275. {
  276. int i = m_Attributes.AddToTail();
  277. m_Attributes[i].m_bObjectId = true;
  278. m_Attributes[i].m_pAttribute = pAttribute;
  279. CopyUniqueId( objectId, &m_Attributes[i].m_ObjectId );
  280. }
  281. //-----------------------------------------------------------------------------
  282. // Adds an element of an attribute array to the fixup list
  283. //-----------------------------------------------------------------------------
  284. void CDmxElementDictionary::AddArrayAttribute( CDmxAttribute *pAttribute, DmxElementDictHandle_t hElement )
  285. {
  286. int i = m_ArrayAttributes.AddToTail();
  287. m_ArrayAttributes[i].m_bObjectId = false;
  288. m_ArrayAttributes[i].m_pAttribute = pAttribute;
  289. m_ArrayAttributes[i].m_hElement = hElement;
  290. }
  291. void CDmxElementDictionary::AddArrayAttribute( CDmxAttribute *pAttribute, const DmObjectId_t &objectId )
  292. {
  293. int i = m_ArrayAttributes.AddToTail();
  294. m_ArrayAttributes[i].m_bObjectId = true;
  295. m_ArrayAttributes[i].m_pAttribute = pAttribute;
  296. CopyUniqueId( objectId, &m_ArrayAttributes[i].m_ObjectId );
  297. }
  298. //-----------------------------------------------------------------------------
  299. // Finds an element into the table
  300. //-----------------------------------------------------------------------------
  301. DmxElementDictHandle_t CDmxElementDictionary::FindElement( CDmxElement *pElement )
  302. {
  303. int nCount = m_Dict.Count();
  304. for ( int i = 0; i < nCount; ++i )
  305. {
  306. if ( pElement == m_Dict[i].m_pElement )
  307. return i;
  308. }
  309. return ELEMENT_DICT_HANDLE_INVALID;
  310. }
  311. //-----------------------------------------------------------------------------
  312. // Finds an element into the table
  313. //-----------------------------------------------------------------------------
  314. DmxElementDictHandle_t CDmxElementDictionary::FindElement( const DmObjectId_t &objectId )
  315. {
  316. int nCount = m_Dict.Count();
  317. for ( int i = 0; i < nCount; ++i )
  318. {
  319. if ( IsUniqueIdEqual( objectId, m_Dict[i].m_Id ) )
  320. return i;
  321. }
  322. return ELEMENT_DICT_HANDLE_INVALID;
  323. }
  324. //-----------------------------------------------------------------------------
  325. // Hook up all element references (which were unserialized as object ids)
  326. //-----------------------------------------------------------------------------
  327. void CDmxElementDictionary::HookUpElementAttributes()
  328. {
  329. int n = m_Attributes.Count();
  330. for ( int i = 0; i < n; ++i )
  331. {
  332. Assert( m_Attributes[i].m_bObjectId );
  333. DmxElementDictHandle_t hElement = FindElement( m_Attributes[i].m_ObjectId );
  334. CDmxElement *pElement = GetElement( hElement );
  335. m_Attributes[i].m_pAttribute->SetValue( pElement );
  336. }
  337. }
  338. //-----------------------------------------------------------------------------
  339. // Hook up all element array references
  340. //-----------------------------------------------------------------------------
  341. void CDmxElementDictionary::HookUpElementArrayAttributes()
  342. {
  343. int n = m_ArrayAttributes.Count();
  344. for ( int i = 0; i < n; ++i )
  345. {
  346. CUtlVector< CDmxElement* > &array = m_ArrayAttributes[i].m_pAttribute->GetArrayForEdit<CDmxElement*>();
  347. if ( !m_ArrayAttributes[i].m_bObjectId )
  348. {
  349. CDmxElement *pElement = GetElement( m_ArrayAttributes[i].m_hElement );
  350. array.AddToTail( pElement );
  351. }
  352. else
  353. {
  354. // search id->handle table (both loaded and unloaded) for id, and if not found, create a new handle, map it to the id and return it
  355. DmxElementDictHandle_t hElement = FindElement( m_ArrayAttributes[i].m_ObjectId );
  356. CDmxElement *pElement = GetElement( hElement );
  357. array.AddToTail( pElement );
  358. }
  359. }
  360. }
  361. //-----------------------------------------------------------------------------
  362. // Hook up all element references (which were unserialized as object ids)
  363. //-----------------------------------------------------------------------------
  364. void CDmxElementDictionary::HookUpElementReferences()
  365. {
  366. HookUpElementArrayAttributes();
  367. HookUpElementAttributes();
  368. }
  369. //-----------------------------------------------------------------------------
  370. // Unserialization class for Key Values 2
  371. //-----------------------------------------------------------------------------
  372. class CDmxSerializerKeyValues2
  373. {
  374. public:
  375. bool Unserialize( const char *pFileName, CUtlBuffer &buf, CDmxElement **ppRoot );
  376. bool Serialize( CUtlBuffer &buf, CDmxElement *pRoot, const char *pFileName );
  377. private:
  378. enum TokenType_t
  379. {
  380. TOKEN_INVALID = -1, // A bogus token
  381. TOKEN_OPEN_BRACE, // {
  382. TOKEN_CLOSE_BRACE, // }
  383. TOKEN_OPEN_BRACKET, // [
  384. TOKEN_CLOSE_BRACKET, // ]
  385. TOKEN_COMMA, // ,
  386. // TOKEN_STRING, // Any non-quoted string
  387. TOKEN_DELIMITED_STRING, // Any quoted string
  388. TOKEN_INCLUDE, // #include
  389. TOKEN_EOF, // End of buffer
  390. };
  391. // Methods related to unserialization
  392. void EatWhitespacesAndComments( CUtlBuffer &buf );
  393. TokenType_t ReadToken( CUtlBuffer &buf, CUtlBuffer &token );
  394. DmxElementDictHandle_t CreateDmxElement( const char *pElementType );
  395. bool UnserializeAttributeValueFromToken( CDmxAttribute *pAttribute, DmAttributeType_t type, CUtlBuffer &tokenBuf );
  396. bool UnserializeElementAttribute( CUtlBuffer &buf, DmxElementDictHandle_t hElement, const char *pAttributeName, const char *pElementType );
  397. bool UnserializeElementArrayAttribute( CUtlBuffer &buf, DmxElementDictHandle_t hElement, const char *pAttributeName );
  398. bool UnserializeArrayAttribute( CUtlBuffer &buf, DmxElementDictHandle_t hElement, const char *pAttributeName, DmAttributeType_t nAttrType );
  399. bool UnserializeAttribute( CUtlBuffer &buf, DmxElementDictHandle_t hElement, const char *pAttributeName, DmAttributeType_t nAttrType );
  400. bool UnserializeId( CUtlBuffer &buf, DmxElementDictHandle_t hElement );
  401. bool UnserializeElement( CUtlBuffer &buf, const char *pElementType, DmxElementDictHandle_t *pHandle );
  402. bool UnserializeElement( CUtlBuffer &buf, DmxElementDictHandle_t *pHandle );
  403. // Methods related to serialization
  404. void SerializeArrayAttribute( CUtlBuffer& buf, CDmxAttribute *pAttribute );
  405. void SerializeElementAttribute( CUtlBuffer& buf, CDmxSerializationDictionary &dict, CDmxAttribute *pAttribute );
  406. void SerializeElementArrayAttribute( CUtlBuffer& buf, CDmxSerializationDictionary &dict, CDmxAttribute *pAttribute );
  407. bool SerializeAttributes( CUtlBuffer& buf, CDmxSerializationDictionary &dict, CDmxElement *pElement );
  408. bool SaveElement( CUtlBuffer& buf, CDmxSerializationDictionary &dict, CDmxElement *pElement, bool bWriteDelimiters = true );
  409. // For unserialization
  410. CDmxElementDictionary m_ElementDict;
  411. DmxElementDictHandle_t m_hRoot;
  412. };
  413. //-----------------------------------------------------------------------------
  414. // Serializes a single element attribute
  415. //-----------------------------------------------------------------------------
  416. void CDmxSerializerKeyValues2::SerializeElementAttribute( CUtlBuffer& buf, CDmxSerializationDictionary &dict, CDmxAttribute *pAttribute )
  417. {
  418. CDmxElement *pElement = pAttribute->GetValue< CDmxElement* >();
  419. if ( dict.ShouldInlineElement( pElement ) )
  420. {
  421. buf.Printf( "\"%s\"\n{\n", pElement->GetTypeString() );
  422. if ( pElement )
  423. {
  424. SaveElement( buf, dict, pElement, false );
  425. }
  426. buf.Printf( "}\n" );
  427. }
  428. else
  429. {
  430. buf.Printf( "\"%s\" \"", CDmxAttribute::s_pAttributeTypeName[ AT_ELEMENT ] );
  431. if ( pElement )
  432. {
  433. ::Serialize( buf, pElement->GetId() );
  434. }
  435. buf.PutChar( '\"' );
  436. }
  437. }
  438. //-----------------------------------------------------------------------------
  439. // Serializes an array element attribute
  440. //-----------------------------------------------------------------------------
  441. void CDmxSerializerKeyValues2::SerializeElementArrayAttribute( CUtlBuffer& buf, CDmxSerializationDictionary &dict, CDmxAttribute *pAttribute )
  442. {
  443. const CUtlVector<CDmxElement*> &array = pAttribute->GetArray< CDmxElement* >();
  444. buf.Printf( "\n[\n" );
  445. buf.PushTab();
  446. int nCount = array.Count();
  447. for ( int i = 0; i < nCount; ++i )
  448. {
  449. CDmxElement *pElement = array[i];
  450. if ( dict.ShouldInlineElement( pElement ) )
  451. {
  452. buf.Printf( "\"%s\"\n{\n", pElement->GetTypeString() );
  453. if ( pElement )
  454. {
  455. SaveElement( buf, dict, pElement, false );
  456. }
  457. buf.PutChar( '}' );
  458. }
  459. else
  460. {
  461. const char *pAttributeType = CDmxAttribute::s_pAttributeTypeName[ AT_ELEMENT ];
  462. buf.Printf( "\"%s\" \"", pAttributeType );
  463. if ( pElement )
  464. {
  465. ::Serialize( buf, pElement->GetId() );
  466. }
  467. buf.PutChar( '\"' );
  468. }
  469. if ( i != nCount - 1 )
  470. {
  471. buf.PutChar( ',' );
  472. }
  473. buf.PutChar( '\n' );
  474. }
  475. buf.PopTab();
  476. buf.Printf( "]" );
  477. }
  478. //-----------------------------------------------------------------------------
  479. // Serializes array attributes
  480. //-----------------------------------------------------------------------------
  481. void CDmxSerializerKeyValues2::SerializeArrayAttribute( CUtlBuffer& buf, CDmxAttribute *pAttribute )
  482. {
  483. int nCount = pAttribute->GetArrayCount();
  484. buf.PutString( "\n[\n" );
  485. buf.PushTab();
  486. for ( int i = 0; i < nCount; ++i )
  487. {
  488. if ( pAttribute->GetType() != AT_STRING_ARRAY )
  489. {
  490. buf.PutChar( '\"' );
  491. buf.PushTab();
  492. }
  493. pAttribute->SerializeElement( i, buf );
  494. if ( pAttribute->GetType() != AT_STRING_ARRAY )
  495. {
  496. buf.PopTab();
  497. buf.PutChar( '\"' );
  498. }
  499. if ( i != nCount - 1 )
  500. {
  501. buf.PutChar( ',' );
  502. }
  503. buf.PutChar( '\n' );
  504. }
  505. buf.PopTab();
  506. buf.PutChar( ']' );
  507. }
  508. //-----------------------------------------------------------------------------
  509. // Serializes all attributes in an element
  510. //-----------------------------------------------------------------------------
  511. static int SortAttributeByName(const void *p1, const void *p2 )
  512. {
  513. const CDmxAttribute **ppAtt1 = (const CDmxAttribute**)p1;
  514. const CDmxAttribute **ppAtt2 = (const CDmxAttribute**)p2;
  515. const char *pAttName1 = (*ppAtt1)->GetName();
  516. const char *pAttName2 = (*ppAtt2)->GetName();
  517. return Q_stricmp( pAttName1, pAttName2 );
  518. }
  519. bool CDmxSerializerKeyValues2::SerializeAttributes( CUtlBuffer& buf, CDmxSerializationDictionary &dict, CDmxElement *pElement )
  520. {
  521. int nCount = pElement->AttributeCount();
  522. CDmxAttribute **ppAttributes = (CDmxAttribute**)stackalloc( nCount * sizeof(CDmxAttribute*) );
  523. for ( int i = 0; i < nCount; ++i )
  524. {
  525. ppAttributes[i] = pElement->GetAttribute( i );
  526. }
  527. // Sort by name
  528. qsort( ppAttributes, nCount, sizeof(CDmxAttribute*), SortAttributeByName );
  529. for ( int i = 0; i < nCount; ++i )
  530. {
  531. CDmxAttribute *pAttribute = ppAttributes[ i ];
  532. const char *pName = pAttribute->GetName( );
  533. DmAttributeType_t nAttrType = pAttribute->GetType();
  534. if ( nAttrType != AT_ELEMENT )
  535. {
  536. buf.Printf( "\"%s\" \"%s\" ", pName, CDmxAttribute::s_pAttributeTypeName[ nAttrType ] );
  537. }
  538. else
  539. {
  540. // Elements either serialize their type name or "element" depending on whether they are inlined
  541. buf.Printf( "\"%s\" ", pName );
  542. }
  543. switch( nAttrType )
  544. {
  545. default:
  546. if ( nAttrType >= AT_FIRST_ARRAY_TYPE )
  547. {
  548. SerializeArrayAttribute( buf, pAttribute );
  549. }
  550. else
  551. {
  552. if ( pAttribute->SerializesOnMultipleLines() )
  553. {
  554. buf.PutChar( '\n' );
  555. }
  556. buf.PutChar( '\"' );
  557. buf.PushTab();
  558. pAttribute->Serialize( buf );
  559. buf.PopTab();
  560. buf.PutChar( '\"' );
  561. }
  562. break;
  563. case AT_STRING:
  564. // Don't explicitly add string delimiters; serialization does that.
  565. pAttribute->Serialize( buf );
  566. break;
  567. case AT_ELEMENT:
  568. SerializeElementAttribute( buf, dict, pAttribute );
  569. break;
  570. case AT_ELEMENT_ARRAY:
  571. SerializeElementArrayAttribute( buf, dict, pAttribute );
  572. break;
  573. }
  574. buf.PutChar( '\n' );
  575. }
  576. return true;
  577. }
  578. bool CDmxSerializerKeyValues2::SaveElement( CUtlBuffer& buf, CDmxSerializationDictionary &dict, CDmxElement *pElement, bool bWriteDelimiters )
  579. {
  580. if ( bWriteDelimiters )
  581. {
  582. buf.Printf( "\"%s\"\n{\n", pElement->GetTypeString() );
  583. }
  584. buf.PushTab();
  585. // explicitly serialize id, now that it's no longer an attribute
  586. buf.PutString( "\"id\" \"elementid\" " );
  587. buf.PutChar( '\"' );
  588. ::Serialize( buf, pElement->GetId() );
  589. buf.PutString( "\"\n" );
  590. SerializeAttributes( buf, dict, pElement );
  591. buf.PopTab();
  592. if ( bWriteDelimiters )
  593. {
  594. buf.Printf( "}\n" );
  595. }
  596. return true;
  597. }
  598. bool CDmxSerializerKeyValues2::Serialize( CUtlBuffer &outBuf, CDmxElement *pRoot, const char *pFormatName )
  599. {
  600. SetSerializationDelimiter( GetCStringCharConversion() );
  601. SetSerializationArrayDelimiter( "," );
  602. bool bFlatMode = !Q_stricmp( pFormatName, "keyvalues2_flat" );
  603. // Save elements, attribute links
  604. CDmxSerializationDictionary dict;
  605. dict.BuildElementList( pRoot, bFlatMode );
  606. // Save elements to buffer
  607. DmxSerializationHandle_t i;
  608. for ( i = dict.FirstRootElement(); i != ELEMENT_DICT_HANDLE_INVALID; i = dict.NextRootElement(i) )
  609. {
  610. SaveElement( outBuf, dict, dict.GetRootElement( i ) );
  611. outBuf.PutChar( '\n' );
  612. }
  613. SetSerializationDelimiter( NULL );
  614. SetSerializationArrayDelimiter( NULL );
  615. return true;
  616. }
  617. //-----------------------------------------------------------------------------
  618. // Eats whitespaces and c++ style comments
  619. //-----------------------------------------------------------------------------
  620. #pragma warning (disable:4706)
  621. void CDmxSerializerKeyValues2::EatWhitespacesAndComments( CUtlBuffer &buf )
  622. {
  623. // eating white spaces and remarks loop
  624. int nMaxPut = buf.TellMaxPut() - buf.TellGet();
  625. int nOffset = 0;
  626. while ( nOffset < nMaxPut )
  627. {
  628. // Eat whitespaces, keep track of line count
  629. const char *pPeek = NULL;
  630. while ( pPeek = (const char *)buf.PeekGet( sizeof(char), nOffset ) )
  631. {
  632. if ( !V_isspace( *pPeek ) )
  633. break;
  634. if ( *pPeek == '\n' )
  635. {
  636. g_KeyValues2ErrorStack.IncrementCurrentLine();
  637. }
  638. if ( ++nOffset >= nMaxPut )
  639. break;
  640. }
  641. // If we don't have a a c++ style comment next, we're done
  642. pPeek = (const char *)buf.PeekGet( 2 * sizeof(char), nOffset );
  643. if ( ( nOffset >= nMaxPut ) || !pPeek || ( pPeek[0] != '/' ) || ( pPeek[1] != '/' ) )
  644. break;
  645. // Deal with c++ style comments
  646. nOffset += 2;
  647. // read complete line
  648. while ( pPeek = (const char *)buf.PeekGet( sizeof(char), nOffset ) )
  649. {
  650. if ( *pPeek == '\n' )
  651. break;
  652. if ( ++nOffset >= nMaxPut )
  653. break;
  654. }
  655. g_KeyValues2ErrorStack.IncrementCurrentLine();
  656. }
  657. buf.SeekGet( CUtlBuffer::SEEK_CURRENT, nOffset );
  658. }
  659. #pragma warning (default:4706)
  660. //-----------------------------------------------------------------------------
  661. // Reads a single token, points the token utlbuffer at it
  662. //-----------------------------------------------------------------------------
  663. CDmxSerializerKeyValues2::TokenType_t CDmxSerializerKeyValues2::ReadToken( CUtlBuffer &buf, CUtlBuffer &token )
  664. {
  665. EatWhitespacesAndComments( buf );
  666. // if message text buffers go over this size
  667. // change this value to make sure they will fit
  668. // affects loading of last active chat window
  669. if ( !buf.IsValid() || ( buf.TellGet() == buf.TellMaxPut() ) )
  670. return TOKEN_EOF;
  671. // Compute token length and type
  672. int nLength = 0;
  673. TokenType_t t = TOKEN_INVALID;
  674. char c = *((const char *)buf.PeekGet());
  675. switch( c )
  676. {
  677. case '{':
  678. nLength = 1;
  679. t = TOKEN_OPEN_BRACE;
  680. break;
  681. case '}':
  682. nLength = 1;
  683. t = TOKEN_CLOSE_BRACE;
  684. break;
  685. case '[':
  686. nLength = 1;
  687. t = TOKEN_OPEN_BRACKET;
  688. break;
  689. case ']':
  690. nLength = 1;
  691. t = TOKEN_CLOSE_BRACKET;
  692. break;
  693. case ',':
  694. nLength = 1;
  695. t = TOKEN_COMMA;
  696. break;
  697. case '\"':
  698. // NOTE: The -1 is because peek includes room for the /0
  699. nLength = buf.PeekDelimitedStringLength( GetCStringCharConversion(), false ) - 1;
  700. if ( (nLength <= 1) || ( *(const char *)buf.PeekGet( nLength - 1 ) != '\"' ))
  701. {
  702. g_KeyValues2ErrorStack.ReportError( "Unexpected EOF in quoted string" );
  703. t = TOKEN_INVALID;
  704. }
  705. else
  706. {
  707. t = TOKEN_DELIMITED_STRING;
  708. }
  709. break;
  710. default:
  711. t = TOKEN_INVALID;
  712. break;
  713. }
  714. // Point the token buffer to the token + update the original buffer get index
  715. token.SetExternalBuffer( (void*)buf.PeekGet(), nLength, nLength, CUtlBuffer::TEXT_BUFFER | CUtlBuffer::READ_ONLY );
  716. buf.SeekGet( CUtlBuffer::SEEK_CURRENT, nLength );
  717. // Count the number of crs in the token + update the current line
  718. const char *pMem = (const char *)token.Base();
  719. for ( int i = 0; i < nLength; ++i )
  720. {
  721. if ( pMem[i] == '\n' )
  722. {
  723. g_KeyValues2ErrorStack.IncrementCurrentLine();
  724. }
  725. }
  726. return t;
  727. }
  728. //-----------------------------------------------------------------------------
  729. // Creates a scene object, adds it to the element dictionary
  730. //-----------------------------------------------------------------------------
  731. DmxElementDictHandle_t CDmxSerializerKeyValues2::CreateDmxElement( const char *pElementType )
  732. {
  733. // See if we can create an element of that type
  734. CDmxElement *pElement = new CDmxElement( pElementType );
  735. return m_ElementDict.InsertElement( pElement );
  736. }
  737. //-----------------------------------------------------------------------------
  738. // Reads an attribute for an element
  739. //-----------------------------------------------------------------------------
  740. bool CDmxSerializerKeyValues2::UnserializeElementAttribute( CUtlBuffer &buf, DmxElementDictHandle_t hElement, const char *pAttributeName, const char *pElementType )
  741. {
  742. CDmxElement *pElement = m_ElementDict.GetElement( hElement );
  743. if ( pElement->HasAttribute( pAttributeName ) )
  744. {
  745. g_KeyValues2ErrorStack.ReportError( "Attribute \"%s\" was defined more than once.\n", pAttributeName );
  746. return false;
  747. }
  748. CDmxAttribute *pAttribute;
  749. {
  750. CDmxElementModifyScope modify( pElement );
  751. pAttribute = pElement->AddAttribute( pAttributeName );
  752. }
  753. DmxElementDictHandle_t h;
  754. bool bOk = UnserializeElement( buf, pElementType, &h );
  755. if ( bOk )
  756. {
  757. CDmxElement *pNewElement = m_ElementDict.GetElement( h );
  758. pAttribute->SetValue( pNewElement );
  759. }
  760. return bOk;
  761. }
  762. //-----------------------------------------------------------------------------
  763. // Reads an attribute for an element array
  764. //-----------------------------------------------------------------------------
  765. bool CDmxSerializerKeyValues2::UnserializeElementArrayAttribute( CUtlBuffer &buf, DmxElementDictHandle_t hElement, const char *pAttributeName )
  766. {
  767. CDmxElement *pElement = m_ElementDict.GetElement( hElement );
  768. if ( pElement->HasAttribute( pAttributeName ) )
  769. {
  770. g_KeyValues2ErrorStack.ReportError( "Attribute \"%s\" was defined more than once.\n", pAttributeName );
  771. return false;
  772. }
  773. CDmxAttribute *pAttribute;
  774. {
  775. CDmxElementModifyScope modify( pElement );
  776. pAttribute = pElement->AddAttribute( pAttributeName );
  777. // NOTE: This allocates an empty array and sets the attribute type appropriately
  778. // for use when there's an empty array
  779. pAttribute->GetArrayForEdit<CDmxElement*>();
  780. }
  781. // Arrays first must have a '[' specified
  782. TokenType_t token;
  783. CUtlBuffer tokenBuf;
  784. CUtlCharConversion *pConv = GetCStringCharConversion();
  785. token = ReadToken( buf, tokenBuf );
  786. if ( token != TOKEN_OPEN_BRACKET )
  787. {
  788. g_KeyValues2ErrorStack.ReportError( "Expecting '[', didn't find it!" );
  789. return false;
  790. }
  791. int nElementIndex = 0;
  792. // Now read a list of array values, separated by commas
  793. while ( buf.IsValid() )
  794. {
  795. token = ReadToken( buf, tokenBuf );
  796. if ( token == TOKEN_INVALID || token == TOKEN_EOF )
  797. {
  798. g_KeyValues2ErrorStack.ReportError( "Expecting ']', didn't find it!" );
  799. return false;
  800. }
  801. // Then, keep reading until we hit a ']'
  802. if ( token == TOKEN_CLOSE_BRACKET )
  803. break;
  804. // If we've already read in an array value, we need to read a comma next
  805. if ( nElementIndex > 0 )
  806. {
  807. if ( token != TOKEN_COMMA )
  808. {
  809. g_KeyValues2ErrorStack.ReportError( "Expecting ',', didn't find it!" );
  810. return false;
  811. }
  812. // Read in the next thing, which should be a value
  813. token = ReadToken( buf, tokenBuf );
  814. }
  815. // Ok, we must be reading an array type value
  816. if ( token != TOKEN_DELIMITED_STRING )
  817. {
  818. g_KeyValues2ErrorStack.ReportError( "Expecting element type, didn't find it!" );
  819. return false;
  820. }
  821. // Get the element type out
  822. char elementType[ 256 ];
  823. tokenBuf.GetDelimitedString( pConv, elementType, sizeof( elementType ) );
  824. // Use the element type to figure out if we're using a element reference or an inlined element
  825. if ( !V_strcmp( elementType, CDmxAttribute::s_pAttributeTypeName[AT_ELEMENT] ) )
  826. {
  827. token = ReadToken( buf, tokenBuf );
  828. // Ok, we must be reading an array type value
  829. if ( token != TOKEN_DELIMITED_STRING )
  830. {
  831. g_KeyValues2ErrorStack.ReportError( "Expecting element reference, didn't find it!" );
  832. return false;
  833. }
  834. // Get the element type out
  835. char elementId[ 256 ];
  836. tokenBuf.GetDelimitedString( pConv, elementId, sizeof( elementId ) );
  837. DmObjectId_t id;
  838. if ( !UniqueIdFromString( &id, elementId ) )
  839. {
  840. g_KeyValues2ErrorStack.ReportError( "Encountered invalid element ID data!" );
  841. return false;
  842. }
  843. Assert( IsUniqueIdValid( id ) );
  844. m_ElementDict.AddArrayAttribute( pAttribute, id );
  845. }
  846. else
  847. {
  848. DmxElementDictHandle_t hArrayElement;
  849. bool bOk = UnserializeElement( buf, elementType, &hArrayElement );
  850. if ( !bOk )
  851. return false;
  852. m_ElementDict.AddArrayAttribute( pAttribute, hArrayElement );
  853. }
  854. // Ok, we've read in another value
  855. ++nElementIndex;
  856. }
  857. return true;
  858. }
  859. //-----------------------------------------------------------------------------
  860. // Unserializes an attribute from a token buffer
  861. //-----------------------------------------------------------------------------
  862. bool CDmxSerializerKeyValues2::UnserializeAttributeValueFromToken( CDmxAttribute *pAttribute, DmAttributeType_t type, CUtlBuffer &tokenBuf )
  863. {
  864. // NOTE: This code is necessary because the attribute code is using Scanf
  865. // which is not really friendly toward delimiters, so we must pass in
  866. // non-delimited buffers. Sucky. There must be a better way of doing this
  867. const char *pBuf = (const char*)tokenBuf.Base();
  868. int nLength = tokenBuf.TellMaxPut();
  869. char *pTemp = (char*)stackalloc( nLength + 1 );
  870. bool bIsString = ( type == AT_STRING ) || ( type == AT_STRING_ARRAY );
  871. if ( !bIsString )
  872. {
  873. nLength = tokenBuf.PeekDelimitedStringLength( GetCStringCharConversion() );
  874. tokenBuf.GetDelimitedString( GetCStringCharConversion(), pTemp, nLength + 1 );
  875. pBuf = pTemp;
  876. }
  877. else
  878. {
  879. SetSerializationDelimiter( GetCStringCharConversion() );
  880. }
  881. bool bOk;
  882. CUtlBuffer buf( pBuf, nLength, CUtlBuffer::TEXT_BUFFER | CUtlBuffer::READ_ONLY );
  883. if ( type < AT_FIRST_ARRAY_TYPE )
  884. {
  885. bOk = pAttribute->Unserialize( type, buf );
  886. }
  887. else
  888. {
  889. bOk = pAttribute->UnserializeElement( type, buf );
  890. }
  891. if ( bIsString )
  892. {
  893. SetSerializationDelimiter( NULL );
  894. }
  895. return bOk;
  896. }
  897. //-----------------------------------------------------------------------------
  898. // Reads an attribute for an element array
  899. //-----------------------------------------------------------------------------
  900. bool CDmxSerializerKeyValues2::UnserializeArrayAttribute( CUtlBuffer &buf, DmxElementDictHandle_t hElement, const char *pAttributeName, DmAttributeType_t nAttrType )
  901. {
  902. CDmxElement *pElement = m_ElementDict.GetElement( hElement );
  903. if ( pElement->HasAttribute( pAttributeName ) )
  904. {
  905. g_KeyValues2ErrorStack.ReportError( "Encountered duplicate attribute definition for attribute \"%s\"!", pAttributeName );
  906. return false;
  907. }
  908. CDmxAttribute *pAttribute;
  909. {
  910. CDmxElementModifyScope modify( pElement );
  911. pAttribute = pElement->AddAttribute( pAttributeName );
  912. }
  913. // Arrays first must have a '[' specified
  914. TokenType_t token;
  915. CUtlBuffer tokenBuf;
  916. token = ReadToken( buf, tokenBuf );
  917. if ( token != TOKEN_OPEN_BRACKET )
  918. {
  919. g_KeyValues2ErrorStack.ReportError( "Expecting '[', didn't find it!" );
  920. return false;
  921. }
  922. int nElementIndex = 0;
  923. // Now read a list of array values, separated by commas
  924. while ( buf.IsValid() )
  925. {
  926. token = ReadToken( buf, tokenBuf );
  927. if ( token == TOKEN_INVALID || token == TOKEN_EOF )
  928. {
  929. g_KeyValues2ErrorStack.ReportError( "Expecting ']', didn't find it!" );
  930. return false;
  931. }
  932. // Then, keep reading until we hit a ']'
  933. if ( token == TOKEN_CLOSE_BRACKET )
  934. break;
  935. // If we've already read in an array value, we need to read a comma next
  936. if ( nElementIndex > 0 )
  937. {
  938. if ( token != TOKEN_COMMA )
  939. {
  940. g_KeyValues2ErrorStack.ReportError( "Expecting ',', didn't find it!" );
  941. return false;
  942. }
  943. // Read in the next thing, which should be a value
  944. token = ReadToken( buf, tokenBuf );
  945. }
  946. // Ok, we must be reading an attributearray value
  947. if ( token != TOKEN_DELIMITED_STRING )
  948. {
  949. g_KeyValues2ErrorStack.ReportError( "Expecting array attribute value, didn't find it!" );
  950. return false;
  951. }
  952. if ( !UnserializeAttributeValueFromToken( pAttribute, nAttrType, tokenBuf ) )
  953. {
  954. g_KeyValues2ErrorStack.ReportError("Error reading in array attribute \"%s\" element %d", pAttributeName, nElementIndex );
  955. return false;
  956. }
  957. // Ok, we've read in another value
  958. ++nElementIndex;
  959. }
  960. return true;
  961. }
  962. //-----------------------------------------------------------------------------
  963. // Reads an attribute for an element
  964. //-----------------------------------------------------------------------------
  965. bool CDmxSerializerKeyValues2::UnserializeAttribute( CUtlBuffer &buf,
  966. DmxElementDictHandle_t hElement, const char *pAttributeName, DmAttributeType_t nAttrType )
  967. {
  968. // Read the attribute value
  969. CUtlBuffer tokenBuf;
  970. TokenType_t token = ReadToken( buf, tokenBuf );
  971. if ( token != TOKEN_DELIMITED_STRING )
  972. {
  973. g_KeyValues2ErrorStack.ReportError( "Expecting quoted attribute value for attribute \"%s\", didn't find one!", pAttributeName );
  974. return false;
  975. }
  976. CDmxElement *pElement = m_ElementDict.GetElement( hElement );
  977. if ( pElement->HasAttribute( pAttributeName ) )
  978. {
  979. g_KeyValues2ErrorStack.ReportError( "Encountered duplicate attribute definition for attribute \"%s\"!", pAttributeName );
  980. return false;
  981. }
  982. CDmxAttribute *pAttribute;
  983. {
  984. CDmxElementModifyScope modify( pElement );
  985. pAttribute = pElement->AddAttribute( pAttributeName );
  986. }
  987. switch( nAttrType )
  988. {
  989. case AT_ELEMENT:
  990. {
  991. // Get the attribute value out
  992. CUtlCharConversion *pConv = GetCStringCharConversion();
  993. int nLength = tokenBuf.PeekDelimitedStringLength( pConv );
  994. char *pAttributeValue = (char*)stackalloc( nLength * sizeof(char) );
  995. tokenBuf.GetDelimitedString( pConv, pAttributeValue, nLength );
  996. // No string? that's ok, it means we have a NULL pointer
  997. if ( !pAttributeValue[0] )
  998. return true;
  999. DmObjectId_t id;
  1000. if ( !UniqueIdFromString( &id, pAttributeValue ) )
  1001. {
  1002. g_KeyValues2ErrorStack.ReportError("Invalid format for element ID encountered for attribute \"%s\"", pAttributeName );
  1003. return false;
  1004. }
  1005. m_ElementDict.AddAttribute( pAttribute, id );
  1006. }
  1007. return true;
  1008. default:
  1009. if ( UnserializeAttributeValueFromToken( pAttribute, nAttrType, tokenBuf ) )
  1010. return true;
  1011. g_KeyValues2ErrorStack.ReportError("Error reading attribute \"%s\"", pAttributeName );
  1012. return false;
  1013. }
  1014. }
  1015. bool CDmxSerializerKeyValues2::UnserializeId( CUtlBuffer &buf, DmxElementDictHandle_t hElement )
  1016. {
  1017. CUtlBuffer tokenBuf;
  1018. TokenType_t token = ReadToken( buf, tokenBuf );
  1019. if ( token != TOKEN_DELIMITED_STRING )
  1020. {
  1021. g_KeyValues2ErrorStack.ReportError( "Expecting quoted value for element ID, didn't find one!" );
  1022. return false;
  1023. }
  1024. CUtlCharConversion *pConv = GetCStringCharConversion();
  1025. int nLength = tokenBuf.PeekDelimitedStringLength( pConv );
  1026. char *pElementId = (char*)stackalloc( nLength * sizeof(char) );
  1027. tokenBuf.GetDelimitedString( pConv, pElementId, nLength );
  1028. DmObjectId_t id;
  1029. if ( !UniqueIdFromString( &id, pElementId ) )
  1030. {
  1031. g_KeyValues2ErrorStack.ReportError( "Encountered invalid element ID data!" );
  1032. return false;
  1033. }
  1034. CDmxElement *pElement = m_ElementDict.GetElement( hElement );
  1035. m_ElementDict.SetElementId( hElement, id );
  1036. pElement->SetId( id );
  1037. return true;
  1038. }
  1039. //-----------------------------------------------------------------------------
  1040. // Unserializes a single element given the type name
  1041. //-----------------------------------------------------------------------------
  1042. bool CDmxSerializerKeyValues2::UnserializeElement( CUtlBuffer &buf, const char *pElementType, DmxElementDictHandle_t *pHandle )
  1043. {
  1044. *pHandle = ELEMENT_DICT_HANDLE_INVALID;
  1045. // Create the element
  1046. DmxElementDictHandle_t hElement = CreateDmxElement( pElementType );
  1047. // Report errors relative to this type name
  1048. CKeyValues2ErrorContext errorReport( pElementType );
  1049. TokenType_t token;
  1050. CUtlBuffer tokenBuf;
  1051. CUtlCharConversion *pConv = GetCStringCharConversion();
  1052. // Then we expect a '{'
  1053. token = ReadToken( buf, tokenBuf );
  1054. if ( token != TOKEN_OPEN_BRACE )
  1055. {
  1056. g_KeyValues2ErrorStack.ReportError( "Expecting '{', didn't find it!" );
  1057. return false;
  1058. }
  1059. while ( buf.IsValid() )
  1060. {
  1061. token = ReadToken( buf, tokenBuf );
  1062. if ( token == TOKEN_INVALID || token == TOKEN_EOF )
  1063. {
  1064. g_KeyValues2ErrorStack.ReportError( "Expecting '}', didn't find it!" );
  1065. return false;
  1066. }
  1067. // Then, keep reading until we hit a '}'
  1068. if ( token == TOKEN_CLOSE_BRACE )
  1069. break;
  1070. // Ok, we must be reading an attribute
  1071. if ( token != TOKEN_DELIMITED_STRING )
  1072. {
  1073. g_KeyValues2ErrorStack.ReportError( "Expecting attribute name, didn't find it!" );
  1074. return false;
  1075. }
  1076. // First, read an attribute name
  1077. char attributeName[ 256 ];
  1078. tokenBuf.GetDelimitedString( pConv, attributeName, sizeof( attributeName ) );
  1079. // Next, read an attribute type
  1080. token = ReadToken( buf, tokenBuf );
  1081. if ( token != TOKEN_DELIMITED_STRING )
  1082. {
  1083. g_KeyValues2ErrorStack.ReportError( "Expecting attribute type for attribute %s, didn't find it!", attributeName );
  1084. return false;
  1085. }
  1086. char attributeType[ 256 ];
  1087. tokenBuf.GetDelimitedString( pConv, attributeType, sizeof( attributeType ) );
  1088. if ( !Q_stricmp( "elementid", attributeType ) )
  1089. {
  1090. if ( Q_stricmp( "id", attributeName ) != 0 )
  1091. return false; // elementid is no longer a valid attribute type - only the id should be of this type
  1092. if ( !UnserializeId( buf, hElement ) )
  1093. return false;
  1094. continue;
  1095. }
  1096. DmAttributeType_t nAttrType = AT_UNKNOWN;
  1097. for ( int i = 0; i < AT_TYPE_COUNT; ++i )
  1098. {
  1099. if ( !Q_stricmp( CDmxAttribute::s_pAttributeTypeName[i], attributeType ) )
  1100. {
  1101. nAttrType = (DmAttributeType_t)i;
  1102. break;
  1103. }
  1104. }
  1105. // Next, read an attribute value
  1106. bool bOk = true;
  1107. switch( nAttrType )
  1108. {
  1109. case AT_UNKNOWN:
  1110. bOk = UnserializeElementAttribute( buf, hElement, attributeName, attributeType );
  1111. break;
  1112. case AT_ELEMENT_ARRAY:
  1113. bOk = UnserializeElementArrayAttribute( buf, hElement, attributeName );
  1114. break;
  1115. default:
  1116. if ( nAttrType >= AT_FIRST_ARRAY_TYPE )
  1117. {
  1118. bOk = UnserializeArrayAttribute( buf, hElement, attributeName, nAttrType );
  1119. }
  1120. else
  1121. {
  1122. bOk = UnserializeAttribute( buf, hElement, attributeName, nAttrType );
  1123. }
  1124. break;
  1125. }
  1126. if ( !bOk )
  1127. return false;
  1128. }
  1129. *pHandle = hElement;
  1130. return true;
  1131. }
  1132. //-----------------------------------------------------------------------------
  1133. // Unserializes a single element
  1134. //-----------------------------------------------------------------------------
  1135. bool CDmxSerializerKeyValues2::UnserializeElement( CUtlBuffer &buf, DmxElementDictHandle_t *pHandle )
  1136. {
  1137. *pHandle = ELEMENT_DICT_HANDLE_INVALID;
  1138. // First, read the type name
  1139. CUtlBuffer tokenBuf;
  1140. TokenType_t token = ReadToken( buf, tokenBuf );
  1141. if ( token == TOKEN_INVALID )
  1142. return false;
  1143. if ( token == TOKEN_EOF )
  1144. return true;
  1145. // Get the type name out
  1146. if ( token != TOKEN_DELIMITED_STRING )
  1147. {
  1148. g_KeyValues2ErrorStack.ReportError( "Expecting element type name, didn't find it!" );
  1149. return false;
  1150. }
  1151. CUtlCharConversion* pConv = GetCStringCharConversion();
  1152. int nLength = tokenBuf.PeekDelimitedStringLength( pConv );
  1153. char *pTypeName = (char*)stackalloc( nLength * sizeof(char) );
  1154. tokenBuf.GetDelimitedString( pConv, pTypeName, nLength );
  1155. return UnserializeElement( buf, pTypeName, pHandle );
  1156. }
  1157. //-----------------------------------------------------------------------------
  1158. // Main entry point for the unserialization
  1159. //-----------------------------------------------------------------------------
  1160. bool CDmxSerializerKeyValues2::Unserialize( const char *pFileName, CUtlBuffer &buf, CDmxElement **ppRoot )
  1161. {
  1162. *ppRoot = NULL;
  1163. g_KeyValues2ErrorStack.SetFilename( pFileName );
  1164. m_hRoot = ELEMENT_DICT_HANDLE_INVALID;
  1165. m_ElementDict.Clear();
  1166. bool bOk = true;
  1167. while ( buf.IsValid() )
  1168. {
  1169. DmxElementDictHandle_t h;
  1170. bOk = UnserializeElement( buf, &h );
  1171. if ( !bOk || ( h == ELEMENT_DICT_HANDLE_INVALID ) )
  1172. break;
  1173. if ( m_hRoot == ELEMENT_DICT_HANDLE_INVALID )
  1174. {
  1175. m_hRoot = h;
  1176. }
  1177. }
  1178. // do this *before* getting the root, since the first element might be deleted due to id conflicts
  1179. m_ElementDict.HookUpElementReferences();
  1180. *ppRoot = m_ElementDict.GetElement( m_hRoot );
  1181. m_ElementDict.Clear();
  1182. if ( !bOk )
  1183. {
  1184. CleanupDMX( *ppRoot );
  1185. *ppRoot = NULL;
  1186. }
  1187. return bOk;
  1188. }
  1189. //-----------------------------------------------------------------------------
  1190. // Unserialization entry point for text files (assumes version has been stripped)
  1191. //-----------------------------------------------------------------------------
  1192. bool UnserializeTextDMX( const char *pFileName, CUtlBuffer &buf, CDmxElement **ppRoot )
  1193. {
  1194. CDmxSerializerKeyValues2 dmxUnserializer;
  1195. return dmxUnserializer.Unserialize( pFileName, buf, ppRoot );
  1196. }
  1197. bool SerializeTextDMX( const char *pFileName, CUtlBuffer &buf, CDmxElement *pRoot )
  1198. {
  1199. CDmxSerializerKeyValues2 dmxSerializer;
  1200. return dmxSerializer.Serialize( buf, pRoot, pFileName );
  1201. }