Merge branch 'Preview4_0' into feature-vfs-security

This commit is contained in:
Robert MacGregor 2022-06-13 08:05:26 -04:00
commit 161ffc62fe
3013 changed files with 348715 additions and 182470 deletions

View file

@ -26,6 +26,41 @@
#include "console/console.h"
VfsXMLPrinter::VfsXMLPrinter(FileStream& stream, bool compact, int depth)
: XMLPrinter(NULL, compact, depth),
m_Stream(stream)
{
}
VfsXMLPrinter::~VfsXMLPrinter()
{
m_Stream.flush();
m_Stream.close();
}
// Add VFS friendly implementations of output functions
void VfsXMLPrinter::Print(const char* format, ...)
{
va_list va;
va_start(va, format);
m_Stream.writeFormattedBuffer(format, va);
va_end(va);
}
void VfsXMLPrinter::Write(const char* data, size_t size)
{
m_Stream.write(size, data);
}
void VfsXMLPrinter::Putc(char ch)
{
m_Stream.write(static_cast<U8>(ch));
}
bool VfsXMLDocument::LoadFile(const char* pFilename)
{
// Expand the file-path.
@ -61,6 +96,114 @@ bool VfsXMLDocument::LoadFile(const char* pFilename)
return true;
}
bool VfsXMLDocument::LoadFile(FileStream& stream)
{
// Delete the existing data:
Clear();
// Clear shadowed error
ClearError();
//TODO: Can't clear location, investigate if this gives issues.
//doc.location.Clear();
// Get the file size, so we can pre-allocate the string. HUGE speed impact.
long length = stream.getStreamSize();
// Strange case, but good to handle up front.
if (length <= 0)
{
SetError(tinyxml2::XML_ERROR_EMPTY_DOCUMENT, 0, 0);
return false;
}
// Subtle bug here. TinyXml did use fgets. But from the XML spec:
// 2.11 End-of-Line Handling
// <snip>
// <quote>
// ...the XML processor MUST behave as if it normalized all line breaks in external
// parsed entities (including the document entity) on input, before parsing, by translating
// both the two-character sequence #xD #xA and any #xD that is not followed by #xA to
// a single #xA character.
// </quote>
//
// It is not clear fgets does that, and certainly isn't clear it works cross platform.
// Generally, you expect fgets to translate from the convention of the OS to the c/unix
// convention, and not work generally.
/*
while( fgets( buf, sizeof(buf), file ) )
{
data += buf;
}
*/
char* buf = new char[length + 1];
buf[0] = 0;
if (!stream.read(length, buf))
{
delete[] buf;
SetError(tinyxml2::XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, 0);
return false;
}
// Process the buffer in place to normalize new lines. (See comment above.)
// Copies from the 'p' to 'q' pointer, where p can advance faster if
// a newline-carriage return is hit.
//
// Wikipedia:
// Systems based on ASCII or a compatible character set use either LF (Line feed, '\n', 0x0A, 10 in decimal) or
// CR (Carriage return, '\r', 0x0D, 13 in decimal) individually, or CR followed by LF (CR+LF, 0x0D 0x0A)...
// * LF: Multics, Unix and Unix-like systems (GNU/Linux, AIX, Xenix, Mac OS X, FreeBSD, etc.), BeOS, Amiga, RISC OS, and others
// * CR+LF: DEC RT-11 and most other early non-Unix, non-IBM OSes, CP/M, MP/M, DOS, OS/2, Microsoft Windows, Symbian OS
// * CR: Commodore 8-bit machines, Apple II family, Mac OS up to version 9 and OS-9
const char* p = buf; // the read head
char* q = buf; // the write head
const char CR = 0x0d;
const char LF = 0x0a;
buf[length] = 0;
while (*p)
{
assert(p < (buf + length));
assert(q <= (buf + length));
assert(q <= p);
if (*p == CR)
{
*q++ = LF;
p++;
if (*p == LF)
{
// check for CR+LF (and skip LF)
p++;
}
}
else
{
*q++ = *p++;
}
}
assert(q <= (buf + length));
*q = 0;
Parse(buf, length);
delete[] buf;
return !Error();
}
bool VfsXMLDocument::SaveFile(FileStream& stream)
{
// Clear any error from the last save, otherwise it will get reported
// for *this* call.
ClearError();
VfsXMLPrinter printer(stream, false, 0);
PrettyXMLPrinter prettyPrinter(printer);
Print(&prettyPrinter);
return !Error();
}
bool VfsXMLDocument::SaveFile(const char* pFilename)
{
// Expand the file-name into the file-path buffer.
@ -119,141 +262,110 @@ void VfsXMLDocument::SetError(tinyxml2::XMLError error, int lineNum, const char*
delete[] buffer;
}
VfsXMLPrinter::VfsXMLPrinter(FileStream& stream, bool compact, int depth)
: XMLPrinter(NULL, compact, depth),
m_Stream(stream)
// Overwrite Visitation of elements to add newlines before attributes
PrettyXMLPrinter::PrettyXMLPrinter(VfsXMLPrinter& innerPrinter, int depth)
: mInnerPrinter(innerPrinter),
mDepth(depth)
{
}
VfsXMLPrinter::~VfsXMLPrinter()
{
m_Stream.flush();
m_Stream.close();
}
void VfsXMLPrinter::Print(const char* format, ...)
{
va_list va;
va_start(va, format);
m_Stream.writeFormattedBuffer(format, va);
va_end(va);
}
void VfsXMLPrinter::Write(const char* data, size_t size)
{
m_Stream.write(size, data);
}
void VfsXMLPrinter::Putc(char ch)
{
m_Stream.write(static_cast<U8>(ch));
}
bool VfsXMLDocument::LoadFile(FileStream& stream)
{
// Delete the existing data:
Clear();
// Clear shadowed error
ClearError();
//TODO: Can't clear location, investigate if this gives issues.
//doc.location.Clear();
// Get the file size, so we can pre-allocate the string. HUGE speed impact.
long length = stream.getStreamSize();
// Strange case, but good to handle up front.
if (length <= 0)
{
SetError(tinyxml2::XML_ERROR_EMPTY_DOCUMENT, 0, 0);
return false;
for (int i = 0; i < ENTITY_RANGE; ++i) {
mEntityFlag[i] = false;
mRestrictedEntityFlag[i] = false;
}
// Subtle bug here. TinyXml did use fgets. But from the XML spec:
// 2.11 End-of-Line Handling
// <snip>
// <quote>
// ...the XML processor MUST behave as if it normalized all line breaks in external
// parsed entities (including the document entity) on input, before parsing, by translating
// both the two-character sequence #xD #xA and any #xD that is not followed by #xA to
// a single #xA character.
// </quote>
//
// It is not clear fgets does that, and certainly isn't clear it works cross platform.
// Generally, you expect fgets to translate from the convention of the OS to the c/unix
// convention, and not work generally.
/*
while( fgets( buf, sizeof(buf), file ) )
{
data += buf;
for (int i = 0; i < NUM_ENTITIES; ++i) {
const char entityValue = entities[i].value;
const unsigned char flagIndex = static_cast<unsigned char>(entityValue);
TIXMLASSERT(flagIndex < ENTITY_RANGE);
mEntityFlag[flagIndex] = true;
}
*/
mRestrictedEntityFlag[static_cast<unsigned char>('&')] = true;
mRestrictedEntityFlag[static_cast<unsigned char>('<')] = true;
mRestrictedEntityFlag[static_cast<unsigned char>('>')] = true; // not required, but consistency is nice
}
char* buf = new char[length + 1];
buf[0] = 0;
void PrettyXMLPrinter::PrintString(const char* p, bool restricted)
{
// Look for runs of bytes between entities to print.
const char* q = p;
if (!stream.read(length, buf))
{
delete [] buf;
SetError(tinyxml2::XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, 0);
return false;
}
// Process the buffer in place to normalize new lines. (See comment above.)
// Copies from the 'p' to 'q' pointer, where p can advance faster if
// a newline-carriage return is hit.
//
// Wikipedia:
// Systems based on ASCII or a compatible character set use either LF (Line feed, '\n', 0x0A, 10 in decimal) or
// CR (Carriage return, '\r', 0x0D, 13 in decimal) individually, or CR followed by LF (CR+LF, 0x0D 0x0A)...
// * LF: Multics, Unix and Unix-like systems (GNU/Linux, AIX, Xenix, Mac OS X, FreeBSD, etc.), BeOS, Amiga, RISC OS, and others
// * CR+LF: DEC RT-11 and most other early non-Unix, non-IBM OSes, CP/M, MP/M, DOS, OS/2, Microsoft Windows, Symbian OS
// * CR: Commodore 8-bit machines, Apple II family, Mac OS up to version 9 and OS-9
const char* p = buf; // the read head
char* q = buf; // the write head
const char CR = 0x0d;
const char LF = 0x0a;
buf[length] = 0;
while (*p)
{
assert(p < (buf+length));
assert(q <= (buf+length));
assert(q <= p);
if (*p == CR)
{
*q++ = LF;
p++;
if (*p == LF)
{
// check for CR+LF (and skip LF)
p++;
if (mProcessEntities) {
const bool* flag = restricted ? mRestrictedEntityFlag : mEntityFlag;
while (*q) {
TIXMLASSERT(p <= q);
// Remember, char is sometimes signed. (How many times has that bitten me?)
if (*q > 0 && *q < ENTITY_RANGE) {
// Check for entities. If one is found, flush
// the stream up until the entity, write the
// entity, and keep looking.
if (flag[static_cast<unsigned char>(*q)]) {
while (p < q) {
const size_t delta = q - p;
const int toPrint = (INT_MAX < delta) ? INT_MAX : static_cast<int>(delta);
mInnerPrinter.Write(p, toPrint);
p += toPrint;
}
bool entityPatternPrinted = false;
for (int i = 0; i < NUM_ENTITIES; ++i) {
if (entities[i].value == *q) {
mInnerPrinter.Putc('&');
mInnerPrinter.Write(entities[i].pattern, entities[i].length);
mInnerPrinter.Putc(';');
entityPatternPrinted = true;
break;
}
}
if (!entityPatternPrinted) {
// TIXMLASSERT( entityPatternPrinted ) causes gcc -Wunused-but-set-variable in release
TIXMLASSERT(false);
}
++p;
}
}
++q;
TIXMLASSERT(p <= q);
}
else
{
*q++ = *p++;
// Flush the remaining string. This will be the entire
// string if an entity wasn't found.
if (p < q) {
const size_t delta = q - p;
const int toPrint = (INT_MAX < delta) ? INT_MAX : static_cast<int>(delta);
mInnerPrinter.Write(p, toPrint);
}
}
assert(q <= (buf+length));
*q = 0;
Parse(buf, length);
delete [] buf;
return !Error();
else {
mInnerPrinter.Write(p);
}
}
bool VfsXMLDocument::SaveFile(FileStream& stream)
bool PrettyXMLPrinter::VisitEnter(const tinyxml2::XMLElement& element, const tinyxml2::XMLAttribute* attribute)
{
// Clear any error from the last save, otherwise it will get reported
// for *this* call.
ClearError();
VfsXMLPrinter printer(stream, false, 0);
Print(&printer);
return !Error();
const tinyxml2::XMLElement* parentElem = 0;
if (element.Parent()) {
parentElem = element.Parent()->ToElement();
}
const bool compactMode = parentElem ? mInnerPrinter.CompactMode(*parentElem) : mInnerPrinter.CompactMode(element);
mInnerPrinter.OpenElement(element.Name(), compactMode);
mDepth++;
while (attribute) {
PushAttribute(attribute->Name(), attribute->Value(), compactMode);
attribute = attribute->Next();
}
return true;
}
void PrettyXMLPrinter::PushAttribute(const char* name, const char* value, bool compactMode)
{
if (compactMode)
{
mInnerPrinter.Putc(' ');
}
else
{
mInnerPrinter.Putc('\n');
mInnerPrinter.PrintSpace(mDepth);
}
mInnerPrinter.Write(name);
mInnerPrinter.Write("=\"");
PrintString(value, false);
mInnerPrinter.Putc('\"');
}

View file

@ -40,9 +40,18 @@ public:
VfsXMLPrinter(FileStream& stream, bool compact = false, int depth = 0);
~VfsXMLPrinter() override;
// Re-implement protected functionality in TinyXML2 library, and make it public
// (This is a bit dirty, but it's necessary for the PrettyXMLPrinter)
bool CompactMode(const tinyxml2::XMLElement& element) override { return tinyxml2::XMLPrinter::CompactMode(element); }
void PrintSpace(int depth) override { tinyxml2::XMLPrinter::PrintSpace(depth); }
inline void Write(const char* data) { Write(data, strlen(data)); }
// Add VFS friendly implementations of output functions
void Print(const char* format, ...) override;
void Write(const char* data, size_t size) override;
void Putc(char ch) override;
// Accept a virtual FileStream instead of a FILE pointer
FileStream& m_Stream;
};
@ -127,4 +136,103 @@ public:
}
};
class PrettyXMLPrinter : public tinyxml2::XMLPrinter
{
// Re-implement private functionality in TinyXML2
static const char LINE_FEED = static_cast<char>(0x0a); // all line endings are normalized to LF
static const char LF = LINE_FEED;
static const char CARRIAGE_RETURN = static_cast<char>(0x0d); // CR gets filtered out
static const char CR = CARRIAGE_RETURN;
static const char SINGLE_QUOTE = '\'';
static const char DOUBLE_QUOTE = '\"';
struct Entity
{
const char* pattern;
int length;
char value;
};
static const int NUM_ENTITIES = 5;
static constexpr Entity entities[NUM_ENTITIES] = {
{"quot", 4, DOUBLE_QUOTE},
{"amp", 3, '&'},
{"apos", 4, SINGLE_QUOTE},
{"lt", 2, '<'},
{"gt", 2, '>'}
};
public:
PrettyXMLPrinter(VfsXMLPrinter& innerPrinter, int depth = 0);
/// Visit a document.
virtual bool VisitEnter(const tinyxml2::XMLDocument& doc)
{
mProcessEntities = doc.ProcessEntities();
return mInnerPrinter.VisitEnter(doc);
}
/// Visit a document.
virtual bool VisitExit(const tinyxml2::XMLDocument& doc)
{
return mInnerPrinter.VisitExit(doc);
}
/// Visit an element.
virtual bool VisitEnter(const tinyxml2::XMLElement& element, const tinyxml2::XMLAttribute* firstAttribute);
/// Visit an element.
virtual bool VisitExit(const tinyxml2::XMLElement& element)
{
mDepth--;
return mInnerPrinter.VisitExit(element);
}
/// Visit a declaration.
virtual bool Visit(const tinyxml2::XMLDeclaration& declaration)
{
return mInnerPrinter.Visit(declaration);
}
/// Visit a text node.
virtual bool Visit(const tinyxml2::XMLText& text)
{
return mInnerPrinter.Visit(text);
}
/// Visit a comment node.
virtual bool Visit(const tinyxml2::XMLComment& comment)
{
return mInnerPrinter.Visit(comment);
}
/// Visit an unknown node.
virtual bool Visit(const tinyxml2::XMLUnknown& unknown)
{
return mInnerPrinter.Visit(unknown);
}
void PushAttribute(const char* name, const char* value, bool compactMode);
// Re-implement private functionality in TinyXML2 library, this is just a copy-paste job
void PrintString(const char*, bool restrictedEntitySet); // prints out, after detecting entities.
// The inner printer we are wrapping, we only support VfsXMLPrinter based classes because
// stock tinyxml printer is very closed
VfsXMLPrinter& mInnerPrinter;
// Track private fields that are necessary for private functionality in TinyXML2
int mDepth;
bool mProcessEntities;
bool mCompactMode;
enum
{
ENTITY_RANGE = 64,
BUF_SIZE = 200
};
bool mEntityFlag[ENTITY_RANGE];
bool mRestrictedEntityFlag[ENTITY_RANGE];
};
#endif //_FSTINYXML_H_

View file

@ -643,6 +643,19 @@ ImplementEnumType(_TamlFormatMode,
// Fetch field count.
const U32 fieldCount = fieldList.size();
ConsoleObject* defaultConObject;
SimObject* defaultObject;
if (!getWriteDefaults())
{
// Create a default object of the same type
defaultConObject = ConsoleObject::create(pSimObject->getClassName());
defaultObject = dynamic_cast<SimObject*>(defaultConObject);
// ***Really*** shouldn't happen
if (!defaultObject)
return;
}
// Iterate fields.
U8 arrayDepth = 0;
TamlCustomNode* currentArrayNode = NULL;
@ -709,9 +722,6 @@ ImplementEnumType(_TamlFormatMode,
if (!pFieldValue)
pFieldValue = StringTable->EmptyString();
if (pField->type == TypeBool)
pFieldValue = dAtob(pFieldValue) ? "true" : "false";
U32 nBufferSize = dStrlen(pFieldValue) + 1;
FrameTemp<char> valueCopy(nBufferSize);
dStrcpy((char *)valueCopy, pFieldValue, nBufferSize);
@ -720,9 +730,20 @@ ImplementEnumType(_TamlFormatMode,
if (!pSimObject->writeField(fieldName, valueCopy))
continue;
if (!getWriteDefaults())
{
//If the field hasn't been changed from the default value, then don't bother writing it out
const char* fieldData = defaultObject->getDataField(fieldName, indexBuffer);
if (fieldData && fieldData[0] != '\0' && dStricmp(fieldData, pFieldValue) == 0)
continue;
}
// Reassign field value.
pFieldValue = valueCopy;
if (pField->type == TypeBool)
pFieldValue = dAtob(pFieldValue) ? "true" : "false";
// Detect and collapse relative path information
char fnBuf[1024];
if ((S32)pField->type == TypeFilename)
@ -741,6 +762,12 @@ ImplementEnumType(_TamlFormatMode,
}
}
}
if (!getWriteDefaults())
{
// Cleanup our created default object
delete defaultConObject;
}
}
//-----------------------------------------------------------------------------
@ -1033,6 +1060,115 @@ ImplementEnumType(_TamlFormatMode,
//-----------------------------------------------------------------------------
tinyxml2::XMLElement* g__write_schema_attribute_element(const AbstractClassRep::Field& field, AbstractClassRep* pType,
tinyxml2::XMLDocument& schemaDocument)
{
// Skip if not a data field.
if (field.type == AbstractClassRep::DeprecatedFieldType ||
field.type == AbstractClassRep::StartGroupFieldType ||
field.type == AbstractClassRep::EndGroupFieldType)
return NULL;
// Skip if the field root is not this type.
if (pType->findFieldRoot(field.pFieldname) != pType)
return NULL;
// Add attribute element.
tinyxml2::XMLElement* pAttributeElement = schemaDocument.NewElement("xs:attribute");
pAttributeElement->SetAttribute("name", field.pFieldname);
// Handle the console type appropriately.
const S32 fieldType = (S32)field.type;
/*
// Is the field an enumeration?
if ( fieldType == TypeEnum )
{
// Yes, so add attribute type.
tinyxml2::XMLElement* pAttributeSimpleTypeElement = schemaDocument.NewElement( "xs:simpleType" );
pAttributeElement->LinkEndChild( pAttributeSimpleTypeElement );
// Add restriction element.
tinyxml2::XMLElement* pAttributeRestrictionElement = schemaDocument.NewElement( "xs:restriction" );
pAttributeRestrictionElement->SetAttribute( "base", "xs:string" );
pAttributeSimpleTypeElement->LinkEndChild( pAttributeRestrictionElement );
// Yes, so fetch enumeration count.
const S32 enumCount = field.table->size;
// Iterate enumeration.
for( S32 index = 0; index < enumCount; ++index )
{
// Add enumeration element.
tinyxml2::XMLElement* pAttributeEnumerationElement = schemaDocument.NewElement( "xs:enumeration" );
pAttributeEnumerationElement->SetAttribute( "value", field.table->table[index].label );
pAttributeRestrictionElement->LinkEndChild( pAttributeEnumerationElement );
}
}
else
{*/
// No, so assume it's a string type initially.
const char* pFieldTypeDescription = "xs:string";
// Handle known types.
if (fieldType == TypeF32)
{
pFieldTypeDescription = "xs:float";
}
else if (fieldType == TypeS8 || fieldType == TypeS32)
{
pFieldTypeDescription = "xs:int";
}
else if (fieldType == TypeBool || fieldType == TypeFlag)
{
pFieldTypeDescription = "xs:boolean";
}
else if (fieldType == TypePoint2F)
{
pFieldTypeDescription = "Point2F_ConsoleType";
}
else if (fieldType == TypePoint2I)
{
pFieldTypeDescription = "Point2I_ConsoleType";
}
else if (fieldType == TypeRectI)
{
pFieldTypeDescription = "RectI_ConsoleType";
}
else if (fieldType == TypeRectF)
{
pFieldTypeDescription = "RectF_ConsoleType";
}
else if (fieldType == TypeColorF)
{
pFieldTypeDescription = "ColorF_ConsoleType";
}
else if (fieldType == TypeColorI)
{
pFieldTypeDescription = "ColorI_ConsoleType";
}
else if (fieldType == TypeAssetId/* ||
fieldType == TypeImageAssetPtr ||
fieldType == TypeAnimationAssetPtr ||
fieldType == TypeAudioAssetPtr*/)
{
pFieldTypeDescription = "AssetId_ConsoleType";
}
// Set attribute type.
pAttributeElement->SetAttribute("type", pFieldTypeDescription);
//}
pAttributeElement->SetAttribute("use", "optional");
return pAttributeElement;
}
String g_sanitize_schema_element_name(String buffer)
{
return buffer.replace("(", "")
.replace(")", "");
}
bool Taml::generateTamlSchema()
{
// Fetch any TAML Schema file reference.
@ -1276,8 +1412,8 @@ ImplementEnumType(_TamlFormatMode,
pSchemaElement->LinkEndChild(pComplexTypeElement);
// Add sequence.
tinyxml2::XMLElement* pSequenceElement = schemaDocument.NewElement("xs:sequence");
pComplexTypeElement->LinkEndChild(pSequenceElement);
tinyxml2::XMLElement* pAllElement = schemaDocument.NewElement("xs:all");
pComplexTypeElement->LinkEndChild(pAllElement);
// Fetch container child class.
AbstractClassRep* pContainerChildClass = pType->getContainerChildClass(true);
@ -1289,7 +1425,7 @@ ImplementEnumType(_TamlFormatMode,
tinyxml2::XMLElement* pChoiceElement = schemaDocument.NewElement("xs:choice");
pChoiceElement->SetAttribute("minOccurs", 0);
pChoiceElement->SetAttribute("maxOccurs", "unbounded");
pSequenceElement->LinkEndChild(pChoiceElement);
pAllElement->LinkEndChild(pChoiceElement);
// Find child group.
HashTable<AbstractClassRep*, StringTableEntry>::Iterator childGroupItr = childGroups.find(pContainerChildClass);
@ -1347,7 +1483,74 @@ ImplementEnumType(_TamlFormatMode,
continue;
// Call schema generation function.
customSchemaFn(pType, pSequenceElement);
customSchemaFn(pType, pAllElement);
}
// Fetch field list.
const AbstractClassRep::FieldList& fields = pType->mFieldList;
// Fetch field count.
const S32 fieldCount = fields.size();
// Generate array attribute groups
for (S32 index = 0; index < fieldCount; ++index)
{
// Fetch field.
const AbstractClassRep::Field& field = fields[index];
if (field.type == AbstractClassRep::StartArrayFieldType)
{
// Add the top-level array identifier
tinyxml2::XMLElement* pArrayElement = schemaDocument.NewElement("xs:element");
dSprintf(buffer, sizeof(buffer), "%s.%s", pType->getClassName(), g_sanitize_schema_element_name(field.pGroupname).c_str());
pArrayElement->SetAttribute("name", buffer);
pArrayElement->SetAttribute("minOccurs", 0);
pAllElement->LinkEndChild(pArrayElement);
// Inline type specification
tinyxml2::XMLElement* pArrayComplexTypeElement = schemaDocument.NewElement("xs:complexType");
pArrayElement->LinkEndChild(pArrayComplexTypeElement);
// Add the actual (repeating) array elements
tinyxml2::XMLElement* pInnerArrayElement = schemaDocument.NewElement("xs:element");
pInnerArrayElement->SetAttribute("name", g_sanitize_schema_element_name(field.pFieldname).c_str());
pInnerArrayElement->SetAttribute("minOccurs", 0);
pInnerArrayElement->SetAttribute("maxOccurs", field.elementCount);
pArrayComplexTypeElement->LinkEndChild(pInnerArrayElement);
// Inline type specification
tinyxml2::XMLElement* pInnerComplexTypeElement = schemaDocument.NewElement("xs:complexType");
pInnerArrayElement->LinkEndChild(pInnerComplexTypeElement);
// Add a reference to the attribute group for the array
tinyxml2::XMLElement* pInnerAttributeGroupElement = schemaDocument.NewElement("xs:attributeGroup");
dSprintf(buffer, sizeof(buffer), "%s_%s_Array_Fields", pType->getClassName(), g_sanitize_schema_element_name(field.pFieldname).c_str());
pInnerAttributeGroupElement->SetAttribute("ref", buffer);
pInnerComplexTypeElement->LinkEndChild(pInnerAttributeGroupElement);
// Add the attribute group itself
tinyxml2::XMLElement* pFieldAttributeGroupElement = schemaDocument.NewElement("xs:attributeGroup");
pFieldAttributeGroupElement->SetAttribute("name", buffer);
pSchemaElement->LinkEndChild(pFieldAttributeGroupElement);
// Keep adding fields to attribute group until we hit the end of the array
for (; index < fieldCount; ++index)
{
const AbstractClassRep::Field& array_field = fields[index];
if (array_field.type == AbstractClassRep::EndArrayFieldType)
{
break;
}
tinyxml2::XMLElement* pAttributeElement = g__write_schema_attribute_element(array_field, pType, schemaDocument);
if (pAttributeElement == NULL)
{
continue;
}
pFieldAttributeGroupElement->LinkEndChild(pAttributeElement);
}
}
}
// Generate field attribute group.
@ -1356,115 +1559,31 @@ ImplementEnumType(_TamlFormatMode,
pFieldAttributeGroupElement->SetAttribute("name", buffer);
pSchemaElement->LinkEndChild(pFieldAttributeGroupElement);
// Fetch field list.
const AbstractClassRep::FieldList& fields = pType->mFieldList;
// Fetcj field count.
const S32 fieldCount = fields.size();
// Iterate static fields (in reverse as most types are organized from the root-fields up).
for (S32 index = fieldCount - 1; index > 0; --index)
{
// Fetch field.
const AbstractClassRep::Field& field = fields[index];
// Skip if not a data field.
if (field.type == AbstractClassRep::DeprecatedFieldType ||
field.type == AbstractClassRep::StartGroupFieldType ||
field.type == AbstractClassRep::EndGroupFieldType)
// Skip fields inside arrays
if (field.type == AbstractClassRep::EndArrayFieldType)
{
for (; index > 0; --index)
{
if (field.type == AbstractClassRep::StartArrayFieldType)
{
break;
}
}
continue;
}
// Skip if the field root is not this type.
if (pType->findFieldRoot(field.pFieldname) != pType)
tinyxml2::XMLElement* pAttributeElement = g__write_schema_attribute_element(field, pType, schemaDocument);
if (pAttributeElement == NULL)
{
continue;
// Add attribute element.
tinyxml2::XMLElement* pAttributeElement = schemaDocument.NewElement("xs:attribute");
pAttributeElement->SetAttribute("name", field.pFieldname);
// Handle the console type appropriately.
const S32 fieldType = (S32)field.type;
/*
// Is the field an enumeration?
if ( fieldType == TypeEnum )
{
// Yes, so add attribute type.
tinyxml2::XMLElement* pAttributeSimpleTypeElement = schemaDocument.NewElement( "xs:simpleType" );
pAttributeElement->LinkEndChild( pAttributeSimpleTypeElement );
// Add restriction element.
tinyxml2::XMLElement* pAttributeRestrictionElement = schemaDocument.NewElement( "xs:restriction" );
pAttributeRestrictionElement->SetAttribute( "base", "xs:string" );
pAttributeSimpleTypeElement->LinkEndChild( pAttributeRestrictionElement );
// Yes, so fetch enumeration count.
const S32 enumCount = field.table->size;
// Iterate enumeration.
for( S32 index = 0; index < enumCount; ++index )
{
// Add enumeration element.
tinyxml2::XMLElement* pAttributeEnumerationElement = schemaDocument.NewElement( "xs:enumeration" );
pAttributeEnumerationElement->SetAttribute( "value", field.table->table[index].label );
pAttributeRestrictionElement->LinkEndChild( pAttributeEnumerationElement );
}
}
else
{*/
// No, so assume it's a string type initially.
const char* pFieldTypeDescription = "xs:string";
// Handle known types.
if (fieldType == TypeF32)
{
pFieldTypeDescription = "xs:float";
}
else if (fieldType == TypeS8 || fieldType == TypeS32)
{
pFieldTypeDescription = "xs:int";
}
else if (fieldType == TypeBool || fieldType == TypeFlag)
{
pFieldTypeDescription = "xs:boolean";
}
else if (fieldType == TypePoint2F)
{
pFieldTypeDescription = "Point2F_ConsoleType";
}
else if (fieldType == TypePoint2I)
{
pFieldTypeDescription = "Point2I_ConsoleType";
}
else if (fieldType == TypeRectI)
{
pFieldTypeDescription = "RectI_ConsoleType";
}
else if (fieldType == TypeRectF)
{
pFieldTypeDescription = "RectF_ConsoleType";
}
else if (fieldType == TypeColorF)
{
pFieldTypeDescription = "ColorF_ConsoleType";
}
else if (fieldType == TypeColorI)
{
pFieldTypeDescription = "ColorI_ConsoleType";
}
else if (fieldType == TypeAssetId/* ||
fieldType == TypeImageAssetPtr ||
fieldType == TypeAnimationAssetPtr ||
fieldType == TypeAudioAssetPtr*/)
{
pFieldTypeDescription = "AssetId_ConsoleType";
}
// Set attribute type.
pAttributeElement->SetAttribute("type", pFieldTypeDescription);
//}
pAttributeElement->SetAttribute("use", "optional");
pFieldAttributeGroupElement->LinkEndChild(pAttributeElement);
}

View file

@ -42,7 +42,7 @@ void TamlCustomField::set( const char* pFieldName, const char* pFieldValue )
// Set field name.
mFieldName = StringTable->insert( pFieldName );
#if TORQUE_DEBUG
#ifdef TORQUE_DEBUG
// Is the field value too big?
if ( dStrlen(pFieldValue) >= sizeof(mFieldValue) )
{

View file

@ -640,7 +640,7 @@ public:
private:
inline TamlCustomField* registerField( TamlCustomField* pCustomField )
{
#if TORQUE_DEBUG
#ifdef TORQUE_DEBUG
// Ensure a field name conflict does not exist.
for( Vector<TamlCustomField*>::iterator nodeFieldItr = mFields.begin(); nodeFieldItr != mFields.end(); ++nodeFieldItr )
{
@ -724,7 +724,7 @@ public:
// Set ignore-empty flag.
pCustomNode->setIgnoreEmpty( ignoreEmpty );
#if TORQUE_DEBUG
#ifdef TORQUE_DEBUG
// Ensure a node name conflict does not exist.
for( TamlCustomNodeVector::iterator nodeItr = mNodes.begin(); nodeItr != mNodes.end(); ++nodeItr )
{

View file

@ -256,8 +256,10 @@ DefineEngineFunction(TamlWrite, bool, (SimObject* simObject, const char* filenam
}
else
{
// No, so warn.
Con::warnf( "TamlWrite() - Setting binary compression is only valid for XML formatting." );
#ifdef TORQUE_DEBUG
// No, so warn.
Con::warnf( "TamlWrite() - Setting binary compression is only valid for XML formatting." );
#endif
}
// Turn-off auto-formatting.

View file

@ -293,8 +293,22 @@ void TamlXmlReader::parseAttributes( tinyxml2::XMLElement* pXmlElement, SimObjec
attributeName == tamlNamedObjectName )
continue;
// Set the field.
pSimObject->setPrefixedDataField(attributeName, NULL, pAttribute->Value());
//See if we have any sort of array index
S32 suffixNum;
String trimmedName = String::GetTrailingNumber(attributeName, suffixNum);
if (!trimmedName.equal(attributeName))
{
char arrayIndexStr[32];
dItoa(suffixNum, arrayIndexStr);
// Set the field.
pSimObject->setPrefixedDataField(StringTable->insert(trimmedName.c_str()), arrayIndexStr, pAttribute->Value());
}
else
{
// Set the field.
pSimObject->setPrefixedDataField(attributeName, NULL, pAttribute->Value());
}
}
}