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