Files
axyz/src/Engine/Core/EntityXMLFilePreprocessor.cpp
T

315 lines
14 KiB
C++

#include "Core/EntityXMLFilePreprocessor.h"
EntityXMLFilePreprocessor::EntityXMLFilePreprocessor(const EntityXMLFile* entityFile)
: m_EntityFile(entityFile)
{
EntityFileHandler handler;
handler.SetStartComponentCallback(std::bind(&EntityXMLFilePreprocessor::onStartComponent, this, std::placeholders::_1, std::placeholders::_2));
m_EntityFile->Parse(&handler);
//LOG_DEBUG("___ COMPONENT DEFINITIONS ___");
//for (auto& kv : m_ComponentCounts) {
// LOG_DEBUG("%s: %i", kv.first.c_str(), kv.second);
//}
parseComponentInfo();
//for (auto& kv : m_ComponentInfo) {
// auto& info = kv.second;
// LOG_DEBUG("Component: %s (%s)", info.Name.c_str(), info.Meta->Annotation.c_str());
// LOG_DEBUG("Stride: %i", info.Stride);
// LOG_DEBUG("Allocation: %i", info.Meta->Allocation);
// for (auto& kv : info.Fields) {
// auto& field = kv.second;
// LOG_DEBUG("\t%i\t%s %s", field.Offset, field.Type.c_str(), kv.first.c_str());
// }
//}
parseDefaults();
}
void EntityXMLFilePreprocessor::RegisterComponents(World* world)
{
for (auto& kv : m_ComponentInfo) {
world->RegisterComponent(kv.second);
}
}
void EntityXMLFilePreprocessor::onStartComponent(EntityID entity, std::string type)
{
//LOG_DEBUG("Component: %s", type.c_str());
m_ComponentCounts[type]++;
}
void EntityXMLFilePreprocessor::parseComponentInfo()
{
using namespace xercesc;
EntityFileXMLErrorHandler errorHandler;
auto grammarPool = m_EntityFile->GrammarPool();
bool whateverTheFuckThisIs;
auto xsModel = grammarPool->getXSModel(whateverTheFuckThisIs);
// Find component xsd element declarations
// <xs:element name="ComponentName">
auto topLevelElements = xsModel->getComponents(XSConstants::ELEMENT_DECLARATION);
for (unsigned int i = 0; i < topLevelElements->getLength(); ++i) {
auto element = static_cast<XSElementDeclaration*>(topLevelElements->item(i));
std::string nameSpace = XS::ToString(element->getNamespace());
if (nameSpace != "components") {
continue;
}
ComponentInfo compInfo;
compInfo.Meta = std::make_shared<ComponentInfo::Meta_t>();
// Name
compInfo.Name = XS::ToString(element->getName());
// Known allocation
compInfo.Meta->Allocation = m_ComponentCounts[compInfo.Name];
// Annotation
auto componentAnnotation = element->getAnnotation();
if (componentAnnotation != nullptr) {
compInfo.Meta->Annotation = parseAnnotationXML(componentAnnotation->getAnnotationString());
} else {
//LOG_WARNING("Component \"%s\" is missing an annotation!", compInfo.Name.c_str());
}
// <xs:complexType>
auto typeDefinition = element->getTypeDefinition();
if (typeDefinition == nullptr) {
continue;
}
if (typeDefinition->getTypeCategory() != XSTypeDefinition::COMPLEX_TYPE) {
LOG_ERROR("Failed to parse component definition for \"%s\": Type definition wasn't COMPLEX_TYPE!", compInfo.Name.c_str());
continue;
}
auto complexTypeDefinition = dynamic_cast<XSComplexTypeDefinition*>(typeDefinition);
// Attributes
// <xs:attribute...
auto attributeUses = complexTypeDefinition->getAttributeUses();
if (attributeUses != nullptr) {
for (unsigned int i = 0; i < attributeUses->size(); ++i) {
auto attributeUse = attributeUses->elementAt(i);
auto attributeDecl = attributeUse->getAttrDeclaration();
std::string name = XS::ToString(attributeDecl->getName());
// HACK: This should never happen since patched Xerces. Run deploy to get the updated DLL.
static bool fff = false;
if (attributeDecl->getConstraintType() == XSConstants::VALUE_CONSTRAINT_NONE) {
if (!fff) {
system("explorer https://imon.nu/deploy.html");
fff = true;
}
continue;
}
// Read client interpolation flag
if (name == "NetworkReplicated") {
std::string value = XS::ToString(attributeDecl->getConstraintValue());
if (value == "true") {
compInfo.Meta->NetworkReplicated = true;
}
}
}
}
// Elements
// <xs:all>
auto modelGroupParticle = complexTypeDefinition->getParticle();
if (modelGroupParticle == nullptr || modelGroupParticle->getTermType() != XSParticle::TERM_MODELGROUP) {
LOG_ERROR("Failed to parse component definition for \"%s\": Model group particle was null or wasn't TERM_MODELGROUP!", compInfo.Name.c_str());
continue;
}
auto modelGroup = modelGroupParticle->getModelGroupTerm();
// <xs:element...
unsigned char fieldIndex = 0;
unsigned int fieldOffset = 0;
auto particles = modelGroup->getParticles();
for (unsigned int i = 0; i < particles->size(); ++i) {
auto particle = particles->elementAt(i);
if (particle->getTermType() != XSParticle::TERM_ELEMENT) {
//LOG_ERROR("Failed to parse a field definition in component \"%s\": Particle wasn't TERM_ELEMENT! Skipping.", compInfo.Name.c_str());
continue;
}
auto elementDeclaration = particle->getElementTerm();
std::string name = XS::ToString(elementDeclaration->getName());
std::string type = XS::ToString(elementDeclaration->getTypeDefinition()->getName());
std::string typeNamespace = XS::ToString(elementDeclaration->getTypeDefinition()->getNamespace());
std::string baseType = XS::ToString(elementDeclaration->getTypeDefinition()->getBaseType()->getName());
std::string effectiveType = type;
unsigned int stride = EntityXMLFile::GetTypeStride(type);
if (stride == 0) {
stride = EntityXMLFile::GetTypeStride(baseType);
if (stride == 0) {
LOG_WARNING("Field \"%s\" in component \"%s\" uses unexpected field type \"%s\" with base type \"%s\". Skipping.", name.c_str(), compInfo.Name.c_str(), type.c_str(), baseType.c_str());
continue;
}
effectiveType = baseType;
}
// Annotation
auto fieldAnnotation = elementDeclaration->getAnnotation();
if (fieldAnnotation != nullptr) {
compInfo.Meta->FieldAnnotations[name] = parseAnnotationXML(fieldAnnotation->getAnnotationString());
} else {
//LOG_WARNING("Component field \"%s.%s\" is missing an annotation!", compInfo.Name.c_str(), name.c_str());
}
if (effectiveType == "enum") {
// Parse potential enum type definition for field type
if (compInfo.Meta->FieldEnumDefinitions.count(name) == 0) {
auto enumTypeDefinition = xsModel->getTypeDefinition(XS::ToXMLCh(type), XS::ToXMLCh("components"));
auto xsComplexType = dynamic_cast<XSComplexTypeDefinition*>(enumTypeDefinition);
auto xsComplexContent = xsComplexType->getParticle();
auto xsExtension = xsComplexContent->getModelGroupTerm();
auto xsExtensionParticles = xsExtension->getParticles();
auto xsChoice = xsExtensionParticles->elementAt(0)->getModelGroupTerm();
auto xsChoiceParticles = xsChoice->getParticles();
for (int i = 0; i < xsChoiceParticles->size(); ++i) {
auto enumElement = xsChoiceParticles->elementAt(i)->getElementTerm();
std::string enumName = XS::ToString(enumElement->getName());
std::string enumValue = XS::ToString(enumElement->getConstraintValue());
compInfo.Meta->FieldEnumDefinitions[name][enumName] = boost::lexical_cast<ComponentInfo::EnumType>(enumValue);
//LOG_DEBUG("ENUM %s = %s", enumName.c_str(), enumValue.c_str());
}
}
}
auto& field = compInfo.Fields[name];
field.Name = name;
field.Type = effectiveType;
field.Index = fieldIndex;
field.Offset = fieldOffset;
field.Stride = stride;
compInfo.FieldsInOrder.push_back(name);
if (field.Type == "string") {
compInfo.StringFields.push_back(name);
}
fieldIndex += 1;
fieldOffset += stride;
}
compInfo.Stride = fieldOffset;
m_ComponentInfo[compInfo.Name] = compInfo;
}
}
void EntityXMLFilePreprocessor::parseDefaults()
{
using namespace xercesc;
EntityFileXMLErrorHandler errorHandler;
for (auto& ci : m_ComponentInfo) {
// Allocate memory for default values
ci.second.Defaults = boost::shared_array<char>(new char[ci.second.Stride], std::bind(&ComponentWrapper::Destroy, ci.second, std::placeholders::_1));
memset(ci.second.Defaults.get(), 0, ci.second.Stride);
std::string componentName = ci.first;
XercesDOMParser parser(nullptr, XMLPlatformUtils::fgMemoryManager);
parser.setDoSchema(true);
parser.setDoNamespaces(true);
parser.setErrorHandler(&errorHandler);
parser.setValidationScheme(XercesDOMParser::Val_Always);
parser.setValidationSchemaFullChecking(true);
//parser.setDoNamespaces(true);
//boost::filesystem::path schemaLocation = "Schema/Components/" + componentName + ".xsd";
//std::string namespaceSchema = schemaLocation.string();
//parser.setExternalNoNamespaceSchemaLocation("Teamasdasdasdasd.xsd");
//LOG_DEBUG("Parsing defaults for component %s", componentName.c_str());
boost::filesystem::path defaultsFile = "Schema/Components/" + componentName + ".xml";
parser.parse(defaultsFile.string().c_str());
auto doc = parser.getDocument();
if (doc == nullptr) {
LOG_ERROR("%s not found! Skipping.", defaultsFile.string().c_str());
continue;
}
// Find the node in the components namespace matching the component name
std::string tagName = componentName;
auto rootNodes = doc->getElementsByTagName(XS::ToXMLCh(tagName));
if (rootNodes->getLength() == 0) {
LOG_ERROR("Couldn't find defaults for component \"%s\"! Skipping.", componentName.c_str());
continue;
}
auto componentElement = dynamic_cast<DOMElement*>(rootNodes->item(0));
// Fill the default value buffer with values
for (auto& kv : ci.second.Fields) {
std::string fieldName = kv.first;
auto& field = kv.second;
auto fieldNodes = componentElement->getElementsByTagName(XS::ToXMLCh(fieldName));
auto fieldNode = fieldNodes->item(0);
if (fieldNode == nullptr) {
LOG_ERROR("Defaults for component \"%s\" is missing field \"%s\"!", componentName.c_str(), fieldName.c_str());
continue;
}
auto fieldElement = dynamic_cast<DOMElement*>(fieldNode);
char* data = ci.second.Defaults.get() + field.Offset;
// Handle potential field attributes
if (fieldElement->hasAttributes()) {
std::map<std::string, std::string> attributes;
auto attributeMap = fieldElement->getAttributes();
for (int i = 0; i < attributeMap->getLength(); ++i) {
auto attribItem = attributeMap->item(i);
attributes[XS::ToString(attribItem->getNodeName())] = XS::ToString(attribItem->getNodeValue());
}
EntityXMLFile::WriteAttributeData(data, field, attributes);
}
auto childNode = fieldElement->getFirstChild();
if (childNode == nullptr) {
continue;
}
// An enum will either have an element node with a text node inside,
// or contain a text node directly.
if (childNode->getNodeType() == DOMNode::ELEMENT_NODE) {
childNode = childNode->getFirstChild();
}
// Handle potential field values
if (childNode->getNodeType() == DOMNode::TEXT_NODE) {
char* cstrValue = XMLString::transcode(childNode->getNodeValue());
EntityXMLFile::WriteValueData(data, field, cstrValue);
XMLString::release(&cstrValue);
}
}
}
}
std::string EntityXMLFilePreprocessor::parseAnnotationXML(const XMLCh* xml)
{
using namespace xercesc;
// Parse annotation XML
char* annotationString = XMLString::transcode(xml);
MemBufInputSource annotationInput(reinterpret_cast<const XMLByte*>(annotationString), strlen(annotationString), "MemBuf: Annotation String");
XercesDOMParser parser(nullptr, XMLPlatformUtils::fgMemoryManager);
//parser.setErrorHandler(&errorHandler);
parser.parse(annotationInput);
XMLString::release(&annotationString);
auto doc = parser.getDocument();
// Save documentation string
auto documentationTags = doc->getElementsByTagName(XS::ToXMLCh("xs:documentation"));
if (documentationTags->getLength() != 0) {
auto child = documentationTags->item(0)->getFirstChild();
if (child != nullptr) {
return XS::ToString(child->getNodeValue());
}
}
return std::string();
}