12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742 |
- /*
- * 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 <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"
- #if AZ_TRAIT_OS_PLATFORM_APPLE || defined(AZ_PLATFORM_LINUX)
- #include <clocale>
- #include <locale>
- class LocaleResetter
- {
- public:
- LocaleResetter()
- {
- m_oldLocale = std::setlocale(LC_NUMERIC, "C");
- }
- ~LocaleResetter()
- {
- std::setlocale(LC_NUMERIC, m_oldLocale);
- }
- private:
- char* m_oldLocale;
- };
- #define SCOPED_LOCALE_RESETTER LocaleResetter l
- #else
- // noop on Windows
- #define SCOPED_LOCALE_RESETTER
- #endif
- // 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];
- SCOPED_LOCALE_RESETTER;
- sprintf_s(str, FLOAT_FMT, value);
- setAttr(key, str);
- }
- void CXmlNode::setAttr(const char* key, double value)
- {
- char str[128];
- SCOPED_LOCALE_RESETTER;
- 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];
- 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];
- SCOPED_LOCALE_RESETTER;
- 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];
- SCOPED_LOCALE_RESETTER;
- 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];
- SCOPED_LOCALE_RESETTER;
- 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];
- SCOPED_LOCALE_RESETTER;
- 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];
- SCOPED_LOCALE_RESETTER;
- 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(key, 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)
- {
- value = (float)atof(svalue);
- return true;
- }
- return false;
- }
- bool CXmlNode::getAttr(const char* key, double& value) const
- {
- const char* svalue = GetValue(key);
- if (svalue)
- {
- value = atof(svalue);
- return true;
- }
- return false;
- }
- bool CXmlNode::getAttr(const char* key, Ang3& value) const
- {
- const char* svalue = GetValue(key);
- if (svalue)
- {
- SCOPED_LOCALE_RESETTER;
- 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)
- {
- SCOPED_LOCALE_RESETTER;
- 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)
- {
- SCOPED_LOCALE_RESETTER;
- 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)
- {
- SCOPED_LOCALE_RESETTER;
- 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)
- {
- SCOPED_LOCALE_RESETTER;
- 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)
- {
- 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
|