123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722 |
- /*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
- #include "CrySystem_precompiled.h"
- #include <stdlib.h>
- #include <expat.h>
- #include "xml.h"
- #include <algorithm>
- #include <stdio.h>
- #include <AzCore/Serialization/Locale.h>
- #include <AzFramework/Archive/IArchive.h>
- #include <CryCommon/Cry_Color.h>
- #include "XMLBinaryReader.h"
- #define FLOAT_FMT "%.8g"
- #define DOUBLE_FMT "%.17g"
- #include "../SimpleStringPool.h"
- #include "System.h"
- // Global counter for memory allocated in XML string pools.
- size_t CSimpleStringPool::g_nTotalAllocInXmlStringPools = 0;
- //////////////////////////////////////////////////////////////////////////
- static int __cdecl ascii_stricmp(const char* dst, const char* src)
- {
- int f, l;
- do
- {
- if (((f = (unsigned char)(*(dst++))) >= 'A') && (f <= 'Z'))
- {
- f -= 'A' - 'a';
- }
- if (((l = (unsigned char)(*(src++))) >= 'A') && (l <= 'Z'))
- {
- l -= 'A' - 'a';
- }
- }
- while (f && (f == l));
- return(f - l);
- }
- //////////////////////////////////////////////////////////////////////////
- XmlStrCmpFunc g_pXmlStrCmp = &ascii_stricmp;
- //////////////////////////////////////////////////////////////////////////
- class CXmlStringData
- : public IXmlStringData
- {
- public:
- int m_nRefCount;
- XmlString m_string;
- CXmlStringData() { m_nRefCount = 0; }
- virtual void AddRef() { ++m_nRefCount; }
- virtual void Release()
- {
- if (--m_nRefCount <= 0)
- {
- delete this;
- }
- }
- virtual const char* GetString() { return m_string.c_str(); };
- virtual size_t GetStringLength() { return m_string.size(); };
- };
- //////////////////////////////////////////////////////////////////////////
- class CXmlStringPool
- : public IXmlStringPool
- {
- public:
- explicit CXmlStringPool(bool bReuseStrings)
- : m_stringPool(bReuseStrings) {}
- const char* AddString(const char* str) { return m_stringPool.Append(str, (int)strlen(str)); }
- void Clear() { m_stringPool.Clear(); }
- void SetBlockSize(unsigned int nBlockSize) { m_stringPool.SetBlockSize(nBlockSize); }
- private:
- CSimpleStringPool m_stringPool;
- };
- //xml_node_allocator XmlDynArrayAlloc::allocator;
- //size_t XmlDynArrayAlloc::m_iAllocated = 0;
- /**
- ******************************************************************************
- * CXmlNode implementation.
- ******************************************************************************
- */
- void CXmlNode::DeleteThis()
- {
- delete this;
- }
- CXmlNode::~CXmlNode()
- {
- m_nRefCount = 1; // removeAllChildsImpl can make an XmlNodeRef to this node whilst it is deleting the children
- // doing this will cause the ref count to be increment and decremented and cause delete to be called again,
- // leading to a recursion crash. upping the ref count here once destruction has started avoids this problem
- removeAllChildsImpl();
- SAFE_DELETE(m_pAttributes);
- m_pStringPool->Release();
- }
- CXmlNode::CXmlNode()
- : m_pStringPool(NULL) // must be changed later.
- , m_tag("")
- , m_content("")
- , m_parent(NULL)
- , m_pChilds(NULL)
- , m_pAttributes(NULL)
- , m_line(0)
- , m_isProcessingInstruction(false)
- {
- m_nRefCount = 0; //TODO: move initialization to IXmlNode constructor
- }
- CXmlNode::CXmlNode(const char* tag, bool bReuseStrings, bool bIsProcessingInstruction)
- : m_content("")
- , m_parent(NULL)
- , m_pChilds(NULL)
- , m_pAttributes(NULL)
- , m_line(0)
- , m_isProcessingInstruction(bIsProcessingInstruction)
- {
- m_nRefCount = 0; //TODO: move initialization to IXmlNode constructor
- m_pStringPool = new CXmlStringPool(bReuseStrings);
- m_pStringPool->AddRef();
- m_tag = m_pStringPool->AddString(tag);
- }
- //////////////////////////////////////////////////////////////////////////
- XmlNodeRef CXmlNode::createNode(const char* tag)
- {
- CXmlNode* pNewNode = new CXmlNode;
- pNewNode->m_pStringPool = m_pStringPool;
- m_pStringPool->AddRef();
- pNewNode->m_tag = m_pStringPool->AddString(tag);
- return XmlNodeRef(pNewNode);
- }
- //////////////////////////////////////////////////////////////////////////
- void CXmlNode::setTag(const char* tag)
- {
- m_tag = m_pStringPool->AddString(tag);
- }
- //////////////////////////////////////////////////////////////////////////
- void CXmlNode::setContent(const char* str)
- {
- m_content = m_pStringPool->AddString(str);
- }
- //////////////////////////////////////////////////////////////////////////
- bool CXmlNode::isTag(const char* tag) const
- {
- return g_pXmlStrCmp(tag, m_tag) == 0;
- }
- const char* CXmlNode::getAttr(const char* key) const
- {
- const char* svalue = GetValue(key);
- if (svalue)
- {
- return svalue;
- }
- return "";
- }
- bool CXmlNode::getAttr(const char* key, const char** value) const
- {
- const char* svalue = GetValue(key);
- if (svalue)
- {
- *value = svalue;
- return true;
- }
- else
- {
- *value = "";
- return false;
- }
- }
- bool CXmlNode::haveAttr(const char* key) const
- {
- if (m_pAttributes)
- {
- XmlAttrConstIter it = GetAttrConstIterator(key);
- if (it != m_pAttributes->end())
- {
- return true;
- }
- }
- return false;
- }
- void CXmlNode::delAttr(const char* key)
- {
- if (m_pAttributes)
- {
- XmlAttrIter it = GetAttrIterator(key);
- if (it != m_pAttributes->end())
- {
- m_pAttributes->erase(it);
- }
- }
- }
- void CXmlNode::removeAllAttributes()
- {
- if (m_pAttributes)
- {
- m_pAttributes->clear();
- SAFE_DELETE(m_pAttributes);
- }
- }
- void CXmlNode::setAttr(const char* key, const char* value)
- {
- if (!m_pAttributes)
- {
- m_pAttributes = new XmlAttributes;
- }
- assert(m_pAttributes);
- XmlAttrIter it = GetAttrIterator(key);
- if (it == m_pAttributes->end())
- {
- XmlAttribute tempAttr;
- tempAttr.key = m_pStringPool->AddString(key);
- tempAttr.value = m_pStringPool->AddString(value);
- m_pAttributes->push_back(tempAttr);
- // Sort attributes.
- //std::sort( m_pAttributes->begin(),m_pAttributes->end() );
- }
- else
- {
- // If already exist, override this member.
- it->value = m_pStringPool->AddString(value);
- }
- }
- void CXmlNode::setAttr(const char* key, int value)
- {
- char str[128];
- azitoa(value, str, AZ_ARRAY_SIZE(str), 10);
- setAttr(key, str);
- }
- void CXmlNode::setAttr(const char* key, unsigned int value)
- {
- char str[128];
- azui64toa(value, str, AZ_ARRAY_SIZE(str), 10);
- setAttr(key, str);
- }
- void CXmlNode::setAttr(const char* key, float value)
- {
- char str[128];
- AZ::Locale::ScopedSerializationLocale localeResetter;
- sprintf_s(str, FLOAT_FMT, value);
- setAttr(key, str);
- }
- void CXmlNode::setAttr(const char* key, double value)
- {
- char str[128];
- AZ::Locale::ScopedSerializationLocale localeResetter;
- sprintf_s(str, DOUBLE_FMT, value);
- setAttr(key, str);
- }
- //////////////////////////////////////////////////////////////////////////
- void CXmlNode::setAttr(const char* key, int64 value)
- {
- char str[32];
- sprintf_s(str, "%" PRId64, value);
- setAttr(key, str);
- }
- //////////////////////////////////////////////////////////////////////////
- void CXmlNode::setAttr(const char* key, uint64 value, bool useHexFormat)
- {
- char str[32] = { 0 };
- if (useHexFormat)
- {
- sprintf_s(str, "%" PRIX64, value);
- }
- else
- {
- sprintf_s(str, "%" PRIu64, value);
- }
- setAttr(key, str);
- }
- void CXmlNode::setAttr(const char* key, const Ang3& value)
- {
- char str[128];
- AZ::Locale::ScopedSerializationLocale localeResetter;
- sprintf_s(str, FLOAT_FMT "," FLOAT_FMT "," FLOAT_FMT, value.x, value.y, value.z);
- setAttr(key, str);
- }
- void CXmlNode::setAttr(const char* key, const Vec3& value)
- {
- char str[128];
- AZ::Locale::ScopedSerializationLocale localeResetter;
- sprintf_s(str, FLOAT_FMT "," FLOAT_FMT "," FLOAT_FMT, value.x, value.y, value.z);
- setAttr(key, str);
- }
- void CXmlNode::setAttr(const char* key, const Vec4& value)
- {
- char str[128];
- AZ::Locale::ScopedSerializationLocale localeResetter;
- sprintf_s(str, FLOAT_FMT "," FLOAT_FMT "," FLOAT_FMT "," FLOAT_FMT, value.x, value.y, value.z, value.w);
- setAttr(key, str);
- }
- void CXmlNode::setAttr(const char* key, const Vec2& value)
- {
- char str[128];
- AZ::Locale::ScopedSerializationLocale localeResetter;
- sprintf_s(str, FLOAT_FMT "," FLOAT_FMT, value.x, value.y);
- setAttr(key, str);
- }
- void CXmlNode::setAttr(const char* key, const Quat& value)
- {
- char str[128];
- AZ::Locale::ScopedSerializationLocale localeResetter;
- sprintf_s(str, FLOAT_FMT "," FLOAT_FMT "," FLOAT_FMT "," FLOAT_FMT, value.w, value.v.x, value.v.y, value.v.z);
- setAttr(key, str);
- }
- //////////////////////////////////////////////////////////////////////////
- bool CXmlNode::getAttr(const char* key, int& value) const
- {
- const char* svalue = GetValue(key);
- if (svalue)
- {
- value = atoi(svalue);
- return true;
- }
- return false;
- }
- bool CXmlNode::getAttr(const char* key, unsigned int& value) const
- {
- const char* svalue = GetValue(key);
- if (svalue)
- {
- value = static_cast<unsigned int>(strtoul(svalue, NULL, 10));
- return true;
- }
- return false;
- }
- //////////////////////////////////////////////////////////////////////////
- bool CXmlNode::getAttr(const char* key, int64& value) const
- {
- const char* svalue = GetValue(key);
- if (svalue)
- {
- value = strtoll(key, nullptr, 10);
- return true;
- }
- return false;
- }
- //////////////////////////////////////////////////////////////////////////
- bool CXmlNode::getAttr(const char* key, uint64& value, bool useHexFormat) const
- {
- const char* svalue = GetValue(key);
- if (svalue)
- {
- value = strtoull(svalue, nullptr, useHexFormat ? 16 : 10);
- return true;
- }
- return false;
- }
- bool CXmlNode::getAttr(const char* key, bool& value) const
- {
- const char* svalue = GetValue(key);
- if (svalue)
- {
- if (azstricmp(svalue, "true") == 0)
- {
- value = true;
- }
- else if (azstricmp(svalue, "false") == 0)
- {
- value = false;
- }
- else
- {
- value = atoi(svalue) != 0;
- }
- return true;
- }
- return false;
- }
- bool CXmlNode::getAttr(const char* key, float& value) const
- {
- const char* svalue = GetValue(key);
- if (svalue)
- {
- AZ::Locale::ScopedSerializationLocale localeResetter;
- value = (float)atof(svalue);
- return true;
- }
- return false;
- }
- bool CXmlNode::getAttr(const char* key, double& value) const
- {
- const char* svalue = GetValue(key);
- if (svalue)
- {
- AZ::Locale::ScopedSerializationLocale localeResetter;
- value = atof(svalue);
- return true;
- }
- return false;
- }
- bool CXmlNode::getAttr(const char* key, Ang3& value) const
- {
- const char* svalue = GetValue(key);
- if (svalue)
- {
- AZ::Locale::ScopedSerializationLocale localeResetter;
- float x, y, z;
- if (azsscanf(svalue, "%f,%f,%f", &x, &y, &z) == 3)
- {
- value(x, y, z);
- return true;
- }
- }
- return false;
- }
- //////////////////////////////////////////////////////////////////////////
- bool CXmlNode::getAttr(const char* key, Vec3& value) const
- {
- const char* svalue = GetValue(key);
- if (svalue)
- {
- AZ::Locale::ScopedSerializationLocale localeResetter;
- float x, y, z;
- if (azsscanf(svalue, "%f,%f,%f", &x, &y, &z) == 3)
- {
- value = Vec3(x, y, z);
- return true;
- }
- }
- return false;
- }
- //////////////////////////////////////////////////////////////////////////
- bool CXmlNode::getAttr(const char* key, Vec4& value) const
- {
- const char* svalue = GetValue(key);
- if (svalue)
- {
- AZ::Locale::ScopedSerializationLocale localeResetter;
- float x, y, z, w;
- if (azsscanf(svalue, "%f,%f,%f,%f", &x, &y, &z, &w) == 4)
- {
- value = Vec4(x, y, z, w);
- return true;
- }
- }
- return false;
- }
- //////////////////////////////////////////////////////////////////////////
- bool CXmlNode::getAttr(const char* key, Vec2& value) const
- {
- const char* svalue = GetValue(key);
- if (svalue)
- {
- AZ::Locale::ScopedSerializationLocale localeResetter;
- float x, y;
- if (azsscanf(svalue, "%f,%f", &x, &y) == 2)
- {
- value = Vec2(x, y);
- return true;
- }
- }
- return false;
- }
- //////////////////////////////////////////////////////////////////////////
- bool CXmlNode::getAttr(const char* key, Quat& value) const
- {
- const char* svalue = GetValue(key);
- if (svalue)
- {
- AZ::Locale::ScopedSerializationLocale localeResetter;
- float w, x, y, z;
- if (azsscanf(svalue, "%f,%f,%f,%f", &w, &x, &y, &z) == 4)
- {
- if (fabs(w) > VEC_EPSILON || fabs(x) > VEC_EPSILON || fabs(y) > VEC_EPSILON || fabs(z) > VEC_EPSILON)
- {
- //[AlexMcC|02.03.10] directly assign to members to avoid triggering the assert in Quat() with data from bad assets
- value.w = w;
- value.v = Vec3(x, y, z);
- return value.IsValid();
- }
- }
- }
- return false;
- }
- //////////////////////////////////////////////////////////////////////////
- bool CXmlNode::getAttr(const char* key, ColorB& value) const
- {
- const char* svalue = GetValue(key);
- if (svalue)
- {
- AZ::Locale::ScopedSerializationLocale localeResetter;
- unsigned int r, g, b, a = 255;
- int numFound = azsscanf(svalue, "%u,%u,%u,%u", &r, &g, &b, &a);
- if (numFound == 3 || numFound == 4)
- {
- // If we only found 3 values, a should be unchanged, and still be 255
- if (r < 256 && g < 256 && b < 256 && a < 256)
- {
- value = ColorB(static_cast<uint8>(r), static_cast<uint8>(g), static_cast<uint8>(b), static_cast<uint8>(a));
- return true;
- }
- }
- }
- return false;
- }
- XmlNodeRef CXmlNode::findChild(const char* tag) const
- {
- if (m_pChilds)
- {
- XmlNodes& childs = *m_pChilds;
- for (int i = 0, num = static_cast<int>(childs.size()); i < num; ++i)
- {
- if (childs[i]->isTag(tag))
- {
- return childs[i];
- }
- }
- }
- return 0;
- }
- void CXmlNode::removeChild(const XmlNodeRef& node)
- {
- if (m_pChilds)
- {
- XmlNodes::iterator it = std::find(m_pChilds->begin(), m_pChilds->end(), (IXmlNode*)node);
- if (it != m_pChilds->end())
- {
- ReleaseChild(*it);
- m_pChilds->erase(it);
- }
- }
- }
- void CXmlNode::removeAllChilds()
- {
- removeAllChildsImpl();
- }
- //////////////////////////////////////////////////////////////////////////
- void CXmlNode::deleteChild(const char* tag)
- {
- if (m_pChilds)
- {
- XmlNodes& childs = *m_pChilds;
- for (int i = 0, num = static_cast<int>(childs.size()); i < num; ++i)
- {
- if (childs[i]->isTag(tag))
- {
- ReleaseChild(childs[i]);
- childs.erase(childs.begin() + i);
- return;
- }
- }
- }
- }
- //! Adds new child node.
- void CXmlNode::addChild(const XmlNodeRef& node)
- {
- if (!m_pChilds)
- {
- m_pChilds = new XmlNodes;
- }
- assert(node != 0);
- IXmlNode* pNode = ((IXmlNode*)node);
- pNode->AddRef();
- m_pChilds->push_back(pNode);
- pNode->setParent(this);
- };
- void CXmlNode::setParent(const XmlNodeRef& inNewParent)
- {
- // note, parent ptrs are not ref counted
- m_parent = inNewParent;
- }
- XmlNodeRef CXmlNode::newChild(const char* tagName)
- {
- XmlNodeRef node = createNode(tagName);
- addChild(node);
- return node;
- }
- //! Get XML Node child nodes.
- XmlNodeRef CXmlNode::getChild(int i) const
- {
- assert(m_pChilds);
- XmlNodes& childs = *m_pChilds;
- assert(i >= 0 && i < (int)childs.size());
- return childs[i];
- }
- //////////////////////////////////////////////////////////////////////////
- void CXmlNode::copyAttributes(XmlNodeRef fromNode)
- {
- IXmlNode* inode = fromNode;
- CXmlNode* n = (CXmlNode*)inode;
- assert(n);
- PREFAST_ASSUME(n);
- if (n != this)
- {
- if (n->m_pAttributes)
- {
- if (!m_pAttributes)
- {
- m_pAttributes = new XmlAttributes;
- }
- if (n->m_pStringPool == m_pStringPool)
- {
- *m_pAttributes = *(n->m_pAttributes);
- }
- else
- {
- XmlAttributes& lhs = *m_pAttributes;
- const XmlAttributes& rhs = *(n->m_pAttributes);
- lhs.resize(rhs.size());
- for (size_t i = 0; i < rhs.size(); ++i)
- {
- lhs[i].key = m_pStringPool->AddString(rhs[i].key);
- lhs[i].value = m_pStringPool->AddString(rhs[i].value);
- }
- }
- }
- else
- {
- SAFE_DELETE(m_pAttributes);
- }
- }
- }
- //////////////////////////////////////////////////////////////////////////
- bool CXmlNode::getAttributeByIndex(int index, const char** key, const char** value)
- {
- if (m_pAttributes)
- {
- XmlAttributes::iterator it = m_pAttributes->begin();
- if (it != m_pAttributes->end())
- {
- std::advance(it, index);
- if (it != m_pAttributes->end())
- {
- *key = it->key;
- *value = it->value;
- return true;
- }
- }
- }
- return false;
- }
- //////////////////////////////////////////////////////////////////////////
- bool CXmlNode::getAttributeByIndex(int index, XmlString& key, XmlString& value)
- {
- if (m_pAttributes)
- {
- XmlAttributes::iterator it = m_pAttributes->begin();
- if (it != m_pAttributes->end())
- {
- std::advance(it, index);
- if (it != m_pAttributes->end())
- {
- key = it->key;
- value = it->value;
- return true;
- }
- }
- }
- return false;
- }
- //////////////////////////////////////////////////////////////////////////
- static void AddTabsToString(XmlString& xml, int level)
- {
- static const char* tabs[] = {
- "",
- " ",
- " ",
- " ",
- " ",
- " ",
- " ",
- " ",
- " ",
- " ",
- " ",
- " ",
- };
- // Add tabs.
- if (level < sizeof(tabs) / sizeof(tabs[0]))
- {
- xml += tabs[level];
- }
- else
- {
- for (int i = 0; i < level; i++)
- {
- xml += " ";
- }
- }
- }
- //////////////////////////////////////////////////////////////////////////
- bool CXmlNode::IsValidXmlString(const char* str) const
- {
- int len = static_cast<int>(strlen(str));
- {
- // Prevents invalid characters not from standard ASCII set to propagate to xml.
- // A bit of hack for efficiency, fixes input string in place.
- char* s = const_cast<char*>(str);
- for (int i = 0; i < len; i++)
- {
- if ((unsigned char)s[i] > 0x7F)
- {
- s[i] = ' ';
- }
- }
- }
- if (strcspn(str, "\"\'&><") == len)
- {
- return true;
- }
- return false;
- }
- //////////////////////////////////////////////////////////////////////////
- XmlString CXmlNode::MakeValidXmlString(const XmlString& instr) const
- {
- XmlString str = instr;
- // check if str contains any invalid characters
- AZ::StringFunc::Replace(str, "&", "&");
- AZ::StringFunc::Replace(str, "\"", """);
- AZ::StringFunc::Replace(str, "\'", "'");
- AZ::StringFunc::Replace(str, "<", "<");
- AZ::StringFunc::Replace(str, ">", ">");
- AZ::StringFunc::Replace(str, "...", ">");
- AZ::StringFunc::Replace(str, "\n", " ");
- return str;
- }
- void CXmlNode::ReleaseChild(IXmlNode* pChild)
- {
- if (pChild)
- {
- if (pChild->getParent() == this) // if check to handle shared children which are supported by the CXmlNode impl
- {
- pChild->setParent(NULL);
- }
- pChild->Release();
- }
- }
- void CXmlNode::removeAllChildsImpl()
- {
- if (m_pChilds)
- {
- for (XmlNodes::iterator iter = m_pChilds->begin(), endIter = m_pChilds->end(); iter != endIter; ++iter)
- {
- ReleaseChild(*iter);
- }
- SAFE_DELETE(m_pChilds);
- }
- }
- //////////////////////////////////////////////////////////////////////////
- void CXmlNode::AddToXmlString(XmlString& xml, int level, AZ::IO::HandleType fileHandle, size_t chunkSize) const
- {
- if (fileHandle != AZ::IO::InvalidHandle && chunkSize > 0)
- {
- auto fileIoBase = AZ::IO::FileIOBase::GetInstance();
- AZ_Assert(fileIoBase != nullptr, "FileIOBase is expected to be initialized for CXmlNode");
- size_t len = xml.length();
- if (len >= chunkSize)
- {
- fileIoBase->Write(fileHandle, xml.c_str(), len);
- xml.assign (""); // should not free memory and does not!
- }
- }
- AddTabsToString(xml, level);
- const bool bHasChildren = (m_pChilds && !m_pChilds->empty());
- // Begin Tag
- if (!m_pAttributes || m_pAttributes->empty())
- {
- xml += "<";
- if (m_isProcessingInstruction)
- {
- xml += "?";
- }
- xml += m_tag;
- if (*m_content == 0 && !bHasChildren)
- {
- if (m_isProcessingInstruction)
- {
- xml += "?>\n";
- }
- else
- {
- // Compact tag form.
- xml += " />\n";
- }
- return;
- }
- xml += ">";
- }
- else
- {
- xml += "<";
- if (m_isProcessingInstruction)
- {
- xml += "?";
- }
- xml += m_tag;
- xml += " ";
- // Put attributes.
- for (XmlAttributes::const_iterator it = m_pAttributes->begin(); it != m_pAttributes->end(); )
- {
- xml += it->key;
- xml += "=\"";
- if (IsValidXmlString(it->value))
- {
- xml += it->value;
- }
- else
- {
- xml += MakeValidXmlString(it->value);
- }
- ++it;
- if (it != m_pAttributes->end())
- {
- xml += "\" ";
- }
- else
- {
- xml += "\"";
- }
- }
- if (*m_content == 0 && !bHasChildren)
- {
- if (m_isProcessingInstruction)
- {
- xml += "?>\n";
- }
- else
- {
- // Compact tag form.
- xml += "/>\n";
- }
- return;
- }
- xml += ">";
- }
- // Put node content.
- if (IsValidXmlString(m_content))
- {
- xml += m_content;
- }
- else
- {
- xml += MakeValidXmlString(m_content);
- }
- if (!bHasChildren)
- {
- xml += "</";
- xml += m_tag;
- xml += ">\n";
- return;
- }
- xml += "\n";
- // Add sub nodes.
- for (XmlNodes::iterator it = m_pChilds->begin(), itEnd = m_pChilds->end(); it != itEnd; ++it)
- {
- IXmlNode* node = *it;
- ((CXmlNode*)node)->AddToXmlString(xml, level + 1, fileHandle, chunkSize);
- }
- // Add tabs.
- AddTabsToString(xml, level);
- xml += "</";
- xml += m_tag;
- xml += ">\n";
- }
- #if !defined(APPLE) && !defined(LINUX) && !defined(AZ_LEGACY_CRYSYSTEM_TRAIT_HASSTPCPY)
- ILINE static char* stpcpy(char* dst, const char* src)
- {
- while (src[0])
- {
- dst[0] = src[0];
- dst++;
- src++;
- }
- return dst;
- }
- #endif
- char* CXmlNode::AddToXmlStringUnsafe(char* xml, int level, char* endPtr, AZ::IO::HandleType fileHandle, size_t chunkSize) const
- {
- const bool bHasChildren = (m_pChilds && !m_pChilds->empty());
- for (int i = 0; i < level; i++)
- {
- *(xml++) = ' ';
- *(xml++) = ' ';
- }
- // Begin Tag
- if (!m_pAttributes || m_pAttributes->empty())
- {
- *(xml++) = '<';
- xml = stpcpy(xml, m_tag);
- if (*m_content == 0 && !bHasChildren)
- {
- *(xml++) = '/';
- *(xml++) = '>';
- *(xml++) = '\n';
- return xml;
- }
- *(xml++) = '>';
- }
- else
- {
- *(xml++) = '<';
- xml = stpcpy(xml, m_tag);
- *(xml++) = ' ';
- // Put attributes.
- for (XmlAttributes::const_iterator it = m_pAttributes->begin(); it != m_pAttributes->end(); )
- {
- xml = stpcpy(xml, it->key);
- *(xml++) = '=';
- *(xml++) = '\"';
- #ifndef _RELEASE
- if (it->value[strcspn(it->value, "\"\'&><")])
- {
- __debugbreak();
- }
- #endif
- xml = stpcpy(xml, it->value);
- ++it;
- *(xml++) = '\"';
- if (it != m_pAttributes->end())
- {
- *(xml++) = ' ';
- }
- }
- if (*m_content == 0 && !bHasChildren)
- {
- // Compact tag form.
- *(xml++) = '/';
- *(xml++) = '>';
- *(xml++) = '\n';
- return xml;
- }
- *(xml++) = '>';
- }
- #ifndef _RELEASE
- if (m_content[strcspn(m_content, "\"\'&><")])
- {
- __debugbreak();
- }
- #endif
- xml = stpcpy(xml, m_content);
- if (!bHasChildren)
- {
- *(xml++) = '<';
- *(xml++) = '/';
- xml = stpcpy(xml, m_tag);
- *(xml++) = '>';
- *(xml++) = '\n';
- return xml;
- }
- *(xml++) = '\n';
- // Add sub nodes.
- for (XmlNodes::iterator it = m_pChilds->begin(), itEnd = m_pChilds->end(); it != itEnd; ++it)
- {
- IXmlNode* node = *it;
- xml = ((CXmlNode*)node)->AddToXmlStringUnsafe(xml, level + 1, endPtr, fileHandle, chunkSize);
- }
- for (int i = 0; i < level; i++)
- {
- *(xml++) = ' ';
- *(xml++) = ' ';
- }
- *(xml++) = '<';
- *(xml++) = '/';
- xml = stpcpy(xml, m_tag);
- *(xml++) = '>';
- *(xml++) = '\n';
- assert(xml < endPtr);
- return xml;
- }
- //////////////////////////////////////////////////////////////////////////
- IXmlStringData* CXmlNode::getXMLData(int nReserveMem) const
- {
- CXmlStringData* pStrData = new CXmlStringData;
- pStrData->m_string.reserve(nReserveMem);
- AddToXmlString(pStrData->m_string, 0);
- return pStrData;
- }
- //////////////////////////////////////////////////////////////////////////
- XmlString CXmlNode::getXML(int level) const
- {
- XmlString xml;
- xml = "";
- xml.reserve(1024);
- AddToXmlString(xml, level);
- return xml;
- }
- // TODO: those 2 saving functions are a bit messy. should probably make a separate one for the use of PlatformAPI
- bool CXmlNode::saveToFile(const char* fileName)
- {
- if (!fileName)
- {
- return false;
- }
- {
- AZ::IO::HandleType fileHandle = gEnv->pCryPak->FOpen(fileName, "wt");
- if (fileHandle != AZ::IO::InvalidHandle)
- {
- #ifdef WIN32
- XmlString xml = getXML(); // this would not work in consoles because the size limits in strings
- const char* sxml = (const char*)xml;
- gEnv->pCryPak->FWrite(sxml, xml.length(), fileHandle);
- gEnv->pCryPak->FClose(fileHandle);
- return true;
- #else
- constexpr size_t chunkSizeBytes = (15 * 1024);
- bool ret = saveToFile(fileName, chunkSizeBytes, fileHandle);
- gEnv->pCryPak->FClose(fileHandle);
- return ret;
- #endif
- }
- return false;
- }
- }
- bool CXmlNode::saveToFile([[maybe_unused]] const char* fileName, size_t chunkSize, AZ::IO::HandleType fileHandle)
- {
- if (AZ::IO::SystemFile::Exists(fileName) && !AZ::IO::SystemFile::IsWritable(fileName))
- {
- AZ::IO::SystemFile::SetWritable(fileName, true);
- }
- if (chunkSize < 256 * 1024) // make at least 256k
- {
- chunkSize = 256 * 1024;
- }
- XmlString xml;
- xml.assign ("");
- xml.reserve(chunkSize * 2); // we reserve double memory, as writing in chunks is not really writing in fixed blocks but a bit fuzzy
- auto fileIoBase = AZ::IO::FileIOBase::GetInstance();
- AZ_Assert(fileIoBase != nullptr, "FileIOBase is expected to be initialized for CXmlNode");
- if (fileHandle == AZ::IO::InvalidHandle)
- {
- return false;
- }
- AddToXmlString(xml, 0, fileHandle, chunkSize);
- size_t len = xml.length();
- if (len > 0)
- {
- fileIoBase->Write(fileHandle, xml.c_str(), len);
- }
- xml.clear(); // xml.resize(0) would not reclaim memory
- return true;
- }
- /**
- ******************************************************************************
- * XmlParserImp class.
- ******************************************************************************
- */
- class XmlParserImp
- : public IXmlStringPool
- {
- public:
- explicit XmlParserImp(bool bReuseStrings);
- ~XmlParserImp();
- void ParseBegin(bool bCleanPools);
- XmlNodeRef ParseFile(const char* filename, XmlString& errorString, bool bCleanPools);
- XmlNodeRef ParseBuffer(const char* buffer, size_t bufLen, XmlString& errorString, bool bCleanPools, bool bSuppressWarnings = false);
- void ParseEnd();
- // Add new string to pool.
- const char* AddString(const char* str) { return m_stringPool.Append(str, (int)strlen(str)); }
- protected:
- void onStartElement(const char* tagName, const char** atts);
- void onEndElement(const char* tagName);
- void onRawData(const char* data);
- static void startElement(void* userData, const char* name, const char** atts)
- {
- ((XmlParserImp*)userData)->onStartElement(name, atts);
- }
- static void endElement(void* userData, const char* name)
- {
- ((XmlParserImp*)userData)->onEndElement(name);
- }
- static void characterData(void* userData, const char* s, int len)
- {
- char str[32700];
- if (len > sizeof(str) - 1)
- {
- assert(0);
- len = sizeof(str) - 1;
- }
- // Note that XML buffer userData has no terminating '\0'.
- memcpy(str, s, len);
- str[len] = 0;
- ((XmlParserImp*)userData)->onRawData(str);
- }
- void CleanStack();
- struct SStackEntity
- {
- XmlNodeRef node;
- std::vector<IXmlNode*> childs; //TODO: is it worth lazily initializing this, like CXmlNode::m_pChilds?
- };
- // First node will become root node.
- std::vector<SStackEntity> m_nodeStack;
- int m_nNodeStackTop;
- XmlNodeRef m_root;
- XML_Parser m_parser;
- CSimpleStringPool m_stringPool;
- };
- //////////////////////////////////////////////////////////////////////////
- void XmlParserImp::CleanStack()
- {
- m_nNodeStackTop = 0;
- for (int i = 0, num = static_cast<int>(m_nodeStack.size()); i < num; i++)
- {
- m_nodeStack[i].node = 0;
- m_nodeStack[i].childs.resize(0);
- }
- }
- /**
- ******************************************************************************
- * XmlParserImp
- ******************************************************************************
- */
- void XmlParserImp::onStartElement(const char* tagName, const char** atts)
- {
- CXmlNode* pCNode = new CXmlNode;
- pCNode->m_pStringPool = this;
- pCNode->m_pStringPool->AddRef();
- pCNode->m_tag = AddString(tagName);
- XmlNodeRef node = pCNode;
- m_nNodeStackTop++;
- if (m_nNodeStackTop >= (int)m_nodeStack.size())
- {
- m_nodeStack.resize(m_nodeStack.size() * 2);
- }
- m_nodeStack[m_nNodeStackTop].node = pCNode;
- m_nodeStack[m_nNodeStackTop - 1].childs.push_back(pCNode);
- if (!m_root)
- {
- m_root = node;
- }
- else
- {
- pCNode->m_parent = (IXmlNode*)m_nodeStack[m_nNodeStackTop - 1].node;
- node->AddRef(); // Childs need to be add refed.
- }
- node->setLine(static_cast<int>(XML_GetCurrentLineNumber((XML_Parser)m_parser)));
- // Call start element callback.
- int i = 0;
- int numAttrs = 0;
- while (atts[i] != 0)
- {
- numAttrs++;
- i += 2;
- }
- if (numAttrs > 0)
- {
- i = 0;
- if (!pCNode->m_pAttributes)
- {
- pCNode->m_pAttributes = new XmlAttributes;
- }
- XmlAttributes& nodeAtts = *(pCNode->m_pAttributes);
- nodeAtts.resize(numAttrs);
- int nAttr = 0;
- while (atts[i] != 0)
- {
- nodeAtts[nAttr].key = AddString(atts[i]);
- nodeAtts[nAttr].value = AddString(atts[i + 1]);
- nAttr++;
- i += 2;
- }
- // Sort attributes.
- //std::sort( pCNode->m_attributes.begin(),pCNode->m_attributes.end() );
- }
- }
- void XmlParserImp::onEndElement([[maybe_unused]] const char* tagName)
- {
- assert(m_nNodeStackTop > 0);
- if (m_nNodeStackTop > 0)
- {
- // Copy current childs to the parent.
- SStackEntity& entry = m_nodeStack[m_nNodeStackTop];
- CXmlNode* currNode = static_cast<CXmlNode*>(static_cast<IXmlNode*>(entry.node));
- if (!entry.childs.empty())
- {
- if (!currNode->m_pChilds)
- {
- currNode->m_pChilds = new CXmlNode::XmlNodes;
- }
- *currNode->m_pChilds = entry.childs;
- }
- entry.childs.resize(0);
- entry.node = NULL;
- }
- m_nNodeStackTop--;
- }
- void XmlParserImp::onRawData(const char* data)
- {
- assert(m_nNodeStackTop >= 0);
- if (m_nNodeStackTop >= 0)
- {
- size_t len = strlen(data);
- if (len > 0)
- {
- if (strspn(data, "\r\n\t ") != len)
- {
- CXmlNode* node = (CXmlNode*)(IXmlNode*)m_nodeStack[m_nNodeStackTop].node;
- if (*node->m_content != '\0')
- {
- node->m_content = m_stringPool.ReplaceString(node->m_content, data);
- }
- else
- {
- node->m_content = AddString(data);
- }
- }
- }
- }
- }
- //////////////////////////////////////////////////////////////////////////
- XmlParserImp::XmlParserImp(bool bReuseStrings)
- : m_nNodeStackTop(0)
- , m_parser(NULL)
- , m_stringPool(bReuseStrings)
- {
- m_nodeStack.resize(32);
- CleanStack();
- }
- XmlParserImp::~XmlParserImp()
- {
- ParseEnd();
- }
- namespace
- {
- void* custom_xml_malloc(size_t nSize)
- {
- return azmalloc(nSize);
- }
- void* custom_xml_realloc(void* p, size_t nSize)
- {
- return azrealloc(p, nSize);
- }
- void custom_xml_free(void* p)
- {
- azfree(p);
- }
- }
- //////////////////////////////////////////////////////////////////////////
- void XmlParserImp::ParseBegin(bool bCleanPools)
- {
- m_root = 0;
- CleanStack();
- if (bCleanPools)
- {
- m_stringPool.Clear();
- }
- XML_Memory_Handling_Suite memHandler;
- memHandler.malloc_fcn = custom_xml_malloc;
- memHandler.realloc_fcn = custom_xml_realloc;
- memHandler.free_fcn = custom_xml_free;
- m_parser = XML_ParserCreate_MM(NULL, &memHandler, NULL);
- XML_SetUserData(m_parser, this);
- XML_SetElementHandler(m_parser, startElement, endElement);
- XML_SetCharacterDataHandler(m_parser, characterData);
- XML_SetEncoding(m_parser, "utf-8");
- }
- //////////////////////////////////////////////////////////////////////////
- void XmlParserImp::ParseEnd()
- {
- if (m_parser)
- {
- XML_ParserFree(m_parser);
- }
- m_parser = 0;
- }
- //////////////////////////////////////////////////////////////////////////
- XmlNodeRef XmlParserImp::ParseBuffer(const char* buffer, size_t bufLen, XmlString& errorString, bool bCleanPools, bool bSuppressWarnings)
- {
- static const char* const errorPrefix = "XML parser: ";
- XmlNodeRef root = 0;
- // Let's try to parse the buffer as binary XML
- {
- XMLBinary::XMLBinaryReader reader;
- XMLBinary::XMLBinaryReader::EResult result;
- root = reader.LoadFromBuffer(XMLBinary::XMLBinaryReader::eBufferMemoryHandling_MakeCopy, buffer, bufLen, result);
- if (root)
- {
- return root;
- }
- if (result != XMLBinary::XMLBinaryReader::eResult_NotBinXml)
- {
- const char* const str = reader.GetErrorDescription();
- errorString = str;
- if (!bSuppressWarnings)
- {
- CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "%s%s (data size: %u)", errorPrefix, str, static_cast<unsigned>(bufLen));
- }
- return 0;
- }
- }
- // This is not binary XML, so let's use text XML parser.
- {
- ParseBegin(bCleanPools);
- m_stringPool.SetBlockSize(static_cast<unsigned>(bufLen) / 16);
- if (XML_Parse(m_parser, buffer, static_cast<int>(bufLen), 1))
- {
- root = m_root;
- }
- else
- {
- char str[1024];
- sprintf_s(str, "%s%s at line %d", errorPrefix, XML_ErrorString(XML_GetErrorCode(m_parser)), (int)XML_GetCurrentLineNumber(m_parser));
- errorString = str;
- if (!bSuppressWarnings)
- {
- CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "%s", str);
- }
- }
- m_root = 0;
- ParseEnd();
- }
- return root;
- }
- //////////////////////////////////////////////////////////////////////////
- XmlNodeRef XmlParserImp::ParseFile(const char* filename, XmlString& errorString, bool bCleanPools)
- {
- if (!filename)
- {
- return 0;
- }
- static const char* const errorPrefix = "XML reader: ";
- XmlNodeRef root = 0;
- char* pFileContents = 0;
- size_t fileSize = 0;
- char str[1024];
- AZStd::fixed_string<256> adjustedFilename;
- AZStd::fixed_string<256> pakPath;
- if (fileSize <= 0)
- {
- CCryFile xmlFile;
- if (!xmlFile.Open(filename, "rb"))
- {
- sprintf_s(str, "%sCan't open file (%s)", errorPrefix, filename);
- errorString = str;
- CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "%s", str);
- return 0;
- }
- fileSize = xmlFile.GetLength();
- if (fileSize <= 0)
- {
- sprintf_s(str, "%sFile is empty (%s)", errorPrefix, filename);
- errorString = str;
- CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "%s", str);
- return 0;
- }
- pFileContents = new char[fileSize];
- if (!pFileContents)
- {
- sprintf_s(str, "%sCan't allocate %u bytes of memory (%s)", errorPrefix, static_cast<unsigned>(fileSize), filename);
- errorString = str;
- CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "%s", str);
- return 0;
- }
- if (xmlFile.ReadRaw(pFileContents, fileSize) != fileSize)
- {
- delete [] pFileContents;
- sprintf_s(str, "%sCan't read file (%s)", errorPrefix, filename);
- errorString = str;
- CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "%s", str);
- return 0;
- }
- AZ::IO::FixedMaxPath resolvedPath(AZ::IO::PosixPathSeparator);
- auto fileIoBase = AZ::IO::FileIOBase::GetInstance();
- AZ_Assert(fileIoBase != nullptr, "FileIOBase is expected to be initialized for CXmlNode");
- if (fileIoBase->ResolvePath(resolvedPath, xmlFile.GetFilename()))
- {
- adjustedFilename = resolvedPath.MakePreferred().Native();
- }
- if (fileIoBase->ResolvePath(resolvedPath, xmlFile.GetPakPath()))
- {
- pakPath = resolvedPath.MakePreferred().Native();
- }
- }
- XMLBinary::XMLBinaryReader reader;
- XMLBinary::XMLBinaryReader::EResult result;
- root = reader.LoadFromBuffer(XMLBinary::XMLBinaryReader::eBufferMemoryHandling_TakeOwnership, pFileContents, fileSize, result);
- if (root)
- {
- return root;
- }
- if (result != XMLBinary::XMLBinaryReader::eResult_NotBinXml)
- {
- delete [] pFileContents;
- sprintf_s(str, "%s%s (%s)", errorPrefix, reader.GetErrorDescription(), filename);
- errorString = str;
- CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "%s", str);
- return 0;
- }
- else
- {
- // not binary XML - refuse to load if in scripts dir and not in bin xml to help reduce hacking
- // wish we could compile the text xml parser out, but too much work to get everything moved over
- constexpr AZStd::fixed_string<32> strScripts{"Scripts/"};
- // exclude files and PAKs from Mods folder
- constexpr AZStd::fixed_string<8> modsStr{"Mods/"};
- if (_strnicmp(filename, strScripts.c_str(), strScripts.length()) == 0 &&
- _strnicmp(adjustedFilename.c_str(), modsStr.c_str(), modsStr.length()) != 0 &&
- _strnicmp(pakPath.c_str(), modsStr.c_str(), modsStr.length()) != 0)
- {
- #ifdef _RELEASE
- CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "Non binary XML found in scripts dir (%s)", filename);
- #endif
- }
- }
- {
- ParseBegin(bCleanPools);
- m_stringPool.SetBlockSize(static_cast<unsigned>(fileSize / 16));
- if (XML_Parse(m_parser, pFileContents, static_cast<int>(fileSize), 1))
- {
- root = m_root;
- }
- else
- {
- sprintf_s(str, "%s%s at line %d (%s)", errorPrefix, XML_ErrorString(XML_GetErrorCode(m_parser)), (int)XML_GetCurrentLineNumber(m_parser), filename);
- errorString = str;
- CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "%s", str);
- }
- m_root = 0;
- ParseEnd();
- }
- delete [] pFileContents;
- return root;
- }
- XmlParser::XmlParser(bool bReuseStrings)
- {
- m_nRefCount = 0;
- m_pImpl = new XmlParserImp(bReuseStrings);
- m_pImpl->AddRef();
- }
- XmlParser::~XmlParser()
- {
- m_pImpl->Release();
- }
- //////////////////////////////////////////////////////////////////////////
- XmlNodeRef XmlParser::ParseBuffer(const char* buffer, int nBufLen, bool bCleanPools, bool bSuppressWarnings)
- {
- m_errorString = "";
- return m_pImpl->ParseBuffer(buffer, nBufLen, m_errorString, bCleanPools, bSuppressWarnings);
- }
- //////////////////////////////////////////////////////////////////////////
- XmlNodeRef XmlParser::ParseFile(const char* filename, bool bCleanPools)
- {
- m_errorString = "";
- return m_pImpl->ParseFile(filename, m_errorString, bCleanPools);
- }
- //////////////////////////////////////////////////////////////////////////
- //
- // Implements special reusable XmlNode for XmlNode pool
- //
- //////////////////////////////////////////////////////////////////////////
- CXmlNodeReuse::CXmlNodeReuse(const char* tag, CXmlNodePool* pPool)
- : m_pPool(pPool)
- {
- SAFE_RELEASE(m_pStringPool);
- m_pStringPool = m_pPool->GetStringPool();
- m_pStringPool->AddRef();
- m_tag = m_pStringPool->AddString(tag);
- }
- void CXmlNodeReuse::Release()
- {
- m_pPool->OnRelease(m_nRefCount, this);
- CXmlNode::Release();
- }
- //////////////////////////////////////////////////////////////////////////
- //
- // Pool of reusable XML nodes with shared string pool
- //
- //////////////////////////////////////////////////////////////////////////
- CXmlNodePool::CXmlNodePool(unsigned int nBlockSize, bool bReuseStrings)
- {
- m_pStringPool = new CXmlStringPool(bReuseStrings);
- assert(m_pStringPool != 0);
- // in order to avoid memory fragmentation
- // allocates 1Mb buffer for shared string pool
- static_cast<CXmlStringPool*>(m_pStringPool)->SetBlockSize(nBlockSize);
- m_pStringPool->AddRef();
- m_nAllocated = 0;
- }
- CXmlNodePool::~CXmlNodePool()
- {
- while (!m_pNodePool.empty())
- {
- CXmlNodeReuse* pNode = m_pNodePool.top();
- m_pNodePool.pop();
- pNode->Release();
- }
- m_pStringPool->Release();
- }
- XmlNodeRef CXmlNodePool::GetXmlNode(const char* sNodeName)
- {
- CXmlNodeReuse* pNode = 0;
- // NOTE: at the moment xml node pool is dedicated for statistics nodes only
- // first at all check if we have already free node
- if (!m_pNodePool.empty())
- {
- pNode = m_pNodePool.top();
- m_pNodePool.pop();
- // init it to new node name
- pNode->setTag(sNodeName);
- m_nAllocated++;
- //if (0 == m_nAllocated % 1000)
- //CryLog("[CXmlNodePool]: already reused nodes [%d]", m_nAllocated);
- }
- else
- {
- // there is no free nodes so create new one
- // later it will be reused as soon as no external references left
- pNode = new CXmlNodeReuse(sNodeName, this);
- assert(pNode != 0);
- // increase ref counter for reusing node later
- pNode->AddRef();
- m_nAllocated++;
- //if (0 == m_nAllocated % 1000)
- //CryLog("[CXmlNodePool]: already allocated nodes [%d]", m_nAllocated);
- }
- return pNode;
- }
- void CXmlNodePool::OnRelease(int iRefCount, void* pThis)
- {
- // each reusable node call OnRelease before parent release
- // since we keep reference on xml node so when ref count equals
- // to 2 means that it is last external object releases reference
- // to reusable node and it can be save for reuse later
- if (2 == iRefCount)
- {
- CXmlNodeReuse* pNode = static_cast<CXmlNodeReuse*>(pThis);
- pNode->removeAllChilds();
- pNode->removeAllAttributes();
- m_pNodePool.push(pNode);
- // decrease totally allocated by xml node pool counter
- // when counter equals to zero it means that all external
- // objects do not have references to allocated reusable node
- // at that point it is safe to clear shared string pool
- m_nAllocated--;
- if (0 == m_nAllocated)
- {
- //CryLog("[CXmlNodePool]: clear shared string pool");
- static_cast<CXmlStringPool*>(m_pStringPool)->Clear();
- }
- }
- }
- #undef SCOPED_LOCALE_RESETTER
|