You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
436 lines
12 KiB
C++
436 lines
12 KiB
C++
/*
|
|
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
|
* its licensors.
|
|
*
|
|
* For complete copyright and license terms please see the LICENSE at the root of this
|
|
* distribution (the "License"). All use of this software is governed by the License,
|
|
* or, if provided, by the license below or the license accompanying this file. Do not
|
|
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
*
|
|
*/
|
|
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
|
|
|
#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;
|
|
}
|