xml.cpp 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include "CrySystem_precompiled.h"
  9. #include <stdlib.h>
  10. #include <expat.h>
  11. #include "xml.h"
  12. #include <algorithm>
  13. #include <stdio.h>
  14. #include <AzCore/Serialization/Locale.h>
  15. #include <AzFramework/Archive/IArchive.h>
  16. #include <CryCommon/Cry_Color.h>
  17. #include "XMLBinaryReader.h"
  18. #define FLOAT_FMT "%.8g"
  19. #define DOUBLE_FMT "%.17g"
  20. #include "../SimpleStringPool.h"
  21. #include "System.h"
  22. // Global counter for memory allocated in XML string pools.
  23. size_t CSimpleStringPool::g_nTotalAllocInXmlStringPools = 0;
  24. //////////////////////////////////////////////////////////////////////////
  25. static int __cdecl ascii_stricmp(const char* dst, const char* src)
  26. {
  27. int f, l;
  28. do
  29. {
  30. if (((f = (unsigned char)(*(dst++))) >= 'A') && (f <= 'Z'))
  31. {
  32. f -= 'A' - 'a';
  33. }
  34. if (((l = (unsigned char)(*(src++))) >= 'A') && (l <= 'Z'))
  35. {
  36. l -= 'A' - 'a';
  37. }
  38. }
  39. while (f && (f == l));
  40. return(f - l);
  41. }
  42. //////////////////////////////////////////////////////////////////////////
  43. XmlStrCmpFunc g_pXmlStrCmp = &ascii_stricmp;
  44. //////////////////////////////////////////////////////////////////////////
  45. class CXmlStringData
  46. : public IXmlStringData
  47. {
  48. public:
  49. int m_nRefCount;
  50. XmlString m_string;
  51. CXmlStringData() { m_nRefCount = 0; }
  52. virtual void AddRef() { ++m_nRefCount; }
  53. virtual void Release()
  54. {
  55. if (--m_nRefCount <= 0)
  56. {
  57. delete this;
  58. }
  59. }
  60. virtual const char* GetString() { return m_string.c_str(); };
  61. virtual size_t GetStringLength() { return m_string.size(); };
  62. };
  63. //////////////////////////////////////////////////////////////////////////
  64. class CXmlStringPool
  65. : public IXmlStringPool
  66. {
  67. public:
  68. explicit CXmlStringPool(bool bReuseStrings)
  69. : m_stringPool(bReuseStrings) {}
  70. const char* AddString(const char* str) { return m_stringPool.Append(str, (int)strlen(str)); }
  71. void Clear() { m_stringPool.Clear(); }
  72. void SetBlockSize(unsigned int nBlockSize) { m_stringPool.SetBlockSize(nBlockSize); }
  73. private:
  74. CSimpleStringPool m_stringPool;
  75. };
  76. //xml_node_allocator XmlDynArrayAlloc::allocator;
  77. //size_t XmlDynArrayAlloc::m_iAllocated = 0;
  78. /**
  79. ******************************************************************************
  80. * CXmlNode implementation.
  81. ******************************************************************************
  82. */
  83. void CXmlNode::DeleteThis()
  84. {
  85. delete this;
  86. }
  87. CXmlNode::~CXmlNode()
  88. {
  89. m_nRefCount = 1; // removeAllChildsImpl can make an XmlNodeRef to this node whilst it is deleting the children
  90. // doing this will cause the ref count to be increment and decremented and cause delete to be called again,
  91. // leading to a recursion crash. upping the ref count here once destruction has started avoids this problem
  92. removeAllChildsImpl();
  93. SAFE_DELETE(m_pAttributes);
  94. m_pStringPool->Release();
  95. }
  96. CXmlNode::CXmlNode()
  97. : m_pStringPool(NULL) // must be changed later.
  98. , m_tag("")
  99. , m_content("")
  100. , m_parent(NULL)
  101. , m_pChilds(NULL)
  102. , m_pAttributes(NULL)
  103. , m_line(0)
  104. , m_isProcessingInstruction(false)
  105. {
  106. m_nRefCount = 0; //TODO: move initialization to IXmlNode constructor
  107. }
  108. CXmlNode::CXmlNode(const char* tag, bool bReuseStrings, bool bIsProcessingInstruction)
  109. : m_content("")
  110. , m_parent(NULL)
  111. , m_pChilds(NULL)
  112. , m_pAttributes(NULL)
  113. , m_line(0)
  114. , m_isProcessingInstruction(bIsProcessingInstruction)
  115. {
  116. m_nRefCount = 0; //TODO: move initialization to IXmlNode constructor
  117. m_pStringPool = new CXmlStringPool(bReuseStrings);
  118. m_pStringPool->AddRef();
  119. m_tag = m_pStringPool->AddString(tag);
  120. }
  121. //////////////////////////////////////////////////////////////////////////
  122. XmlNodeRef CXmlNode::createNode(const char* tag)
  123. {
  124. CXmlNode* pNewNode = new CXmlNode;
  125. pNewNode->m_pStringPool = m_pStringPool;
  126. m_pStringPool->AddRef();
  127. pNewNode->m_tag = m_pStringPool->AddString(tag);
  128. return XmlNodeRef(pNewNode);
  129. }
  130. //////////////////////////////////////////////////////////////////////////
  131. void CXmlNode::setTag(const char* tag)
  132. {
  133. m_tag = m_pStringPool->AddString(tag);
  134. }
  135. //////////////////////////////////////////////////////////////////////////
  136. void CXmlNode::setContent(const char* str)
  137. {
  138. m_content = m_pStringPool->AddString(str);
  139. }
  140. //////////////////////////////////////////////////////////////////////////
  141. bool CXmlNode::isTag(const char* tag) const
  142. {
  143. return g_pXmlStrCmp(tag, m_tag) == 0;
  144. }
  145. const char* CXmlNode::getAttr(const char* key) const
  146. {
  147. const char* svalue = GetValue(key);
  148. if (svalue)
  149. {
  150. return svalue;
  151. }
  152. return "";
  153. }
  154. bool CXmlNode::getAttr(const char* key, const char** value) const
  155. {
  156. const char* svalue = GetValue(key);
  157. if (svalue)
  158. {
  159. *value = svalue;
  160. return true;
  161. }
  162. else
  163. {
  164. *value = "";
  165. return false;
  166. }
  167. }
  168. bool CXmlNode::haveAttr(const char* key) const
  169. {
  170. if (m_pAttributes)
  171. {
  172. XmlAttrConstIter it = GetAttrConstIterator(key);
  173. if (it != m_pAttributes->end())
  174. {
  175. return true;
  176. }
  177. }
  178. return false;
  179. }
  180. void CXmlNode::delAttr(const char* key)
  181. {
  182. if (m_pAttributes)
  183. {
  184. XmlAttrIter it = GetAttrIterator(key);
  185. if (it != m_pAttributes->end())
  186. {
  187. m_pAttributes->erase(it);
  188. }
  189. }
  190. }
  191. void CXmlNode::removeAllAttributes()
  192. {
  193. if (m_pAttributes)
  194. {
  195. m_pAttributes->clear();
  196. SAFE_DELETE(m_pAttributes);
  197. }
  198. }
  199. void CXmlNode::setAttr(const char* key, const char* value)
  200. {
  201. if (!m_pAttributes)
  202. {
  203. m_pAttributes = new XmlAttributes;
  204. }
  205. assert(m_pAttributes);
  206. XmlAttrIter it = GetAttrIterator(key);
  207. if (it == m_pAttributes->end())
  208. {
  209. XmlAttribute tempAttr;
  210. tempAttr.key = m_pStringPool->AddString(key);
  211. tempAttr.value = m_pStringPool->AddString(value);
  212. m_pAttributes->push_back(tempAttr);
  213. // Sort attributes.
  214. //std::sort( m_pAttributes->begin(),m_pAttributes->end() );
  215. }
  216. else
  217. {
  218. // If already exist, override this member.
  219. it->value = m_pStringPool->AddString(value);
  220. }
  221. }
  222. void CXmlNode::setAttr(const char* key, int value)
  223. {
  224. char str[128];
  225. azitoa(value, str, AZ_ARRAY_SIZE(str), 10);
  226. setAttr(key, str);
  227. }
  228. void CXmlNode::setAttr(const char* key, unsigned int value)
  229. {
  230. char str[128];
  231. azui64toa(value, str, AZ_ARRAY_SIZE(str), 10);
  232. setAttr(key, str);
  233. }
  234. void CXmlNode::setAttr(const char* key, float value)
  235. {
  236. char str[128];
  237. AZ::Locale::ScopedSerializationLocale localeResetter;
  238. sprintf_s(str, FLOAT_FMT, value);
  239. setAttr(key, str);
  240. }
  241. void CXmlNode::setAttr(const char* key, double value)
  242. {
  243. char str[128];
  244. AZ::Locale::ScopedSerializationLocale localeResetter;
  245. sprintf_s(str, DOUBLE_FMT, value);
  246. setAttr(key, str);
  247. }
  248. //////////////////////////////////////////////////////////////////////////
  249. void CXmlNode::setAttr(const char* key, int64 value)
  250. {
  251. char str[32];
  252. sprintf_s(str, "%" PRId64, value);
  253. setAttr(key, str);
  254. }
  255. //////////////////////////////////////////////////////////////////////////
  256. void CXmlNode::setAttr(const char* key, uint64 value, bool useHexFormat)
  257. {
  258. char str[32] = { 0 };
  259. if (useHexFormat)
  260. {
  261. sprintf_s(str, "%" PRIX64, value);
  262. }
  263. else
  264. {
  265. sprintf_s(str, "%" PRIu64, value);
  266. }
  267. setAttr(key, str);
  268. }
  269. void CXmlNode::setAttr(const char* key, const Ang3& value)
  270. {
  271. char str[128];
  272. AZ::Locale::ScopedSerializationLocale localeResetter;
  273. sprintf_s(str, FLOAT_FMT "," FLOAT_FMT "," FLOAT_FMT, value.x, value.y, value.z);
  274. setAttr(key, str);
  275. }
  276. void CXmlNode::setAttr(const char* key, const Vec3& value)
  277. {
  278. char str[128];
  279. AZ::Locale::ScopedSerializationLocale localeResetter;
  280. sprintf_s(str, FLOAT_FMT "," FLOAT_FMT "," FLOAT_FMT, value.x, value.y, value.z);
  281. setAttr(key, str);
  282. }
  283. void CXmlNode::setAttr(const char* key, const Vec4& value)
  284. {
  285. char str[128];
  286. AZ::Locale::ScopedSerializationLocale localeResetter;
  287. sprintf_s(str, FLOAT_FMT "," FLOAT_FMT "," FLOAT_FMT "," FLOAT_FMT, value.x, value.y, value.z, value.w);
  288. setAttr(key, str);
  289. }
  290. void CXmlNode::setAttr(const char* key, const Vec2& value)
  291. {
  292. char str[128];
  293. AZ::Locale::ScopedSerializationLocale localeResetter;
  294. sprintf_s(str, FLOAT_FMT "," FLOAT_FMT, value.x, value.y);
  295. setAttr(key, str);
  296. }
  297. void CXmlNode::setAttr(const char* key, const Quat& value)
  298. {
  299. char str[128];
  300. AZ::Locale::ScopedSerializationLocale localeResetter;
  301. sprintf_s(str, FLOAT_FMT "," FLOAT_FMT "," FLOAT_FMT "," FLOAT_FMT, value.w, value.v.x, value.v.y, value.v.z);
  302. setAttr(key, str);
  303. }
  304. //////////////////////////////////////////////////////////////////////////
  305. bool CXmlNode::getAttr(const char* key, int& value) const
  306. {
  307. const char* svalue = GetValue(key);
  308. if (svalue)
  309. {
  310. value = atoi(svalue);
  311. return true;
  312. }
  313. return false;
  314. }
  315. bool CXmlNode::getAttr(const char* key, unsigned int& value) const
  316. {
  317. const char* svalue = GetValue(key);
  318. if (svalue)
  319. {
  320. value = static_cast<unsigned int>(strtoul(svalue, NULL, 10));
  321. return true;
  322. }
  323. return false;
  324. }
  325. //////////////////////////////////////////////////////////////////////////
  326. bool CXmlNode::getAttr(const char* key, int64& value) const
  327. {
  328. const char* svalue = GetValue(key);
  329. if (svalue)
  330. {
  331. value = strtoll(key, nullptr, 10);
  332. return true;
  333. }
  334. return false;
  335. }
  336. //////////////////////////////////////////////////////////////////////////
  337. bool CXmlNode::getAttr(const char* key, uint64& value, bool useHexFormat) const
  338. {
  339. const char* svalue = GetValue(key);
  340. if (svalue)
  341. {
  342. value = strtoull(svalue, nullptr, useHexFormat ? 16 : 10);
  343. return true;
  344. }
  345. return false;
  346. }
  347. bool CXmlNode::getAttr(const char* key, bool& value) const
  348. {
  349. const char* svalue = GetValue(key);
  350. if (svalue)
  351. {
  352. if (azstricmp(svalue, "true") == 0)
  353. {
  354. value = true;
  355. }
  356. else if (azstricmp(svalue, "false") == 0)
  357. {
  358. value = false;
  359. }
  360. else
  361. {
  362. value = atoi(svalue) != 0;
  363. }
  364. return true;
  365. }
  366. return false;
  367. }
  368. bool CXmlNode::getAttr(const char* key, float& value) const
  369. {
  370. const char* svalue = GetValue(key);
  371. if (svalue)
  372. {
  373. AZ::Locale::ScopedSerializationLocale localeResetter;
  374. value = (float)atof(svalue);
  375. return true;
  376. }
  377. return false;
  378. }
  379. bool CXmlNode::getAttr(const char* key, double& value) const
  380. {
  381. const char* svalue = GetValue(key);
  382. if (svalue)
  383. {
  384. AZ::Locale::ScopedSerializationLocale localeResetter;
  385. value = atof(svalue);
  386. return true;
  387. }
  388. return false;
  389. }
  390. bool CXmlNode::getAttr(const char* key, Ang3& value) const
  391. {
  392. const char* svalue = GetValue(key);
  393. if (svalue)
  394. {
  395. AZ::Locale::ScopedSerializationLocale localeResetter;
  396. float x, y, z;
  397. if (azsscanf(svalue, "%f,%f,%f", &x, &y, &z) == 3)
  398. {
  399. value(x, y, z);
  400. return true;
  401. }
  402. }
  403. return false;
  404. }
  405. //////////////////////////////////////////////////////////////////////////
  406. bool CXmlNode::getAttr(const char* key, Vec3& value) const
  407. {
  408. const char* svalue = GetValue(key);
  409. if (svalue)
  410. {
  411. AZ::Locale::ScopedSerializationLocale localeResetter;
  412. float x, y, z;
  413. if (azsscanf(svalue, "%f,%f,%f", &x, &y, &z) == 3)
  414. {
  415. value = Vec3(x, y, z);
  416. return true;
  417. }
  418. }
  419. return false;
  420. }
  421. //////////////////////////////////////////////////////////////////////////
  422. bool CXmlNode::getAttr(const char* key, Vec4& value) const
  423. {
  424. const char* svalue = GetValue(key);
  425. if (svalue)
  426. {
  427. AZ::Locale::ScopedSerializationLocale localeResetter;
  428. float x, y, z, w;
  429. if (azsscanf(svalue, "%f,%f,%f,%f", &x, &y, &z, &w) == 4)
  430. {
  431. value = Vec4(x, y, z, w);
  432. return true;
  433. }
  434. }
  435. return false;
  436. }
  437. //////////////////////////////////////////////////////////////////////////
  438. bool CXmlNode::getAttr(const char* key, Vec2& value) const
  439. {
  440. const char* svalue = GetValue(key);
  441. if (svalue)
  442. {
  443. AZ::Locale::ScopedSerializationLocale localeResetter;
  444. float x, y;
  445. if (azsscanf(svalue, "%f,%f", &x, &y) == 2)
  446. {
  447. value = Vec2(x, y);
  448. return true;
  449. }
  450. }
  451. return false;
  452. }
  453. //////////////////////////////////////////////////////////////////////////
  454. bool CXmlNode::getAttr(const char* key, Quat& value) const
  455. {
  456. const char* svalue = GetValue(key);
  457. if (svalue)
  458. {
  459. AZ::Locale::ScopedSerializationLocale localeResetter;
  460. float w, x, y, z;
  461. if (azsscanf(svalue, "%f,%f,%f,%f", &w, &x, &y, &z) == 4)
  462. {
  463. if (fabs(w) > VEC_EPSILON || fabs(x) > VEC_EPSILON || fabs(y) > VEC_EPSILON || fabs(z) > VEC_EPSILON)
  464. {
  465. //[AlexMcC|02.03.10] directly assign to members to avoid triggering the assert in Quat() with data from bad assets
  466. value.w = w;
  467. value.v = Vec3(x, y, z);
  468. return value.IsValid();
  469. }
  470. }
  471. }
  472. return false;
  473. }
  474. //////////////////////////////////////////////////////////////////////////
  475. bool CXmlNode::getAttr(const char* key, ColorB& value) const
  476. {
  477. const char* svalue = GetValue(key);
  478. if (svalue)
  479. {
  480. AZ::Locale::ScopedSerializationLocale localeResetter;
  481. unsigned int r, g, b, a = 255;
  482. int numFound = azsscanf(svalue, "%u,%u,%u,%u", &r, &g, &b, &a);
  483. if (numFound == 3 || numFound == 4)
  484. {
  485. // If we only found 3 values, a should be unchanged, and still be 255
  486. if (r < 256 && g < 256 && b < 256 && a < 256)
  487. {
  488. value = ColorB(static_cast<uint8>(r), static_cast<uint8>(g), static_cast<uint8>(b), static_cast<uint8>(a));
  489. return true;
  490. }
  491. }
  492. }
  493. return false;
  494. }
  495. XmlNodeRef CXmlNode::findChild(const char* tag) const
  496. {
  497. if (m_pChilds)
  498. {
  499. XmlNodes& childs = *m_pChilds;
  500. for (int i = 0, num = static_cast<int>(childs.size()); i < num; ++i)
  501. {
  502. if (childs[i]->isTag(tag))
  503. {
  504. return childs[i];
  505. }
  506. }
  507. }
  508. return 0;
  509. }
  510. void CXmlNode::removeChild(const XmlNodeRef& node)
  511. {
  512. if (m_pChilds)
  513. {
  514. XmlNodes::iterator it = std::find(m_pChilds->begin(), m_pChilds->end(), (IXmlNode*)node);
  515. if (it != m_pChilds->end())
  516. {
  517. ReleaseChild(*it);
  518. m_pChilds->erase(it);
  519. }
  520. }
  521. }
  522. void CXmlNode::removeAllChilds()
  523. {
  524. removeAllChildsImpl();
  525. }
  526. //////////////////////////////////////////////////////////////////////////
  527. void CXmlNode::deleteChild(const char* tag)
  528. {
  529. if (m_pChilds)
  530. {
  531. XmlNodes& childs = *m_pChilds;
  532. for (int i = 0, num = static_cast<int>(childs.size()); i < num; ++i)
  533. {
  534. if (childs[i]->isTag(tag))
  535. {
  536. ReleaseChild(childs[i]);
  537. childs.erase(childs.begin() + i);
  538. return;
  539. }
  540. }
  541. }
  542. }
  543. //! Adds new child node.
  544. void CXmlNode::addChild(const XmlNodeRef& node)
  545. {
  546. if (!m_pChilds)
  547. {
  548. m_pChilds = new XmlNodes;
  549. }
  550. assert(node != 0);
  551. IXmlNode* pNode = ((IXmlNode*)node);
  552. pNode->AddRef();
  553. m_pChilds->push_back(pNode);
  554. pNode->setParent(this);
  555. };
  556. void CXmlNode::setParent(const XmlNodeRef& inNewParent)
  557. {
  558. // note, parent ptrs are not ref counted
  559. m_parent = inNewParent;
  560. }
  561. XmlNodeRef CXmlNode::newChild(const char* tagName)
  562. {
  563. XmlNodeRef node = createNode(tagName);
  564. addChild(node);
  565. return node;
  566. }
  567. //! Get XML Node child nodes.
  568. XmlNodeRef CXmlNode::getChild(int i) const
  569. {
  570. assert(m_pChilds);
  571. XmlNodes& childs = *m_pChilds;
  572. assert(i >= 0 && i < (int)childs.size());
  573. return childs[i];
  574. }
  575. //////////////////////////////////////////////////////////////////////////
  576. void CXmlNode::copyAttributes(XmlNodeRef fromNode)
  577. {
  578. IXmlNode* inode = fromNode;
  579. CXmlNode* n = (CXmlNode*)inode;
  580. assert(n);
  581. PREFAST_ASSUME(n);
  582. if (n != this)
  583. {
  584. if (n->m_pAttributes)
  585. {
  586. if (!m_pAttributes)
  587. {
  588. m_pAttributes = new XmlAttributes;
  589. }
  590. if (n->m_pStringPool == m_pStringPool)
  591. {
  592. *m_pAttributes = *(n->m_pAttributes);
  593. }
  594. else
  595. {
  596. XmlAttributes& lhs = *m_pAttributes;
  597. const XmlAttributes& rhs = *(n->m_pAttributes);
  598. lhs.resize(rhs.size());
  599. for (size_t i = 0; i < rhs.size(); ++i)
  600. {
  601. lhs[i].key = m_pStringPool->AddString(rhs[i].key);
  602. lhs[i].value = m_pStringPool->AddString(rhs[i].value);
  603. }
  604. }
  605. }
  606. else
  607. {
  608. SAFE_DELETE(m_pAttributes);
  609. }
  610. }
  611. }
  612. //////////////////////////////////////////////////////////////////////////
  613. bool CXmlNode::getAttributeByIndex(int index, const char** key, const char** value)
  614. {
  615. if (m_pAttributes)
  616. {
  617. XmlAttributes::iterator it = m_pAttributes->begin();
  618. if (it != m_pAttributes->end())
  619. {
  620. std::advance(it, index);
  621. if (it != m_pAttributes->end())
  622. {
  623. *key = it->key;
  624. *value = it->value;
  625. return true;
  626. }
  627. }
  628. }
  629. return false;
  630. }
  631. //////////////////////////////////////////////////////////////////////////
  632. bool CXmlNode::getAttributeByIndex(int index, XmlString& key, XmlString& value)
  633. {
  634. if (m_pAttributes)
  635. {
  636. XmlAttributes::iterator it = m_pAttributes->begin();
  637. if (it != m_pAttributes->end())
  638. {
  639. std::advance(it, index);
  640. if (it != m_pAttributes->end())
  641. {
  642. key = it->key;
  643. value = it->value;
  644. return true;
  645. }
  646. }
  647. }
  648. return false;
  649. }
  650. //////////////////////////////////////////////////////////////////////////
  651. static void AddTabsToString(XmlString& xml, int level)
  652. {
  653. static const char* tabs[] = {
  654. "",
  655. " ",
  656. " ",
  657. " ",
  658. " ",
  659. " ",
  660. " ",
  661. " ",
  662. " ",
  663. " ",
  664. " ",
  665. " ",
  666. };
  667. // Add tabs.
  668. if (level < sizeof(tabs) / sizeof(tabs[0]))
  669. {
  670. xml += tabs[level];
  671. }
  672. else
  673. {
  674. for (int i = 0; i < level; i++)
  675. {
  676. xml += " ";
  677. }
  678. }
  679. }
  680. //////////////////////////////////////////////////////////////////////////
  681. bool CXmlNode::IsValidXmlString(const char* str) const
  682. {
  683. int len = static_cast<int>(strlen(str));
  684. {
  685. // Prevents invalid characters not from standard ASCII set to propagate to xml.
  686. // A bit of hack for efficiency, fixes input string in place.
  687. char* s = const_cast<char*>(str);
  688. for (int i = 0; i < len; i++)
  689. {
  690. if ((unsigned char)s[i] > 0x7F)
  691. {
  692. s[i] = ' ';
  693. }
  694. }
  695. }
  696. if (strcspn(str, "\"\'&><") == len)
  697. {
  698. return true;
  699. }
  700. return false;
  701. }
  702. //////////////////////////////////////////////////////////////////////////
  703. XmlString CXmlNode::MakeValidXmlString(const XmlString& instr) const
  704. {
  705. XmlString str = instr;
  706. // check if str contains any invalid characters
  707. AZ::StringFunc::Replace(str, "&", "&amp;");
  708. AZ::StringFunc::Replace(str, "\"", "&quot;");
  709. AZ::StringFunc::Replace(str, "\'", "&apos;");
  710. AZ::StringFunc::Replace(str, "<", "&lt;");
  711. AZ::StringFunc::Replace(str, ">", "&gt;");
  712. AZ::StringFunc::Replace(str, "...", "&gt;");
  713. AZ::StringFunc::Replace(str, "\n", "&#10;");
  714. return str;
  715. }
  716. void CXmlNode::ReleaseChild(IXmlNode* pChild)
  717. {
  718. if (pChild)
  719. {
  720. if (pChild->getParent() == this) // if check to handle shared children which are supported by the CXmlNode impl
  721. {
  722. pChild->setParent(NULL);
  723. }
  724. pChild->Release();
  725. }
  726. }
  727. void CXmlNode::removeAllChildsImpl()
  728. {
  729. if (m_pChilds)
  730. {
  731. for (XmlNodes::iterator iter = m_pChilds->begin(), endIter = m_pChilds->end(); iter != endIter; ++iter)
  732. {
  733. ReleaseChild(*iter);
  734. }
  735. SAFE_DELETE(m_pChilds);
  736. }
  737. }
  738. //////////////////////////////////////////////////////////////////////////
  739. void CXmlNode::AddToXmlString(XmlString& xml, int level, AZ::IO::HandleType fileHandle, size_t chunkSize) const
  740. {
  741. if (fileHandle != AZ::IO::InvalidHandle && chunkSize > 0)
  742. {
  743. auto fileIoBase = AZ::IO::FileIOBase::GetInstance();
  744. AZ_Assert(fileIoBase != nullptr, "FileIOBase is expected to be initialized for CXmlNode");
  745. size_t len = xml.length();
  746. if (len >= chunkSize)
  747. {
  748. fileIoBase->Write(fileHandle, xml.c_str(), len);
  749. xml.assign (""); // should not free memory and does not!
  750. }
  751. }
  752. AddTabsToString(xml, level);
  753. const bool bHasChildren = (m_pChilds && !m_pChilds->empty());
  754. // Begin Tag
  755. if (!m_pAttributes || m_pAttributes->empty())
  756. {
  757. xml += "<";
  758. if (m_isProcessingInstruction)
  759. {
  760. xml += "?";
  761. }
  762. xml += m_tag;
  763. if (*m_content == 0 && !bHasChildren)
  764. {
  765. if (m_isProcessingInstruction)
  766. {
  767. xml += "?>\n";
  768. }
  769. else
  770. {
  771. // Compact tag form.
  772. xml += " />\n";
  773. }
  774. return;
  775. }
  776. xml += ">";
  777. }
  778. else
  779. {
  780. xml += "<";
  781. if (m_isProcessingInstruction)
  782. {
  783. xml += "?";
  784. }
  785. xml += m_tag;
  786. xml += " ";
  787. // Put attributes.
  788. for (XmlAttributes::const_iterator it = m_pAttributes->begin(); it != m_pAttributes->end(); )
  789. {
  790. xml += it->key;
  791. xml += "=\"";
  792. if (IsValidXmlString(it->value))
  793. {
  794. xml += it->value;
  795. }
  796. else
  797. {
  798. xml += MakeValidXmlString(it->value);
  799. }
  800. ++it;
  801. if (it != m_pAttributes->end())
  802. {
  803. xml += "\" ";
  804. }
  805. else
  806. {
  807. xml += "\"";
  808. }
  809. }
  810. if (*m_content == 0 && !bHasChildren)
  811. {
  812. if (m_isProcessingInstruction)
  813. {
  814. xml += "?>\n";
  815. }
  816. else
  817. {
  818. // Compact tag form.
  819. xml += "/>\n";
  820. }
  821. return;
  822. }
  823. xml += ">";
  824. }
  825. // Put node content.
  826. if (IsValidXmlString(m_content))
  827. {
  828. xml += m_content;
  829. }
  830. else
  831. {
  832. xml += MakeValidXmlString(m_content);
  833. }
  834. if (!bHasChildren)
  835. {
  836. xml += "</";
  837. xml += m_tag;
  838. xml += ">\n";
  839. return;
  840. }
  841. xml += "\n";
  842. // Add sub nodes.
  843. for (XmlNodes::iterator it = m_pChilds->begin(), itEnd = m_pChilds->end(); it != itEnd; ++it)
  844. {
  845. IXmlNode* node = *it;
  846. ((CXmlNode*)node)->AddToXmlString(xml, level + 1, fileHandle, chunkSize);
  847. }
  848. // Add tabs.
  849. AddTabsToString(xml, level);
  850. xml += "</";
  851. xml += m_tag;
  852. xml += ">\n";
  853. }
  854. #if !defined(APPLE) && !defined(LINUX) && !defined(AZ_LEGACY_CRYSYSTEM_TRAIT_HASSTPCPY)
  855. ILINE static char* stpcpy(char* dst, const char* src)
  856. {
  857. while (src[0])
  858. {
  859. dst[0] = src[0];
  860. dst++;
  861. src++;
  862. }
  863. return dst;
  864. }
  865. #endif
  866. char* CXmlNode::AddToXmlStringUnsafe(char* xml, int level, char* endPtr, AZ::IO::HandleType fileHandle, size_t chunkSize) const
  867. {
  868. const bool bHasChildren = (m_pChilds && !m_pChilds->empty());
  869. for (int i = 0; i < level; i++)
  870. {
  871. *(xml++) = ' ';
  872. *(xml++) = ' ';
  873. }
  874. // Begin Tag
  875. if (!m_pAttributes || m_pAttributes->empty())
  876. {
  877. *(xml++) = '<';
  878. xml = stpcpy(xml, m_tag);
  879. if (*m_content == 0 && !bHasChildren)
  880. {
  881. *(xml++) = '/';
  882. *(xml++) = '>';
  883. *(xml++) = '\n';
  884. return xml;
  885. }
  886. *(xml++) = '>';
  887. }
  888. else
  889. {
  890. *(xml++) = '<';
  891. xml = stpcpy(xml, m_tag);
  892. *(xml++) = ' ';
  893. // Put attributes.
  894. for (XmlAttributes::const_iterator it = m_pAttributes->begin(); it != m_pAttributes->end(); )
  895. {
  896. xml = stpcpy(xml, it->key);
  897. *(xml++) = '=';
  898. *(xml++) = '\"';
  899. #ifndef _RELEASE
  900. if (it->value[strcspn(it->value, "\"\'&><")])
  901. {
  902. __debugbreak();
  903. }
  904. #endif
  905. xml = stpcpy(xml, it->value);
  906. ++it;
  907. *(xml++) = '\"';
  908. if (it != m_pAttributes->end())
  909. {
  910. *(xml++) = ' ';
  911. }
  912. }
  913. if (*m_content == 0 && !bHasChildren)
  914. {
  915. // Compact tag form.
  916. *(xml++) = '/';
  917. *(xml++) = '>';
  918. *(xml++) = '\n';
  919. return xml;
  920. }
  921. *(xml++) = '>';
  922. }
  923. #ifndef _RELEASE
  924. if (m_content[strcspn(m_content, "\"\'&><")])
  925. {
  926. __debugbreak();
  927. }
  928. #endif
  929. xml = stpcpy(xml, m_content);
  930. if (!bHasChildren)
  931. {
  932. *(xml++) = '<';
  933. *(xml++) = '/';
  934. xml = stpcpy(xml, m_tag);
  935. *(xml++) = '>';
  936. *(xml++) = '\n';
  937. return xml;
  938. }
  939. *(xml++) = '\n';
  940. // Add sub nodes.
  941. for (XmlNodes::iterator it = m_pChilds->begin(), itEnd = m_pChilds->end(); it != itEnd; ++it)
  942. {
  943. IXmlNode* node = *it;
  944. xml = ((CXmlNode*)node)->AddToXmlStringUnsafe(xml, level + 1, endPtr, fileHandle, chunkSize);
  945. }
  946. for (int i = 0; i < level; i++)
  947. {
  948. *(xml++) = ' ';
  949. *(xml++) = ' ';
  950. }
  951. *(xml++) = '<';
  952. *(xml++) = '/';
  953. xml = stpcpy(xml, m_tag);
  954. *(xml++) = '>';
  955. *(xml++) = '\n';
  956. assert(xml < endPtr);
  957. return xml;
  958. }
  959. //////////////////////////////////////////////////////////////////////////
  960. IXmlStringData* CXmlNode::getXMLData(int nReserveMem) const
  961. {
  962. CXmlStringData* pStrData = new CXmlStringData;
  963. pStrData->m_string.reserve(nReserveMem);
  964. AddToXmlString(pStrData->m_string, 0);
  965. return pStrData;
  966. }
  967. //////////////////////////////////////////////////////////////////////////
  968. XmlString CXmlNode::getXML(int level) const
  969. {
  970. XmlString xml;
  971. xml = "";
  972. xml.reserve(1024);
  973. AddToXmlString(xml, level);
  974. return xml;
  975. }
  976. // TODO: those 2 saving functions are a bit messy. should probably make a separate one for the use of PlatformAPI
  977. bool CXmlNode::saveToFile(const char* fileName)
  978. {
  979. if (!fileName)
  980. {
  981. return false;
  982. }
  983. {
  984. AZ::IO::HandleType fileHandle = gEnv->pCryPak->FOpen(fileName, "wt");
  985. if (fileHandle != AZ::IO::InvalidHandle)
  986. {
  987. #ifdef WIN32
  988. XmlString xml = getXML(); // this would not work in consoles because the size limits in strings
  989. const char* sxml = (const char*)xml;
  990. gEnv->pCryPak->FWrite(sxml, xml.length(), fileHandle);
  991. gEnv->pCryPak->FClose(fileHandle);
  992. return true;
  993. #else
  994. constexpr size_t chunkSizeBytes = (15 * 1024);
  995. bool ret = saveToFile(fileName, chunkSizeBytes, fileHandle);
  996. gEnv->pCryPak->FClose(fileHandle);
  997. return ret;
  998. #endif
  999. }
  1000. return false;
  1001. }
  1002. }
  1003. bool CXmlNode::saveToFile([[maybe_unused]] const char* fileName, size_t chunkSize, AZ::IO::HandleType fileHandle)
  1004. {
  1005. if (AZ::IO::SystemFile::Exists(fileName) && !AZ::IO::SystemFile::IsWritable(fileName))
  1006. {
  1007. AZ::IO::SystemFile::SetWritable(fileName, true);
  1008. }
  1009. if (chunkSize < 256 * 1024) // make at least 256k
  1010. {
  1011. chunkSize = 256 * 1024;
  1012. }
  1013. XmlString xml;
  1014. xml.assign ("");
  1015. xml.reserve(chunkSize * 2); // we reserve double memory, as writing in chunks is not really writing in fixed blocks but a bit fuzzy
  1016. auto fileIoBase = AZ::IO::FileIOBase::GetInstance();
  1017. AZ_Assert(fileIoBase != nullptr, "FileIOBase is expected to be initialized for CXmlNode");
  1018. if (fileHandle == AZ::IO::InvalidHandle)
  1019. {
  1020. return false;
  1021. }
  1022. AddToXmlString(xml, 0, fileHandle, chunkSize);
  1023. size_t len = xml.length();
  1024. if (len > 0)
  1025. {
  1026. fileIoBase->Write(fileHandle, xml.c_str(), len);
  1027. }
  1028. xml.clear(); // xml.resize(0) would not reclaim memory
  1029. return true;
  1030. }
  1031. /**
  1032. ******************************************************************************
  1033. * XmlParserImp class.
  1034. ******************************************************************************
  1035. */
  1036. class XmlParserImp
  1037. : public IXmlStringPool
  1038. {
  1039. public:
  1040. explicit XmlParserImp(bool bReuseStrings);
  1041. ~XmlParserImp();
  1042. void ParseBegin(bool bCleanPools);
  1043. XmlNodeRef ParseFile(const char* filename, XmlString& errorString, bool bCleanPools);
  1044. XmlNodeRef ParseBuffer(const char* buffer, size_t bufLen, XmlString& errorString, bool bCleanPools, bool bSuppressWarnings = false);
  1045. void ParseEnd();
  1046. // Add new string to pool.
  1047. const char* AddString(const char* str) { return m_stringPool.Append(str, (int)strlen(str)); }
  1048. protected:
  1049. void onStartElement(const char* tagName, const char** atts);
  1050. void onEndElement(const char* tagName);
  1051. void onRawData(const char* data);
  1052. static void startElement(void* userData, const char* name, const char** atts)
  1053. {
  1054. ((XmlParserImp*)userData)->onStartElement(name, atts);
  1055. }
  1056. static void endElement(void* userData, const char* name)
  1057. {
  1058. ((XmlParserImp*)userData)->onEndElement(name);
  1059. }
  1060. static void characterData(void* userData, const char* s, int len)
  1061. {
  1062. char str[32700];
  1063. if (len > sizeof(str) - 1)
  1064. {
  1065. assert(0);
  1066. len = sizeof(str) - 1;
  1067. }
  1068. // Note that XML buffer userData has no terminating '\0'.
  1069. memcpy(str, s, len);
  1070. str[len] = 0;
  1071. ((XmlParserImp*)userData)->onRawData(str);
  1072. }
  1073. void CleanStack();
  1074. struct SStackEntity
  1075. {
  1076. XmlNodeRef node;
  1077. std::vector<IXmlNode*> childs; //TODO: is it worth lazily initializing this, like CXmlNode::m_pChilds?
  1078. };
  1079. // First node will become root node.
  1080. std::vector<SStackEntity> m_nodeStack;
  1081. int m_nNodeStackTop;
  1082. XmlNodeRef m_root;
  1083. XML_Parser m_parser;
  1084. CSimpleStringPool m_stringPool;
  1085. };
  1086. //////////////////////////////////////////////////////////////////////////
  1087. void XmlParserImp::CleanStack()
  1088. {
  1089. m_nNodeStackTop = 0;
  1090. for (int i = 0, num = static_cast<int>(m_nodeStack.size()); i < num; i++)
  1091. {
  1092. m_nodeStack[i].node = 0;
  1093. m_nodeStack[i].childs.resize(0);
  1094. }
  1095. }
  1096. /**
  1097. ******************************************************************************
  1098. * XmlParserImp
  1099. ******************************************************************************
  1100. */
  1101. void XmlParserImp::onStartElement(const char* tagName, const char** atts)
  1102. {
  1103. CXmlNode* pCNode = new CXmlNode;
  1104. pCNode->m_pStringPool = this;
  1105. pCNode->m_pStringPool->AddRef();
  1106. pCNode->m_tag = AddString(tagName);
  1107. XmlNodeRef node = pCNode;
  1108. m_nNodeStackTop++;
  1109. if (m_nNodeStackTop >= (int)m_nodeStack.size())
  1110. {
  1111. m_nodeStack.resize(m_nodeStack.size() * 2);
  1112. }
  1113. m_nodeStack[m_nNodeStackTop].node = pCNode;
  1114. m_nodeStack[m_nNodeStackTop - 1].childs.push_back(pCNode);
  1115. if (!m_root)
  1116. {
  1117. m_root = node;
  1118. }
  1119. else
  1120. {
  1121. pCNode->m_parent = (IXmlNode*)m_nodeStack[m_nNodeStackTop - 1].node;
  1122. node->AddRef(); // Childs need to be add refed.
  1123. }
  1124. node->setLine(static_cast<int>(XML_GetCurrentLineNumber((XML_Parser)m_parser)));
  1125. // Call start element callback.
  1126. int i = 0;
  1127. int numAttrs = 0;
  1128. while (atts[i] != 0)
  1129. {
  1130. numAttrs++;
  1131. i += 2;
  1132. }
  1133. if (numAttrs > 0)
  1134. {
  1135. i = 0;
  1136. if (!pCNode->m_pAttributes)
  1137. {
  1138. pCNode->m_pAttributes = new XmlAttributes;
  1139. }
  1140. XmlAttributes& nodeAtts = *(pCNode->m_pAttributes);
  1141. nodeAtts.resize(numAttrs);
  1142. int nAttr = 0;
  1143. while (atts[i] != 0)
  1144. {
  1145. nodeAtts[nAttr].key = AddString(atts[i]);
  1146. nodeAtts[nAttr].value = AddString(atts[i + 1]);
  1147. nAttr++;
  1148. i += 2;
  1149. }
  1150. // Sort attributes.
  1151. //std::sort( pCNode->m_attributes.begin(),pCNode->m_attributes.end() );
  1152. }
  1153. }
  1154. void XmlParserImp::onEndElement([[maybe_unused]] const char* tagName)
  1155. {
  1156. assert(m_nNodeStackTop > 0);
  1157. if (m_nNodeStackTop > 0)
  1158. {
  1159. // Copy current childs to the parent.
  1160. SStackEntity& entry = m_nodeStack[m_nNodeStackTop];
  1161. CXmlNode* currNode = static_cast<CXmlNode*>(static_cast<IXmlNode*>(entry.node));
  1162. if (!entry.childs.empty())
  1163. {
  1164. if (!currNode->m_pChilds)
  1165. {
  1166. currNode->m_pChilds = new CXmlNode::XmlNodes;
  1167. }
  1168. *currNode->m_pChilds = entry.childs;
  1169. }
  1170. entry.childs.resize(0);
  1171. entry.node = NULL;
  1172. }
  1173. m_nNodeStackTop--;
  1174. }
  1175. void XmlParserImp::onRawData(const char* data)
  1176. {
  1177. assert(m_nNodeStackTop >= 0);
  1178. if (m_nNodeStackTop >= 0)
  1179. {
  1180. size_t len = strlen(data);
  1181. if (len > 0)
  1182. {
  1183. if (strspn(data, "\r\n\t ") != len)
  1184. {
  1185. CXmlNode* node = (CXmlNode*)(IXmlNode*)m_nodeStack[m_nNodeStackTop].node;
  1186. if (*node->m_content != '\0')
  1187. {
  1188. node->m_content = m_stringPool.ReplaceString(node->m_content, data);
  1189. }
  1190. else
  1191. {
  1192. node->m_content = AddString(data);
  1193. }
  1194. }
  1195. }
  1196. }
  1197. }
  1198. //////////////////////////////////////////////////////////////////////////
  1199. XmlParserImp::XmlParserImp(bool bReuseStrings)
  1200. : m_nNodeStackTop(0)
  1201. , m_parser(NULL)
  1202. , m_stringPool(bReuseStrings)
  1203. {
  1204. m_nodeStack.resize(32);
  1205. CleanStack();
  1206. }
  1207. XmlParserImp::~XmlParserImp()
  1208. {
  1209. ParseEnd();
  1210. }
  1211. namespace
  1212. {
  1213. void* custom_xml_malloc(size_t nSize)
  1214. {
  1215. return azmalloc(nSize);
  1216. }
  1217. void* custom_xml_realloc(void* p, size_t nSize)
  1218. {
  1219. return azrealloc(p, nSize);
  1220. }
  1221. void custom_xml_free(void* p)
  1222. {
  1223. azfree(p);
  1224. }
  1225. }
  1226. //////////////////////////////////////////////////////////////////////////
  1227. void XmlParserImp::ParseBegin(bool bCleanPools)
  1228. {
  1229. m_root = 0;
  1230. CleanStack();
  1231. if (bCleanPools)
  1232. {
  1233. m_stringPool.Clear();
  1234. }
  1235. XML_Memory_Handling_Suite memHandler;
  1236. memHandler.malloc_fcn = custom_xml_malloc;
  1237. memHandler.realloc_fcn = custom_xml_realloc;
  1238. memHandler.free_fcn = custom_xml_free;
  1239. m_parser = XML_ParserCreate_MM(NULL, &memHandler, NULL);
  1240. XML_SetUserData(m_parser, this);
  1241. XML_SetElementHandler(m_parser, startElement, endElement);
  1242. XML_SetCharacterDataHandler(m_parser, characterData);
  1243. XML_SetEncoding(m_parser, "utf-8");
  1244. }
  1245. //////////////////////////////////////////////////////////////////////////
  1246. void XmlParserImp::ParseEnd()
  1247. {
  1248. if (m_parser)
  1249. {
  1250. XML_ParserFree(m_parser);
  1251. }
  1252. m_parser = 0;
  1253. }
  1254. //////////////////////////////////////////////////////////////////////////
  1255. XmlNodeRef XmlParserImp::ParseBuffer(const char* buffer, size_t bufLen, XmlString& errorString, bool bCleanPools, bool bSuppressWarnings)
  1256. {
  1257. static const char* const errorPrefix = "XML parser: ";
  1258. XmlNodeRef root = 0;
  1259. // Let's try to parse the buffer as binary XML
  1260. {
  1261. XMLBinary::XMLBinaryReader reader;
  1262. XMLBinary::XMLBinaryReader::EResult result;
  1263. root = reader.LoadFromBuffer(XMLBinary::XMLBinaryReader::eBufferMemoryHandling_MakeCopy, buffer, bufLen, result);
  1264. if (root)
  1265. {
  1266. return root;
  1267. }
  1268. if (result != XMLBinary::XMLBinaryReader::eResult_NotBinXml)
  1269. {
  1270. const char* const str = reader.GetErrorDescription();
  1271. errorString = str;
  1272. if (!bSuppressWarnings)
  1273. {
  1274. CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "%s%s (data size: %u)", errorPrefix, str, static_cast<unsigned>(bufLen));
  1275. }
  1276. return 0;
  1277. }
  1278. }
  1279. // This is not binary XML, so let's use text XML parser.
  1280. {
  1281. ParseBegin(bCleanPools);
  1282. m_stringPool.SetBlockSize(static_cast<unsigned>(bufLen) / 16);
  1283. if (XML_Parse(m_parser, buffer, static_cast<int>(bufLen), 1))
  1284. {
  1285. root = m_root;
  1286. }
  1287. else
  1288. {
  1289. char str[1024];
  1290. sprintf_s(str, "%s%s at line %d", errorPrefix, XML_ErrorString(XML_GetErrorCode(m_parser)), (int)XML_GetCurrentLineNumber(m_parser));
  1291. errorString = str;
  1292. if (!bSuppressWarnings)
  1293. {
  1294. CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "%s", str);
  1295. }
  1296. }
  1297. m_root = 0;
  1298. ParseEnd();
  1299. }
  1300. return root;
  1301. }
  1302. //////////////////////////////////////////////////////////////////////////
  1303. XmlNodeRef XmlParserImp::ParseFile(const char* filename, XmlString& errorString, bool bCleanPools)
  1304. {
  1305. if (!filename)
  1306. {
  1307. return 0;
  1308. }
  1309. static const char* const errorPrefix = "XML reader: ";
  1310. XmlNodeRef root = 0;
  1311. char* pFileContents = 0;
  1312. size_t fileSize = 0;
  1313. char str[1024];
  1314. AZStd::fixed_string<256> adjustedFilename;
  1315. AZStd::fixed_string<256> pakPath;
  1316. if (fileSize <= 0)
  1317. {
  1318. CCryFile xmlFile;
  1319. if (!xmlFile.Open(filename, "rb"))
  1320. {
  1321. sprintf_s(str, "%sCan't open file (%s)", errorPrefix, filename);
  1322. errorString = str;
  1323. CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "%s", str);
  1324. return 0;
  1325. }
  1326. fileSize = xmlFile.GetLength();
  1327. if (fileSize <= 0)
  1328. {
  1329. sprintf_s(str, "%sFile is empty (%s)", errorPrefix, filename);
  1330. errorString = str;
  1331. CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "%s", str);
  1332. return 0;
  1333. }
  1334. pFileContents = new char[fileSize];
  1335. if (!pFileContents)
  1336. {
  1337. sprintf_s(str, "%sCan't allocate %u bytes of memory (%s)", errorPrefix, static_cast<unsigned>(fileSize), filename);
  1338. errorString = str;
  1339. CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "%s", str);
  1340. return 0;
  1341. }
  1342. if (xmlFile.ReadRaw(pFileContents, fileSize) != fileSize)
  1343. {
  1344. delete [] pFileContents;
  1345. sprintf_s(str, "%sCan't read file (%s)", errorPrefix, filename);
  1346. errorString = str;
  1347. CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "%s", str);
  1348. return 0;
  1349. }
  1350. AZ::IO::FixedMaxPath resolvedPath(AZ::IO::PosixPathSeparator);
  1351. auto fileIoBase = AZ::IO::FileIOBase::GetInstance();
  1352. AZ_Assert(fileIoBase != nullptr, "FileIOBase is expected to be initialized for CXmlNode");
  1353. if (fileIoBase->ResolvePath(resolvedPath, xmlFile.GetFilename()))
  1354. {
  1355. adjustedFilename = resolvedPath.MakePreferred().Native();
  1356. }
  1357. if (fileIoBase->ResolvePath(resolvedPath, xmlFile.GetPakPath()))
  1358. {
  1359. pakPath = resolvedPath.MakePreferred().Native();
  1360. }
  1361. }
  1362. XMLBinary::XMLBinaryReader reader;
  1363. XMLBinary::XMLBinaryReader::EResult result;
  1364. root = reader.LoadFromBuffer(XMLBinary::XMLBinaryReader::eBufferMemoryHandling_TakeOwnership, pFileContents, fileSize, result);
  1365. if (root)
  1366. {
  1367. return root;
  1368. }
  1369. if (result != XMLBinary::XMLBinaryReader::eResult_NotBinXml)
  1370. {
  1371. delete [] pFileContents;
  1372. sprintf_s(str, "%s%s (%s)", errorPrefix, reader.GetErrorDescription(), filename);
  1373. errorString = str;
  1374. CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "%s", str);
  1375. return 0;
  1376. }
  1377. else
  1378. {
  1379. // not binary XML - refuse to load if in scripts dir and not in bin xml to help reduce hacking
  1380. // wish we could compile the text xml parser out, but too much work to get everything moved over
  1381. constexpr AZStd::fixed_string<32> strScripts{"Scripts/"};
  1382. // exclude files and PAKs from Mods folder
  1383. constexpr AZStd::fixed_string<8> modsStr{"Mods/"};
  1384. if (_strnicmp(filename, strScripts.c_str(), strScripts.length()) == 0 &&
  1385. _strnicmp(adjustedFilename.c_str(), modsStr.c_str(), modsStr.length()) != 0 &&
  1386. _strnicmp(pakPath.c_str(), modsStr.c_str(), modsStr.length()) != 0)
  1387. {
  1388. #ifdef _RELEASE
  1389. CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "Non binary XML found in scripts dir (%s)", filename);
  1390. #endif
  1391. }
  1392. }
  1393. {
  1394. ParseBegin(bCleanPools);
  1395. m_stringPool.SetBlockSize(static_cast<unsigned>(fileSize / 16));
  1396. if (XML_Parse(m_parser, pFileContents, static_cast<int>(fileSize), 1))
  1397. {
  1398. root = m_root;
  1399. }
  1400. else
  1401. {
  1402. sprintf_s(str, "%s%s at line %d (%s)", errorPrefix, XML_ErrorString(XML_GetErrorCode(m_parser)), (int)XML_GetCurrentLineNumber(m_parser), filename);
  1403. errorString = str;
  1404. CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "%s", str);
  1405. }
  1406. m_root = 0;
  1407. ParseEnd();
  1408. }
  1409. delete [] pFileContents;
  1410. return root;
  1411. }
  1412. XmlParser::XmlParser(bool bReuseStrings)
  1413. {
  1414. m_nRefCount = 0;
  1415. m_pImpl = new XmlParserImp(bReuseStrings);
  1416. m_pImpl->AddRef();
  1417. }
  1418. XmlParser::~XmlParser()
  1419. {
  1420. m_pImpl->Release();
  1421. }
  1422. //////////////////////////////////////////////////////////////////////////
  1423. XmlNodeRef XmlParser::ParseBuffer(const char* buffer, int nBufLen, bool bCleanPools, bool bSuppressWarnings)
  1424. {
  1425. m_errorString = "";
  1426. return m_pImpl->ParseBuffer(buffer, nBufLen, m_errorString, bCleanPools, bSuppressWarnings);
  1427. }
  1428. //////////////////////////////////////////////////////////////////////////
  1429. XmlNodeRef XmlParser::ParseFile(const char* filename, bool bCleanPools)
  1430. {
  1431. m_errorString = "";
  1432. return m_pImpl->ParseFile(filename, m_errorString, bCleanPools);
  1433. }
  1434. //////////////////////////////////////////////////////////////////////////
  1435. //
  1436. // Implements special reusable XmlNode for XmlNode pool
  1437. //
  1438. //////////////////////////////////////////////////////////////////////////
  1439. CXmlNodeReuse::CXmlNodeReuse(const char* tag, CXmlNodePool* pPool)
  1440. : m_pPool(pPool)
  1441. {
  1442. SAFE_RELEASE(m_pStringPool);
  1443. m_pStringPool = m_pPool->GetStringPool();
  1444. m_pStringPool->AddRef();
  1445. m_tag = m_pStringPool->AddString(tag);
  1446. }
  1447. void CXmlNodeReuse::Release()
  1448. {
  1449. m_pPool->OnRelease(m_nRefCount, this);
  1450. CXmlNode::Release();
  1451. }
  1452. //////////////////////////////////////////////////////////////////////////
  1453. //
  1454. // Pool of reusable XML nodes with shared string pool
  1455. //
  1456. //////////////////////////////////////////////////////////////////////////
  1457. CXmlNodePool::CXmlNodePool(unsigned int nBlockSize, bool bReuseStrings)
  1458. {
  1459. m_pStringPool = new CXmlStringPool(bReuseStrings);
  1460. assert(m_pStringPool != 0);
  1461. // in order to avoid memory fragmentation
  1462. // allocates 1Mb buffer for shared string pool
  1463. static_cast<CXmlStringPool*>(m_pStringPool)->SetBlockSize(nBlockSize);
  1464. m_pStringPool->AddRef();
  1465. m_nAllocated = 0;
  1466. }
  1467. CXmlNodePool::~CXmlNodePool()
  1468. {
  1469. while (!m_pNodePool.empty())
  1470. {
  1471. CXmlNodeReuse* pNode = m_pNodePool.top();
  1472. m_pNodePool.pop();
  1473. pNode->Release();
  1474. }
  1475. m_pStringPool->Release();
  1476. }
  1477. XmlNodeRef CXmlNodePool::GetXmlNode(const char* sNodeName)
  1478. {
  1479. CXmlNodeReuse* pNode = 0;
  1480. // NOTE: at the moment xml node pool is dedicated for statistics nodes only
  1481. // first at all check if we have already free node
  1482. if (!m_pNodePool.empty())
  1483. {
  1484. pNode = m_pNodePool.top();
  1485. m_pNodePool.pop();
  1486. // init it to new node name
  1487. pNode->setTag(sNodeName);
  1488. m_nAllocated++;
  1489. //if (0 == m_nAllocated % 1000)
  1490. //CryLog("[CXmlNodePool]: already reused nodes [%d]", m_nAllocated);
  1491. }
  1492. else
  1493. {
  1494. // there is no free nodes so create new one
  1495. // later it will be reused as soon as no external references left
  1496. pNode = new CXmlNodeReuse(sNodeName, this);
  1497. assert(pNode != 0);
  1498. // increase ref counter for reusing node later
  1499. pNode->AddRef();
  1500. m_nAllocated++;
  1501. //if (0 == m_nAllocated % 1000)
  1502. //CryLog("[CXmlNodePool]: already allocated nodes [%d]", m_nAllocated);
  1503. }
  1504. return pNode;
  1505. }
  1506. void CXmlNodePool::OnRelease(int iRefCount, void* pThis)
  1507. {
  1508. // each reusable node call OnRelease before parent release
  1509. // since we keep reference on xml node so when ref count equals
  1510. // to 2 means that it is last external object releases reference
  1511. // to reusable node and it can be save for reuse later
  1512. if (2 == iRefCount)
  1513. {
  1514. CXmlNodeReuse* pNode = static_cast<CXmlNodeReuse*>(pThis);
  1515. pNode->removeAllChilds();
  1516. pNode->removeAllAttributes();
  1517. m_pNodePool.push(pNode);
  1518. // decrease totally allocated by xml node pool counter
  1519. // when counter equals to zero it means that all external
  1520. // objects do not have references to allocated reusable node
  1521. // at that point it is safe to clear shared string pool
  1522. m_nAllocated--;
  1523. if (0 == m_nAllocated)
  1524. {
  1525. //CryLog("[CXmlNodePool]: clear shared string pool");
  1526. static_cast<CXmlStringPool*>(m_pStringPool)->Clear();
  1527. }
  1528. }
  1529. }
  1530. #undef SCOPED_LOCALE_RESETTER