Code archives/File Utilities/BlitzXML
This code has been declared by its author to be Public Domain code.
Download source code
| |||||
| BlitzXML makes it easy to load, manipulate, and save XML files. BlitzXML is a library of functions for manipulating xml data, including a fast (parses roughly twice as fast as Microsoft Internet Explorer's XML viewer) xml parser and saver. XML is widely used anywhere from word-processors to level builders. Download the BlitzXML Documentation, Blitz3D Example Code and media (example.xml) The example code should give you a good idea how XML can be used in game development, world builders, applications, etc. Original BlitzXML Forum Thread | |||||
;============================= BlitzXML =================================
;Copyright (C) 2005 John Judnich
;BlitzXML is an XML (eXtendable Markup Language) function library for
;blitz. You don't even need to have any knowledge of the syntax of XML
;to use BlitzXML, although an understanding of the terms and structure
;is helpful.
;At the user's (programmer's) point of view,
;BlitzXML is a way of storing bits of data similar to the way folders
;are stored on a hard drive. For example an node (item) named "inventory" may contain
;child nodes (sub-items) such as, "key". Each node may have an unlimited
;amount of sub-nodes, and each sub-node may have sub-sub-nodes, etc.
;Each node may have a name, and a number of attributes. Nodes may
;contain both sub-nodes (called children), and text data. To get a
;better idea how xml works, look at "example.xml".
;When a data structure is constructed with BltizXML, it may be saved
;to a file using XML syntax, which is really just a more strict form of
;HTML. XML files may also be loaded and parsed into BlitzXML just as
;easily. Due to the flexibility of XML, this library should be very
;useful for level editors, games that load levels from XML files,
;or any program requiring structured Or
;complex data to be saved to and loaded from a file.
;========================================================================
;**** Constant Declarations =============================================
Const MAX_ATTRIBUTES = 32 ;The maximum number of attributes a xmlNode may have
Const MAX_ERRORS = 64 ;The maximum number of errors and warnings that will be processed until the xml parser aborts the operation
Const PARSER_RECURSE = 1024 ;The maximum number of virtually "recursive" steps for the parser.
Global XML_INDENTATION$ = Chr$(9) ;The character(s) used to indent when saving files
;**** Type Declarations =================================================
;xmlNode Type - This is the main building block of BlitzXML. All xml data
;is stored with this Type, which gets manipulated by the user. The xml data
;can then be loaded from and saved to files.
Type xmlNode
Field Name$ ;The name of the node
Field AttributeCount ;The number of attributes for the node
Field AttributeName$[MAX_ATTRIBUTES] ;The attribute name array
Field AttributeValue$[MAX_ATTRIBUTES] ;The attribute value array
Field Contents$ ;The data contents of the node
Field Parent.xmlNode ;This node's parent node. If this is set to Null, then this node is the "root" node
Field Level ;This is the node's level
;These fields are manipulated by xml_RegisterChild(), xml_GetChild(), and xml_UnregisterChild()
Field ChildCount ;The number of the node's children
Field ChildBank ;A memory bank of handles to the node's children
End Type
;**** Global Declarations ===============================================
Global xml_Error$[MAX_ERRORS]
Global xml_ErrorPos[MAX_ERRORS]
Global xml_ErrorCount
;**** Interface Functions ===============================================
;Interface functions are the functions the user (the programmer) uses
;in their program, unlike the internal functions, which are only called
;by these functions.
;This function returns the level the node is at. If the node is the
;root node, it is at level 0. If it is a child of the root node,
;the level will be 1. If it is a child of a child of the root node,
;the level of 2 will be returned, etc.
Function xmlNodeLevel(Node)
this.xmlNode = Object.xmlNode(Node)
Return this\Level
End Function
;This returns a node's parent node. If the node has no parent (if it's
;the root node), 0 will be returned.
Function xmlNodeParent(Node)
this.xmlNode = Object.xmlNode(Node)
If this\Parent = Null Then Return 0
Return Handle(this\Parent)
End Function
;This returns the number of children the node has. In many cases,
;the node will contain no children, therefore returning 0.
Function xmlNodeChildCount(Node)
this.xmlNode = Object.xmlNode(Node)
Return this\ChildCount
End Function
;This returns one of the node's children, specified by ChildIndex.
;ChildIndex may be set anywhere from 1 to the amount of children
;the node has, which can be obtained from the xmlNodeChildCount()
;function.
Function xmlNodeChild(Node, ChildIndex)
this.xmlNode = Object.xmlNode(Node)
Return Handle(xml_GetChild(this, ChildIndex))
End Function
;This function will search for the first node matching the specified
;name and parent. Specifying a parent (optional) will only search
;nodes that are children of the specified parent node. If you only want
;to find a direct child of this node (not sub-childs), set Recurse to False.
Function xmlNodeFind(Name$, Parent, Recurse = True)
parentnode.xmlNode = Object.xmlNode(Parent)
For this.xmlNode = Each xmlNode
If this\Parent = parentnode Then
If Lower(this\Name) = Lower(Name) Then
Return Handle(this)
End If
If Recurse = True And this\ChildCount > 0 Then
ret = xmlNodeFind(Name, Handle(this), True)
If ret <> 0 Then Return ret
End If
End If
Next
End Function
;This function adds a new node to the "tree" of existing xml nodes. Set
;ParentNode to the node you would like this to be a child of, or set it
;to 0 if this is the "root" node. Note: only one root node is allowed.
;Optionally, Name$ can be set to a name the node will initially be given,
;although the node can be renamed later with xmlNodeNameSet()
Function xmlNodeAdd(ParentNode, Name$="NewNode")
this.xmlNode = New xmlNode
parent.xmlNode = Object.xmlNode(ParentNode)
this\Parent = parent
If parent = Null Then
this\Level = 0
Else
top.xmlNode = parent
If parent\ChildCount = 0 Then
top.xmlNode = parent
Else
top.xmlNode = Object.xmlNode( xmlNodeChild(ParentNode, 1) )
End If
Insert this After top
this\Level = parent\Level + 1
xml_RegisterChild(parent, this)
End If
this\Name = Name
Return Handle(this)
End Function
;This function deletes the given node, including all of it's children
;(sub-nodes), if there are any. Ignore the ChildIndex variable, as it
;is used internally when recursively deleting the node's children.
;This can be used to delete an entire XML file in memory by deleting it's
;handle (root node)
Function xmlNodeDelete(Node, ChildIndex = 0)
this.xmlNode = Object.xmlNode(Node)
For i = 1 To this\ChildCount
xmlNodeDelete(Handle(xml_GetChild(this, 1)), 1) ;The index is always 1 because the list will keep getting smaller while they are getting deleted - (just like holding down the delete key at the beginning of a document)
Next
If this\Parent <> Null Then
If ChildIndex = 0 Then
For i = 1 To this\Parent\ChildCount
If xml_GetChild(this\Parent, i) = this Then ChildIndex = i:Exit
Next
End If
xml_UnregisterChild(this\Parent, ChildIndex)
End If
FreeBank this\ChildBank
Delete this
End Function
;This sets a node's name. Note: A node's name must not be a blank string
Function xmlNodeNameSet(Node, Name$)
this.xmlNode = Object.xmlNode(Node)
this\Name = Name
End Function
;This returns the name of a node
Function xmlNodeNameGet$(Node)
this.xmlNode = Object.xmlNode(Node)
Return this\Name
End Function
;This sets the value of an attribute of a node. If the attribute does
;not exist, it will be created. The attribute's value may be any valid
;string of characters, not including double quotes. The value is allowed
;to be a blank string.
;Example:
;xmlNodeAttributeSet(node, "alpha", "0.7")
Function xmlNodeAttributeValueSet(Node, Attribute$, Value$)
this.xmlNode = Object.xmlNode(Node)
;Check if the attribute exists or not
indx = 0
For i = 1 To this\AttributeCount
If Attribute = this\AttributeName[i] Then indx = i:Exit
Next
;Create a new attribute if it doesn't exist
If indx = 0 Then
this\AttributeCount = this\AttributeCount + 1
this\AttributeName[this\AttributeCount] = Attribute
indx = this\AttributeCount
End If
;Set the attribute's value
this\AttributeValue[indx] = Value
End Function
;This returns the value of the specified attribute, if it exists. If it
;doesn't exist, a blank string will be returned.
;Example:
;EntityAlpha Entity\Mesh, xmlNodeAttributeGet(Entity\Node, "alpha")
Function xmlNodeAttributeValueGet$(Node, Attribute$)
this.xmlNode = Object.xmlNode(Node)
;Find the attribute
indx=0
For i = 1 To this\AttributeCount
If Attribute = this\AttributeName[i] Then indx = i:Exit
Next
;If the attribute exists, return it's value. If not, return a blank string
If indx = 0 Then
Return ""
Else
Return this\AttributeValue[indx]
End If
End Function
;This sets the name of an attribute (NOT it's value). Note: attribute
;names are case sensitive
;Example:
;xmlNodeAttributeNameSet(node,"pitch","Xang")
Function xmlNodeAttributeNameSet(Node, Attribute$, NewName$)
this.xmlNode = Object.xmlNode(Node)
;Find the attribute
indx = 0
For i = 1 To this\AttributeCount
If Attribute = this\AttributeName[i] Then indx = i:Exit
Next
;If the attribute exists, rename it
If indx <> 0 Then
this\AttributeName[indx] = NewName
End If
End Function
;This deletes an attribute. Once a new attribute is created when
;using the xmlNodeAttributeSet() function, it will continue to
;reside in memory, and be saved to a file even if it's value is
;blank. To remove an un-used (or used) attribute of a node, use
;this function.
;Example:
;xmlNodeAttributeDelete(node, "hidden")
Function xmlNodeAttributeDelete(Node, Attribute$)
this.xmlNode = Object.xmlNode(Node)
;Find the attribute
indx = 0
For i = 1 To this\AttributeCount
If Attribute = this\AttributeName[i] Then indx = i:Exit
Next
;Delete the attribute, if it exists
If indx <> 0 Then
this\AttributeName[indx] = this\AttributeName[this\AttributeCount]
this\AttributeValue[indx] = this\AttributeValue[this\AttributeCount]
this\AttributeCount = this\AttributeCount - 1
End If
End Function
;This sets a node's data string. A node's data is a string of
;text contained within the opening and closing node tags.
;Example:
;xmlNodeDataSet(titlenode, "BlitzXML")
Function xmlNodeDataSet(Node, NodeData$)
this.xmlNode = Object.xmlNode(Node)
this\Contents = NodeData
End Function
;This returns a node's data string. A node's data is a string
;of text contained within the opening and closing node tags.
Function xmlNodeDataGet$(Node)
this.xmlNode = Object.xmlNode(Node)
Return this\Contents
End Function
;This function saves all XML nodes to the specified file.
;If any errors occur, false will be returned, if not, true
;will be returned.
Function xmlSave(FileName$, Node)
this.xmlNode = Object.xmlNode(Node)
file = WriteFile(FileName)
If file = 0 Then xml_AddError("Error writing XML file (possibly, file is in use, or is the folder/drive/file is write protected).", 0):Return
WriteLine file, "<?xml version="+Chr(34)+"1.0"+Chr(34)+" ?>"
xml_WriteNode(file, this)
CloseFile file
End Function
Function xml_WriteNode(File, Node.xmlNode)
Local NodeContents$, Indent$, Indent2$
NodeContents = Node\Name
For i = 1 To Node\AttributeCount
NodeContents = NodeContents + " " + Node\AttributeName[i] + "=" + Chr$(34) + Node\AttributeValue[i] + Chr$(34)
Next
Indent = String$(XML_INDENTATION$, Node\Level)
Indent2 = String$(XML_INDENTATION$, Node\Level+1)
If Node\ChildCount = 0 Then
If Node\Contents = "" Then
WriteLine File, Indent + "<" + NodeContents + "/>"
Else
WriteLine File, Indent + "<" + NodeContents + ">" + Node\Contents + "</" + Node\Name + ">"
End If
Else
WriteLine File, Indent + "<" + NodeContents + ">"
If Node\Contents <> "" Then WriteLine File, Indent2 + Node\Contents
For i = 1 To Node\ChildCount
xml_WriteNode(File, Object.xmlNode(xmlNodeChild(Handle(Node), i)))
Next
WriteLine File, Indent + "</" + Node\Name + ">"
End If
End Function
;This function loads and parses XML nodes from the specified XML file.
;Note: This (BlitzXML's xml parser) only supports xml files with standard
;xml tags and attributes with values enclosed in quotes. If the file
;is loaded successfully with no errors, a handle to the root node of the file
;will be returned. If not, 0 will be returned.
;Errors can be accessed using the xmlError$() and xmlErrorCount() functions.
Function xmlLoad(FileName$)
Local attribute$[MAX_ATTRIBUTES]
Local value$[MAX_ATTRIBUTES]
Local nodestack[PARSER_RECURSE]
Local rootnode
DebugLog "Loading XML file: " + FileName
xml_ClearErrors()
begintime = MilliSecs()
;Open the file
file = ReadFile(FileName)
If file = False Then
xml_AddError("Error opening XML file: File does not exist.", 0)
Return 0
End If
If Eof(file) = -1 Then
xml_AddError("Error opening XML file: File is already in use by another program.", 0)
Return 0
End If
;Read in all tags
stacklevel = 0
While Eof(file) = False
;Get the next tag or data section
tag$ = xml_NextItem(file)
If tag$ <> "" Then
If xml_ItemType = 2 Then
;Node contents
xmlNodeDataSet(nodestack[stacklevel - 1], Trim(Trim(xmlNodeDataGet(nodestack[stacklevel - 1])) + " " + Trim(tag)))
Else
;Check if it's a closing tag, opening tag, or stand-alone tag
If Left(tag,1) = "/" Then
;Closing tag
stacklevel = stacklevel - 1
tmp.xmlNode = Object.xmlNode(nodestack[stacklevel])
If tag <> "/" + tmp\Name Then xml_AddError("Unclosed tag (found <"+tag+">, expected </"+tmp\Name+">", FilePos(file))
Else
;Create a new node
If stacklevel > 0 Then parent = nodestack[stacklevel - 1] Else parent = 0
node = xmlNodeAdd(parent)
If stacklevel = 0 Then rootnode = node
;Get the name and attributes from the tag
For i = 0 To attr:attribute[i] = "":value[i] = "":Next:attr = 0:opened = False:name$ = ""
length = Len(tag)
For i = 1 To length
ch$ = Mid(tag, i, 1)
If attr = 0 And ch = " " Then attr = attr + 1
If ch = "=" Then attr = -attr
If ch = Chr(34) Then
If attr > 0 Then xml_AddError("Expecting equals symbol", FilePos(file))
opened = 1 - opened
If opened = False Then attr = Abs(attr):attr = attr + 1
End If
If ch <> Chr(34) And attr < 0 And opened Then value[-attr] = value[-attr] + ch
If attr = 0 Then
name = name + ch
Else
If attr > 0 And ch <> Chr(34) And ch<>" " Then attribute[attr] = attribute[attr] + ch
End If
Next
For i = 1 To attr-1
xmlNodeAttributeValueSet(node, attribute[i], value[i])
Next
xmlNodeNameSet(node, name)
nodestack[stacklevel] = node
If Right(tag,1) = "/" Then
;Stand-alone tag
Else
;Opening tag
stacklevel = stacklevel + 1
End If
End If
End If
End If
Wend
CloseFile file
endtime = MilliSecs()
parsetime# = (endtime - begintime) / 1000.0
If xmlErrorCount > 0 Then DebugLog "Parse failed" Else DebugLog "Parse completed in "+parsetime+" seconds."
If xmlErrorCount > 0 Then Return 0 Else Return rootnode
End Function
;This function returns the number of errors and warnings from the last
;file parse performed.
Function xmlErrorCount()
Return xml_ErrorCount
End Function
;This returns the position of the specified error (in characters from
;the beginning of the file
Function xmlErrorPosition(ErrorNumber)
If ErrorNumber > xml_ErrorCount Then Return 0
Return xml_ErrorPos[ErrorNumber]
End Function
;This returns the description of the requested error.
Function xmlError$(ErrorNumber)
If ErrorNumber > xml_ErrorCount Then Return ""
Return xml_Error[ErrorNumber]
End Function
;**** Internal Functions ================================================
;Internal functions should not be called from ANYWHERE but from other
;BlitzXML functions. These functions are undocumented, and you should
;NOT use them.
Global xml_ItemType
Function xml_NextItem$(file)
Local tag$
While Eof(file) = False
ch = ReadByte(file)
If txt$ <> "" And (ch = 60 Or ch = 13) Then xml_ItemType = 2:SeekFile file,FilePos(file)-1:Return txt
If ch <> 13 And ch <> 15 And ch <> 10 Then txt$ = txt$ + Chr(ch)
If ch = 13 And txt <> "" Then txt = txt + " "
If ch = 60 Then ;<
If opened = True Then xml_AddError("Expecting closing bracket (>)", FilePos(file))
opened = True
End If
If ch = 62 Then ;>
txt = ""
If opened = False Then xml_AddError("Expecting opening bracket (<)", FilePos(file))
opened = False
If Left(tag,4) = "<!--" Or Left(tag,2) = "<?" Then
If Left(tag,4) = "<!--" And Right(tag,2) <> "--" Then xml_AddError("Expecting correct comment closure (-->)", FilePos(file))
If Left(tag,4) = "<?" And Right(tag,2) <> "?" Then xml_AddError("Expecting correct header closure (?>)", FilePos(file))
tag = ""
Else
xml_ItemType = 1
Return Right(tag,Len(tag)-1)
End If
End If
If opened Then tag = tag + Chr(ch)
Wend
End Function
Function xml_RegisterChild(Node.xmlNode, Child.xmlNode)
;Incriment the child count
Node\ChildCount = Node\ChildCount + 1
;Allocate memory for the data
If Node\ChildBank = False Then
Node\ChildBank = CreateBank(4)
Else
ResizeBank Node\ChildBank, Node\ChildCount * 4
End If
;Write the data
Value = Handle(Child)
PokeInt Node\ChildBank, (Node\ChildCount - 1) * 4, Value
End Function
Function xml_GetChild.xmlNode(Node.xmlNode, ChildIndex)
;Check if the ChildIndex is valid
If ChildIndex > Node\ChildCount Then Return Null
;Get the child xmlNode object and return it
Value = PeekInt(Node\ChildBank, (ChildIndex - 1) * 4)
this.xmlNode = Object.xmlNode(Value)
Return this
End Function
Function xml_UnregisterChild(Node.xmlNode, ChildIndex)
;Check if the ChildIndex is valid
If ChildIndex > Node\ChildCount Then Return False
;"Swap" the child-to-be-deleted with the last child on the list, so the last child on the list is now the child to be deleted
;(actually, it doesn't swap - to optimize it a little, the child-to-be-deleted doesn't get copied anywhere because it's not gonna be used)
Value = PeekInt(Node\ChildBank, (Node\ChildCount - 1) * 4)
PokeInt Node\ChildBank, (ChildIndex - 1) * 4, Value
;Downsize the bank, erasing the last child on the list which would be the child-to-be-deleted
ResizeBank Node\ChildBank, (Node\ChildCount - 1) * 4
Node\ChildCount = Node\ChildCount - 1
Return True
End Function
Function xml_ClearErrors()
xml_ErrorCount = 0
End Function
Function xml_AddError(Description$, pos)
xml_ErrorCount = xml_ErrorCound + 1
xml_ErrorPos[xml_ErrorCount] = pos
xml_Error[xml_ErrorCount] = Description
DebugLog "Error at char #"+pos+": "+Description
End Function |
Comments
| ||
| All looks great and I can see you to load an XML file. How about a sample of how to save an XML file? |
| ||
| Hey John J, This is absolutely awesome. Thanks for sharing such a handy set of functions! Cold Harbour -- I just worked up a little piece that saves an XML file out. Do you want me to post it? I actually found that saving the XML was much simpler than loading it-- Just make a bunch of nodes using XMLNodeAdd, then call XMLSave(filename$) and you're done! best, roland |
| ||
| Really great stuff! Using this alot in the project i'm working on! |
| ||
| Thanks! Great work. |
| ||
| Note: This code archive item has been out of date for a while (this is now up-to-date with the latest version). Please re-download now to get the latest version (with bug-fixes, extra features, etc.) |
| ||
| Roland, thanks the penny's dropped now. Thanks for a great library John J. |
| ||
The results were not what I expected for the material "logs" :- <mesh> - <submeshes> - <submesh material="Logs" usesharedvertices="false" use32bitindexes="false"> - <faces count="10"> <face v1="0" v2="1" v3="2" /> <face v1="1" v2="0" v3="3" /> <face v1="3" v2="0" v3="4" /> <face v1="5" v2="6" v3="7" /> <face v1="6" v2="5" v3="8" /> <face v1="9" v2="10" v3="11" /> <face v1="10" v2="9" v3="12" /> <face v1="12" v2="9" v3="13" /> <face v1="14" v2="15" v3="16" /> <face v1="15" v2="14" v3="17" /> </faces> - <geometry vertexcount="18"> - <vertexbuffer positions="true" normals="true" colours_diffuse="false" texture_coords="1" texture_coord_dimensions_0="2"> - <vertex> <position x="3.34101563592847" y="2.13995" z="-11.7980971035329" /> <normal x="0.0" y="0.0" z="-1.0" /> <texcoord u="-3.65377912940559" v="-1.34027777777778" /> </vertex> - <vertex> <position x="10.2053656359285" y="0.0" z="-11.7980971035329" /> <normal x="0.0" y="0.0" z="-1.0" /> <texcoord u="-11.16072357385" v="1.0" /> </vertex> - <vertex> <position x="3.34101563592847" y="0.0" z="-11.7980971035329" /> <normal x="0.0" y="0.0" z="-1.0" /> <texcoord u="-3.65377912940559" v="1.0" /> </vertex> - <vertex> <position x="10.2053656359285" y="2.13995" z="-11.7980971035329" /> <normal x="0.0" y="0.0" z="-1.0" /> <texcoord u="-11.16072357385" v="-1.34027777777778" /> </vertex> - <vertex> <position x="6.77319063592847" y="4.07035" z="-11.7980971035329" /> <normal x="0.0" y="0.0" z="-1.0" /> <texcoord u="-7.40725135162781" v="-3.45138888888889" /> </vertex> - <vertex> <position x="10.2053656359285" y="0.0" z="-11.7980971035329" /> <normal x="1.0" y="0.0" z="0.0" /> <texcoord u="12.9025558875031" v="1.0" /> </vertex> - <vertex> <position x="10.2053656359285" y="2.13995" z="-2.33024710353288" /> <normal x="1.0" y="0.0" z="0.0" /> <texcoord u="2.54838922083648" v="-1.34027777777778" /> </vertex> - <vertex> <position x="10.2053656359285" y="0.0" z="-2.33024710353288" /> <normal x="1.0" y="0.0" z="0.0" /> <texcoord u="2.54838922083648" v="1.0" /> </vertex> - <vertex> <position x="10.2053656359285" y="2.13995" z="-11.7980971035329" /> <normal x="1.0" y="0.0" z="0.0" /> <texcoord u="12.9025558875031" v="-1.34027777777778" /> </vertex> - <vertex> <position x="10.2053656359285" y="2.13995" z="-2.33024710353288" /> <normal x="0.0" y="0.0" z="1.0" /> <texcoord u="11.16072357385" v="-1.34027777777778" /> </vertex> - <vertex> <position x="3.34101563592847" y="0.0" z="-2.33024710353288" /> <normal x="0.0" y="0.0" z="1.0" /> <texcoord u="3.65377912940559" v="1.0" /> </vertex> - <vertex> <position x="10.2053656359285" y="0.0" z="-2.33024710353288" /> <normal x="0.0" y="0.0" z="1.0" /> <texcoord u="11.16072357385" v="1.0" /> </vertex> - <vertex> <position x="3.34101563592847" y="2.13995" z="-2.33024710353288" /> <normal x="0.0" y="0.0" z="1.0" /> <texcoord u="3.65377912940559" v="-1.34027777777778" /> </vertex> - <vertex> <position x="6.77319063592847" y="4.07035" z="-2.33024710353288" /> <normal x="0.0" y="0.0" z="1.0" /> <texcoord u="7.40725135162781" v="-3.45138888888889" /> </vertex> - <vertex> <position x="3.34101563592847" y="2.13995" z="-11.7980971035329" /> <normal x="-1.0" y="0.0" z="0.0" /> <texcoord u="-12.9025558875031" v="-1.34027777777778" /> </vertex> - <vertex> <position x="3.34101563592847" y="0.0" z="-2.33024710353288" /> <normal x="-1.0" y="0.0" z="0.0" /> <texcoord u="-2.54838922083648" v="1.0" /> </vertex> - <vertex> <position x="3.34101563592847" y="2.13995" z="-2.33024710353288" /> <normal x="-1.0" y="0.0" z="0.0" /> <texcoord u="-2.54838922083648" v="-1.34027777777778" /> </vertex> - <vertex> <position x="3.34101563592847" y="0.0" z="-11.7980971035329" /> <normal x="-1.0" y="0.0" z="0.0" /> <texcoord u="-12.9025558875031" v="1.0" /> </vertex> </vertexbuffer> </geometry> </submesh> - <submesh material="Shingles-Asphalt01" usesharedvertices="false" use32bitindexes="false"> - <faces count="4"> <face v1="0" v2="1" v3="2" /> <face v1="1" v2="0" v3="3" /> <face v1="4" v2="5" v3="6" /> <face v1="5" v2="4" v3="7" /> </faces> - <geometry vertexcount="8"> - <vertexbuffer positions="true" normals="true" colours_diffuse="false" texture_coords="1" texture_coord_dimensions_0="2"> - <vertex> <position x="10.2053656359285" y="2.13995" z="-2.33024710353288" /> <normal x="0.490222958435144" y="0.871597069191433" z="0.0" /> <texcoord u="4.58710059750566" v="16.4447129102341" /> </vertex> - <vertex> <position x="6.77319063592847" y="4.07035" z="-11.7980971035329" /> <normal x="0.490222958435144" y="0.871597069191433" z="0.0" /> <texcoord u="23.2246005975057" v="8.69313796945595" /> </vertex> - <vertex> <position x="6.77319063592847" y="4.07035" z="-2.33024710353288" /> <normal x="0.490222958435144" y="0.871597069191433" z="0.0" /> <texcoord u="4.58710059750566" v="8.69313796945595" /> </vertex> - <vertex> <position x="10.2053656359285" y="2.13995" z="-11.7980971035329" /> <normal x="0.490222958435144" y="0.871597069191433" z="0.0" /> <texcoord u="23.2246005975057" v="16.4447129102341" /> </vertex> - <vertex> <position x="6.77319063592847" y="4.07035" z="-2.33024710353288" /> <normal x="-0.490222958435144" y="0.871597069191433" z="0.0" /> <texcoord u="-4.58710059750566" v="-14.5489608783791" /> </vertex> - <vertex> <position x="3.34101563592847" y="2.13995" z="-11.7980971035329" /> <normal x="-0.490222958435144" y="0.871597069191433" z="0.0" /> <texcoord u="-23.2246005975057" v="-6.79738593760097" /> </vertex> - <vertex> <position x="3.34101563592847" y="2.13995" z="-2.33024710353288" /> <normal x="-0.490222958435144" y="0.871597069191433" z="0.0" /> <texcoord u="-4.58710059750566" v="-6.79738593760097" /> </vertex> - <vertex> <position x="6.77319063592847" y="4.07035" z="-11.7980971035329" /> <normal x="-0.490222958435144" y="0.871597069191433" z="0.0" /> <texcoord u="-23.2246005975057" v="-14.5489608783791" /> </vertex> </vertexbuffer> </geometry> </submesh> - <submesh material="Concrete" usesharedvertices="false" use32bitindexes="false"> - <faces count="2"> <face v1="0" v2="1" v3="2" /> <face v1="1" v2="0" v3="3" /> </faces> - <geometry vertexcount="4"> - <vertexbuffer positions="true" normals="true" colours_diffuse="false" texture_coords="1" texture_coord_dimensions_0="2"> - <vertex> <position x="10.2053656359285" y="0.0" z="-11.7980971035329" /> <normal x="0.0" y="-1.0" z="0.0" /> <texcoord u="-8.37054268038753" v="-8.67691691562736" /> </vertex> - <vertex> <position x="3.34101563592847" y="0.0" z="-2.33024710353288" /> <normal x="0.0" y="-1.0" z="0.0" /> <texcoord u="-2.74033434705419" v="-0.911291915627359" /> </vertex> - <vertex> <position x="3.34101563592847" y="0.0" z="-11.7980971035329" /> <normal x="0.0" y="-1.0" z="0.0" /> <texcoord u="-2.74033434705419" v="-8.67691691562736" /> </vertex> - <vertex> <position x="10.2053656359285" y="0.0" z="-2.33024710353288" /> <normal x="0.0" y="-1.0" z="0.0" /> <texcoord u="-8.37054268038753" v="-0.911291915627359" /> </vertex> </vertexbuffer> </geometry> </submesh> </submeshes> </mesh> Results after processing: Loading XML file: f:\ogre\media\models\house1.mesh.xml Parse completed in 0.297 seconds. 1 Name=mesh Name=submeshes Name=submesh material,Logs usesharedvertices,false use32bitindexes,false Name=submesh material,Concrete usesharedvertices,false use32bitindexes,false Name=faces count,2 Name=geometry vertexcount,4 Name=vertexbuffer positions,true normals,true colours_diffuse,false texture_coords,1 texture_coord_dimensions_0,2 Name=vertex Name=vertex Name=position x,10.2053656359285 y,0.0 z,-2.33024710353288 Name=texcoord u,-8.37054268038753 v,-0.911291915627359 Name=normal x,0.0 y,-1.0 z,0.0 Name=vertex Name=position x,3.34101563592847 y,0.0 z,-11.7980971035329 Name=texcoord u,-2.74033434705419 v,-8.67691691562736 Name=normal x,0.0 y,-1.0 z,0.0 Name=vertex Name=position x,3.34101563592847 y,0.0 z,-2.33024710353288 Name=texcoord u,-2.74033434705419 v,-0.911291915627359 Name=normal x,0.0 y,-1.0 z,0.0 Name=position x,10.2053656359285 y,0.0 z,-11.7980971035329 Name=texcoord u,-8.37054268038753 v,-8.67691691562736 Name=normal x,0.0 y,-1.0 z,0.0 Name=face v1,0 v2,1 v3,2 Name=face v1,1 v2,0 v3,3 Name=submesh material,Shingles-Asphalt01 usesharedvertices,false use32bitindexes,false Name=faces count,4 Name=geometry vertexcount,8 Name=vertexbuffer positions,true normals,true colours_diffuse,false texture_coords,1 texture_coord_dimensions_0,2 Name=vertex Name=vertex Name=position x,6.77319063592847 y,4.07035 z,-11.7980971035329 Name=texcoord u,-23.2246005975057 v,-14.5489608783791 Name=normal x,-0.490222958435144 y,0.871597069191433 z,0.0 Name=vertex Name=position x,3.34101563592847 y,2.13995 z,-2.33024710353288 Name=texcoord u,-4.58710059750566 v,-6.79738593760097 Name=normal x,-0.490222958435144 y,0.871597069191433 z,0.0 Name=vertex Name=position x,3.34101563592847 y,2.13995 z,-11.7980971035329 Name=texcoord u,-23.2246005975057 v,-6.79738593760097 Name=normal x,-0.490222958435144 y,0.871597069191433 z,0.0 Name=vertex Name=position x,6.77319063592847 y,4.07035 z,-2.33024710353288 Name=texcoord u,-4.58710059750566 v,-14.5489608783791 Name=normal x,-0.490222958435144 y,0.871597069191433 z,0.0 Name=vertex Name=position x,10.2053656359285 y,2.13995 z,-11.7980971035329 Name=texcoord u,23.2246005975057 v,16.4447129102341 Name=normal x,0.490222958435144 y,0.871597069191433 z,0.0 Name=vertex Name=position x,6.77319063592847 y,4.07035 z,-2.33024710353288 Name=texcoord u,4.58710059750566 v,8.69313796945595 Name=normal x,0.490222958435144 y,0.871597069191433 z,0.0 Name=vertex Name=position x,6.77319063592847 y,4.07035 z,-11.7980971035329 Name=texcoord u,23.2246005975057 v,8.69313796945595 Name=normal x,0.490222958435144 y,0.871597069191433 z,0.0 Name=position x,10.2053656359285 y,2.13995 z,-2.33024710353288 Name=texcoord u,4.58710059750566 v,16.4447129102341 Name=normal x,0.490222958435144 y,0.871597069191433 z,0.0 Name=face v1,0 v2,1 v3,2 Name=face v1,5 v2,4 v3,7 Name=face v1,4 v2,5 v3,6 Name=face v1,1 v2,0 v3,3 Name=faces count,10 Name=geometry vertexcount,18 Name=vertexbuffer positions,true normals,true colours_diffuse,false texture_coords,1 texture_coord_dimensions_0,2 Name=vertex Name=vertex Name=position x,3.34101563592847 y,0.0 z,-11.7980971035329 Name=texcoord u,-12.9025558875031 v,1.0 Name=normal x,-1.0 y,0.0 z,0.0 Name=vertex Name=position x,3.34101563592847 y,2.13995 z,-2.33024710353288 Name=texcoord u,-2.54838922083648 v,-1.34027777777778 Name=normal x,-1.0 y,0.0 z,0.0 Name=vertex Name=position x,3.34101563592847 y,0.0 z,-2.33024710353288 Name=texcoord u,-2.54838922083648 v,1.0 Name=normal x,-1.0 y,0.0 z,0.0 Name=vertex Name=position x,3.34101563592847 y,2.13995 z,-11.7980971035329 Name=texcoord u,-12.9025558875031 v,-1.34027777777778 Name=normal x,-1.0 y,0.0 z,0.0 Name=vertex Name=position x,6.77319063592847 y,4.07035 z,-2.33024710353288 Name=texcoord u,7.40725135162781 v,-3.45138888888889 Name=normal x,0.0 y,0.0 z,1.0 Name=vertex Name=position x,3.34101563592847 y,2.13995 z,-2.33024710353288 Name=texcoord u,3.65377912940559 v,-1.34027777777778 Name=normal x,0.0 y,0.0 z,1.0 Name=vertex Name=position x,10.2053656359285 y,0.0 z,-2.33024710353288 Name=texcoord u,11.16072357385 v,1.0 Name=normal x,0.0 y,0.0 z,1.0 Name=vertex Name=position x,3.34101563592847 y,0.0 z,-2.33024710353288 Name=texcoord u,3.65377912940559 v,1.0 Name=normal x,0.0 y,0.0 z,1.0 Name=vertex Name=position x,10.2053656359285 y,2.13995 z,-2.33024710353288 Name=texcoord u,11.16072357385 v,-1.34027777777778 Name=normal x,0.0 y,0.0 z,1.0 Name=vertex Name=position x,10.2053656359285 y,2.13995 z,-11.7980971035329 Name=texcoord u,12.9025558875031 v,-1.34027777777778 Name=normal x,1.0 y,0.0 z,0.0 Name=vertex Name=position x,10.2053656359285 y,0.0 z,-2.33024710353288 Name=texcoord u,2.54838922083648 v,1.0 Name=normal x,1.0 y,0.0 z,0.0 Name=vertex Name=position x,10.2053656359285 y,2.13995 z,-2.33024710353288 Name=texcoord u,2.54838922083648 v,-1.34027777777778 Name=normal x,1.0 y,0.0 z,0.0 Name=vertex Name=position x,10.2053656359285 y,0.0 z,-11.7980971035329 Name=texcoord u,12.9025558875031 v,1.0 Name=normal x,1.0 y,0.0 z,0.0 Name=vertex Name=position x,6.77319063592847 y,4.07035 z,-11.7980971035329 Name=texcoord u,-7.40725135162781 v,-3.45138888888889 Name=normal x,0.0 y,0.0 z,-1.0 Name=vertex Name=position x,10.2053656359285 y,2.13995 z,-11.7980971035329 Name=texcoord u,-11.16072357385 v,-1.34027777777778 Name=normal x,0.0 y,0.0 z,-1.0 Name=vertex Name=position x,3.34101563592847 y,0.0 z,-11.7980971035329 Name=texcoord u,-3.65377912940559 v,1.0 Name=normal x,0.0 y,0.0 z,-1.0 Name=vertex Name=position x,10.2053656359285 y,0.0 z,-11.7980971035329 Name=texcoord u,-11.16072357385 v,1.0 Name=normal x,0.0 y,0.0 z,-1.0 Name=position x,3.34101563592847 y,2.13995 z,-11.7980971035329 Name=texcoord u,-3.65377912940559 v,-1.34027777777778 Name=normal x,0.0 y,0.0 z,-1.0 Name=face v1,0 v2,1 v3,2 Name=face v1,15 v2,14 v3,17 Name=face v1,14 v2,15 v3,16 Name=face v1,12 v2,9 v3,13 Name=face v1,10 v2,9 v3,12 Name=face v1,9 v2,10 v3,11 Name=face v1,6 v2,5 v3,8 Name=face v1,5 v2,6 v3,7 Name=face v1,3 v2,0 v3,4 Name=face v1,1 v2,0 v3,3 |
| ||
| (Crossposted from showcase thread) Hate to bump this thread but: A: BlitzXML is sufficiently wonderful to merit it anyway, and B: I found a bug: Self-closing tags without attributes seem to add a spare "/" with each save. As in <players////>. When reloaded, it then keeps all but the last slash as part of the node\Name$ string, which generally makes it unusable. Here's my simple fix:
xmlRootNode=xmlLoad("settings.xml")
;This next loop is due to a bug in BlitzXML which passes the closing / in a tag as part of the name itself. We remove it here.
For n.xmlNode=Each xmlNode
name$=n\name$
While Right$(name$,1)="/"
name$=Left$(name$,Len(name$)-1)
Wend
n\name$=name$
Next
Thanks John J. for a great library. I'm just getting into config files and this is definitely the way to go. This website sorely needs a recommended solutions area, or rankings or something so folks don't have to browse everything to find gems like this. |
| ||
little bug on error countFunction xml_AddError(Description$, pos) xml_ErrorCount = xml_ErrorCount + 1 xml_ErrorCount instead of xml_ErrorCound Nice job whatever ! [edit] an other error in the function "xml_NextItem" 1/ If txt$ <> "" And (ch = 60 Or ch = 13) Then [...]:Return txt 2/ If ch <> 13 And ch <> 15 And ch <> 10 Then txt$ = txt$ + Chr(ch) 3/ If ch = 13 And txt <> "" Then txt = txt + " " The last condition will never happen other error in the same function : If Left(tag,4) = "<?" And Right(tag,2) <> "?" Then [...] length of words does not match conditions |
| ||
| I was having some problems with it bombing out if a tag was empty, like if you set in their example.xml - <author/> The contents should be blank, but instead they just don't exist - and Blitz3d would bomb out. Fixed with: ;This returns a node's data string. A node's data is a string ;of text contained within the opening and closing node tags. Function xmlNodeDataGet$(Node) this.xmlNode = Object.xmlNode(Node) If this.xmlNode <> Null Then Return this\Contents Else Return "" EndIf End Function I was also having problems writing out empty tags, as if the contents were ending in "/", so to address that: Function xml_WriteNode(File, Node.xmlNode) Local NodeContents$, Indent$, Indent2$ NodeContents = Node\Name For i = 1 To Node\AttributeCount NodeContents = NodeContents + " " + Node\AttributeName[i] + "=" + Chr$(34) + Node\AttributeValue[i] + Chr$(34) Next Indent = String$(XML_INDENTATION$, Node\Level) Indent2 = String$(XML_INDENTATION$, Node\Level+1) If Node\ChildCount = 0 Then If Node\Contents = "" Then If (Right(NodeContents,1) <> "/") Then WriteLine File, Indent + "<" + NodeContents + "/>" Else WriteLine File, Indent + "<" + NodeContents + ">" EndIf Else WriteLine File, Indent + "<" + NodeContents + ">" + Node\Contents + "</" + Node\Name + ">" End If Else WriteLine File, Indent + "<" + NodeContents + ">" If Node\Contents <> "" Then WriteLine File, Indent2 + Node\Contents For i = 1 To Node\ChildCount xml_WriteNode(File, Object.xmlNode(xmlNodeChild(Handle(Node), i))) Next WriteLine File, Indent + "</" + Node\Name + ">" End If End Function |
| ||
I also editted the xmlNodeAttributeValueGet$() function so that if I had a value like <tag value="<test>"> and I needed to escape the less than and greater than symbols, I can use <tag value="<test>"> and translate those back to < / > on the fly back to whoever called the function, so it doesn't error out trying to match up < with < inside quotes.Function xmlNodeAttributeValueGet$(Node, Attribute$) this.xmlNode = Object.xmlNode(Node) ;Find the attribute indx=0 For i = 1 To this\AttributeCount If Attribute = this\AttributeName[i] Then indx = i:Exit Next ;If the attribute exists, return it's value. If not, return a blank string If indx = 0 Then Return "" Else ; old code - return the value directly ;Return this\AttributeValue[indx] ; new code - substituate < to < and > to > Return Replace$(Replace$(this\AttributeValue[indx], "<", "<"), ">", ">") End If End Function |
| ||
| Another preserved Library thanks to WaybackMachine! =D http://web.archive.org/web/20070111180625/http://www.alsbonsai.com/john/BlitzXML_v1.71.zip |
Code Archives Forum