SPEC-7531 Change Code/CryEngine to Code/Legacy (#1634)
* git mv Code\CryEngine Code\Legacy * redirecting CMakeLists.txt * fixing uic warning * Some more CryEngine mentions * validation scripts Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
#
|
||||
# Copyright (c) Contributors to the Open 3D Engine Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
#
|
||||
#
|
||||
|
||||
ly_add_target(
|
||||
NAME CrySystem.XMLBinary STATIC
|
||||
NAMESPACE Legacy
|
||||
FILES_CMAKE
|
||||
crysystem_xmlbinary_files.cmake
|
||||
INCLUDE_DIRECTORIES
|
||||
PUBLIC
|
||||
.
|
||||
BUILD_DEPENDENCIES
|
||||
PUBLIC
|
||||
Legacy::CryCommon
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd
|
||||
and Clark Cooper
|
||||
Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Expat maintainers.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef CRYINCLUDE_CRYSYSTEM_XML_READWRITEXMLSINK_H
|
||||
#define CRYINCLUDE_CRYSYSTEM_XML_READWRITEXMLSINK_H
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <ISystem.h>
|
||||
#include <IReadWriteXMLSink.h>
|
||||
|
||||
class CReadWriteXMLSink
|
||||
: public IReadWriteXMLSink
|
||||
{
|
||||
public:
|
||||
bool ReadXML(const char* definitionFile, const char* dataFile, IReadXMLSink* pSink);
|
||||
bool ReadXML(const char* definitionFile, XmlNodeRef node, IReadXMLSink* pSink);
|
||||
bool ReadXML(XmlNodeRef definition, const char* dataFile, IReadXMLSink* pSink);
|
||||
bool ReadXML(XmlNodeRef definition, XmlNodeRef node, IReadXMLSink* pSink);
|
||||
|
||||
XmlNodeRef CreateXMLFromSource(const char* definitionFile, IWriteXMLSource* pSource);
|
||||
bool WriteXML(const char* definitionFile, const char* dataFile, IWriteXMLSource* pSource);
|
||||
};
|
||||
|
||||
|
||||
// helper to define the if/else chain that we need in a few locations...
|
||||
// types must match IReadXMLSink::TValueTypes
|
||||
#define XML_SET_PROPERTY_HELPER(ELSE_LOAD_PROPERTY) \
|
||||
if (false) {; } \
|
||||
ELSE_LOAD_PROPERTY(Vec3); \
|
||||
ELSE_LOAD_PROPERTY(int); \
|
||||
ELSE_LOAD_PROPERTY(float); \
|
||||
ELSE_LOAD_PROPERTY(string); \
|
||||
ELSE_LOAD_PROPERTY(bool);
|
||||
|
||||
|
||||
#endif // CRYINCLUDE_CRYSYSTEM_XML_READWRITEXMLSINK_H
|
||||
@@ -0,0 +1,681 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "CrySystem_precompiled.h"
|
||||
#include "ReadWriteXMLSink.h"
|
||||
|
||||
#include <ISystem.h>
|
||||
#include <stack>
|
||||
|
||||
typedef std::map<string, XmlNodeRef> IdTable;
|
||||
struct SParseParams
|
||||
{
|
||||
IdTable idTable;
|
||||
XmlNodeRef useAlways;
|
||||
bool strict;
|
||||
|
||||
SParseParams()
|
||||
{
|
||||
strict = true;
|
||||
}
|
||||
};
|
||||
|
||||
static XmlNodeRef Clone(XmlNodeRef source);
|
||||
static void CopyAttributes(const XmlNodeRef& source, XmlNodeRef& dest);
|
||||
static bool IsOptionalReadXML(const SParseParams& parseParams, XmlNodeRef& definition);
|
||||
static bool CheckEnum(const SParseParams& parseParams, const char* name, XmlNodeRef& definition, XmlNodeRef& data);
|
||||
|
||||
static bool LoadTableInner(const SParseParams& parseParams, XmlNodeRef& definition, XmlNodeRef& data, IReadXMLSink* pSink);
|
||||
static bool LoadArray(const SParseParams& parseParams, XmlNodeRef& definition, XmlNodeRef& data, IReadXMLSink* pSink);
|
||||
static bool LoadProperty(const SParseParams& parseParams, XmlNodeRef& definition, XmlNodeRef& data, IReadXMLSink* pSink);
|
||||
static bool LoadTable(const SParseParams& parseParams, XmlNodeRef& definition, XmlNodeRef& data, IReadXMLSink* pSink);
|
||||
static bool LoadReferencedId(const SParseParams& parseParams, XmlNodeRef& definition, XmlNodeRef& data, IReadXMLSink* pSink);
|
||||
static bool LoadSomething(const SParseParams& parseParams, XmlNodeRef& definition, XmlNodeRef& data, IReadXMLSink* pSink);
|
||||
static bool LoadArraySetValueTable(const SParseParams& parseParams, XmlNodeRef& definition, XmlNodeRef& data, IReadXMLSink* pSink, int elem);
|
||||
|
||||
typedef bool (* LoadArraySetValue)(const SParseParams& parseParams, XmlNodeRef& definition, XmlNodeRef& data, IReadXMLSink* pSink, int elem);
|
||||
typedef bool (* LoadDefinitionFunction)(const SParseParams& parseParams, XmlNodeRef& definition, XmlNodeRef& data, IReadXMLSink* pSink);
|
||||
|
||||
|
||||
template <class T>
|
||||
struct ReadPropertyTyped;
|
||||
|
||||
template <class T>
|
||||
struct ReadPropertyTyped
|
||||
{
|
||||
static bool Load(const SParseParams& parseParams, const char* name, XmlNodeRef& definition, XmlNodeRef& data, IReadXMLSink* pSink)
|
||||
{
|
||||
T value;
|
||||
memset(&value, 0, sizeof(T));
|
||||
|
||||
if (!pSink->IsCreationMode())
|
||||
{
|
||||
if (!data->haveAttr(name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!data->getAttr(name, value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!CheckEnum(parseParams, name, definition, data))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
IReadXMLSink::TValue vvalue(value);
|
||||
pSink->SetValue(name, vvalue, definition);
|
||||
return true;
|
||||
}
|
||||
static bool LoadArray([[maybe_unused]] const SParseParams& parseParams, XmlNodeRef& definition, XmlNodeRef& data, IReadXMLSink* pSink, int elem)
|
||||
{
|
||||
T value;
|
||||
memset(&value, 0, sizeof(T));
|
||||
|
||||
if (!pSink->IsCreationMode())
|
||||
{
|
||||
if (!data->haveAttr("value"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!data->getAttr("value", value))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
IReadXMLSink::TValue vvalue(value);
|
||||
pSink->SetAt(elem, vvalue, definition);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ReadPropertyTyped<string>
|
||||
{
|
||||
static bool Load(const SParseParams& parseParams, const char* name, XmlNodeRef& definition, XmlNodeRef& data, IReadXMLSink* pSink)
|
||||
{
|
||||
const char* value = 0;
|
||||
|
||||
if (!pSink->IsCreationMode())
|
||||
{
|
||||
if (!data->haveAttr(name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!CheckEnum(parseParams, name, definition, data))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
value = data->getAttr(name);
|
||||
}
|
||||
|
||||
IReadXMLSink::TValue vvalue(value);
|
||||
pSink->SetValue(name, vvalue, definition);
|
||||
return true;
|
||||
}
|
||||
static bool LoadArray([[maybe_unused]] const SParseParams& parseParams, XmlNodeRef& definition, XmlNodeRef& data, IReadXMLSink* pSink, int elem)
|
||||
{
|
||||
const char* value = 0;
|
||||
|
||||
if (!pSink->IsCreationMode())
|
||||
{
|
||||
if (!data->haveAttr("value"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
value = data->getAttr("value");
|
||||
}
|
||||
|
||||
IReadXMLSink::TValue vvalue(value);
|
||||
pSink->SetAt(elem, vvalue, definition);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
XmlNodeRef Clone(XmlNodeRef source)
|
||||
{
|
||||
assert(source != (IXmlNode*)NULL);
|
||||
|
||||
// Can't use clone() on XmlNodeRef objects since they can contain a CXMLBinaryNode, which doesn't support
|
||||
// clone(). Instead we just create a regular xml node and manually copy content, tag, attributes and children
|
||||
XmlNodeRef cloned = GetISystem()->CreateXmlNode(source->getTag());
|
||||
cloned->setContent(source->getContent());
|
||||
CopyAttributes(source, cloned);
|
||||
const int iChildCount = source->getChildCount();
|
||||
for (int i = 0; i < iChildCount; ++i)
|
||||
{
|
||||
cloned->addChild(Clone(source->getChild(i)));
|
||||
}
|
||||
|
||||
return cloned;
|
||||
}
|
||||
|
||||
void CopyAttributes(const XmlNodeRef& source, XmlNodeRef& dest)
|
||||
{
|
||||
// Not as fast as CXmlNode::copyAttributes(), but that method will have undefined behavior if the XmlNodeRef contains
|
||||
// a CBinaryXmlNode object
|
||||
int nNumAttributes = source->getNumAttributes();
|
||||
for (int i = 0; i < nNumAttributes; ++i)
|
||||
{
|
||||
const char* key = NULL;
|
||||
const char* value = NULL;
|
||||
if (source->getAttributeByIndex(i, &key, &value))
|
||||
{
|
||||
dest->setAttr(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool IsOptionalReadXML(const SParseParams& parseParams, XmlNodeRef& definition)
|
||||
{
|
||||
// If strict mode is off, then everything is optional
|
||||
if (parseParams.strict == false)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool optional = false;
|
||||
definition->getAttr("optional", optional);
|
||||
return optional;
|
||||
}
|
||||
|
||||
bool CheckEnum([[maybe_unused]] const SParseParams& parseParams, [[maybe_unused]] const char* name, XmlNodeRef& definition, [[maybe_unused]] XmlNodeRef& data)
|
||||
{
|
||||
if (XmlNodeRef enumNode = definition->findChild("Enum"))
|
||||
{
|
||||
// If strict mode is off, then no need to check the enum value
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadProperty(const SParseParams& parseParams, XmlNodeRef& definition, XmlNodeRef& data, IReadXMLSink* pSink)
|
||||
{
|
||||
const char* name = definition->getAttr("name");
|
||||
if (0 == strlen(name))
|
||||
{
|
||||
CryLog("Property has no name");
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* type = definition->getAttr("type");
|
||||
if (0 == strlen(type))
|
||||
{
|
||||
CryLog("Property '%s' has no type", type);
|
||||
return false;
|
||||
}
|
||||
|
||||
XmlNodeRef dataToRead = data;
|
||||
if (!pSink->IsCreationMode())
|
||||
{
|
||||
// This check is done so the data xml can specify child elements instead of attributes if desired,
|
||||
// since the rest of the code assume only attributes
|
||||
if (XmlNodeRef childRef = data->findChild(name))
|
||||
{
|
||||
if (data->haveAttr(name))
|
||||
{
|
||||
CryLog("Duplicate definition (attribute and element) for %s", name);
|
||||
return false;
|
||||
}
|
||||
if (childRef->getChildCount())
|
||||
{
|
||||
CryLog("Property-style elements can not have children (property was %s)", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
dataToRead = GetISystem()->CreateXmlNode(data->getTag());
|
||||
|
||||
string content = childRef->getContent();
|
||||
dataToRead->setAttr(name, content.Trim().c_str());
|
||||
}
|
||||
|
||||
if (!dataToRead->haveAttr(name))
|
||||
{
|
||||
if (!IsOptionalReadXML(parseParams, definition))
|
||||
{
|
||||
CryLog("Failed to load property %s", name);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool ok = false;
|
||||
#define LOAD_PROPERTY(whichType) else if (0 == strcmp(type, #whichType)) ok = ReadPropertyTyped<whichType>::Load(parseParams, name, definition, dataToRead, pSink)
|
||||
XML_SET_PROPERTY_HELPER(LOAD_PROPERTY);
|
||||
#undef LOAD_PROPERTY
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
CryLog("Failed loading attribute %s of type %s", name, type);
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool LoadArraySetValueTable(const SParseParams& parseParams, XmlNodeRef& definition, XmlNodeRef& data, IReadXMLSink* pSink, int elem)
|
||||
{
|
||||
IReadXMLSinkPtr pChildSink = pSink->BeginTableAt(elem, definition);
|
||||
|
||||
if (pSink->IsCreationMode() && definition->haveAttr("type"))
|
||||
{
|
||||
if (!LoadSomething(parseParams, definition, data, &*pChildSink))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!LoadTableInner(parseParams, definition, data, &*pChildSink))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pChildSink->EndTableAt(elem))
|
||||
{
|
||||
CryLog("Failed to finish table at element %d", elem);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadArray(const SParseParams& parseParams, XmlNodeRef& definition, XmlNodeRef& data, IReadXMLSink* pSink)
|
||||
{
|
||||
const char* name = definition->getAttr("name");
|
||||
if (0 == strlen(name))
|
||||
{
|
||||
CryLog("Array has no name");
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* elementName = definition->getAttr("elementName");
|
||||
if (0 == strlen(elementName))
|
||||
{
|
||||
elementName = "element";
|
||||
}
|
||||
|
||||
bool validateArray = true;
|
||||
if (definition->haveAttr(elementName))
|
||||
{
|
||||
definition->getAttr("validate", validateArray);
|
||||
}
|
||||
|
||||
|
||||
XmlNodeRef childData;
|
||||
|
||||
if (!pSink->IsCreationMode())
|
||||
{
|
||||
childData = data->findChild(name);
|
||||
if (!childData)
|
||||
{
|
||||
bool ok = IsOptionalReadXML(parseParams, definition);
|
||||
if (!ok)
|
||||
{
|
||||
CryLog("Failed to load child table %s", name);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
}
|
||||
|
||||
IReadXMLSinkPtr childSink = pSink->BeginArray(name, definition);
|
||||
if (!childSink)
|
||||
{
|
||||
CryLog("Failed to begin array named %s", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
LoadArraySetValue setter = NULL;
|
||||
if (definition->haveAttr("type"))
|
||||
{
|
||||
setter = NULL;
|
||||
const char* type = definition->getAttr("type");
|
||||
#define SETTER_PROPERTY(whichType) else if (0 == strcmp(type, #whichType)) setter = ReadPropertyTyped<whichType>::LoadArray
|
||||
XML_SET_PROPERTY_HELPER(SETTER_PROPERTY);
|
||||
#undef SETTER_PROPERTY
|
||||
if (!setter)
|
||||
{
|
||||
CryLog("Unknown type %s in array %s", type, name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
setter = LoadArraySetValueTable;
|
||||
}
|
||||
|
||||
if (!pSink->IsCreationMode())
|
||||
{
|
||||
int numElems = childData->getChildCount();
|
||||
int elem = 1;
|
||||
for (int i = 0; i < numElems; i++)
|
||||
{
|
||||
XmlNodeRef elemData = childData->getChild(i);
|
||||
if (0 == strcmp(elemData->getTag(), elementName))
|
||||
{
|
||||
int increment = 1;
|
||||
if (elemData->haveAttr("_index"))
|
||||
{
|
||||
if (!elemData->getAttr("_index", elem))
|
||||
{
|
||||
CryLog("_index is not an integer in array %s (pos hint=%d)", name, elem);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!setter(parseParams, definition, elemData, &*childSink, elem))
|
||||
{
|
||||
CryLog("Failed loading element %d of array %s", elem, name);
|
||||
return false;
|
||||
}
|
||||
elem += increment;
|
||||
}
|
||||
else if (validateArray)
|
||||
{
|
||||
CryLog("Invalid node %s in array %s", elemData->getTag(), name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// only process array content for the array being created
|
||||
if (0 == strcmp(name, pSink->GetCreationNode()->getAttr("name")))
|
||||
{
|
||||
if (!setter(parseParams, definition, data, &*childSink, 1))
|
||||
{
|
||||
CryLog("[ReadXML CreationMode]: Failed loading element %d of array %s", 1, name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!pSink->EndArray(name))
|
||||
{
|
||||
CryLog("Failed to finish array named %s", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadTable(const SParseParams& parseParams, XmlNodeRef& definition, XmlNodeRef& data, IReadXMLSink* pSink)
|
||||
{
|
||||
const char* name = definition->getAttr("name");
|
||||
if (0 == strlen(name))
|
||||
{
|
||||
CryLog("Child-table has no name");
|
||||
return false;
|
||||
}
|
||||
|
||||
XmlNodeRef childData;
|
||||
|
||||
if (!pSink->IsCreationMode())
|
||||
{
|
||||
childData = data->findChild(name);
|
||||
if (!childData)
|
||||
{
|
||||
bool ok = IsOptionalReadXML(parseParams, definition);
|
||||
if (!ok)
|
||||
{
|
||||
CryLog("Failed to load child table %s", name);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
}
|
||||
|
||||
IReadXMLSinkPtr childSink = pSink->BeginTable(name, definition);
|
||||
if (!childSink)
|
||||
{
|
||||
CryLog("Sink creation failed for table %s", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (!LoadTableInner(parseParams, definition, childData, childSink))
|
||||
{
|
||||
CryLog("Failed to load data for child table %s", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!pSink->EndTable(name))
|
||||
{
|
||||
CryLog("Table %s failed to complete in sink", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadSomething(const SParseParams& parseParams, XmlNodeRef& nodeDefinition, XmlNodeRef& data, IReadXMLSink* pSink)
|
||||
{
|
||||
// Ignore if it's the useAlways array
|
||||
if (parseParams.useAlways == nodeDefinition)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct
|
||||
{
|
||||
const char* name;
|
||||
LoadDefinitionFunction loader;
|
||||
} loaderTypes[] = {
|
||||
{"Property", &LoadProperty},
|
||||
{"Array", &LoadArray},
|
||||
{"Table", &LoadTable},
|
||||
{"Use", &LoadReferencedId},
|
||||
};
|
||||
static const int numLoaderTypes = sizeof(loaderTypes) / sizeof(*loaderTypes);
|
||||
|
||||
const char* nodeDefinitionTag = nodeDefinition->getTag();
|
||||
bool ok = false;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < numLoaderTypes; i++)
|
||||
{
|
||||
if (0 == strcmp(loaderTypes[i].name, nodeDefinitionTag))
|
||||
{
|
||||
ok = loaderTypes[i].loader(parseParams, nodeDefinition, data, pSink);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (0 == _stricmp("Settings", nodeDefinitionTag))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
if (i == numLoaderTypes)
|
||||
{
|
||||
CryLog("Invalid definition node type %s, line %d", nodeDefinitionTag, nodeDefinition->getLine());
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool LoadReferencedId(const SParseParams& parseParams, XmlNodeRef& definition, XmlNodeRef& data, IReadXMLSink* pSink)
|
||||
{
|
||||
IdTable::const_iterator iter = parseParams.idTable.find(definition->getAttr("id"));
|
||||
if (iter == parseParams.idTable.end())
|
||||
{
|
||||
CryLog("No definition with id '%s'", definition->getAttr("id"));
|
||||
return false;
|
||||
}
|
||||
XmlNodeRef useDefinition = Clone(iter->second);
|
||||
CopyAttributes(definition, useDefinition);
|
||||
|
||||
return LoadSomething(parseParams, useDefinition, data, pSink);
|
||||
}
|
||||
|
||||
bool LoadTableInner(const SParseParams& parseParams, XmlNodeRef& definition, XmlNodeRef& data, IReadXMLSink* pSink)
|
||||
{
|
||||
const int nChildrenDefinition = definition->getChildCount();
|
||||
|
||||
for (int nChildDefinition = 0; nChildDefinition < nChildrenDefinition; nChildDefinition++)
|
||||
{
|
||||
XmlNodeRef nodeDefinition = definition->getChild(nChildDefinition);
|
||||
|
||||
if (!LoadSomething(parseParams, nodeDefinition, data, pSink))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (parseParams.useAlways != (IXmlNode*)NULL)
|
||||
{
|
||||
assert(!definition->haveAttr("type"));
|
||||
|
||||
const int nUseAlwaysDefCount = parseParams.useAlways->getChildCount();
|
||||
for (int i = 0; i < nUseAlwaysDefCount; ++i)
|
||||
{
|
||||
XmlNodeRef nodeDefinition = parseParams.useAlways->getChild(i);
|
||||
|
||||
// Don't continue loading useAlways nodes in creation mode
|
||||
if (!pSink->IsCreationMode())
|
||||
{
|
||||
if (!LoadSomething(parseParams, nodeDefinition, data, pSink))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CReadWriteXMLSink::ReadXML(XmlNodeRef rootDefinition, XmlNodeRef rootData, IReadXMLSink* pSink)
|
||||
{
|
||||
if (!pSink->IsCreationMode())
|
||||
{
|
||||
if (0 == rootData)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (0 != strcmp(rootDefinition->getTag(), "Definition"))
|
||||
{
|
||||
CryLog("Root tag of definition file was %s; expected Definition", rootDefinition->getTag());
|
||||
return false;
|
||||
}
|
||||
if (rootDefinition->haveAttr("root"))
|
||||
{
|
||||
if (0 != strcmp(rootDefinition->getAttr("root"), rootData->getTag()))
|
||||
{
|
||||
CryLog("Root data has wrong tag; was %s expected %s", rootData->getTag(), rootDefinition->getAttr("root"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SParseParams parseParams;
|
||||
parseParams.useAlways = rootDefinition->findChild("AllowAlways");
|
||||
|
||||
if (XmlNodeRef settingsParams = rootDefinition->findChild("Settings"))
|
||||
{
|
||||
settingsParams->getAttr("strict", parseParams.strict);
|
||||
}
|
||||
|
||||
// scan for id's in the structure (for the Use member)
|
||||
std::stack<XmlNodeRef> scanStack;
|
||||
scanStack.push(rootDefinition);
|
||||
while (!scanStack.empty())
|
||||
{
|
||||
XmlNodeRef refNode = scanStack.top();
|
||||
scanStack.pop();
|
||||
|
||||
int numChildren = refNode->getChildCount();
|
||||
const char* tag = refNode->getTag();
|
||||
|
||||
for (int i = 0; i < numChildren; i++)
|
||||
{
|
||||
const XmlNodeRef& childNodeRef = refNode->getChild(i);
|
||||
if (parseParams.useAlways != childNodeRef)
|
||||
{
|
||||
scanStack.push(childNodeRef);
|
||||
}
|
||||
}
|
||||
|
||||
// If the element has an attribute id="" and is not a "<Use>" element add it to the idTable map
|
||||
if (refNode->haveAttr("id") && 0 != strcmp("Use", tag))
|
||||
{
|
||||
parseParams.idTable[refNode->getAttr("id")] = refNode;
|
||||
}
|
||||
}
|
||||
|
||||
if (pSink->IsCreationMode() && rootDefinition->haveAttr("type"))
|
||||
{
|
||||
// if creating from a 0-child definition node, load itself
|
||||
if (!LoadSomething(parseParams, rootDefinition, rootData, pSink))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// load content
|
||||
if (!LoadTableInner(parseParams, rootDefinition, rootData, pSink))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ok = pSink->Complete();
|
||||
if (!ok)
|
||||
{
|
||||
CryLog("Warning: sink failed to complete reading");
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool CReadWriteXMLSink::ReadXML(XmlNodeRef definition, const char* dataFile, IReadXMLSink* pSink)
|
||||
{
|
||||
XmlNodeRef rootData = GetISystem()->LoadXmlFromFile(dataFile);
|
||||
if (!rootData)
|
||||
{
|
||||
CryLog("Unable to load XML-Lua data file: %s", dataFile);
|
||||
return false;
|
||||
}
|
||||
return ReadXML(definition, rootData, pSink);
|
||||
}
|
||||
|
||||
bool CReadWriteXMLSink::ReadXML(const char* definitionFile, XmlNodeRef rootData, IReadXMLSink* pSink)
|
||||
{
|
||||
XmlNodeRef rootDefinition = GetISystem()->LoadXmlFromFile(definitionFile);
|
||||
if (!rootDefinition)
|
||||
{
|
||||
CryLog("Unable to load XML-Lua definition file: %s", definitionFile);
|
||||
return false;
|
||||
}
|
||||
return ReadXML(rootDefinition, rootData, pSink);
|
||||
}
|
||||
|
||||
bool CReadWriteXMLSink::ReadXML(const char* definitionFile, const char* dataFile, IReadXMLSink* pSink)
|
||||
{
|
||||
XmlNodeRef rootData = GetISystem()->LoadXmlFromFile(dataFile);
|
||||
if (!rootData)
|
||||
{
|
||||
CryLog("Unable to load XML-Lua data file: %s", dataFile);
|
||||
return false;
|
||||
}
|
||||
if (!ReadXML(definitionFile, rootData, pSink))
|
||||
{
|
||||
CryLog("Unable to load file %s", dataFile);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "CrySystem_precompiled.h"
|
||||
#include "SerializeXMLReader.h"
|
||||
#include <ISystem.h>
|
||||
|
||||
#define TAG_SCRIPT_VALUE "v"
|
||||
#define TAG_SCRIPT_TYPE "t"
|
||||
#define TAG_SCRIPT_NAME "n"
|
||||
|
||||
//#define LOG_SERIALIZE_STACK(tag,szName) CryLogAlways( "<%s> %s/%s",tag,GetStackInfo(),szName );
|
||||
#define LOG_SERIALIZE_STACK(tag, szName)
|
||||
|
||||
CSerializeXMLReaderImpl::CSerializeXMLReaderImpl(const XmlNodeRef& nodeRef)
|
||||
: m_nErrors(0)
|
||||
{
|
||||
//m_curTime = gEnv->pTimer->GetFrameStartTime();
|
||||
assert(!!nodeRef);
|
||||
m_nodeStack.push_back(CParseState());
|
||||
m_nodeStack.back().Init(nodeRef);
|
||||
}
|
||||
|
||||
bool CSerializeXMLReaderImpl::Value(const char* name, int8& value)
|
||||
{
|
||||
DefaultValue(value); // Set input value to default.
|
||||
if (m_nErrors)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int temp;
|
||||
bool bResult = Value(name, temp);
|
||||
if (temp < -128 || temp > 127)
|
||||
{
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "Attribute %s is out of range (%d)", name, temp);
|
||||
Failed();
|
||||
bResult = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = temp;
|
||||
}
|
||||
return bResult;
|
||||
}
|
||||
|
||||
bool CSerializeXMLReaderImpl::Value(const char* name, string& value)
|
||||
{
|
||||
DefaultValue(value); // Set input value to default.
|
||||
if (m_nErrors)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!CurNode()->haveAttr(name))
|
||||
{
|
||||
//CryWarning( VALIDATOR_MODULE_SYSTEM,VALIDATOR_WARNING,"No such attribute %s (invalid type?)", name);
|
||||
//Failed();
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = CurNode()->getAttr(name);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CSerializeXMLReaderImpl::Value(const char* name, CTimeValue& value)
|
||||
{
|
||||
DefaultValue(value); // Set input value to default.
|
||||
if (m_nErrors)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
XmlNodeRef nodeRef = CurNode();
|
||||
if (!nodeRef)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (0 == strcmp("zero", nodeRef->getAttr(name)))
|
||||
{
|
||||
value = CTimeValue(0.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
float delta;
|
||||
if (!GetAttr(nodeRef, name, delta))
|
||||
{
|
||||
//CryWarning( VALIDATOR_MODULE_SYSTEM,VALIDATOR_WARNING,"Failed to read time value %s", name);
|
||||
//Failed();
|
||||
value = gEnv->pTimer->GetFrameStartTime(); // in case we don't find the node, it was assumed to be the default value (0.0)
|
||||
// 0.0 means current time, whereas "zero" really means CTimeValue(0.0), see above
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = CTimeValue(gEnv->pTimer->GetFrameStartTime() + delta);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CSerializeXMLReaderImpl::Value(const char* name, XmlNodeRef& value)
|
||||
{
|
||||
DefaultValue(value); // Set input value to default.
|
||||
if (m_nErrors)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (BeginOptionalGroup(name, true))
|
||||
{
|
||||
value = CurNode()->getChild(0);
|
||||
EndGroup();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CSerializeXMLReaderImpl::BeginGroup(const char* szName)
|
||||
{
|
||||
if (m_nErrors)
|
||||
{
|
||||
//CryWarning( VALIDATOR_MODULE_SYSTEM,VALIDATOR_WARNING,"BeginGroup %s called on non-existant group", szName);
|
||||
m_nErrors++;
|
||||
}
|
||||
else if (XmlNodeRef node = NextOf(szName))
|
||||
{
|
||||
m_nodeStack.push_back(CParseState());
|
||||
m_nodeStack.back().Init(node);
|
||||
LOG_SERIALIZE_STACK("BeginGroup:ok", szName);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_SERIALIZE_STACK("BeginGroup:fail", szName);
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "!BeginGroup( %s ) not found", szName);
|
||||
m_nErrors++;
|
||||
}
|
||||
}
|
||||
|
||||
bool CSerializeXMLReaderImpl::BeginOptionalGroup(const char* szName, [[maybe_unused]] bool condition)
|
||||
{
|
||||
if (m_nErrors)
|
||||
{
|
||||
//CryWarning( VALIDATOR_MODULE_SYSTEM,VALIDATOR_WARNING,"BeginOptionalGroup %s called on non-existant group", szName);
|
||||
m_nErrors++;
|
||||
}
|
||||
else if (XmlNodeRef node = NextOf(szName))
|
||||
{
|
||||
m_nodeStack.push_back(CParseState());
|
||||
m_nodeStack.back().Init(node);
|
||||
LOG_SERIALIZE_STACK("BeginOptionalGroup:ok", szName);
|
||||
return true;
|
||||
}
|
||||
LOG_SERIALIZE_STACK("BeginOptionalGroup:fail", szName);
|
||||
return false;
|
||||
}
|
||||
|
||||
void CSerializeXMLReaderImpl::EndGroup()
|
||||
{
|
||||
if (m_nErrors)
|
||||
{
|
||||
m_nErrors--;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_SERIALIZE_STACK("EndGroup", "");
|
||||
m_nodeStack.pop_back();
|
||||
}
|
||||
assert(!m_nodeStack.empty());
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
const char* CSerializeXMLReaderImpl::GetStackInfo() const
|
||||
{
|
||||
static string str;
|
||||
str.assign("");
|
||||
for (int i = 0; i < (int)m_nodeStack.size(); i++)
|
||||
{
|
||||
const char* name = m_nodeStack[i].m_node->getAttr(TAG_SCRIPT_NAME);
|
||||
if (name && name[0])
|
||||
{
|
||||
str += name;
|
||||
}
|
||||
else
|
||||
{
|
||||
str += m_nodeStack[i].m_node->getTag();
|
||||
}
|
||||
if (i != m_nodeStack.size() - 1)
|
||||
{
|
||||
str += "/";
|
||||
}
|
||||
}
|
||||
return str.c_str();
|
||||
}
|
||||
|
||||
void CSerializeXMLReaderImpl::GetMemoryUsage(ICrySizer* pSizer) const
|
||||
{
|
||||
pSizer->Add(*this);
|
||||
pSizer->AddContainer(m_nodeStack);
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef CRYINCLUDE_CRYSYSTEM_XML_SERIALIZEXMLREADER_H
|
||||
#define CRYINCLUDE_CRYSYSTEM_XML_SERIALIZEXMLREADER_H
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "SimpleSerialize.h"
|
||||
#include <stack>
|
||||
#include <IXml.h>
|
||||
#include <ITimer.h>
|
||||
#include <IValidator.h>
|
||||
#include <ISystem.h>
|
||||
#include "xml.h"
|
||||
|
||||
class CSerializeXMLReaderImpl
|
||||
: public CSimpleSerializeImpl<true, eST_SaveGame>
|
||||
{
|
||||
public:
|
||||
CSerializeXMLReaderImpl(const XmlNodeRef& nodeRef);
|
||||
|
||||
template <class T_Value>
|
||||
ILINE bool GetAttr(const XmlNodeRef& node, const char* name, T_Value& value)
|
||||
{
|
||||
XmlStrCmpFunc pPrevCmpFunc = g_pXmlStrCmp;
|
||||
g_pXmlStrCmp = &strcmp; // Do case-sensitive compare
|
||||
bool bReturn = node->getAttr(name, value);
|
||||
g_pXmlStrCmp = pPrevCmpFunc;
|
||||
return bReturn;
|
||||
}
|
||||
ILINE bool GetAttr(const XmlNodeRef& node, const char* name, SSerializeString& value)
|
||||
{
|
||||
XmlStrCmpFunc pPrevCmpFunc = g_pXmlStrCmp;
|
||||
g_pXmlStrCmp = &strcmp; // Do case-sensitive compare
|
||||
bool bReturn = node->haveAttr(name);
|
||||
if (bReturn)
|
||||
{
|
||||
value = node->getAttr(name);
|
||||
}
|
||||
g_pXmlStrCmp = pPrevCmpFunc;
|
||||
return bReturn;
|
||||
}
|
||||
ILINE bool GetAttr([[maybe_unused]] XmlNodeRef& node, [[maybe_unused]] const char* name, [[maybe_unused]] const string& value)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
ILINE bool GetAttr([[maybe_unused]] const XmlNodeRef& node, [[maybe_unused]] const char* name, [[maybe_unused]] SNetObjectID& value)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class T_Value>
|
||||
bool Value(const char* name, T_Value& value)
|
||||
{
|
||||
DefaultValue(value); // Set input value to default.
|
||||
if (m_nErrors)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!GetAttr(CurNode(), name, value))
|
||||
{
|
||||
//CryWarning( VALIDATOR_MODULE_SYSTEM,VALIDATOR_WARNING,"Unable to read attribute %s (invalid type?)", name);
|
||||
//Failed();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Value(const char* name, int8& value);
|
||||
bool Value(const char* name, string& value);
|
||||
bool Value(const char* name, CTimeValue& value);
|
||||
bool Value(const char* name, XmlNodeRef& value);
|
||||
|
||||
template <class T_Value, class T_Policy>
|
||||
bool Value(const char* name, T_Value& value, [[maybe_unused]] const T_Policy& policy)
|
||||
{
|
||||
return Value(name, value);
|
||||
}
|
||||
|
||||
void BeginGroup(const char* szName);
|
||||
bool BeginOptionalGroup(const char* szName, bool condition);
|
||||
void EndGroup();
|
||||
const char* GetStackInfo() const;
|
||||
|
||||
void GetMemoryUsage(ICrySizer* pSizer) const;
|
||||
|
||||
private:
|
||||
//CTimeValue m_curTime;
|
||||
XmlNodeRef CurNode() { return m_nodeStack.back().m_node; }
|
||||
XmlNodeRef NextOf(const char* name)
|
||||
{
|
||||
XmlStrCmpFunc pPrevCmpFunc = g_pXmlStrCmp;
|
||||
g_pXmlStrCmp = &strcmp; // Do case-sensitive compare
|
||||
assert(!m_nodeStack.empty());
|
||||
CParseState& ps = m_nodeStack.back();
|
||||
XmlNodeRef node = ps.GetNext(name);
|
||||
g_pXmlStrCmp = pPrevCmpFunc;
|
||||
return node;
|
||||
}
|
||||
|
||||
class CParseState
|
||||
{
|
||||
public:
|
||||
CParseState() {}
|
||||
void Init(const XmlNodeRef& node)
|
||||
{
|
||||
m_node = node;
|
||||
m_nCurrent = 0;
|
||||
}
|
||||
|
||||
XmlNodeRef GetNext(const char* name)
|
||||
{
|
||||
int i;
|
||||
int num = m_node->getChildCount();
|
||||
for (i = m_nCurrent; i < num; i++)
|
||||
{
|
||||
XmlNodeRef child = m_node->getChild(i);
|
||||
if (strcmp(child->getTag(), name) == 0)
|
||||
{
|
||||
m_nCurrent = i + 1;
|
||||
return child;
|
||||
}
|
||||
}
|
||||
int ncount = min(m_nCurrent, num);
|
||||
// Try searching from begining.
|
||||
for (i = 0; i < ncount; i++)
|
||||
{
|
||||
XmlNodeRef child = m_node->getChild(i);
|
||||
if (strcmp(child->getTag(), name) == 0)
|
||||
{
|
||||
m_nCurrent = i + 1;
|
||||
return child;
|
||||
}
|
||||
}
|
||||
return XmlNodeRef();
|
||||
}
|
||||
|
||||
public:
|
||||
// TODO: make this much more efficient
|
||||
int m_nCurrent;
|
||||
XmlNodeRef m_node;
|
||||
};
|
||||
|
||||
int m_nErrors;
|
||||
std::vector<CParseState> m_nodeStack;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Set Defaults.
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void DefaultValue(bool& v) const { v = false; }
|
||||
void DefaultValue(float& v) const { v = 0; }
|
||||
void DefaultValue(double& v) const { v = 0; }
|
||||
void DefaultValue(int8& v) const { v = 0; }
|
||||
void DefaultValue(uint8& v) const { v = 0; }
|
||||
void DefaultValue(int16& v) const { v = 0; }
|
||||
void DefaultValue(uint16& v) const { v = 0; }
|
||||
void DefaultValue(int32& v) const { v = 0; }
|
||||
void DefaultValue(uint32& v) const { v = 0; }
|
||||
void DefaultValue(int64& v) const { v = 0; }
|
||||
void DefaultValue(uint64& v) const { v = 0; }
|
||||
void DefaultValue(Vec2& v) const { v.x = 0; v.y = 0; }
|
||||
void DefaultValue(Vec3& v) const { v.x = 0; v.y = 0; v.z = 0; }
|
||||
void DefaultValue(Ang3& v) const { v.x = 0; v.y = 0; v.z = 0; }
|
||||
void DefaultValue(Quat& v) const { v.w = 1.0f; v.v.x = 0; v.v.y = 0; v.v.z = 0; }
|
||||
void DefaultValue(CTimeValue& v) const { v.SetValue(0); }
|
||||
//void DefaultValue( char *str ) const { if (str) str[0] = 0; }
|
||||
void DefaultValue(string& str) const { str = ""; }
|
||||
void DefaultValue([[maybe_unused]] const string& str) const {}
|
||||
void DefaultValue([[maybe_unused]] SNetObjectID& id) const {}
|
||||
void DefaultValue([[maybe_unused]] SSerializeString& str) const {}
|
||||
void DefaultValue(XmlNodeRef& ref) const { ref = NULL; }
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
};
|
||||
|
||||
#endif // CRYINCLUDE_CRYSYSTEM_XML_SERIALIZEXMLREADER_H
|
||||
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "CrySystem_precompiled.h"
|
||||
#include "SerializeXMLWriter.h"
|
||||
|
||||
static const size_t MAX_NODE_STACK_DEPTH = 40;
|
||||
|
||||
#define TAG_SCRIPT_VALUE "v"
|
||||
#define TAG_SCRIPT_TYPE "t"
|
||||
#define TAG_SCRIPT_NAME "n"
|
||||
|
||||
CSerializeXMLWriterImpl::CSerializeXMLWriterImpl(const XmlNodeRef& nodeRef)
|
||||
{
|
||||
m_curTime = gEnv->pTimer->GetFrameStartTime();
|
||||
assert(!!nodeRef);
|
||||
m_nodeStack.push_back(nodeRef);
|
||||
|
||||
m_luaSaveStack.reserve(10);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
CSerializeXMLWriterImpl::~CSerializeXMLWriterImpl()
|
||||
{
|
||||
if (m_nodeStack.size() != 1)
|
||||
{
|
||||
// Node stack is incorrect.
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "!BeginGroup/EndGroup mismatch in SaveGame");
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CSerializeXMLWriterImpl::Value(const char* name, CTimeValue value)
|
||||
{
|
||||
if (value == CTimeValue(0.0f))
|
||||
{
|
||||
AddValue(name, "zero");
|
||||
}
|
||||
else
|
||||
{
|
||||
AddValue(name, (value - m_curTime).GetSeconds());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CSerializeXMLWriterImpl::Value(const char* name, XmlNodeRef& value)
|
||||
{
|
||||
if (BeginOptionalGroup(name, value != NULL))
|
||||
{
|
||||
CurNode()->addChild(value);
|
||||
EndGroup();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void CSerializeXMLWriterImpl::BeginGroup(const char* szName)
|
||||
{
|
||||
if (strchr(szName, ' ') != 0)
|
||||
{
|
||||
assert(0 && "Spaces in group name not supported");
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "!Spaces in group name not supported: %s/%s", GetStackInfo(), szName);
|
||||
}
|
||||
XmlNodeRef node = CreateNodeNamed(szName);
|
||||
CurNode()->addChild(node);
|
||||
m_nodeStack.push_back(node);
|
||||
if (m_nodeStack.size() > MAX_NODE_STACK_DEPTH)
|
||||
{
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "!Too Deep Node Stack:\r\n%s", GetStackInfo());
|
||||
}
|
||||
}
|
||||
|
||||
bool CSerializeXMLWriterImpl::BeginOptionalGroup(const char* szName, bool condition)
|
||||
{
|
||||
if (condition)
|
||||
{
|
||||
BeginGroup(szName);
|
||||
return true;
|
||||
}
|
||||
|
||||
return condition;
|
||||
}
|
||||
|
||||
XmlNodeRef CSerializeXMLWriterImpl::CreateNodeNamed(const char* name)
|
||||
{
|
||||
XmlNodeRef newNode = CurNode()->createNode(name);
|
||||
return newNode;
|
||||
}
|
||||
|
||||
void CSerializeXMLWriterImpl::EndGroup()
|
||||
{
|
||||
if (m_nodeStack.size() == 1)
|
||||
{
|
||||
//
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "!Misplaced EndGroup() for BeginGroup(%s)", CurNode()->getTag());
|
||||
}
|
||||
assert(!m_nodeStack.empty());
|
||||
m_nodeStack.pop_back();
|
||||
assert(!m_nodeStack.empty());
|
||||
}
|
||||
|
||||
void CSerializeXMLWriterImpl::GetMemoryUsage(ICrySizer* pSizer) const
|
||||
{
|
||||
pSizer->Add(*this);
|
||||
pSizer->AddObject(m_nodeStack);
|
||||
pSizer->AddContainer(m_luaSaveStack);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
const char* CSerializeXMLWriterImpl::GetStackInfo() const
|
||||
{
|
||||
static string str;
|
||||
str.assign("");
|
||||
for (int i = 0; i < (int)m_nodeStack.size(); i++)
|
||||
{
|
||||
const char* name = m_nodeStack[i]->getAttr(TAG_SCRIPT_NAME);
|
||||
if (name && name[0])
|
||||
{
|
||||
str += name;
|
||||
}
|
||||
else
|
||||
{
|
||||
str += m_nodeStack[i]->getTag();
|
||||
}
|
||||
if (i != m_nodeStack.size() - 1)
|
||||
{
|
||||
str += "/";
|
||||
}
|
||||
}
|
||||
return str.c_str();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
const char* CSerializeXMLWriterImpl::GetLuaStackInfo() const
|
||||
{
|
||||
static string str;
|
||||
str.assign("");
|
||||
for (int i = 0; i < (int)m_luaSaveStack.size(); i++)
|
||||
{
|
||||
const char* name = m_luaSaveStack[i];
|
||||
str += name;
|
||||
if (i != m_luaSaveStack.size() - 1)
|
||||
{
|
||||
str += ".";
|
||||
}
|
||||
}
|
||||
return str.c_str();
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef CRYINCLUDE_CRYSYSTEM_XML_SERIALIZEXMLWRITER_H
|
||||
#define CRYINCLUDE_CRYSYSTEM_XML_SERIALIZEXMLWRITER_H
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <ISystem.h>
|
||||
#include <ITimer.h>
|
||||
#include <IXml.h>
|
||||
#include "IValidator.h"
|
||||
#include "SimpleSerialize.h"
|
||||
|
||||
class CSerializeXMLWriterImpl
|
||||
: public CSimpleSerializeImpl<false, eST_SaveGame>
|
||||
{
|
||||
public:
|
||||
CSerializeXMLWriterImpl(const XmlNodeRef& nodeRef);
|
||||
~CSerializeXMLWriterImpl();
|
||||
|
||||
template <class T_Value>
|
||||
bool Value(const char* name, T_Value& value)
|
||||
{
|
||||
AddValue(name, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T_Value, class T_Policy>
|
||||
bool Value(const char* name, T_Value& value, [[maybe_unused]] const T_Policy& policy)
|
||||
{
|
||||
return Value(name, value);
|
||||
}
|
||||
|
||||
bool Value(const char* name, CTimeValue value);
|
||||
bool Value(const char* name, XmlNodeRef& value);
|
||||
|
||||
void BeginGroup(const char* szName);
|
||||
bool BeginOptionalGroup(const char* szName, bool condition);
|
||||
void EndGroup();
|
||||
|
||||
void GetMemoryUsage(ICrySizer* pSizer) const;
|
||||
|
||||
private:
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Vars.
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
CTimeValue m_curTime;
|
||||
|
||||
std::vector<XmlNodeRef> m_nodeStack;
|
||||
std::vector<const char*> m_luaSaveStack;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
ILINE const XmlNodeRef& CurNode()
|
||||
{
|
||||
assert(!m_nodeStack.empty());
|
||||
if (m_nodeStack.empty())
|
||||
{
|
||||
static XmlNodeRef temp = GetISystem()->CreateXmlNode("Error");
|
||||
return temp;
|
||||
}
|
||||
return m_nodeStack.back();
|
||||
}
|
||||
|
||||
XmlNodeRef CreateNodeNamed(const char* name);
|
||||
|
||||
template <class T>
|
||||
void AddValue(const char* name, const T& value)
|
||||
{
|
||||
if (strchr(name, ' ') != 0)
|
||||
{
|
||||
assert(0 && "Spaces in Value name not supported");
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "!Spaces in Value name not supported: %s in Group %s", name, GetStackInfo());
|
||||
return;
|
||||
}
|
||||
if (GetISystem()->IsDevMode() && CurNode())
|
||||
{
|
||||
// Check if this attribute already added.
|
||||
if (CurNode()->haveAttr(name))
|
||||
{
|
||||
assert(0);
|
||||
CryWarning(VALIDATOR_MODULE_SYSTEM, VALIDATOR_WARNING, "!Duplicate tag Value( \"%s\" ) in Group %s", name, GetStackInfo());
|
||||
}
|
||||
}
|
||||
|
||||
if (!IsDefaultValue(value))
|
||||
{
|
||||
CurNode()->setAttr(name, value);
|
||||
}
|
||||
}
|
||||
void AddValue(const char* name, const SSerializeString& value)
|
||||
{
|
||||
AddValue(name, value.c_str());
|
||||
}
|
||||
void AddValue([[maybe_unused]] const char* name, [[maybe_unused]] const SNetObjectID& value)
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
template <class T>
|
||||
void AddTypedValue(const char* name, const T& value, const char* type)
|
||||
{
|
||||
if (!IsDefaultValue(value))
|
||||
{
|
||||
XmlNodeRef newNode = CreateNodeNamed(name);
|
||||
newNode->setAttr("v", value);
|
||||
newNode->setAttr("t", type);
|
||||
}
|
||||
}
|
||||
|
||||
// Used for printing currebnt stack info for warnings.
|
||||
const char* GetStackInfo() const;
|
||||
const char* GetLuaStackInfo() const;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Check For Defaults.
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool IsDefaultValue(bool v) const { return v == false; };
|
||||
bool IsDefaultValue(float v) const { return v == 0; };
|
||||
bool IsDefaultValue(double v) const { return v == 0; };
|
||||
bool IsDefaultValue(int8 v) const { return v == 0; };
|
||||
bool IsDefaultValue(uint8 v) const { return v == 0; };
|
||||
bool IsDefaultValue(int16 v) const { return v == 0; };
|
||||
bool IsDefaultValue(uint16 v) const { return v == 0; };
|
||||
bool IsDefaultValue(int32 v) const { return v == 0; };
|
||||
bool IsDefaultValue(uint32 v) const { return v == 0; };
|
||||
bool IsDefaultValue(int64 v) const { return v == 0; };
|
||||
bool IsDefaultValue(uint64 v) const { return v == 0; };
|
||||
bool IsDefaultValue(const Vec2& v) const { return v.x == 0 && v.y == 0; };
|
||||
bool IsDefaultValue(const Vec3& v) const { return v.x == 0 && v.y == 0 && v.z == 0; };
|
||||
bool IsDefaultValue(const Ang3& v) const { return v.x == 0 && v.y == 0 && v.z == 0; };
|
||||
bool IsDefaultValue(const Quat& v) const { return v.w == 1.0f && v.v.x == 0 && v.v.y == 0 && v.v.z == 0; };
|
||||
bool IsDefaultValue(const CTimeValue& v) const { return v.GetValue() == 0; };
|
||||
bool IsDefaultValue(const char* str) const { return !str || !*str; };
|
||||
bool IsDefaultValue(const string& str) const { return str.empty(); };
|
||||
bool IsDefaultValue(const SSerializeString& str) const { return str.empty(); };
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
|
||||
template <class T>
|
||||
bool IsDefaultValue( const T& v ) const { return false; };
|
||||
*/
|
||||
};
|
||||
|
||||
#endif // CRYINCLUDE_CRYSYSTEM_XML_SERIALIZEXMLWRITER_H
|
||||
@@ -0,0 +1,430 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "CrySystem_precompiled.h"
|
||||
#include "ReadWriteXMLSink.h"
|
||||
|
||||
#include <stack>
|
||||
|
||||
typedef std::map<string, XmlNodeRef> IdTable;
|
||||
|
||||
static bool IsOptionalWriteXML(XmlNodeRef& definition);
|
||||
|
||||
static bool SaveTableInner(const IdTable&, XmlNodeRef& definition, XmlNodeRef& data, IWriteXMLSource* pSource);
|
||||
static bool SaveReferencedId(const IdTable&, XmlNodeRef& definition, XmlNodeRef& data, IWriteXMLSource* pSource);
|
||||
static bool SaveSomething(const IdTable&, XmlNodeRef& definition, XmlNodeRef& data, IWriteXMLSource* pSource);
|
||||
static bool SaveArray(const IdTable&, XmlNodeRef& definition, XmlNodeRef& data, IWriteXMLSource* pSource);
|
||||
static bool SaveProperty(const IdTable&, XmlNodeRef& definition, XmlNodeRef& data, IWriteXMLSource* pSource);
|
||||
static bool SaveTable(const IdTable&, XmlNodeRef& definition, XmlNodeRef& data, IWriteXMLSource* pSource);
|
||||
static bool SaveArraySetValueTable(const IdTable&, XmlNodeRef& definition, XmlNodeRef& data, IWriteXMLSource* pSource, int elem);
|
||||
|
||||
typedef bool (* SaveArraySetValue)(const IdTable&, XmlNodeRef& definition, XmlNodeRef& data, IWriteXMLSource* pSource, int elem);
|
||||
typedef bool (* SaveDefinitionFunction)(const IdTable&, XmlNodeRef& definition, XmlNodeRef& data, IWriteXMLSource* pSource);
|
||||
|
||||
template <class T>
|
||||
struct WritePropertyTyped;
|
||||
|
||||
template <class T>
|
||||
struct WritePropertyTyped
|
||||
{
|
||||
static bool Save(const char* name, XmlNodeRef& definition, XmlNodeRef& data, IWriteXMLSource* pSource)
|
||||
{
|
||||
IWriteXMLSource::TValue vvalue((T()));
|
||||
if (!pSource->GetValue(name, vvalue, definition))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
T* pValue = AZStd::get_if<T>(&vvalue);
|
||||
if (!pValue)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
data->setAttr(name, *pValue);
|
||||
return true;
|
||||
}
|
||||
static bool SaveArray([[maybe_unused]] const IdTable& idTable, XmlNodeRef& definition, XmlNodeRef& data, IWriteXMLSource* pSource, int elem)
|
||||
{
|
||||
IWriteXMLSource::TValue vvalue((T()));
|
||||
if (!pSource->GetAt(elem, vvalue, definition))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
T* pValue = AZStd::get_if<T>(&vvalue);
|
||||
if (!pValue)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
data->setAttr("value", *pValue);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct WritePropertyTyped<string>
|
||||
: public WritePropertyTyped<const char*>
|
||||
{
|
||||
};
|
||||
|
||||
bool IsOptionalWriteXML(XmlNodeRef& definition)
|
||||
{
|
||||
bool optional = false;
|
||||
definition->getAttr("optional", optional);
|
||||
return optional;
|
||||
}
|
||||
|
||||
bool SaveProperty([[maybe_unused]] const IdTable& idTable, XmlNodeRef& definition, XmlNodeRef& data, IWriteXMLSource* pSource)
|
||||
{
|
||||
const char* name = definition->getAttr("name");
|
||||
if (0 == strlen(name))
|
||||
{
|
||||
CryLog("Property has no name");
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* type = definition->getAttr("type");
|
||||
if (0 == strlen(type))
|
||||
{
|
||||
CryLog("Property '%s' has no type", type);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsOptionalWriteXML(definition) && !pSource->HaveValue(name))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ok = false;
|
||||
#define SAVE_PROPERTY(whichType) else if (0 == strcmp(type, #whichType)) ok = WritePropertyTyped<whichType>::Save(name, definition, data, pSource)
|
||||
XML_SET_PROPERTY_HELPER(SAVE_PROPERTY);
|
||||
#undef SAVE_PROPERTY
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
CryLog("Failed loading attribute %s of type %s", name, type);
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool SaveArraySetValueTable(const IdTable& idTable, XmlNodeRef& definition, XmlNodeRef& data, IWriteXMLSource* pSource, int elem)
|
||||
{
|
||||
IWriteXMLSourcePtr pChildSource = pSource->BeginTableAt(elem);
|
||||
if (!pChildSource)
|
||||
{
|
||||
CryLog("Failed to find source table at %d", elem);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SaveTableInner(idTable, definition, data, &*pChildSource))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!pChildSource->EndTableAt(elem))
|
||||
{
|
||||
CryLog("Failed to finish table at element %d", elem);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SaveArray(const IdTable& idTable, XmlNodeRef& definition, XmlNodeRef& data, IWriteXMLSource* pSource)
|
||||
{
|
||||
const char* name = definition->getAttr("name");
|
||||
if (0 == strlen(name))
|
||||
{
|
||||
CryLog("Array has no name");
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* elementName = definition->getAttr("elementName");
|
||||
if (0 == strlen(elementName))
|
||||
{
|
||||
elementName = "element";
|
||||
}
|
||||
|
||||
bool validateArray = true;
|
||||
if (definition->haveAttr(elementName))
|
||||
{
|
||||
definition->getAttr("validate", validateArray);
|
||||
}
|
||||
|
||||
size_t numElems = 0;
|
||||
IWriteXMLSourcePtr childSource = pSource->BeginArray(name, &numElems, definition);
|
||||
if (!childSource)
|
||||
{
|
||||
bool ok = IsOptionalWriteXML(definition);
|
||||
if (!ok)
|
||||
{
|
||||
CryLog("Failed to begin array named %s", name);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
XmlNodeRef childData = data->createNode(name);
|
||||
|
||||
SaveArraySetValue setter = NULL;
|
||||
if (definition->haveAttr("type"))
|
||||
{
|
||||
setter = NULL;
|
||||
const char* type = definition->getAttr("type");
|
||||
#define SETTER_PROPERTY(whichType) else if (0 == strcmp(type, #whichType)) setter = WritePropertyTyped<whichType>::SaveArray
|
||||
XML_SET_PROPERTY_HELPER(SETTER_PROPERTY);
|
||||
#undef SETTER_PROPERTY
|
||||
if (!setter)
|
||||
{
|
||||
CryLog("Unknown type %s in array %s", type, name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
setter = SaveArraySetValueTable;
|
||||
}
|
||||
|
||||
bool needIndex = false;
|
||||
for (size_t i = 1; i <= numElems; i++)
|
||||
{
|
||||
if (!childSource->HaveElemAt(i))
|
||||
{
|
||||
needIndex = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
XmlNodeRef elemData = childData->createNode(elementName);
|
||||
if (needIndex)
|
||||
{
|
||||
elemData->setAttr("_index", i);
|
||||
}
|
||||
needIndex = false;
|
||||
|
||||
if (!setter(idTable, definition, elemData, &*childSource, i))
|
||||
{
|
||||
CryLog("Failed saving element %d of array %s", int(i), name);
|
||||
return false;
|
||||
}
|
||||
|
||||
childData->addChild(elemData);
|
||||
}
|
||||
}
|
||||
|
||||
if (!pSource->EndArray(name))
|
||||
{
|
||||
CryLog("Failed to finish array named %s", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
data->addChild(childData);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SaveTable(const IdTable& idTable, XmlNodeRef& definition, XmlNodeRef& data, IWriteXMLSource* pSource)
|
||||
{
|
||||
const char* name = definition->getAttr("name");
|
||||
if (0 == strlen(name))
|
||||
{
|
||||
CryLog("Child-table has no name");
|
||||
return false;
|
||||
}
|
||||
|
||||
IWriteXMLSourcePtr childSource = pSource->BeginTable(name);
|
||||
if (!childSource)
|
||||
{
|
||||
bool ok = IsOptionalWriteXML(definition);
|
||||
if (!ok)
|
||||
{
|
||||
CryLog("Source creation failed for table %s", name);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
XmlNodeRef childData = data->createNode(name);
|
||||
|
||||
if (!SaveTableInner(idTable, definition, childData, childSource))
|
||||
{
|
||||
CryLog("Failed to load data for child table %s", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!pSource->EndTable(name))
|
||||
{
|
||||
CryLog("Table %s failed to complete in sink", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
data->addChild(childData);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SaveSomething(const IdTable& idTable, XmlNodeRef& nodeDefinition, XmlNodeRef& data, IWriteXMLSource* pSource)
|
||||
{
|
||||
static struct
|
||||
{
|
||||
const char* name;
|
||||
SaveDefinitionFunction saver;
|
||||
} saverTypes[] = {
|
||||
{"Property", &SaveProperty},
|
||||
{"Array", &SaveArray},
|
||||
{"Table", &SaveTable},
|
||||
{"Use", &SaveReferencedId},
|
||||
};
|
||||
static const int numSaverTypes = sizeof(saverTypes) / sizeof(*saverTypes);
|
||||
|
||||
const char* nodeDefinitionTag = nodeDefinition->getTag();
|
||||
bool ok = false;
|
||||
int i;
|
||||
for (i = 0; i < numSaverTypes; i++)
|
||||
{
|
||||
if (0 == strcmp(saverTypes[i].name, nodeDefinitionTag))
|
||||
{
|
||||
ok = saverTypes[i].saver(idTable, nodeDefinition, data, pSource);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ok)
|
||||
{
|
||||
if (i == numSaverTypes)
|
||||
{
|
||||
CryLog("Invalid definition node type %s", nodeDefinitionTag);
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool SaveReferencedId(const IdTable& idTable, XmlNodeRef& definition, XmlNodeRef& data, IWriteXMLSource* pSource)
|
||||
{
|
||||
IdTable::const_iterator iter = idTable.find(definition->getAttr("id"));
|
||||
if (iter == idTable.end())
|
||||
{
|
||||
CryLog("No definition with id '%s'", definition->getAttr("id"));
|
||||
return false;
|
||||
}
|
||||
XmlNodeRef useDefinition = iter->second;
|
||||
useDefinition = useDefinition->clone();
|
||||
int numAttrs = definition->getNumAttributes();
|
||||
for (int i = 0; i < numAttrs; i++)
|
||||
{
|
||||
const char* key, * value;
|
||||
definition->getAttributeByIndex(i, &key, &value);
|
||||
useDefinition->setAttr(key, value);
|
||||
}
|
||||
return SaveSomething(idTable, useDefinition, data, pSource);
|
||||
}
|
||||
|
||||
bool SaveTableInner(const IdTable& idTable, XmlNodeRef& definition, XmlNodeRef& data, IWriteXMLSource* pSource)
|
||||
{
|
||||
const int nChildrenDefinition = definition->getChildCount();
|
||||
for (int nChildDefinition = 0; nChildDefinition < nChildrenDefinition; nChildDefinition++)
|
||||
{
|
||||
XmlNodeRef nodeDefinition = definition->getChild(nChildDefinition);
|
||||
|
||||
if (!SaveSomething(idTable, nodeDefinition, data, pSource))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
XmlNodeRef CReadWriteXMLSink::CreateXMLFromSource(const char* definitionFile, IWriteXMLSource* pSource)
|
||||
{
|
||||
XmlNodeRef rootDefinition = GetISystem()->LoadXmlFromFile(definitionFile);
|
||||
if (!rootDefinition)
|
||||
{
|
||||
CryLog("Unable to load XML-Lua definition file: %s", definitionFile);
|
||||
return 0;
|
||||
}
|
||||
if (0 != strcmp(rootDefinition->getTag(), "Definition"))
|
||||
{
|
||||
CryLog("Root tag of definition file was %s; expected Definition", rootDefinition->getTag());
|
||||
return 0;
|
||||
}
|
||||
const char* rootNode = "Root";
|
||||
if (rootDefinition->haveAttr("root"))
|
||||
{
|
||||
rootNode = rootDefinition->getAttr("root");
|
||||
}
|
||||
XmlNodeRef rootData = GetISystem()->CreateXmlNode(rootNode);
|
||||
|
||||
XmlNodeRef allowAlways = rootDefinition->findChild("AllowAlways");
|
||||
if (allowAlways != 0)
|
||||
{
|
||||
rootDefinition->removeChild(allowAlways);
|
||||
}
|
||||
|
||||
XmlNodeRef settingsParams = rootDefinition->findChild("Settings");
|
||||
if (settingsParams != 0)
|
||||
{
|
||||
rootDefinition->removeChild(settingsParams);
|
||||
}
|
||||
|
||||
// scan for id's in the structure (for the Use member)
|
||||
IdTable idTable;
|
||||
std::stack<XmlNodeRef> scanStack;
|
||||
scanStack.push(rootDefinition);
|
||||
while (!scanStack.empty())
|
||||
{
|
||||
XmlNodeRef refNode = scanStack.top();
|
||||
scanStack.pop();
|
||||
|
||||
int numChildren = refNode->getChildCount();
|
||||
const char* tag = refNode->getTag();
|
||||
|
||||
for (int i = 0; i < numChildren; i++)
|
||||
{
|
||||
scanStack.push(refNode->getChild(i));
|
||||
}
|
||||
|
||||
if (refNode->haveAttr("id") && 0 != strcmp("Use", tag))
|
||||
{
|
||||
idTable[refNode->getAttr("id")] = refNode;
|
||||
}
|
||||
|
||||
if (allowAlways != 0 && (!strcmp("Table", tag) || !strcmp("Array", tag)))
|
||||
{
|
||||
for (int i = 0; i < allowAlways->getChildCount(); ++i)
|
||||
{
|
||||
refNode->addChild(allowAlways->getChild(i)->clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!SaveTableInner(idTable, rootDefinition, rootData, pSource))
|
||||
{
|
||||
CryLog("Error createing xml using definition %s", definitionFile);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ok = pSource->Complete();
|
||||
if (!ok)
|
||||
{
|
||||
CryLog("Warning: sink failed to complete writing");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return rootData;
|
||||
}
|
||||
|
||||
bool CReadWriteXMLSink::WriteXML(const char* definitionFile, const char* dataFile, IWriteXMLSource* pSource)
|
||||
{
|
||||
XmlNodeRef data = CreateXMLFromSource(definitionFile, pSource);
|
||||
if (!data)
|
||||
{
|
||||
CryLog("Failed creating %s", dataFile);
|
||||
return false;
|
||||
}
|
||||
if (!data->saveToFile(dataFile))
|
||||
{
|
||||
CryLog("Failed saving %s", dataFile);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,381 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <platform.h>
|
||||
#include "XMLBinaryNode.h"
|
||||
#include <CrySizer.h>
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
CBinaryXmlData::CBinaryXmlData()
|
||||
: pNodes(0)
|
||||
, pAttributes(0)
|
||||
, pChildIndices(0)
|
||||
, pStringData(0)
|
||||
, pFileContents(0)
|
||||
, nFileSize(0)
|
||||
, bOwnsFileContentsMemory(true)
|
||||
, pBinaryNodes(0)
|
||||
, nRefCount(0)
|
||||
{
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
CBinaryXmlData::~CBinaryXmlData()
|
||||
{
|
||||
if (bOwnsFileContentsMemory)
|
||||
{
|
||||
delete [] pFileContents;
|
||||
}
|
||||
pFileContents = 0;
|
||||
|
||||
delete [] pBinaryNodes;
|
||||
pBinaryNodes = 0;
|
||||
}
|
||||
|
||||
void CBinaryXmlData::GetMemoryUsage(ICrySizer* pSizer) const
|
||||
{
|
||||
pSizer->AddObject(pFileContents, nFileSize);
|
||||
const XMLBinary::BinaryFileHeader* pHeader = reinterpret_cast<const XMLBinary::BinaryFileHeader*>(pFileContents);
|
||||
pSizer->AddObject(pBinaryNodes, sizeof(CBinaryXmlNode) * pHeader->nNodeCount);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// CBinaryXmlNode implementation.
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// collect allocated memory informations
|
||||
void CBinaryXmlNode::GetMemoryUsage(ICrySizer* pSizer) const
|
||||
{
|
||||
pSizer->AddObject(m_pData);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
XmlNodeRef CBinaryXmlNode::getParent() const
|
||||
{
|
||||
const XMLBinary::Node* const pNode = _node();
|
||||
if (pNode->nParentIndex != (XMLBinary::NodeIndex)-1)
|
||||
{
|
||||
return &m_pData->pBinaryNodes[pNode->nParentIndex];
|
||||
}
|
||||
return XmlNodeRef();
|
||||
}
|
||||
|
||||
XmlNodeRef CBinaryXmlNode::createNode([[maybe_unused]] const char* tag)
|
||||
{
|
||||
assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CBinaryXmlNode::isTag(const char* tag) const
|
||||
{
|
||||
return g_pXmlStrCmp(tag, getTag()) == 0;
|
||||
}
|
||||
|
||||
const char* CBinaryXmlNode::getAttr(const char* key) const
|
||||
{
|
||||
const char* svalue = GetValue(key);
|
||||
if (svalue)
|
||||
{
|
||||
return svalue;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
bool CBinaryXmlNode::getAttr(const char* key, const char** value) const
|
||||
{
|
||||
const char* svalue = GetValue(key);
|
||||
if (svalue)
|
||||
{
|
||||
*value = svalue;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
*value = "";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool CBinaryXmlNode::haveAttr(const char* key) const
|
||||
{
|
||||
return (GetValue(key) != 0);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CBinaryXmlNode::getAttr(const char* key, int& value) const
|
||||
{
|
||||
const char* svalue = GetValue(key);
|
||||
if (svalue)
|
||||
{
|
||||
value = atoi(svalue);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CBinaryXmlNode::getAttr(const char* key, unsigned int& value) const
|
||||
{
|
||||
const char* svalue = GetValue(key);
|
||||
if (svalue)
|
||||
{
|
||||
value = strtoul(svalue, NULL, 10);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CBinaryXmlNode::getAttr(const char* key, int64& value) const
|
||||
{
|
||||
const char* svalue = GetValue(key);
|
||||
if (svalue)
|
||||
{
|
||||
azsscanf(svalue, "%" PRId64, &value);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CBinaryXmlNode::getAttr(const char* key, uint64& value, bool useHexFormat) const
|
||||
{
|
||||
const char* svalue = GetValue(key);
|
||||
if (svalue)
|
||||
{
|
||||
if (useHexFormat)
|
||||
{
|
||||
azsscanf(svalue, "%" PRIX64, &value);
|
||||
}
|
||||
else
|
||||
{
|
||||
azsscanf(svalue, "%" PRIu64, &value);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CBinaryXmlNode::getAttr(const char* key, bool& value) const
|
||||
{
|
||||
const char* svalue = GetValue(key);
|
||||
if (svalue)
|
||||
{
|
||||
value = atoi(svalue) != 0;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CBinaryXmlNode::getAttr(const char* key, float& value) const
|
||||
{
|
||||
const char* svalue = GetValue(key);
|
||||
if (svalue)
|
||||
{
|
||||
value = (float)atof(svalue);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CBinaryXmlNode::getAttr(const char* key, double& value) const
|
||||
{
|
||||
const char* svalue = GetValue(key);
|
||||
if (svalue)
|
||||
{
|
||||
value = atof(svalue);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CBinaryXmlNode::getAttr(const char* key, Ang3& value) const
|
||||
{
|
||||
const char* svalue = GetValue(key);
|
||||
if (svalue)
|
||||
{
|
||||
float x, y, z;
|
||||
if (azsscanf(svalue, "%f,%f,%f", &x, &y, &z) == 3)
|
||||
{
|
||||
value(x, y, z);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CBinaryXmlNode::getAttr(const char* key, Vec3& value) const
|
||||
{
|
||||
const char* svalue = GetValue(key);
|
||||
if (svalue)
|
||||
{
|
||||
float x, y, z;
|
||||
if (azsscanf(svalue, "%f,%f,%f", &x, &y, &z) == 3)
|
||||
{
|
||||
value = Vec3(x, y, z);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CBinaryXmlNode::getAttr(const char* key, Vec4& value) const
|
||||
{
|
||||
const char* svalue = GetValue(key);
|
||||
if (svalue)
|
||||
{
|
||||
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 CBinaryXmlNode::getAttr(const char* key, Vec3d& value) const
|
||||
{
|
||||
const char* svalue = GetValue(key);
|
||||
if (svalue)
|
||||
{
|
||||
double x, y, z;
|
||||
if (azsscanf(svalue, "%lf,%lf,%lf", &x, &y, &z) == 3)
|
||||
{
|
||||
value = Vec3d(x, y, z);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CBinaryXmlNode::getAttr(const char* key, Vec2& value) const
|
||||
{
|
||||
const char* svalue = GetValue(key);
|
||||
if (svalue)
|
||||
{
|
||||
float x, y;
|
||||
if (azsscanf(svalue, "%f,%f", &x, &y) == 2)
|
||||
{
|
||||
value = Vec2(x, y);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CBinaryXmlNode::getAttr(const char* key, Vec2d& value) const
|
||||
{
|
||||
const char* svalue = GetValue(key);
|
||||
if (svalue)
|
||||
{
|
||||
double x, y;
|
||||
if (azsscanf(svalue, "%lf,%lf", &x, &y) == 2)
|
||||
{
|
||||
value = Vec2d(x, y);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CBinaryXmlNode::getAttr(const char* key, Quat& value) const
|
||||
{
|
||||
const char* svalue = GetValue(key);
|
||||
if (svalue)
|
||||
{
|
||||
float w, x, y, z;
|
||||
if (azsscanf(svalue, "%f,%f,%f,%f", &w, &x, &y, &z) == 4)
|
||||
{
|
||||
value = Quat(w, x, y, z);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CBinaryXmlNode::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(r, g, b, a);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
XmlNodeRef CBinaryXmlNode::findChild(const char* tag) const
|
||||
{
|
||||
const XMLBinary::Node* const pNode = _node();
|
||||
const uint32 nFirst = pNode->nFirstChildIndex;
|
||||
const uint32 nAfterLast = pNode->nFirstChildIndex + pNode->nChildCount;
|
||||
for (uint32 i = nFirst; i < nAfterLast; ++i)
|
||||
{
|
||||
const char* sChildTag = m_pData->pStringData + m_pData->pNodes[m_pData->pChildIndices[i]].nTagStringOffset;
|
||||
if (g_pXmlStrCmp(tag, sChildTag) == 0)
|
||||
{
|
||||
return m_pData->pBinaryNodes + m_pData->pChildIndices[i];
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//! Get XML Node child nodes.
|
||||
XmlNodeRef CBinaryXmlNode::getChild(int i) const
|
||||
{
|
||||
const XMLBinary::Node* const pNode = _node();
|
||||
assert(i >= 0 && i < (int)pNode->nChildCount);
|
||||
return m_pData->pBinaryNodes + m_pData->pChildIndices[pNode->nFirstChildIndex + i];
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CBinaryXmlNode::getAttributeByIndex(int index, const char** key, const char** value)
|
||||
{
|
||||
const XMLBinary::Node* const pNode = _node();
|
||||
if (index >= 0 && index < pNode->nAttributeCount)
|
||||
{
|
||||
const XMLBinary::Attribute& attr = m_pData->pAttributes[pNode->nFirstAttributeIndex + index];
|
||||
*key = _string(attr.nKeyStringOffset);
|
||||
*value = _string(attr.nValueStringOffset);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CBinaryXmlNode::getAttributeByIndex(int index, XmlString& key, XmlString& value)
|
||||
{
|
||||
const XMLBinary::Node* const pNode = _node();
|
||||
if (index >= 0 && index < pNode->nAttributeCount)
|
||||
{
|
||||
const XMLBinary::Attribute& attr = m_pData->pAttributes[pNode->nFirstAttributeIndex + index];
|
||||
key = _string(attr.nKeyStringOffset);
|
||||
value = _string(attr.nValueStringOffset);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef CRYINCLUDE_CRYSYSTEM_XML_XMLBINARYNODE_H
|
||||
#define CRYINCLUDE_CRYSYSTEM_XML_XMLBINARYNODE_H
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <algorithm>
|
||||
#include "IXml.h"
|
||||
#include "XMLBinaryHeaders.h"
|
||||
|
||||
// Compare function for string comparison, can be strcmp or _stricmp
|
||||
typedef int (__cdecl * XmlStrCmpFunc)(const char* str1, const char* str2);
|
||||
extern XmlStrCmpFunc g_pXmlStrCmp;
|
||||
|
||||
class CBinaryXmlNode;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
class CBinaryXmlData
|
||||
{
|
||||
public:
|
||||
const XMLBinary::Node* pNodes;
|
||||
const XMLBinary::Attribute* pAttributes;
|
||||
const XMLBinary::NodeIndex* pChildIndices;
|
||||
const char* pStringData;
|
||||
|
||||
const char* pFileContents;
|
||||
size_t nFileSize;
|
||||
bool bOwnsFileContentsMemory;
|
||||
|
||||
CBinaryXmlNode* pBinaryNodes;
|
||||
|
||||
int nRefCount;
|
||||
|
||||
CBinaryXmlData();
|
||||
~CBinaryXmlData();
|
||||
|
||||
void GetMemoryUsage(ICrySizer* pSizer) const;
|
||||
};
|
||||
|
||||
// forward declaration
|
||||
namespace XMLBinary
|
||||
{
|
||||
class XMLBinaryReader;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// CBinaryXmlNode class only used for fast read only binary XML import
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
class CBinaryXmlNode
|
||||
: public IXmlNode
|
||||
{
|
||||
public:
|
||||
|
||||
// collect allocated memory informations
|
||||
void GetMemoryUsage(ICrySizer* pSizer) const;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Custom new/delete with pool allocator.
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//void* operator new( size_t nSize );
|
||||
//void operator delete( void *ptr );
|
||||
|
||||
virtual void DeleteThis() { }
|
||||
|
||||
//! Create new XML node.
|
||||
XmlNodeRef createNode(const char* tag);
|
||||
|
||||
// Summary:
|
||||
// Reference counting.
|
||||
virtual void AddRef() { ++m_pData->nRefCount; };
|
||||
// Notes:
|
||||
// When ref count reach zero XML node dies.
|
||||
virtual void Release()
|
||||
{
|
||||
if (--m_pData->nRefCount <= 0)
|
||||
{
|
||||
delete m_pData;
|
||||
}
|
||||
};
|
||||
|
||||
//! Get XML node tag.
|
||||
const char* getTag() const { return _string(_node()->nTagStringOffset); };
|
||||
void setTag([[maybe_unused]] const char* tag) { assert(0); };
|
||||
|
||||
//! Return true if given tag is equal to node tag.
|
||||
bool isTag(const char* tag) const;
|
||||
|
||||
//! Get XML Node attributes.
|
||||
virtual int getNumAttributes() const { return (int)_node()->nAttributeCount; };
|
||||
//! Return attribute key and value by attribute index.
|
||||
virtual bool getAttributeByIndex(int index, const char** key, const char** value);
|
||||
//! Return attribute key and value by attribute index, string version.
|
||||
virtual bool getAttributeByIndex(int index, XmlString& key, XmlString& value);
|
||||
|
||||
|
||||
virtual void shareChildren([[maybe_unused]] const XmlNodeRef& fromNode) { assert(0); };
|
||||
virtual void copyAttributes(XmlNodeRef fromNode) { assert(0); };
|
||||
|
||||
//! Get XML Node attribute for specified key.
|
||||
const char* getAttr(const char* key) const;
|
||||
|
||||
//! Get XML Node attribute for specified key.
|
||||
// Returns true if the attribute exists, false otherwise.
|
||||
bool getAttr(const char* key, const char** value) const;
|
||||
|
||||
//! Check if attributes with specified key exist.
|
||||
bool haveAttr(const char* key) const;
|
||||
|
||||
XmlNodeRef newChild([[maybe_unused]] const char* tagName) { assert(0); return 0; };
|
||||
void replaceChild([[maybe_unused]] int inChild, [[maybe_unused]] const XmlNodeRef& node) { assert(0); };
|
||||
void insertChild([[maybe_unused]] int inChild, [[maybe_unused]] const XmlNodeRef& node) { assert(0); };
|
||||
void addChild([[maybe_unused]] const XmlNodeRef& node) { assert(0); };
|
||||
void removeChild([[maybe_unused]] const XmlNodeRef& node) { assert(0); };
|
||||
|
||||
//! Remove all child nodes.
|
||||
void removeAllChilds() { assert(0); };
|
||||
|
||||
//! Get number of child XML nodes.
|
||||
int getChildCount() const { return (int)_node()->nChildCount; };
|
||||
|
||||
//! Get XML Node child nodes.
|
||||
XmlNodeRef getChild(int i) const;
|
||||
|
||||
//! Find node with specified tag.
|
||||
XmlNodeRef findChild(const char* tag) const;
|
||||
void deleteChild([[maybe_unused]] const char* tag) { assert(0); };
|
||||
void deleteChildAt([[maybe_unused]] int nIndex) { assert(0); };
|
||||
|
||||
//! Get parent XML node.
|
||||
XmlNodeRef getParent() const;
|
||||
|
||||
//! Returns content of this node.
|
||||
const char* getContent() const { return _string(_node()->nContentStringOffset); };
|
||||
void setContent([[maybe_unused]] const char* str) { assert(0); };
|
||||
|
||||
XmlNodeRef clone() { assert(0); return 0; };
|
||||
|
||||
//! Returns line number for XML tag.
|
||||
int getLine() const { return 0; };
|
||||
//! Set line number in xml.
|
||||
void setLine([[maybe_unused]] int line) { assert(0); };
|
||||
|
||||
//! Returns XML of this node and sub nodes.
|
||||
virtual IXmlStringData* getXMLData([[maybe_unused]] int nReserveMem = 0) const { assert(0); return 0; };
|
||||
XmlString getXML([[maybe_unused]] int level = 0) const { assert(0); return ""; };
|
||||
bool saveToFile([[maybe_unused]] const char* fileName) { assert(0); return false; }; // saves in one huge chunk
|
||||
bool saveToFile([[maybe_unused]] const char* fileName, [[maybe_unused]] size_t chunkSizeBytes, [[maybe_unused]] AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle) { assert(0); return false; }; // save in small memory chunks
|
||||
|
||||
//! Set new XML Node attribute (or override attribute with same key).
|
||||
void setAttr([[maybe_unused]] const char* key, [[maybe_unused]] const char* value) { assert(0); };
|
||||
void setAttr([[maybe_unused]] const char* key, [[maybe_unused]] int value) { assert(0); };
|
||||
void setAttr([[maybe_unused]] const char* key, [[maybe_unused]] unsigned int value) { assert(0); };
|
||||
void setAttr([[maybe_unused]] const char* key, [[maybe_unused]] int64 value) { assert(0); };
|
||||
void setAttr([[maybe_unused]] const char* key, [[maybe_unused]] uint64 value, [[maybe_unused]] bool useHexFormat = true /* ignored */) { assert(0); };
|
||||
void setAttr([[maybe_unused]] const char* key, [[maybe_unused]] float value) { assert(0); };
|
||||
void setAttr([[maybe_unused]] const char* key, [[maybe_unused]] f64 value) { assert(0); };
|
||||
void setAttr([[maybe_unused]] const char* key, [[maybe_unused]] const Vec2& value) { assert(0); };
|
||||
void setAttr([[maybe_unused]] const char* key, [[maybe_unused]] const Vec2d& value) { assert(0); };
|
||||
void setAttr([[maybe_unused]] const char* key, [[maybe_unused]] const Ang3& value) { assert(0); };
|
||||
void setAttr([[maybe_unused]] const char* key, [[maybe_unused]] const Vec3& value) { assert(0); };
|
||||
void setAttr([[maybe_unused]] const char* key, [[maybe_unused]] const Vec4& value) { assert(0); };
|
||||
void setAttr([[maybe_unused]] const char* key, [[maybe_unused]] const Vec3d& value) { assert(0); };
|
||||
void setAttr([[maybe_unused]] const char* key, [[maybe_unused]] const Quat& value) { assert(0); };
|
||||
void delAttr([[maybe_unused]] const char* key) { assert(0); };
|
||||
void removeAllAttributes() { assert(0); };
|
||||
|
||||
//! Get attribute value of node.
|
||||
bool getAttr(const char* key, int& value) const;
|
||||
bool getAttr(const char* key, unsigned int& value) const;
|
||||
bool getAttr(const char* key, int64& value) const;
|
||||
bool getAttr(const char* key, uint64& value, bool useHexFormat = true /* ignored */) const;
|
||||
bool getAttr(const char* key, float& value) const;
|
||||
bool getAttr(const char* key, f64& value) const;
|
||||
bool getAttr(const char* key, bool& value) const;
|
||||
bool getAttr(const char* key, XmlString& value) const {const char* v(NULL); bool boHasAttribute(getAttr(key, &v)); value = v; return boHasAttribute; }
|
||||
bool getAttr(const char* key, Vec2& value) const;
|
||||
bool getAttr(const char* key, Vec2d& value) const;
|
||||
bool getAttr(const char* key, Ang3& value) const;
|
||||
bool getAttr(const char* key, Vec3& value) const;
|
||||
bool getAttr(const char* key, Vec4& value) const;
|
||||
bool getAttr(const char* key, Vec3d& value) const;
|
||||
bool getAttr(const char* key, Quat& value) const;
|
||||
bool getAttr(const char* key, ColorB& value) const;
|
||||
// bool getAttr( const char *key,CString &value ) const { XmlString v; if (getAttr(key,v)) { value = (const char*)v; return true; } else return false; }
|
||||
|
||||
|
||||
private:
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// INTERNAL METHODS
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
const char* GetValue(const char* key) const
|
||||
{
|
||||
const XMLBinary::Attribute* const pAttributes = m_pData->pAttributes;
|
||||
const char* const pStringData = m_pData->pStringData;
|
||||
|
||||
const int nFirst = _node()->nFirstAttributeIndex;
|
||||
const int nLast = nFirst + _node()->nAttributeCount;
|
||||
for (int i = nFirst; i < nLast; i++)
|
||||
{
|
||||
const char* const attrKey = pStringData + pAttributes[i].nKeyStringOffset;
|
||||
if (g_pXmlStrCmp(key, attrKey) == 0)
|
||||
{
|
||||
const char* attrValue = pStringData + pAttributes[i].nValueStringOffset;
|
||||
return attrValue;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Return current node in binary data.
|
||||
const XMLBinary::Node* _node() const
|
||||
{
|
||||
return &m_pData->pNodes[this - m_pData->pBinaryNodes];
|
||||
}
|
||||
|
||||
const char* _string(int nIndex) const
|
||||
{
|
||||
return m_pData->pStringData + nIndex;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void setParent([[maybe_unused]] const XmlNodeRef& inRef) { assert(0); }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
private:
|
||||
CBinaryXmlData* m_pData;
|
||||
|
||||
friend class XMLBinary::XMLBinaryReader;
|
||||
};
|
||||
|
||||
#endif // CRYINCLUDE_CRYSYSTEM_XML_XMLBINARYNODE_H
|
||||
@@ -0,0 +1,271 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <platform.h>
|
||||
#include "XMLBinaryReader.h"
|
||||
#include "XMLBinaryNode.h"
|
||||
#include "CryPath.h"
|
||||
|
||||
|
||||
XMLBinary::XMLBinaryReader::XMLBinaryReader()
|
||||
{
|
||||
m_errorDescription[0] = 0;
|
||||
}
|
||||
|
||||
|
||||
XMLBinary::XMLBinaryReader::~XMLBinaryReader()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
const char* XMLBinary::XMLBinaryReader::GetErrorDescription() const
|
||||
{
|
||||
return &m_errorDescription[0];
|
||||
}
|
||||
|
||||
|
||||
void XMLBinary::XMLBinaryReader::SetErrorDescription(const char* text)
|
||||
{
|
||||
cry_strcpy(m_errorDescription, text);
|
||||
}
|
||||
|
||||
|
||||
XmlNodeRef XMLBinary::XMLBinaryReader::LoadFromFile(
|
||||
const char* filename,
|
||||
XMLBinary::XMLBinaryReader::EResult& result)
|
||||
{
|
||||
m_errorDescription[0] = 0;
|
||||
result = eResult_Error;
|
||||
|
||||
CCryFile xmlFile;
|
||||
|
||||
if (!xmlFile.Open(filename, "rb"))
|
||||
{
|
||||
SetErrorDescription("Can't open file.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
const size_t fileSize = xmlFile.GetLength();
|
||||
if (fileSize < sizeof(BinaryFileHeader))
|
||||
{
|
||||
result = eResult_NotBinXml;
|
||||
SetErrorDescription("File is not a binary XML file (file size is too small).");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Read in the entire file - this buffer will not be deallocated immediately, since the nodes
|
||||
// will contain pointers directly into it. It will be deleted once the reference count on the
|
||||
// CBinaryXmlData object reaches 0 again.
|
||||
|
||||
char* const pFileContents = new char[fileSize];
|
||||
|
||||
if (!pFileContents)
|
||||
{
|
||||
SetErrorDescription("Can't allocate memory for binary XML file contents.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (xmlFile.ReadRaw(pFileContents, fileSize) != fileSize)
|
||||
{
|
||||
delete [] pFileContents;
|
||||
SetErrorDescription("Failed to read binary XML file, the file is corrupt.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Check(pFileContents, fileSize, result);
|
||||
|
||||
if (result != eResult_Success)
|
||||
{
|
||||
delete [] pFileContents;
|
||||
return 0;
|
||||
}
|
||||
|
||||
CBinaryXmlData* const pData = Create(pFileContents, fileSize, result);
|
||||
|
||||
if (result != eResult_Success)
|
||||
{
|
||||
assert(pData == 0);
|
||||
delete [] pFileContents;
|
||||
return 0;
|
||||
}
|
||||
|
||||
assert(pData);
|
||||
pData->bOwnsFileContentsMemory = true;
|
||||
|
||||
// Return first node
|
||||
return &pData->pBinaryNodes[0];
|
||||
}
|
||||
|
||||
|
||||
XmlNodeRef XMLBinary::XMLBinaryReader::LoadFromBuffer(
|
||||
EBufferMemoryHandling bufferMemoryHandling,
|
||||
const char* buffer,
|
||||
size_t size,
|
||||
XMLBinary::XMLBinaryReader::EResult& result)
|
||||
{
|
||||
m_errorDescription[0] = 0;
|
||||
result = eResult_Error;
|
||||
|
||||
Check(buffer, size, result);
|
||||
|
||||
if (result != eResult_Success)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
CBinaryXmlData* pData = 0;
|
||||
|
||||
if (bufferMemoryHandling == eBufferMemoryHandling_MakeCopy)
|
||||
{
|
||||
char* ownBuffer = new char[size];
|
||||
if (!ownBuffer)
|
||||
{
|
||||
SetErrorDescription("Can't allocate memory for binary XML data.");
|
||||
return 0;
|
||||
}
|
||||
memcpy(ownBuffer, buffer, size);
|
||||
|
||||
pData = Create(ownBuffer, size, result);
|
||||
|
||||
if (result != eResult_Success)
|
||||
{
|
||||
assert(pData == 0);
|
||||
delete [] ownBuffer;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(bufferMemoryHandling == eBufferMemoryHandling_TakeOwnership);
|
||||
|
||||
pData = Create(buffer, size, result);
|
||||
|
||||
if (result != eResult_Success)
|
||||
{
|
||||
assert(pData == 0);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
assert(pData);
|
||||
pData->bOwnsFileContentsMemory = true;
|
||||
|
||||
// Return first node
|
||||
return &pData->pBinaryNodes[0];
|
||||
}
|
||||
|
||||
|
||||
void XMLBinary::XMLBinaryReader::Check(const char* buffer, size_t size, EResult& result)
|
||||
{
|
||||
m_errorDescription[0] = 0;
|
||||
result = eResult_Error;
|
||||
|
||||
if (buffer == 0)
|
||||
{
|
||||
SetErrorDescription("Buffer is null.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (size < sizeof(BinaryFileHeader))
|
||||
{
|
||||
result = eResult_NotBinXml;
|
||||
SetErrorDescription("Not a binary XML - data size is too small.");
|
||||
return;
|
||||
}
|
||||
|
||||
const BinaryFileHeader& header = *(reinterpret_cast<const BinaryFileHeader*>(buffer));
|
||||
|
||||
CheckHeader(header, size, result);
|
||||
}
|
||||
|
||||
|
||||
void XMLBinary::XMLBinaryReader::CheckHeader(const BinaryFileHeader& header, size_t size, EResult& result)
|
||||
{
|
||||
assert(size >= sizeof(BinaryFileHeader));
|
||||
|
||||
m_errorDescription[0] = 0;
|
||||
|
||||
// Check the signature of the file to make sure that it is a binary XML file.
|
||||
{
|
||||
static const char signature[] = "CryXmlB";
|
||||
COMPILE_TIME_ASSERT(sizeof(signature) == sizeof(header.szSignature));
|
||||
if (memcmp(header.szSignature, signature, sizeof(header.szSignature)) != 0)
|
||||
{
|
||||
result = eResult_NotBinXml;
|
||||
SetErrorDescription("Not a binary XML - has no signature.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check contents of the file header.
|
||||
const uint32 nNodeTableEnd = header.nNodeTablePosition + header.nNodeCount * sizeof(Node);
|
||||
const uint32 nChildTableEnd = header.nChildTablePosition + header.nChildCount * sizeof(NodeIndex);
|
||||
const uint32 nAttributeTableEnd = header.nAttributeTablePosition + header.nAttributeCount * sizeof(Attribute);
|
||||
const uint32 nStringDataEnd = header.nStringDataPosition + header.nStringDataSize;
|
||||
|
||||
bool bCorrupt = false;
|
||||
bCorrupt = bCorrupt || header.nXMLSize > size;
|
||||
bCorrupt = bCorrupt || nNodeTableEnd > header.nChildTablePosition;
|
||||
bCorrupt = bCorrupt || nChildTableEnd > header.nAttributeTablePosition;
|
||||
bCorrupt = bCorrupt || nAttributeTableEnd > header.nStringDataPosition;
|
||||
bCorrupt = bCorrupt || nStringDataEnd > header.nXMLSize;
|
||||
if (bCorrupt)
|
||||
{
|
||||
result = eResult_Error;
|
||||
SetErrorDescription("Binary XML data is corrupt.");
|
||||
return;
|
||||
}
|
||||
|
||||
result = eResult_Success;
|
||||
}
|
||||
|
||||
|
||||
CBinaryXmlData* XMLBinary::XMLBinaryReader::Create(const char* buffer, size_t size, EResult& result)
|
||||
{
|
||||
assert((buffer != 0) && (size >= sizeof(BinaryFileHeader)));
|
||||
|
||||
m_errorDescription[0] = 0;
|
||||
result = eResult_Error;
|
||||
|
||||
CBinaryXmlData* const pData = new CBinaryXmlData;
|
||||
if (!pData)
|
||||
{
|
||||
SetErrorDescription("Can't allocate memory for binary XML object.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
pData->pFileContents = buffer;
|
||||
pData->nFileSize = size;
|
||||
pData->bOwnsFileContentsMemory = false;
|
||||
|
||||
const BinaryFileHeader& header = *(reinterpret_cast<const BinaryFileHeader*>(buffer));
|
||||
|
||||
// Create nodes
|
||||
pData->pBinaryNodes = new CBinaryXmlNode[header.nNodeCount];
|
||||
if (!pData->pBinaryNodes)
|
||||
{
|
||||
delete pData;
|
||||
SetErrorDescription("Can't allocate memory for binary XML nodes.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
pData->pAttributes = reinterpret_cast<const Attribute*>(buffer + header.nAttributeTablePosition);
|
||||
pData->pChildIndices = reinterpret_cast<const NodeIndex*>(buffer + header.nChildTablePosition);
|
||||
pData->pNodes = reinterpret_cast<const Node*>(buffer + header.nNodeTablePosition);
|
||||
pData->pStringData = buffer + header.nStringDataPosition;
|
||||
|
||||
for (uint32 nNode = 0; nNode < header.nNodeCount; ++nNode)
|
||||
{
|
||||
CBinaryXmlNode* const pNode = &pData->pBinaryNodes[nNode];
|
||||
pNode->m_nRefCount = 0;
|
||||
pNode->m_pData = pData;
|
||||
}
|
||||
|
||||
result = eResult_Success;
|
||||
return pData;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef CRYINCLUDE_CRYSYSTEM_XML_XMLBINARYREADER_H
|
||||
#define CRYINCLUDE_CRYSYSTEM_XML_XMLBINARYREADER_H
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "XMLBinaryHeaders.h"
|
||||
#include "IXml.h"
|
||||
#include "CryFile.h"
|
||||
|
||||
class CBinaryXmlData;
|
||||
|
||||
namespace XMLBinary
|
||||
{
|
||||
class XMLBinaryReader
|
||||
{
|
||||
public:
|
||||
enum EResult
|
||||
{
|
||||
eResult_Success,
|
||||
eResult_NotBinXml,
|
||||
eResult_Error
|
||||
};
|
||||
|
||||
enum EBufferMemoryHandling
|
||||
{
|
||||
eBufferMemoryHandling_MakeCopy,
|
||||
eBufferMemoryHandling_TakeOwnership
|
||||
};
|
||||
|
||||
public:
|
||||
XMLBinaryReader();
|
||||
~XMLBinaryReader();
|
||||
|
||||
XmlNodeRef LoadFromFile(const char* filename, EResult& result);
|
||||
|
||||
// Note: if bufferMemoryHandling == eBufferMemoryHandling_TakeOwnership and
|
||||
// returned result is eResult_Success, then buffer's memory is owned and
|
||||
// will be released by XMLBinaryReader (by a 'delete[] buffer' call).
|
||||
// Otherwise, the caller is responsible for releasing buffer's memory.
|
||||
XmlNodeRef LoadFromBuffer(EBufferMemoryHandling bufferMemoryHandling, const char* buffer, size_t size, EResult& result);
|
||||
|
||||
const char* GetErrorDescription() const;
|
||||
|
||||
private:
|
||||
void Check(const char* buffer, size_t size, EResult& result);
|
||||
void CheckHeader(const BinaryFileHeader& layout, size_t size, EResult& result);
|
||||
CBinaryXmlData* Create(const char* buffer, size_t size, EResult& result);
|
||||
void SetErrorDescription(const char* text);
|
||||
|
||||
private:
|
||||
char m_errorDescription[64];
|
||||
};
|
||||
}
|
||||
|
||||
#endif // CRYINCLUDE_CRYSYSTEM_XML_XMLBINARYREADER_H
|
||||
@@ -0,0 +1,335 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <platform.h>
|
||||
#include "XMLBinaryWriter.h"
|
||||
#include "CryEndian.h"
|
||||
#include <string.h> // memcpy()
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
namespace XMLBinary
|
||||
{
|
||||
void SwapEndianness_Node(Node& t)
|
||||
{
|
||||
SwapEndian(t.nTagStringOffset, true);
|
||||
SwapEndian(t.nContentStringOffset, true);
|
||||
SwapEndian(t.nAttributeCount, true);
|
||||
SwapEndian(t.nChildCount, true);
|
||||
SwapEndian(t.nParentIndex, true);
|
||||
SwapEndian(t.nFirstAttributeIndex, true);
|
||||
SwapEndian(t.nFirstChildIndex, true);
|
||||
}
|
||||
|
||||
void SwapEndianness_Attribute(Attribute& t)
|
||||
{
|
||||
SwapEndian(t.nKeyStringOffset, true);
|
||||
SwapEndian(t.nValueStringOffset, true);
|
||||
}
|
||||
|
||||
void SwapEndianness_Header(BinaryFileHeader& t)
|
||||
{
|
||||
SwapEndian(t.nXMLSize, true);
|
||||
SwapEndian(t.nNodeTablePosition, true);
|
||||
SwapEndian(t.nNodeCount, true);
|
||||
SwapEndian(t.nAttributeTablePosition, true);
|
||||
SwapEndian(t.nAttributeCount, true);
|
||||
SwapEndian(t.nChildTablePosition, true);
|
||||
SwapEndian(t.nChildCount, true);
|
||||
SwapEndian(t.nStringDataPosition, true);
|
||||
SwapEndian(t.nStringDataSize, true);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
XMLBinary::CXMLBinaryWriter::CXMLBinaryWriter()
|
||||
{
|
||||
m_nStringDataSize = 0;
|
||||
}
|
||||
|
||||
static void align(size_t& nPosition, const size_t nAlignment)
|
||||
{
|
||||
const size_t nPadSize = ((nPosition + (nAlignment - 1)) & ~(nAlignment - 1)) - nPosition;
|
||||
nPosition += nPadSize;
|
||||
}
|
||||
|
||||
static void alignWrite(XMLBinary::IDataWriter* const pFile, size_t& nPosition, const size_t nAlignment)
|
||||
{
|
||||
size_t nPadSize = ((nPosition + (nAlignment - 1)) & ~(nAlignment - 1)) - nPosition;
|
||||
|
||||
if (nPadSize > 0)
|
||||
{
|
||||
nPosition += nPadSize;
|
||||
|
||||
static const char zeroes[32] = { 0 };
|
||||
|
||||
while (nPadSize > 0)
|
||||
{
|
||||
const size_t n = (nPadSize <= sizeof(zeroes)) ? nPadSize : sizeof(zeroes);
|
||||
nPadSize -= n;
|
||||
pFile->Write(zeroes, n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void write(XMLBinary::IDataWriter* const pFile, size_t& nPosition, const void* const pData, const size_t nDataSize)
|
||||
{
|
||||
pFile->Write(pData, nDataSize);
|
||||
nPosition += nDataSize;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool XMLBinary::CXMLBinaryWriter::WriteNode(IDataWriter* pFile, XmlNodeRef node, bool bNeedSwapEndian, XMLBinary::IFilter* pFilter, string& error)
|
||||
{
|
||||
error = "";
|
||||
|
||||
// Scan the node tree, building a flat node list, attribute list and string table.
|
||||
m_nStringDataSize = 0;
|
||||
|
||||
if (!CompileTables(node, pFilter, error))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static const uint nMaxNodeCount = (NodeIndex) ~0;
|
||||
if (m_nodes.size() > nMaxNodeCount)
|
||||
{
|
||||
error.Format("XMLBinary: Too many nodes: %d (max is %i)", m_nodes.size(), nMaxNodeCount);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initialize the file header.
|
||||
size_t nTheoreticalPosition = 0;
|
||||
static const size_t nAlignment = sizeof(uint32);
|
||||
|
||||
BinaryFileHeader header;
|
||||
static const char signature[] = "CryXmlB";
|
||||
COMPILE_TIME_ASSERT(sizeof(signature) == sizeof(header.szSignature));
|
||||
memcpy(header.szSignature, signature, sizeof(header.szSignature));
|
||||
nTheoreticalPosition += sizeof(header);
|
||||
align(nTheoreticalPosition, nAlignment);
|
||||
|
||||
header.nNodeTablePosition = nTheoreticalPosition;
|
||||
header.nNodeCount = int(m_nodes.size());
|
||||
nTheoreticalPosition += header.nNodeCount * sizeof(Node);
|
||||
align(nTheoreticalPosition, nAlignment);
|
||||
|
||||
header.nChildTablePosition = nTheoreticalPosition;
|
||||
header.nChildCount = int(m_childs.size());
|
||||
nTheoreticalPosition += header.nChildCount * sizeof(NodeIndex);
|
||||
align(nTheoreticalPosition, nAlignment);
|
||||
|
||||
header.nAttributeTablePosition = nTheoreticalPosition;
|
||||
header.nAttributeCount = int(m_attributes.size());
|
||||
nTheoreticalPosition += header.nAttributeCount * sizeof(Attribute);
|
||||
align(nTheoreticalPosition, nAlignment);
|
||||
|
||||
header.nStringDataPosition = nTheoreticalPosition;
|
||||
header.nStringDataSize = m_nStringDataSize;
|
||||
nTheoreticalPosition += header.nStringDataSize;
|
||||
|
||||
header.nXMLSize = nTheoreticalPosition;
|
||||
|
||||
// Swap endianness of the data structures
|
||||
if (bNeedSwapEndian)
|
||||
{
|
||||
SwapEndianness_Header(header);
|
||||
for (size_t i = 0, iCount = m_nodes.size(); i < iCount; ++i)
|
||||
{
|
||||
SwapEndianness_Node(m_nodes[i]);
|
||||
}
|
||||
for (size_t i = 0, iCount = m_attributes.size(); i < iCount; ++i)
|
||||
{
|
||||
SwapEndianness_Attribute(m_attributes[i]);
|
||||
}
|
||||
for (size_t i = 0, iCount = m_childs.size(); i < iCount; ++i)
|
||||
{
|
||||
SwapEndian(m_childs[i], true);
|
||||
}
|
||||
}
|
||||
|
||||
// Write file
|
||||
{
|
||||
nTheoreticalPosition = 0;
|
||||
|
||||
// Write out the file header.
|
||||
write(pFile, nTheoreticalPosition, &header, sizeof(header));
|
||||
alignWrite(pFile, nTheoreticalPosition, nAlignment);
|
||||
|
||||
// Write out the node table.
|
||||
if (!m_nodes.empty())
|
||||
{
|
||||
write(pFile, nTheoreticalPosition, &m_nodes[0], sizeof(m_nodes[0]) * m_nodes.size());
|
||||
alignWrite(pFile, nTheoreticalPosition, nAlignment);
|
||||
}
|
||||
|
||||
// Write out the children table.
|
||||
if (!m_childs.empty())
|
||||
{
|
||||
write(pFile, nTheoreticalPosition, &m_childs[0], sizeof(m_childs[0]) * m_childs.size());
|
||||
alignWrite(pFile, nTheoreticalPosition, nAlignment);
|
||||
}
|
||||
|
||||
// Write out the attribute table.
|
||||
if (!m_attributes.empty())
|
||||
{
|
||||
write(pFile, nTheoreticalPosition, &m_attributes[0], sizeof(m_attributes[0]) * m_attributes.size());
|
||||
alignWrite(pFile, nTheoreticalPosition, nAlignment);
|
||||
}
|
||||
|
||||
// Write out the data of all the m_strings.
|
||||
for (size_t nString = 0; nString < m_strings.size(); ++nString)
|
||||
{
|
||||
pFile->Write(m_strings[nString].c_str(), m_strings[nString].size() + 1);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool XMLBinary::CXMLBinaryWriter::CompileTables(XmlNodeRef node, XMLBinary::IFilter* pFilter, string& error)
|
||||
{
|
||||
bool ok = CompileTablesForNode(node, -1, pFilter, error);
|
||||
ok = ok && CompileChildTable(node, pFilter, error);
|
||||
return ok;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool XMLBinary::CXMLBinaryWriter::CompileTablesForNode(XmlNodeRef node, int nParentIndex, XMLBinary::IFilter* pFilter, string& error)
|
||||
{
|
||||
// Add the tag to the string table.
|
||||
int nTagStringOffset = AddString(node->getTag());
|
||||
|
||||
// Add the content string to the string table.
|
||||
int nContentStringOffset = AddString(node->getContent());
|
||||
|
||||
// Add all the attributes to the attributes table.
|
||||
const char* szKey;
|
||||
const char* szValue;
|
||||
const int nFirstAttributeIndex = int(m_attributes.size());
|
||||
for (int i = 0, attrCount = node->getNumAttributes(); i < attrCount; ++i)
|
||||
{
|
||||
if (node->getAttributeByIndex(i, &szKey, &szValue) &&
|
||||
(!pFilter || pFilter->IsAccepted(IFilter::eType_AttributeName, szKey)))
|
||||
{
|
||||
// Add the key and the value to the string table.
|
||||
Attribute attribute;
|
||||
attribute.nKeyStringOffset = AddString(szKey);
|
||||
attribute.nValueStringOffset = AddString(szValue);
|
||||
|
||||
// Add the attribute to the attribute table.
|
||||
m_attributes.push_back(attribute);
|
||||
}
|
||||
}
|
||||
const int nAttributeCount = int(m_attributes.size()) - nFirstAttributeIndex;
|
||||
|
||||
static const int nMaxAttributeCount = (uint16) ~0;
|
||||
if (nAttributeCount > nMaxAttributeCount)
|
||||
{
|
||||
error.Format("XMLBinary: Too many attributes in a node: %d (max is %i)", nAttributeCount, nMaxAttributeCount);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add ourselves to the node list.
|
||||
const int nIndex = int(m_nodes.size());
|
||||
{
|
||||
Node nd;
|
||||
memset(&nd, 0, sizeof(nd));
|
||||
nd.nTagStringOffset = nTagStringOffset;
|
||||
nd.nContentStringOffset = nContentStringOffset;
|
||||
nd.nParentIndex = nParentIndex;
|
||||
nd.nFirstAttributeIndex = nFirstAttributeIndex;
|
||||
nd.nAttributeCount = nAttributeCount;
|
||||
|
||||
m_nodes.push_back(nd);
|
||||
}
|
||||
|
||||
m_nodesMap.insert(NodesMap::value_type(node, nIndex));
|
||||
|
||||
// Recurse to the child nodes.
|
||||
int nChildCount = 0;
|
||||
static const int nMaxChildCount = (uint16) ~0;
|
||||
for (int nChild = 0, numChilds = node->getChildCount(); nChild < numChilds; ++nChild)
|
||||
{
|
||||
XmlNodeRef childNode = node->getChild(nChild);
|
||||
if (!pFilter || pFilter->IsAccepted(IFilter::eType_ElementName, childNode->getTag()))
|
||||
{
|
||||
if (++nChildCount > nMaxChildCount)
|
||||
{
|
||||
error.Format("XMLBinary: Too many children in node '%s': %d (max is %i)", childNode->getTag(), nChildCount, nMaxChildCount);
|
||||
return false;
|
||||
}
|
||||
if (!CompileTablesForNode(childNode, nIndex, pFilter, error))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_nodes[nIndex].nChildCount = nChildCount;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool XMLBinary::CXMLBinaryWriter::CompileChildTable(XmlNodeRef node, XMLBinary::IFilter* pFilter, string& error)
|
||||
{
|
||||
const int nIndex = m_nodesMap.find(node)->second; // Assume node always exist in map.
|
||||
const int nFirstChildIndex = (int)m_childs.size();
|
||||
|
||||
Node& nd = m_nodes[nIndex];
|
||||
nd.nFirstChildIndex = nFirstChildIndex;
|
||||
|
||||
int nChildCount = 0;
|
||||
for (int nChild = 0, numChilds = node->getChildCount(); nChild < numChilds; ++nChild)
|
||||
{
|
||||
XmlNodeRef childNode = node->getChild(nChild);
|
||||
if (!pFilter || pFilter->IsAccepted(IFilter::eType_ElementName, childNode->getTag()))
|
||||
{
|
||||
++nChildCount;
|
||||
const int nChildIndex = m_nodesMap.find(childNode)->second; // Assume node always exist in map.
|
||||
m_childs.push_back(nChildIndex);
|
||||
}
|
||||
}
|
||||
if (nChildCount != nd.nChildCount)
|
||||
{
|
||||
error.Format("XMLBinary: Internal error in CompileChildTable()");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Recurse to the child nodes.
|
||||
for (int nChild = 0, numChilds = node->getChildCount(); nChild < numChilds; ++nChild)
|
||||
{
|
||||
XmlNodeRef childNode = node->getChild(nChild);
|
||||
if (!pFilter || pFilter->IsAccepted(IFilter::eType_ElementName, childNode->getTag()))
|
||||
{
|
||||
if (!CompileChildTable(childNode, pFilter, error))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
int XMLBinary::CXMLBinaryWriter::AddString(const XmlString& sString)
|
||||
{
|
||||
// If we have such string already, then we will re-use its data.
|
||||
StringMap::const_iterator itStringEntry = m_stringMap.find(sString);
|
||||
if (itStringEntry == m_stringMap.end())
|
||||
{
|
||||
// We don't have such string yet, so we should add it to the tables.
|
||||
m_strings.push_back(sString);
|
||||
itStringEntry = m_stringMap.insert(StringMap::value_type(sString, m_nStringDataSize)).first;
|
||||
m_nStringDataSize += sString.length() + 1;
|
||||
}
|
||||
|
||||
// Return offset of the string in the string data buffer.
|
||||
return (*itStringEntry).second;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef CRYINCLUDE_CRYSYSTEM_XML_XMLBINARYWRITER_H
|
||||
#define CRYINCLUDE_CRYSYSTEM_XML_XMLBINARYWRITER_H
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "IXml.h"
|
||||
#include "XMLBinaryHeaders.h"
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
class IXMLDataSink;
|
||||
|
||||
namespace XMLBinary
|
||||
{
|
||||
class CXMLBinaryWriter
|
||||
{
|
||||
public:
|
||||
CXMLBinaryWriter();
|
||||
bool WriteNode(IDataWriter* pFile, XmlNodeRef node, bool bNeedSwapEndian, XMLBinary::IFilter* pFilter, string& error);
|
||||
|
||||
private:
|
||||
bool CompileTables(XmlNodeRef node, XMLBinary::IFilter* pFilter, string& error);
|
||||
|
||||
bool CompileTablesForNode(XmlNodeRef node, int nParentIndex, XMLBinary::IFilter* pFilter, string& error);
|
||||
bool CompileChildTable(XmlNodeRef node, XMLBinary::IFilter* pFilter, string& error);
|
||||
int AddString(const XmlString& sString);
|
||||
|
||||
private:
|
||||
// tables.
|
||||
typedef std::map<IXmlNode*, int> NodesMap;
|
||||
typedef std::map<string, uint> StringMap;
|
||||
|
||||
std::vector<Node> m_nodes;
|
||||
NodesMap m_nodesMap;
|
||||
std::vector<Attribute> m_attributes;
|
||||
std::vector<NodeIndex> m_childs;
|
||||
std::vector<string> m_strings;
|
||||
StringMap m_stringMap;
|
||||
|
||||
uint m_nStringDataSize;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // CRYINCLUDE_CRYSYSTEM_XML_XMLBINARYWRITER_H
|
||||
@@ -0,0 +1,423 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "CrySystem_precompiled.h"
|
||||
#include "XMLPatcher.h"
|
||||
#include "StringUtils.h"
|
||||
|
||||
CXMLPatcher::CXMLPatcher(XmlNodeRef& patchXML)
|
||||
{
|
||||
m_patchXML = patchXML;
|
||||
|
||||
#if DATA_PATCH_DEBUG
|
||||
m_pDumpFilesCVar = REGISTER_INT("g_datapatcher_dumpfiles", 0, NULL, "will dump a copy of every file data patched, before and after patching");
|
||||
#endif
|
||||
}
|
||||
|
||||
CXMLPatcher::~CXMLPatcher()
|
||||
{
|
||||
#if DATA_PATCH_DEBUG
|
||||
if (IConsole* pIC = gEnv->pConsole)
|
||||
{
|
||||
pIC->UnregisterVariable(m_pDumpFilesCVar->GetName());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
XmlNodeRef CXMLPatcher::DuplicateForPatching(
|
||||
const XmlNodeRef& inOrig,
|
||||
bool inShareChildren)
|
||||
{
|
||||
XmlNodeRef newNode(0);
|
||||
|
||||
if (m_patchXML)
|
||||
{
|
||||
newNode = m_patchXML->createNode(inOrig->getTag());
|
||||
if (newNode)
|
||||
{
|
||||
// copy attributes in a safe way, copyAttributes() itself assumes the node being copied from is of the same type
|
||||
int numAttr = inOrig->getNumAttributes();
|
||||
|
||||
for (int i = 0; i < numAttr; i++)
|
||||
{
|
||||
const char* pKey, * pValue;
|
||||
if (inOrig->getAttributeByIndex(i, &pKey, &pValue))
|
||||
{
|
||||
newNode->setAttr(pKey, pValue);
|
||||
}
|
||||
}
|
||||
|
||||
if (inShareChildren)
|
||||
{
|
||||
newNode->shareChildren(inOrig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return newNode;
|
||||
}
|
||||
|
||||
void CXMLPatcher::PatchFail(
|
||||
const char* pInReason)
|
||||
{
|
||||
CryLogAlways("Failed to apply data patch for file '%s' - reason '%s'", m_pFileBeingPatched, pInReason);
|
||||
}
|
||||
|
||||
XmlNodeRef CXMLPatcher::FindPatchForFile(
|
||||
const char* pInFileToPatch)
|
||||
{
|
||||
XmlNodeRef result;
|
||||
|
||||
if (m_patchXML)
|
||||
{
|
||||
for (int i = 0, m = m_patchXML->getChildCount(); i < m; i++)
|
||||
{
|
||||
XmlNodeRef child = m_patchXML->getChild(i);
|
||||
|
||||
if (child->isTag("patch"))
|
||||
{
|
||||
const char* pForFile = child->getAttr("forfile");
|
||||
|
||||
if (pForFile && CryStringUtils::stristr(pForFile, pInFileToPatch) != 0)
|
||||
{
|
||||
result = child;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
XmlNodeRef CXMLPatcher::ApplyPatchToNode(
|
||||
const XmlNodeRef& inNode,
|
||||
const XmlNodeRef& inPatch)
|
||||
{
|
||||
XmlNodeRef result = inNode;
|
||||
|
||||
for (int i = 0, m = inPatch->getChildCount(); i < m; i++)
|
||||
{
|
||||
XmlNodeRef patchNode = inPatch->getChild(i);
|
||||
if (!patchNode || _stricmp(patchNode->getTag(), "patchnode") != 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int indexToPatch;
|
||||
if (!patchNode->getAttr("index", indexToPatch))
|
||||
{
|
||||
PatchFail("found patchnode missing index");
|
||||
continue;
|
||||
}
|
||||
|
||||
int maxChildren = result->getChildCount();
|
||||
|
||||
if ((indexToPatch < 0 || indexToPatch >= maxChildren) && indexToPatch != -1)
|
||||
{
|
||||
PatchFail("patchnode index out of valid range");
|
||||
continue;
|
||||
}
|
||||
|
||||
XmlNodeRef childToPatch = (indexToPatch != -1) ? result->getChild(indexToPatch) : XmlNodeRef(0);
|
||||
XmlNodeRef matchTag = GetMatchTag(patchNode);
|
||||
|
||||
if (childToPatch && matchTag && !CompareTags(matchTag, childToPatch))
|
||||
{
|
||||
PatchFail("patch failed to apply, data did not match what was expected");
|
||||
continue;
|
||||
}
|
||||
|
||||
// we need to apply a patch to this child, make it patchable by duplicating the node
|
||||
|
||||
if (inNode == result)
|
||||
{
|
||||
// make parent patchable if not already
|
||||
result = DuplicateForPatching(inNode, true);
|
||||
}
|
||||
|
||||
if (XmlNodeRef insertTag = GetInsertTag(patchNode))
|
||||
{
|
||||
// insert a new child after this node
|
||||
XmlNodeRef newChild = DuplicateForPatching(insertTag, true); // have to duplicate it as we don't have an 'insert shared child' function
|
||||
result->insertChild(indexToPatch + 1, newChild);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (indexToPatch == -1)
|
||||
{
|
||||
PatchFail("child indices of -1 can only be used when inserting new nodes");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
bool shouldReplaceChildren = false;
|
||||
|
||||
if (XmlNodeRef replaceTag = GetReplaceTag(patchNode, &shouldReplaceChildren))
|
||||
{
|
||||
XmlNodeRef newChild = DuplicateForPatching(replaceTag, false);
|
||||
|
||||
if (!shouldReplaceChildren)
|
||||
{
|
||||
newChild->shareChildren(childToPatch);
|
||||
}
|
||||
else
|
||||
{
|
||||
// note: this is inserting children that belong to the data patcher into the data being patched
|
||||
// this is fine to do, as long as the caller doesn't make any permanent changes to the xml tree
|
||||
// returned. if they did they would alter the patcher's nodes and thus affect future patches
|
||||
// applied using the same patch
|
||||
// as most callers are working with binary xmls they don't try and modify them - as this is not
|
||||
// a supported operation
|
||||
// note, if a second patch was applied to this patched tree containing the patch nodes, it
|
||||
// wouldn't mess up the patch, as patching a tree never modifies it, it always returns a new tree
|
||||
// that may share parts of the original tree
|
||||
newChild->shareChildren(replaceTag);
|
||||
}
|
||||
|
||||
result->replaceChild(indexToPatch, newChild);
|
||||
|
||||
childToPatch = newChild;
|
||||
}
|
||||
|
||||
if (XmlNodeRef deleteTag = GetDeleteTag(patchNode))
|
||||
{
|
||||
result->deleteChildAt(indexToPatch);
|
||||
childToPatch = 0; // deleted - don't recurse into it
|
||||
}
|
||||
|
||||
if (childToPatch)
|
||||
{
|
||||
// Apply recursively
|
||||
XmlNodeRef newChild = ApplyPatchToNode(childToPatch, patchNode);
|
||||
|
||||
// child has been patched, insert new child into parent
|
||||
if (newChild != childToPatch)
|
||||
{
|
||||
result->replaceChild(indexToPatch, newChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
XmlNodeRef CXMLPatcher::ApplyXMLDataPatch(
|
||||
const XmlNodeRef& inNode,
|
||||
const char* pInXMLFileName)
|
||||
{
|
||||
XmlNodeRef result = inNode;
|
||||
|
||||
if (m_patchingEnabled)
|
||||
{
|
||||
if (m_patchXML)
|
||||
{
|
||||
XmlNodeRef patchForFile = FindPatchForFile(pInXMLFileName);
|
||||
if (patchForFile)
|
||||
{
|
||||
m_pFileBeingPatched = pInXMLFileName;
|
||||
CryLog("Applying game data patch to %s", pInXMLFileName);
|
||||
XmlNodeRef containerNode = m_patchXML->createNode("");
|
||||
containerNode->addChild(inNode);
|
||||
containerNode = ApplyPatchToNode(containerNode, patchForFile);
|
||||
result = containerNode->getChild(0);
|
||||
m_pFileBeingPatched = NULL;
|
||||
|
||||
#if DATA_PATCH_DEBUG
|
||||
if (inNode != result)
|
||||
{
|
||||
DumpFiles(pInXMLFileName, inNode, result);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
XmlNodeRef CXMLPatcher::GetMatchTag(
|
||||
const XmlNodeRef& inNode)
|
||||
{
|
||||
XmlNodeRef result;
|
||||
XmlNodeRef nr = inNode->findChild("match");
|
||||
if (nr && nr->getChildCount() == 1)
|
||||
{
|
||||
result = nr->getChild(0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
XmlNodeRef CXMLPatcher::GetReplaceTag(
|
||||
const XmlNodeRef& inNode,
|
||||
bool* outShouldReplaceChildren)
|
||||
{
|
||||
XmlNodeRef result;
|
||||
XmlNodeRef nr = inNode->findChild("replacewith");
|
||||
if (nr && nr->getChildCount() == 1)
|
||||
{
|
||||
if (!nr->getAttr("replaceChildren", *outShouldReplaceChildren))
|
||||
{
|
||||
*outShouldReplaceChildren = false;
|
||||
}
|
||||
result = nr->getChild(0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
XmlNodeRef CXMLPatcher::GetInsertTag(
|
||||
const XmlNodeRef& inNode)
|
||||
{
|
||||
XmlNodeRef result;
|
||||
XmlNodeRef nr = inNode->findChild("insertAfter");
|
||||
if (nr && nr->getChildCount() == 1)
|
||||
{
|
||||
result = nr->getChild(0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
XmlNodeRef CXMLPatcher::GetDeleteTag(
|
||||
const XmlNodeRef& inNode)
|
||||
{
|
||||
XmlNodeRef result = inNode->findChild("delete");
|
||||
return result;
|
||||
}
|
||||
|
||||
// compares the two tags for equality of tag and attributes
|
||||
// used to ensure the source data being patched meets the patches expectations
|
||||
// only compares tag and attribs, doesn't do deep compare of children
|
||||
bool CXMLPatcher::CompareTags(
|
||||
const XmlNodeRef& inA,
|
||||
const XmlNodeRef& inB)
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
if (inA != inB)
|
||||
{
|
||||
result = false;
|
||||
|
||||
if (_stricmp(inA->getTag(), inB->getTag()) == 0)
|
||||
{
|
||||
if (inA->getNumAttributes() == inB->getNumAttributes())
|
||||
{
|
||||
result = true;
|
||||
|
||||
for (int i = 0, m = inA->getNumAttributes(); i < m; i++)
|
||||
{
|
||||
const char* pAKey, * pBKey;
|
||||
const char* pAValue, * pBValue;
|
||||
|
||||
inA->getAttributeByIndex(i, &pAKey, &pAValue);
|
||||
inB->getAttributeByIndex(i, &pBKey, &pBValue);
|
||||
|
||||
if (_stricmp(pAKey, pBKey) || _stricmp(pAValue, pBValue))
|
||||
{
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#if DATA_PATCH_DEBUG
|
||||
|
||||
static const char* k_lotsOfTabs = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
|
||||
|
||||
#define INDENT() \
|
||||
if (inIndent > 0) \
|
||||
{ \
|
||||
pPak->FWrite(k_lotsOfTabs, inIndent, inFileHandle); \
|
||||
}
|
||||
|
||||
void CXMLPatcher::DumpXMLNodes(
|
||||
AZ::IO::HandleType inFileHandle,
|
||||
int inIndent,
|
||||
const XmlNodeRef& inNode,
|
||||
CryFixedStringT<512>* ioTempString)
|
||||
{
|
||||
auto pPak = gEnv->pCryPak;
|
||||
|
||||
inIndent = min(inIndent, int(sizeof(k_lotsOfTabs) - 1));
|
||||
|
||||
INDENT();
|
||||
|
||||
ioTempString->Format("<%s ", inNode->getTag());
|
||||
|
||||
pPak->FWrite(ioTempString->c_str(), ioTempString->length(), inFileHandle);
|
||||
|
||||
for (int i = 0, m = inNode->getNumAttributes(); i < m; i++)
|
||||
{
|
||||
const char* pKey, * pVal;
|
||||
inNode->getAttributeByIndex(i, &pKey, &pVal);
|
||||
ioTempString->Format("%s=\"%s\" ", pKey, pVal);
|
||||
pPak->FWrite(ioTempString->c_str(), ioTempString->length(), inFileHandle);
|
||||
}
|
||||
pPak->FWrite(">\n", 2, inFileHandle);
|
||||
|
||||
for (int i = 0, m = inNode->getChildCount(); i < m; i++)
|
||||
{
|
||||
DumpXMLNodes(inFileHandle, inIndent + 1, inNode->getChild(i), ioTempString);
|
||||
}
|
||||
|
||||
INDENT();
|
||||
ioTempString->Format("</%s>\n", inNode->getTag());
|
||||
pPak->FWrite(ioTempString->c_str(), ioTempString->length(), inFileHandle);
|
||||
}
|
||||
|
||||
|
||||
void CXMLPatcher::DumpFiles(
|
||||
const char* pInXMLFileName,
|
||||
const XmlNodeRef& inBefore,
|
||||
const XmlNodeRef& inAfter)
|
||||
{
|
||||
if (m_pDumpFilesCVar->GetIVal())
|
||||
{
|
||||
CryLog("Dumping before and after data files for '%s'", pInXMLFileName);
|
||||
|
||||
const char* pOrigFileName = strrchr(pInXMLFileName, '/');
|
||||
if (pOrigFileName)
|
||||
{
|
||||
pOrigFileName++;
|
||||
|
||||
DumpXMLFile(string().Format("PATCH_%s", pOrigFileName), inBefore);
|
||||
|
||||
CryFixedStringT<128> newFileName(pOrigFileName);
|
||||
newFileName.replace(".xml", "_patched.xml");
|
||||
|
||||
DumpXMLFile(string().Format("PATCH_%s", newFileName.c_str()), inAfter);
|
||||
}
|
||||
else
|
||||
{
|
||||
CryLog("Couldn't determine file name for path '%s' can't output diffs", pInXMLFileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CXMLPatcher::DumpXMLFile(
|
||||
const char* pInFilePath,
|
||||
const XmlNodeRef& inNode)
|
||||
{
|
||||
auto pIPak = GetISystem()->GetIPak();
|
||||
AZ::IO::HandleType fileHandle = pIPak->FOpen(pInFilePath, "wb");
|
||||
|
||||
if (fileHandle != AZ::IO::InvalidHandle)
|
||||
{
|
||||
CryFixedStringT<512> tempStr;
|
||||
|
||||
DumpXMLNodes(fileHandle, 0, inNode, &tempStr);
|
||||
|
||||
pIPak->FClose(fileHandle);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef CRYINCLUDE_CRYSYSTEM_XML_XMLPATCHER_H
|
||||
#define CRYINCLUDE_CRYSYSTEM_XML_XMLPATCHER_H
|
||||
#pragma once
|
||||
|
||||
|
||||
#if defined(WIN32) && !defined(_RELEASE)
|
||||
#define DATA_PATCH_DEBUG 1
|
||||
#else
|
||||
#define DATA_PATCH_DEBUG 0
|
||||
#endif
|
||||
|
||||
class CXMLPatcher
|
||||
{
|
||||
protected:
|
||||
#if DATA_PATCH_DEBUG
|
||||
ICVar * m_pDumpFilesCVar;
|
||||
#endif
|
||||
|
||||
XmlNodeRef m_patchXML;
|
||||
const char* m_pFileBeingPatched;
|
||||
bool m_patchingEnabled;
|
||||
|
||||
void PatchFail(
|
||||
const char* pInReason);
|
||||
XmlNodeRef ApplyPatchToNode(
|
||||
const XmlNodeRef& inNode,
|
||||
const XmlNodeRef& inPatch);
|
||||
|
||||
XmlNodeRef DuplicateForPatching(
|
||||
const XmlNodeRef& inOrig,
|
||||
bool inShareChildren);
|
||||
|
||||
bool CompareTags(
|
||||
const XmlNodeRef& inA,
|
||||
const XmlNodeRef& inB);
|
||||
XmlNodeRef GetMatchTag(
|
||||
const XmlNodeRef& inNode);
|
||||
XmlNodeRef GetReplaceTag(
|
||||
const XmlNodeRef& inNode,
|
||||
bool* outShouldReplaceChildren);
|
||||
XmlNodeRef GetInsertTag(
|
||||
const XmlNodeRef& inNode);
|
||||
XmlNodeRef GetDeleteTag(
|
||||
const XmlNodeRef& inNode);
|
||||
XmlNodeRef FindPatchForFile(
|
||||
const char* pInFileToPatch);
|
||||
|
||||
#if DATA_PATCH_DEBUG
|
||||
void DumpXMLNodes(
|
||||
AZ::IO::HandleType inFileHandle,
|
||||
int inIndent,
|
||||
const XmlNodeRef& inNode,
|
||||
CryFixedStringT<512>* ioTempString);
|
||||
void DumpFiles(
|
||||
const char* pInXMLFileName,
|
||||
const XmlNodeRef& inBefore,
|
||||
const XmlNodeRef& inAfter);
|
||||
void DumpXMLFile(
|
||||
const char* pInFilePath,
|
||||
const XmlNodeRef& inNode);
|
||||
#endif
|
||||
|
||||
public:
|
||||
CXMLPatcher(XmlNodeRef& patchXML);
|
||||
~CXMLPatcher();
|
||||
|
||||
XmlNodeRef ApplyXMLDataPatch(
|
||||
const XmlNodeRef& inNode,
|
||||
const char* pInXMLFileName);
|
||||
};
|
||||
|
||||
#endif // CRYINCLUDE_CRYSYSTEM_XML_XMLPATCHER_H
|
||||
@@ -0,0 +1,712 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "CrySystem_precompiled.h"
|
||||
#include <IXml.h>
|
||||
#include "xml.h"
|
||||
#include "XmlUtils.h"
|
||||
#include "ReadWriteXMLSink.h"
|
||||
|
||||
#include "../SimpleStringPool.h"
|
||||
#include "SerializeXMLReader.h"
|
||||
#include "SerializeXMLWriter.h"
|
||||
|
||||
#include "XMLBinaryWriter.h"
|
||||
#include "XMLBinaryReader.h"
|
||||
|
||||
#include "XMLPatcher.h"
|
||||
#include <md5.h>
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
CXmlNode_PoolAlloc* g_pCXmlNode_PoolAlloc = 0;
|
||||
#ifdef CRY_COLLECT_XML_NODE_STATS
|
||||
SXmlNodeStats* g_pCXmlNode_Stats = 0;
|
||||
#endif
|
||||
|
||||
extern bool g_bEnableBinaryXmlLoading;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
CXmlUtils::CXmlUtils(ISystem* pSystem)
|
||||
{
|
||||
m_pSystem = pSystem;
|
||||
m_pSystem->GetISystemEventDispatcher()->RegisterListener(this);
|
||||
|
||||
// create IReadWriteXMLSink object
|
||||
m_pReadWriteXMLSink = new CReadWriteXMLSink();
|
||||
g_pCXmlNode_PoolAlloc = new CXmlNode_PoolAlloc;
|
||||
#ifdef CRY_COLLECT_XML_NODE_STATS
|
||||
g_pCXmlNode_Stats = new SXmlNodeStats();
|
||||
#endif
|
||||
m_pStatsXmlNodePool = 0;
|
||||
#ifndef _RELEASE
|
||||
m_statsThreadOwner = CryGetCurrentThreadId();
|
||||
#endif
|
||||
m_pXMLPatcher = NULL;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
CXmlUtils::~CXmlUtils()
|
||||
{
|
||||
m_pSystem->GetISystemEventDispatcher()->RemoveListener(this);
|
||||
delete g_pCXmlNode_PoolAlloc;
|
||||
#ifdef CRY_COLLECT_XML_NODE_STATS
|
||||
delete g_pCXmlNode_Stats;
|
||||
#endif
|
||||
SAFE_DELETE(m_pStatsXmlNodePool);
|
||||
SAFE_DELETE(m_pXMLPatcher);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
IXmlParser* CXmlUtils::CreateXmlParser()
|
||||
{
|
||||
const bool bReuseStrings = false; //TODO: do we ever want to reuse strings here?
|
||||
return new XmlParser(bReuseStrings);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
XmlNodeRef CXmlUtils::LoadXmlFromFile(const char* sFilename, bool bReuseStrings, bool bEnablePatching)
|
||||
{
|
||||
XmlParser parser(bReuseStrings);
|
||||
XmlNodeRef node = parser.ParseFile(sFilename, true);
|
||||
|
||||
// XmlParser is supposed to log warnings and errors (if any),
|
||||
// so we don't need to call parser.getErrorString(),
|
||||
// CryLog() etc here.
|
||||
|
||||
if (node && bEnablePatching && m_pXMLPatcher)
|
||||
{
|
||||
node = m_pXMLPatcher->ApplyXMLDataPatch(node, sFilename);
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
XmlNodeRef CXmlUtils::LoadXmlFromBuffer(const char* buffer, size_t size, bool bReuseStrings, bool bSuppressWarnings)
|
||||
{
|
||||
XmlParser parser(bReuseStrings);
|
||||
XmlNodeRef node = parser.ParseBuffer(buffer, size, true, bSuppressWarnings);
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
void GetMD5(const char* pSrcBuffer, int nSrcSize, char signatureMD5[16])
|
||||
{
|
||||
MD5Context md5c;
|
||||
MD5Init(&md5c);
|
||||
MD5Update(&md5c, (unsigned char*)pSrcBuffer, nSrcSize);
|
||||
MD5Final((unsigned char*)signatureMD5, &md5c);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
const char* CXmlUtils::HashXml(XmlNodeRef node)
|
||||
{
|
||||
static char signature[16 * 2 + 1];
|
||||
static char temp[16];
|
||||
static const char* hex = "0123456789abcdef";
|
||||
XmlString str = node->getXML();
|
||||
GetMD5(str.data(), str.length(), temp);
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
signature[2 * i + 0] = hex[((uint8)temp[i]) >> 4];
|
||||
signature[2 * i + 1] = hex[((uint8)temp[i]) & 0xf];
|
||||
}
|
||||
signature[16 * 2] = 0;
|
||||
return signature;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
IReadWriteXMLSink* CXmlUtils::GetIReadWriteXMLSink()
|
||||
{
|
||||
return m_pReadWriteXMLSink;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
class CXmlSerializer
|
||||
: public IXmlSerializer
|
||||
{
|
||||
public:
|
||||
CXmlSerializer()
|
||||
: m_nRefCount(0)
|
||||
, m_pReaderImpl(NULL)
|
||||
, m_pReaderSer(NULL)
|
||||
, m_pWriterSer(NULL)
|
||||
, m_pWriterImpl(NULL)
|
||||
{
|
||||
}
|
||||
~CXmlSerializer()
|
||||
{
|
||||
ClearAll();
|
||||
}
|
||||
void ClearAll()
|
||||
{
|
||||
SAFE_DELETE(m_pReaderSer);
|
||||
SAFE_DELETE(m_pReaderImpl);
|
||||
SAFE_DELETE(m_pWriterSer);
|
||||
SAFE_DELETE(m_pWriterImpl);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
virtual void AddRef() { ++m_nRefCount; }
|
||||
virtual void Release()
|
||||
{
|
||||
if (--m_nRefCount <= 0)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
virtual ISerialize* GetWriter(XmlNodeRef& node)
|
||||
{
|
||||
ClearAll();
|
||||
m_pWriterImpl = new CSerializeXMLWriterImpl(node);
|
||||
m_pWriterSer = new CSimpleSerializeWithDefaults<CSerializeXMLWriterImpl>(*m_pWriterImpl);
|
||||
return m_pWriterSer;
|
||||
}
|
||||
virtual ISerialize* GetReader(XmlNodeRef& node)
|
||||
{
|
||||
ClearAll();
|
||||
m_pReaderImpl = new CSerializeXMLReaderImpl(node);
|
||||
m_pReaderSer = new CSimpleSerializeWithDefaults<CSerializeXMLReaderImpl>(*m_pReaderImpl);
|
||||
return m_pReaderSer;
|
||||
}
|
||||
|
||||
virtual void GetMemoryUsage(ICrySizer* pSizer) const
|
||||
{
|
||||
pSizer->Add(*this);
|
||||
pSizer->AddObject(m_pReaderImpl);
|
||||
pSizer->AddObject(m_pWriterImpl);
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
private:
|
||||
int m_nRefCount;
|
||||
CSerializeXMLReaderImpl* m_pReaderImpl;
|
||||
CSimpleSerializeWithDefaults<CSerializeXMLReaderImpl>* m_pReaderSer;
|
||||
|
||||
CSerializeXMLWriterImpl* m_pWriterImpl;
|
||||
CSimpleSerializeWithDefaults<CSerializeXMLWriterImpl>* m_pWriterSer;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
IXmlSerializer* CXmlUtils::CreateXmlSerializer()
|
||||
{
|
||||
return new CXmlSerializer;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CXmlUtils::GetMemoryUsage(ICrySizer* pSizer)
|
||||
{
|
||||
{
|
||||
SIZER_COMPONENT_NAME(pSizer, "Nodes");
|
||||
g_pCXmlNode_PoolAlloc->GetMemoryUsage(pSizer);
|
||||
}
|
||||
|
||||
#ifdef CRY_COLLECT_XML_NODE_STATS
|
||||
// yes, slow
|
||||
std::vector<const CXmlNode*> rootNodes;
|
||||
{
|
||||
TXmlNodeSet::const_iterator iter = g_pCXmlNode_Stats->nodeSet.begin();
|
||||
TXmlNodeSet::const_iterator iterEnd = g_pCXmlNode_Stats->nodeSet.end();
|
||||
while (iter != iterEnd)
|
||||
{
|
||||
const CXmlNode* pNode = *iter;
|
||||
if (pNode->getParent() == 0)
|
||||
{
|
||||
rootNodes.push_back(pNode);
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
|
||||
// use the following to log to console
|
||||
#if 0
|
||||
CryLogAlways("NumXMLRootNodes=%d NumXMLNodes=%d TotalAllocs=%d TotalFrees=%d",
|
||||
rootNodes.size(), g_pCXmlNode_Stats->nodeSet.size(),
|
||||
g_pCXmlNode_Stats->nAllocs, g_pCXmlNode_Stats->nFrees);
|
||||
#endif
|
||||
|
||||
|
||||
// use the following to debug the nodes in the system
|
||||
#if 0
|
||||
{
|
||||
std::vector<const CXmlNode*>::const_iterator iter = rootNodes.begin();
|
||||
std::vector<const CXmlNode*>::const_iterator iterEnd = rootNodes.end();
|
||||
while (iter != iterEnd)
|
||||
{
|
||||
const CXmlNode* pNode = *iter;
|
||||
CryLogAlways("Node 0x%p Tag='%s'", pNode, pNode->getTag());
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// only for debugging, add it as pseudo numbers to the CrySizer.
|
||||
// shift it by 10, so we get the actual number
|
||||
{
|
||||
SIZER_COMPONENT_NAME(pSizer, "#NumTotalNodes");
|
||||
pSizer->Add("#NumTotalNodes", g_pCXmlNode_Stats->nodeSet.size() << 10);
|
||||
}
|
||||
|
||||
{
|
||||
SIZER_COMPONENT_NAME(pSizer, "#NumRootNodes");
|
||||
pSizer->Add("#NumRootNodes", rootNodes.size() << 10);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CXmlUtils::OnSystemEvent(ESystemEvent event, [[maybe_unused]] UINT_PTR wparam, [[maybe_unused]] UINT_PTR lparam)
|
||||
{
|
||||
switch (event)
|
||||
{
|
||||
case ESYSTEM_EVENT_LEVEL_POST_UNLOAD:
|
||||
case ESYSTEM_EVENT_LEVEL_LOAD_END:
|
||||
g_pCXmlNode_PoolAlloc->FreeMemoryIfEmpty();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
class CXmlBinaryDataWriterFile
|
||||
: public XMLBinary::IDataWriter
|
||||
{
|
||||
public:
|
||||
CXmlBinaryDataWriterFile(const char* file)
|
||||
{
|
||||
m_fileHandle = gEnv->pCryPak->FOpen(file, "wb");
|
||||
}
|
||||
~CXmlBinaryDataWriterFile()
|
||||
{
|
||||
if (m_fileHandle != AZ::IO::InvalidHandle)
|
||||
{
|
||||
gEnv->pCryPak->FClose(m_fileHandle);
|
||||
}
|
||||
};
|
||||
virtual bool IsOk()
|
||||
{
|
||||
return m_fileHandle != AZ::IO::InvalidHandle;
|
||||
}
|
||||
;
|
||||
virtual void Write(const void* pData, size_t size)
|
||||
{
|
||||
if (m_fileHandle != AZ::IO::InvalidHandle)
|
||||
{
|
||||
gEnv->pCryPak->FWrite(pData, size, 1, m_fileHandle);
|
||||
}
|
||||
}
|
||||
private:
|
||||
AZ::IO::HandleType m_fileHandle;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CXmlUtils::SaveBinaryXmlFile(const char* filename, XmlNodeRef root)
|
||||
{
|
||||
CXmlBinaryDataWriterFile fileSink(filename);
|
||||
if (!fileSink.IsOk())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
XMLBinary::CXMLBinaryWriter writer;
|
||||
string error;
|
||||
return writer.WriteNode(&fileSink, root, false, 0, error);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
XmlNodeRef CXmlUtils::LoadBinaryXmlFile(const char* filename, bool bEnablePatching)
|
||||
{
|
||||
XMLBinary::XMLBinaryReader reader;
|
||||
XMLBinary::XMLBinaryReader::EResult result;
|
||||
XmlNodeRef root = reader.LoadFromFile(filename, result);
|
||||
|
||||
if (result == XMLBinary::XMLBinaryReader::eResult_Success && bEnablePatching == true && m_pXMLPatcher != NULL)
|
||||
{
|
||||
root = m_pXMLPatcher->ApplyXMLDataPatch(root, filename);
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CXmlUtils::EnableBinaryXmlLoading(bool bEnable)
|
||||
{
|
||||
bool bPrev = g_bEnableBinaryXmlLoading;
|
||||
g_bEnableBinaryXmlLoading = bEnable;
|
||||
return bPrev;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
class CXmlTableReader
|
||||
: public IXmlTableReader
|
||||
{
|
||||
public:
|
||||
CXmlTableReader();
|
||||
virtual ~CXmlTableReader();
|
||||
|
||||
virtual void Release();
|
||||
|
||||
virtual bool Begin(XmlNodeRef rootNode);
|
||||
virtual int GetEstimatedRowCount();
|
||||
virtual bool ReadRow(int& rowIndex);
|
||||
virtual bool ReadCell(int& columnIndex, const char*& pContent, size_t& contentSize);
|
||||
float GetCurrentRowHeight() override;
|
||||
|
||||
private:
|
||||
bool m_bExcel;
|
||||
|
||||
XmlNodeRef m_tableNode;
|
||||
|
||||
XmlNodeRef m_rowNode;
|
||||
|
||||
float m_currentRowHeight;
|
||||
int m_rowNodeIndex;
|
||||
int m_row;
|
||||
|
||||
int m_columnNodeIndex; // used if m_bExcel == true
|
||||
int m_column;
|
||||
|
||||
size_t m_rowTextSize; // used if m_bExcel == false
|
||||
size_t m_rowTextPos; // used if m_bExcel == false
|
||||
};
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
CXmlTableReader::CXmlTableReader()
|
||||
{
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
CXmlTableReader::~CXmlTableReader()
|
||||
{
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void CXmlTableReader::Release()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CXmlTableReader::Begin(XmlNodeRef rootNode)
|
||||
{
|
||||
m_tableNode = 0;
|
||||
|
||||
if (!rootNode)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
XmlNodeRef worksheetNode = rootNode->findChild("Worksheet");
|
||||
if (worksheetNode)
|
||||
{
|
||||
m_bExcel = true;
|
||||
m_tableNode = worksheetNode->findChild("Table");
|
||||
}
|
||||
else
|
||||
{
|
||||
m_bExcel = false;
|
||||
m_tableNode = rootNode->findChild("Table");
|
||||
}
|
||||
|
||||
m_rowNode = 0;
|
||||
m_rowNodeIndex = -1;
|
||||
m_row = -1;
|
||||
|
||||
return (m_tableNode != 0);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
int CXmlTableReader::GetEstimatedRowCount()
|
||||
{
|
||||
if (!m_tableNode)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return m_tableNode->getChildCount();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CXmlTableReader::ReadRow(int& rowIndex)
|
||||
{
|
||||
m_currentRowHeight = 0.0f;
|
||||
if (!m_tableNode)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_columnNodeIndex = -1;
|
||||
m_column = -1;
|
||||
|
||||
const int rowNodeCount = m_tableNode->getChildCount();
|
||||
|
||||
if (m_bExcel)
|
||||
{
|
||||
for (;; )
|
||||
{
|
||||
if (++m_rowNodeIndex >= rowNodeCount)
|
||||
{
|
||||
m_rowNodeIndex = rowNodeCount;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_rowNode = m_tableNode->getChild(m_rowNodeIndex);
|
||||
if (!m_rowNode)
|
||||
{
|
||||
m_rowNodeIndex = rowNodeCount;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_rowNode->isTag("Row"))
|
||||
{
|
||||
m_rowNode = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
++m_row;
|
||||
|
||||
int index = 0;
|
||||
if (m_rowNode->getAttr("ss:Index", index))
|
||||
{
|
||||
--index; // one-based -> zero-based
|
||||
if (index < m_row)
|
||||
{
|
||||
m_rowNodeIndex = rowNodeCount;
|
||||
m_rowNode = 0;
|
||||
return false;
|
||||
}
|
||||
m_row = index;
|
||||
}
|
||||
float height;
|
||||
if (m_rowNode->getAttr("ss:Height", height))
|
||||
{
|
||||
m_currentRowHeight = height;
|
||||
}
|
||||
|
||||
rowIndex = m_row;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
m_rowTextSize = 0;
|
||||
m_rowTextPos = 0;
|
||||
|
||||
if (++m_rowNodeIndex >= rowNodeCount)
|
||||
{
|
||||
m_rowNodeIndex = rowNodeCount;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_rowNode = m_tableNode->getChild(m_rowNodeIndex);
|
||||
if (!m_rowNode)
|
||||
{
|
||||
m_rowNodeIndex = rowNodeCount;
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* const pContent = m_rowNode->getContent();
|
||||
if (pContent)
|
||||
{
|
||||
m_rowTextSize = strlen(pContent);
|
||||
}
|
||||
|
||||
m_row = m_rowNodeIndex;
|
||||
rowIndex = m_rowNodeIndex;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
bool CXmlTableReader::ReadCell(int& columnIndex, const char*& pContent, size_t& contentSize)
|
||||
{
|
||||
pContent = 0;
|
||||
contentSize = 0;
|
||||
|
||||
if (!m_tableNode)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_rowNode)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_bExcel)
|
||||
{
|
||||
const int columnNodeCount = m_rowNode->getChildCount();
|
||||
|
||||
for (;; )
|
||||
{
|
||||
if (++m_columnNodeIndex >= columnNodeCount)
|
||||
{
|
||||
m_columnNodeIndex = columnNodeCount;
|
||||
return false;
|
||||
}
|
||||
|
||||
XmlNodeRef columnNode = m_rowNode->getChild(m_columnNodeIndex);
|
||||
if (!columnNode)
|
||||
{
|
||||
m_columnNodeIndex = columnNodeCount;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!columnNode->isTag("Cell"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
++m_column;
|
||||
|
||||
int index = 0;
|
||||
if (columnNode->getAttr("ss:Index", index))
|
||||
{
|
||||
--index; // one-based -> zero-based
|
||||
if (index < m_column)
|
||||
{
|
||||
m_columnNodeIndex = columnNodeCount;
|
||||
return false;
|
||||
}
|
||||
m_column = index;
|
||||
}
|
||||
columnIndex = m_column;
|
||||
|
||||
XmlNodeRef dataNode = columnNode->findChild("Data");
|
||||
if (dataNode)
|
||||
{
|
||||
pContent = dataNode->getContent();
|
||||
if (pContent)
|
||||
{
|
||||
contentSize = strlen(pContent);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
if (m_rowTextPos >= m_rowTextSize)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* const pRowContent = m_rowNode->getContent();
|
||||
if (!pRowContent)
|
||||
{
|
||||
m_rowTextPos = m_rowTextSize;
|
||||
return false;
|
||||
}
|
||||
pContent = &pRowContent[m_rowTextPos];
|
||||
|
||||
columnIndex = ++m_column;
|
||||
|
||||
for (;; )
|
||||
{
|
||||
char c = pRowContent[m_rowTextPos++];
|
||||
|
||||
if ((c == '\n') || (c == '\0'))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (c == '\r')
|
||||
{
|
||||
// ignore all '\r' chars
|
||||
for (;; )
|
||||
{
|
||||
c = pRowContent[m_rowTextPos++];
|
||||
if ((c == '\n') || (c == '\0'))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (c != '\r')
|
||||
{
|
||||
// broken data. '\r' expected to be followed by '\n' or '\0'.
|
||||
contentSize = 0;
|
||||
m_rowTextPos = m_rowTextSize;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
++contentSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float CXmlTableReader::GetCurrentRowHeight()
|
||||
{
|
||||
return m_currentRowHeight;
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
IXmlTableReader* CXmlUtils::CreateXmlTableReader()
|
||||
{
|
||||
return new CXmlTableReader;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Init xml stats nodes pool
|
||||
void CXmlUtils::InitStatsXmlNodePool(uint32 nPoolSize)
|
||||
{
|
||||
CHECK_STATS_THREAD_OWNERSHIP();
|
||||
if (0 == m_pStatsXmlNodePool)
|
||||
{
|
||||
// create special xml node pools for game statistics
|
||||
|
||||
const bool bReuseStrings = true; // TODO parameterise?
|
||||
m_pStatsXmlNodePool = new CXmlNodePool(nPoolSize, bReuseStrings);
|
||||
assert(m_pStatsXmlNodePool);
|
||||
}
|
||||
else
|
||||
{
|
||||
CryLog("[CXmlNodePool]: Xml stats nodes pool already initialized");
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Creates new xml node for statistics.
|
||||
XmlNodeRef CXmlUtils::CreateStatsXmlNode(const char* sNodeName)
|
||||
{
|
||||
CHECK_STATS_THREAD_OWNERSHIP();
|
||||
if (0 == m_pStatsXmlNodePool)
|
||||
{
|
||||
CryLog("[CXmlNodePool]: Xml stats nodes pool isn't initialized. Perform default initialization.");
|
||||
InitStatsXmlNodePool();
|
||||
}
|
||||
return m_pStatsXmlNodePool->GetXmlNode(sNodeName);
|
||||
}
|
||||
|
||||
void CXmlUtils::SetStatsOwnerThread([[maybe_unused]] threadID threadId)
|
||||
{
|
||||
#ifndef _RELEASE
|
||||
m_statsThreadOwner = threadId;
|
||||
#endif
|
||||
}
|
||||
|
||||
void CXmlUtils::FlushStatsXmlNodePool()
|
||||
{
|
||||
CHECK_STATS_THREAD_OWNERSHIP();
|
||||
if (m_pStatsXmlNodePool)
|
||||
{
|
||||
if (m_pStatsXmlNodePool->empty())
|
||||
{
|
||||
SAFE_DELETE(m_pStatsXmlNodePool);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CXmlUtils::SetXMLPatcher(XmlNodeRef* pPatcher)
|
||||
{
|
||||
SAFE_DELETE(m_pXMLPatcher);
|
||||
|
||||
if (pPatcher != NULL)
|
||||
{
|
||||
m_pXMLPatcher = new CXMLPatcher(*pPatcher);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef CRYINCLUDE_CRYSYSTEM_XML_XMLUTILS_H
|
||||
#define CRYINCLUDE_CRYSYSTEM_XML_XMLUTILS_H
|
||||
#pragma once
|
||||
|
||||
#include "ISystem.h"
|
||||
|
||||
#ifdef _RELEASE
|
||||
#define CHECK_STATS_THREAD_OWNERSHIP()
|
||||
#else
|
||||
#define CHECK_STATS_THREAD_OWNERSHIP() if (m_statsThreadOwner != CryGetCurrentThreadId()) {__debugbreak(); }
|
||||
#endif
|
||||
|
||||
class CXmlNodePool;
|
||||
class CXMLPatcher;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Implements IXmlUtils interface.
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
class CXmlUtils
|
||||
: public IXmlUtils
|
||||
, public ISystemEventListener
|
||||
{
|
||||
public:
|
||||
CXmlUtils(ISystem* pSystem);
|
||||
virtual ~CXmlUtils();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// IXmlUtils
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
virtual IXmlParser* CreateXmlParser();
|
||||
|
||||
// Load xml from file, returns 0 if load failed.
|
||||
virtual XmlNodeRef LoadXmlFromFile(const char* sFilename, bool bReuseStrings = false, bool bEnablePatching = true);
|
||||
// Load xml from memory buffer, returns 0 if load failed.
|
||||
virtual XmlNodeRef LoadXmlFromBuffer(const char* buffer, size_t size, bool bReuseStrings = false, bool bSuppressWarnings = false);
|
||||
|
||||
// create an MD5 hash of an XML file
|
||||
virtual const char* HashXml(XmlNodeRef node);
|
||||
|
||||
// Get an object that can read a xml into a IReadXMLSink
|
||||
// and write a xml from a IWriteXMLSource
|
||||
virtual IReadWriteXMLSink* GetIReadWriteXMLSink();
|
||||
|
||||
virtual IXmlSerializer* CreateXmlSerializer();
|
||||
|
||||
virtual bool SaveBinaryXmlFile(const char* sFilename, XmlNodeRef root);
|
||||
virtual XmlNodeRef LoadBinaryXmlFile(const char* sFilename, bool bEnablePatching = true);
|
||||
|
||||
virtual bool EnableBinaryXmlLoading(bool bEnable);
|
||||
|
||||
// Create XML Table reader.
|
||||
virtual IXmlTableReader* CreateXmlTableReader();
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// ISystemEventListener
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
virtual void OnSystemEvent(ESystemEvent event, UINT_PTR wparam, UINT_PTR lparam);
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
virtual void GetMemoryUsage(ICrySizer* pSizer);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Init xml stats nodes pool
|
||||
virtual void InitStatsXmlNodePool(uint32 nPoolSize = 1024*1024);
|
||||
|
||||
// Create new xml node for statistics
|
||||
virtual XmlNodeRef CreateStatsXmlNode(const char* sNodeName = "");
|
||||
|
||||
// Set owner thread
|
||||
virtual void SetStatsOwnerThread(threadID threadId);
|
||||
|
||||
// Free memory if stats xml node pool is empty
|
||||
virtual void FlushStatsXmlNodePool();
|
||||
|
||||
// Set the XML Patcher. This is an XML object that modifies named XML files as they are loaded
|
||||
// EXCEPT for xml files loaded from a buffer, for which names aren't passed in
|
||||
virtual void SetXMLPatcher(XmlNodeRef* pPatcher);
|
||||
|
||||
private:
|
||||
ISystem* m_pSystem;
|
||||
IReadWriteXMLSink* m_pReadWriteXMLSink;
|
||||
CXmlNodePool* m_pStatsXmlNodePool;
|
||||
CXMLPatcher* m_pXMLPatcher; //If set, applies data patches to any XML file that is loaded by this class
|
||||
#ifndef _RELEASE
|
||||
threadID m_statsThreadOwner;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // CRYINCLUDE_CRYSYSTEM_XML_XMLUTILS_H
|
||||
@@ -0,0 +1,15 @@
|
||||
#
|
||||
# Copyright (c) Contributors to the Open 3D Engine Project
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
#
|
||||
#
|
||||
|
||||
set(FILES
|
||||
XMLBinaryNode.cpp
|
||||
XMLBinaryNode.h
|
||||
XMLBinaryReader.cpp
|
||||
XMLBinaryReader.h
|
||||
XMLBinaryWriter.cpp
|
||||
XMLBinaryWriter.h
|
||||
)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,438 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef CRYINCLUDE_CRYSYSTEM_XML_XML_H
|
||||
#define CRYINCLUDE_CRYSYSTEM_XML_XML_H
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <algorithm>
|
||||
#include <PoolAllocator.h>
|
||||
#include <stack>
|
||||
|
||||
#include "IXml.h"
|
||||
|
||||
// track some XML stats. only to find persistent XML nodes in the system
|
||||
// slow, so disable by default
|
||||
//#define CRY_COLLECT_XML_NODE_STATS
|
||||
//#undef CRY_COLLECT_XML_NODE_STATS
|
||||
|
||||
|
||||
struct IXmlStringPool
|
||||
{
|
||||
public:
|
||||
IXmlStringPool() { m_refCount = 0; }
|
||||
virtual ~IXmlStringPool() {};
|
||||
void AddRef() { m_refCount++; };
|
||||
void Release()
|
||||
{
|
||||
if (--m_refCount <= 0)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
};
|
||||
virtual const char* AddString(const char* str) = 0;
|
||||
virtual void GetMemoryUsage(ICrySizer* pSizer) const = 0;
|
||||
private:
|
||||
int m_refCount;
|
||||
};
|
||||
|
||||
/************************************************************************/
|
||||
/* XmlParser class, Parse xml and return root xml node if success. */
|
||||
/************************************************************************/
|
||||
class XmlParser
|
||||
: public IXmlParser
|
||||
{
|
||||
public:
|
||||
explicit XmlParser(bool bReuseStrings);
|
||||
~XmlParser();
|
||||
|
||||
void AddRef()
|
||||
{
|
||||
++m_nRefCount;
|
||||
}
|
||||
|
||||
void Release()
|
||||
{
|
||||
if (--m_nRefCount <= 0)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
virtual XmlNodeRef ParseFile(const char* filename, bool bCleanPools);
|
||||
|
||||
virtual XmlNodeRef ParseBuffer(const char* buffer, int nBufLen, bool bCleanPools, bool bSuppressWarnings = false);
|
||||
|
||||
const char* getErrorString() const { return m_errorString; }
|
||||
|
||||
void GetMemoryUsage(ICrySizer* pSizer) const;
|
||||
|
||||
private:
|
||||
int m_nRefCount;
|
||||
XmlString m_errorString;
|
||||
class XmlParserImp* m_pImpl;
|
||||
};
|
||||
|
||||
// Compare function for string comparasion, can be strcmp or _stricmp
|
||||
typedef int (__cdecl * XmlStrCmpFunc)(const char* str1, const char* str2);
|
||||
extern XmlStrCmpFunc g_pXmlStrCmp;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// XmlAttribute class
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
struct XmlAttribute
|
||||
{
|
||||
const char* key;
|
||||
const char* value;
|
||||
|
||||
void GetMemoryUsage([[maybe_unused]] ICrySizer* pSizer) const{}
|
||||
|
||||
bool operator<(const XmlAttribute& attr) const { return g_pXmlStrCmp(key, attr.key) < 0; }
|
||||
bool operator>(const XmlAttribute& attr) const { return g_pXmlStrCmp(key, attr.key) > 0; }
|
||||
bool operator==(const XmlAttribute& attr) const { return g_pXmlStrCmp(key, attr.key) == 0; }
|
||||
bool operator!=(const XmlAttribute& attr) const { return g_pXmlStrCmp(key, attr.key) != 0; }
|
||||
};
|
||||
|
||||
//! Xml node attributes class.
|
||||
|
||||
typedef std::vector<XmlAttribute> XmlAttributes;
|
||||
typedef XmlAttributes::iterator XmlAttrIter;
|
||||
typedef XmlAttributes::const_iterator XmlAttrConstIter;
|
||||
|
||||
/**
|
||||
******************************************************************************
|
||||
* CXmlNode class
|
||||
* Never use CXmlNode directly instead use reference counted XmlNodeRef.
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
class CXmlNode
|
||||
: public IXmlNode
|
||||
{
|
||||
public:
|
||||
//! Constructor.
|
||||
CXmlNode();
|
||||
CXmlNode(const char* tag, bool bReuseStrings, bool bIsProcessingInstruction = false);
|
||||
//! Destructor.
|
||||
~CXmlNode();
|
||||
|
||||
// collect allocated memory informations
|
||||
void GetMemoryUsage(ICrySizer* pSizer) const;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Custom new/delete with pool allocator.
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//void* operator new( size_t nSize );
|
||||
//void operator delete( void *ptr );
|
||||
|
||||
virtual void DeleteThis();
|
||||
|
||||
//! Create new XML node.
|
||||
XmlNodeRef createNode(const char* tag);
|
||||
|
||||
//! Get XML node tag.
|
||||
const char* getTag() const { return m_tag; };
|
||||
void setTag(const char* tag);
|
||||
|
||||
//! Return true if given tag equal to node tag.
|
||||
bool isTag(const char* tag) const;
|
||||
|
||||
//! Get XML Node attributes.
|
||||
virtual int getNumAttributes() const { return m_pAttributes ? (int)m_pAttributes->size() : 0; };
|
||||
//! Return attribute key and value by attribute index.
|
||||
virtual bool getAttributeByIndex(int index, const char** key, const char** value);
|
||||
|
||||
//! Return attribute key and value by attribute index, string version.
|
||||
virtual bool getAttributeByIndex(int index, XmlString& key, XmlString& value);
|
||||
|
||||
|
||||
virtual void copyAttributes(XmlNodeRef fromNode);
|
||||
virtual void shareChildren(const XmlNodeRef& fromNode);
|
||||
|
||||
//! Get XML Node attribute for specified key.
|
||||
const char* getAttr(const char* key) const;
|
||||
|
||||
//! Get XML Node attribute for specified key.
|
||||
// Returns true if the attribute existes, alse otherwise.
|
||||
bool getAttr(const char* key, const char** value) const;
|
||||
|
||||
//! Check if attributes with specified key exist.
|
||||
bool haveAttr(const char* key) const;
|
||||
|
||||
//! Creates new xml node and add it to childs list.
|
||||
XmlNodeRef newChild(const char* tagName);
|
||||
|
||||
//! Adds new child node.
|
||||
void addChild(const XmlNodeRef& node);
|
||||
//! Remove child node.
|
||||
void removeChild(const XmlNodeRef& node);
|
||||
|
||||
void insertChild(int nIndex, const XmlNodeRef& node);
|
||||
void replaceChild(int nIndex, const XmlNodeRef& node);
|
||||
|
||||
//! Remove all child nodes.
|
||||
void removeAllChilds();
|
||||
|
||||
//! Get number of child XML nodes.
|
||||
int getChildCount() const { return m_pChilds ? (int)m_pChilds->size() : 0; };
|
||||
|
||||
//! Get XML Node child nodes.
|
||||
XmlNodeRef getChild(int i) const;
|
||||
|
||||
//! Find node with specified tag.
|
||||
XmlNodeRef findChild(const char* tag) const;
|
||||
void deleteChild(const char* tag);
|
||||
void deleteChildAt(int nIndex);
|
||||
|
||||
//! Get parent XML node.
|
||||
XmlNodeRef getParent() const { return m_parent; }
|
||||
void setParent(const XmlNodeRef& inRef);
|
||||
|
||||
|
||||
//! Returns content of this node.
|
||||
const char* getContent() const { return m_content; };
|
||||
void setContent(const char* str);
|
||||
|
||||
XmlNodeRef clone();
|
||||
|
||||
//! Returns line number for XML tag.
|
||||
int getLine() const { return m_line; };
|
||||
//! Set line number in xml.
|
||||
void setLine(int line) { m_line = line; };
|
||||
|
||||
//! Returns XML of this node and sub nodes.
|
||||
virtual IXmlStringData* getXMLData(int nReserveMem = 0) const;
|
||||
XmlString getXML(int level = 0) const;
|
||||
XmlString getXMLUnsafe(int level, char* tmpBuffer, uint32 sizeOfTmpBuffer) const;
|
||||
bool saveToFile(const char* fileName); // saves in one huge chunk
|
||||
bool saveToFile(const char* fileName, size_t chunkSizeBytes, AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle); // save in small memory chunks
|
||||
|
||||
//! Set new XML Node attribute (or override attribute with same key).
|
||||
void setAttr(const char* key, const char* value);
|
||||
void setAttr(const char* key, int value);
|
||||
void setAttr(const char* key, unsigned int value);
|
||||
void setAttr(const char* key, int64 value);
|
||||
void setAttr(const char* key, uint64 value, bool useHexFormat = true);
|
||||
void setAttr(const char* key, float value);
|
||||
void setAttr(const char* key, double value);
|
||||
void setAttr(const char* key, const Vec2& value);
|
||||
void setAttr(const char* key, const Vec2d& value);
|
||||
void setAttr(const char* key, const Ang3& value);
|
||||
void setAttr(const char* key, const Vec3& value);
|
||||
void setAttr(const char* key, const Vec4& value);
|
||||
void setAttr(const char* key, const Vec3d& value);
|
||||
void setAttr(const char* key, const Quat& value);
|
||||
|
||||
//! Delete attrbute.
|
||||
void delAttr(const char* key);
|
||||
//! Remove all node attributes.
|
||||
void removeAllAttributes();
|
||||
|
||||
//! Get attribute value of node.
|
||||
bool getAttr(const char* key, int& value) const;
|
||||
bool getAttr(const char* key, unsigned int& value) const;
|
||||
bool getAttr(const char* key, int64& value) const;
|
||||
bool getAttr(const char* key, uint64& value, bool useHexFormat = true /*ignored*/) const;
|
||||
bool getAttr(const char* key, float& value) const;
|
||||
bool getAttr(const char* key, double& value) const;
|
||||
bool getAttr(const char* key, bool& value) const;
|
||||
|
||||
bool getAttr(const char* key, XmlString& value) const {const char* v(NULL); bool boHasAttribute(getAttr(key, &v)); value = v; return boHasAttribute; }
|
||||
|
||||
bool getAttr(const char* key, Vec2& value) const;
|
||||
bool getAttr(const char* key, Vec2d& value) const;
|
||||
bool getAttr(const char* key, Ang3& value) const;
|
||||
bool getAttr(const char* key, Vec3& value) const;
|
||||
bool getAttr(const char* key, Vec4& value) const;
|
||||
bool getAttr(const char* key, Vec3d& value) const;
|
||||
bool getAttr(const char* key, Quat& value) const;
|
||||
bool getAttr(const char* key, ColorB& value) const;
|
||||
|
||||
protected:
|
||||
|
||||
private:
|
||||
CXmlNode(const CXmlNode&);
|
||||
CXmlNode& operator = (const CXmlNode&);
|
||||
|
||||
private:
|
||||
void ReleaseChild(IXmlNode* pChild);
|
||||
void removeAllChildsImpl();
|
||||
|
||||
void AddToXmlString(XmlString& xml, int level, AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle, size_t chunkSizeBytes = 0) const;
|
||||
char* AddToXmlStringUnsafe(char* xml, int level, char* endPtr, AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle, size_t chunkSizeBytes = 0) const;
|
||||
XmlString MakeValidXmlString(const XmlString& xml) const;
|
||||
bool IsValidXmlString(const char* str) const;
|
||||
XmlAttrConstIter GetAttrConstIterator(const char* key) const
|
||||
{
|
||||
assert(m_pAttributes);
|
||||
|
||||
XmlAttribute tempAttr;
|
||||
tempAttr.key = key;
|
||||
|
||||
XmlAttributes::const_iterator it = std::find(m_pAttributes->begin(), m_pAttributes->end(), tempAttr);
|
||||
return it;
|
||||
|
||||
/*
|
||||
XmlAttributes::const_iterator it = std::lower_bound( m_attributes.begin(),m_attributes.end(),tempAttr );
|
||||
if (it != m_attributes.end() && _stricmp(it->key,key) == 0)
|
||||
return it;
|
||||
return m_attributes.end();
|
||||
*/
|
||||
}
|
||||
XmlAttrIter GetAttrIterator(const char* key)
|
||||
{
|
||||
assert(m_pAttributes);
|
||||
|
||||
XmlAttribute tempAttr;
|
||||
tempAttr.key = key;
|
||||
|
||||
XmlAttributes::iterator it = std::find(m_pAttributes->begin(), m_pAttributes->end(), tempAttr);
|
||||
return it;
|
||||
|
||||
// XmlAttributes::iterator it = std::lower_bound( m_attributes.begin(),m_attributes.end(),tempAttr );
|
||||
//if (it != m_attributes.end() && _stricmp(it->key,key) == 0)
|
||||
//return it;
|
||||
//return m_attributes.end();
|
||||
}
|
||||
const char* GetValue(const char* key) const
|
||||
{
|
||||
if (m_pAttributes)
|
||||
{
|
||||
XmlAttrConstIter it = GetAttrConstIterator(key);
|
||||
if (it != m_pAttributes->end())
|
||||
{
|
||||
return it->value;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected:
|
||||
// String pool used by this node.
|
||||
IXmlStringPool* m_pStringPool;
|
||||
|
||||
//! Tag of XML node.
|
||||
const char* m_tag;
|
||||
|
||||
private:
|
||||
|
||||
//! Content of XML node.
|
||||
const char* m_content;
|
||||
//! Parent XML node.
|
||||
IXmlNode* m_parent;
|
||||
|
||||
//typedef DynArray<CXmlNode*,XmlDynArrayAlloc> XmlNodes;
|
||||
typedef std::vector<IXmlNode*> XmlNodes;
|
||||
//XmlNodes m_childs;
|
||||
XmlNodes* m_pChilds;
|
||||
|
||||
//! Xml node attributes.
|
||||
//XmlAttributes m_attributes;
|
||||
XmlAttributes* m_pAttributes;
|
||||
|
||||
//! Line in XML file where this node firstly appeared (useful for debugging).
|
||||
int m_line;
|
||||
|
||||
bool m_isProcessingInstruction;
|
||||
friend class XmlParserImp;
|
||||
};
|
||||
|
||||
typedef stl::PoolAllocatorNoMT<sizeof(CXmlNode)> CXmlNode_PoolAlloc;
|
||||
extern CXmlNode_PoolAlloc* g_pCXmlNode_PoolAlloc;
|
||||
|
||||
#ifdef CRY_COLLECT_XML_NODE_STATS
|
||||
typedef std::set<CXmlNode*> TXmlNodeSet; // yes, slow, but really only for one-shot debugging
|
||||
struct SXmlNodeStats
|
||||
{
|
||||
SXmlNodeStats()
|
||||
: nAllocs(0)
|
||||
, nFrees(0) {}
|
||||
TXmlNodeSet nodeSet;
|
||||
uint32 nAllocs;
|
||||
uint32 nFrees;
|
||||
};
|
||||
extern SXmlNodeStats* g_pCXmlNode_Stats;
|
||||
#endif
|
||||
|
||||
/*
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
inline void* CXmlNode::operator new( size_t nSize )
|
||||
{
|
||||
void *ptr = g_pCXmlNode_PoolAlloc->Allocate();
|
||||
if (ptr)
|
||||
{
|
||||
memset( ptr,0,nSize ); // Clear objects memory.
|
||||
#ifdef CRY_COLLECT_XML_NODE_STATS
|
||||
g_pCXmlNode_Stats->nodeSet.insert(reinterpret_cast<CXmlNode*> (ptr));
|
||||
++g_pCXmlNode_Stats->nAllocs;
|
||||
#endif
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
inline void CXmlNode::operator delete( void *ptr )
|
||||
{
|
||||
if (ptr)
|
||||
{
|
||||
g_pCXmlNode_PoolAlloc->Deallocate(ptr);
|
||||
#ifdef CRY_COLLECT_XML_NODE_STATS
|
||||
g_pCXmlNode_Stats->nodeSet.erase(reinterpret_cast<CXmlNode*> (ptr));
|
||||
++g_pCXmlNode_Stats->nFrees;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Reusable XmlNode for XmlNode pool with shared xml string pool
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
class CXmlNodePool;
|
||||
|
||||
class CXmlNodeReuse
|
||||
: public CXmlNode
|
||||
{
|
||||
public:
|
||||
CXmlNodeReuse(const char* tag, CXmlNodePool* pPool);
|
||||
virtual void Release();
|
||||
|
||||
protected:
|
||||
CXmlNodePool* m_pPool;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Pool of reusable XML nodes with shared string pool
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
class CXmlNodePool
|
||||
{
|
||||
public:
|
||||
CXmlNodePool(unsigned int nBlockSize, bool bReuseStrings);
|
||||
virtual ~CXmlNodePool();
|
||||
|
||||
XmlNodeRef GetXmlNode(const char* sNodeName);
|
||||
bool empty() const { return (m_nAllocated == 0); }
|
||||
|
||||
protected:
|
||||
virtual void OnRelease(int iRefCount, void* pThis);
|
||||
IXmlStringPool* GetStringPool() { return m_pStringPool; }
|
||||
|
||||
private:
|
||||
friend class CXmlNodeReuse;
|
||||
|
||||
IXmlStringPool* m_pStringPool;
|
||||
unsigned int m_nAllocated;
|
||||
std::stack<CXmlNodeReuse*> m_pNodePool;
|
||||
};
|
||||
|
||||
|
||||
#endif // CRYINCLUDE_CRYSYSTEM_XML_XML_H
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef CRYINCLUDE_CRYSYSTEM_XML_XML_STRING_H
|
||||
#define CRYINCLUDE_CRYSYSTEM_XML_XML_STRING_H
|
||||
#pragma once
|
||||
|
||||
|
||||
typedef string xml_string;
|
||||
|
||||
#endif // CRYINCLUDE_CRYSYSTEM_XML_XML_STRING_H
|
||||
Reference in New Issue
Block a user