November 25, 2020, 05:53:37 AM

Author Topic: [bb] BlitzXML by John J. [ 1+ years ago ]  (Read 562 times)

Offline BlitzBot

  • Jr. Member
  • **
  • Posts: 1
[bb] BlitzXML by John J. [ 1+ years ago ]
« on: June 29, 2017, 12:28:39 AM »
Title : BlitzXML
Author : John J.
Posted : 1+ years ago

Description : 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.

<a href="http://www.alsbonsai.com/john/BlitzXML_v1.71.zip" target="_blank">Download the BlitzXML Documentation, Blitz3D Example Code and media (example.xml)[/url]
The example code should give you a good idea how XML can be used in game development, world builders, applications, etc.

<a href="../Community/posts9939.html?topic=46647#518787" target="_blank">Original BlitzXML Forum Thread[/url]


Code :
Code: BlitzBasic
  1. ;============================= BlitzXML =================================
  2. ;Copyright (C) 2005 John Judnich
  3.  
  4. ;BlitzXML is an XML (eXtendable Markup Language) function library for
  5. ;blitz. You don't even need to have any knowledge of the syntax of XML
  6. ;to use BlitzXML, although an understanding of the terms and structure
  7. ;is helpful.
  8.  
  9. ;At the user's (programmer's) point of view,
  10. ;BlitzXML is a way of storing bits of data similar to the way folders
  11. ;are stored on a hard drive. For example an node (item) named "inventory" may contain
  12. ;child nodes (sub-items) such as, "key". Each node may have an unlimited
  13. ;amount of sub-nodes, and each sub-node may have sub-sub-nodes, etc.
  14. ;Each node may have a name, and a number of attributes. Nodes may
  15. ;contain both sub-nodes (called children), and text data. To get a
  16. ;better idea how xml works, look at "example.xml".
  17.  
  18. ;When a data structure is constructed with BltizXML, it may be saved
  19. ;to a file using XML syntax, which is really just a more strict form of
  20. ;HTML. XML files may also be loaded and parsed into BlitzXML just as
  21. ;easily. Due to the flexibility of XML, this library should be very
  22. ;useful for level editors, games that load levels from XML files,
  23. ;or any program requiring structured Or
  24. ;complex data to be saved to and loaded from a file.
  25.  
  26. ;========================================================================
  27.  
  28.  
  29.  
  30. ;**** Constant Declarations =============================================
  31.  
  32. Const MAX_ATTRIBUTES = 32       ;The maximum number of attributes a xmlNode may have
  33. Const MAX_ERRORS = 64           ;The maximum number of errors and warnings that will be processed until the xml parser aborts the operation
  34. Const PARSER_RECURSE = 1024 ;The maximum number of virtually "recursive" steps for the parser.
  35. Global XML_INDENTATION$ = Chr$(9)       ;The character(s) used to indent when saving files
  36.  
  37. ;**** Type Declarations =================================================
  38.  
  39. ;xmlNode Type - This is the main building block of BlitzXML. All xml data
  40. ;is stored with this Type, which gets manipulated by the user. The xml data
  41. ;can then be loaded from and saved to files.
  42. Type xmlNode
  43.         Field Name$                                                             ;The name of the node
  44.         Field AttributeCount                                    ;The number of attributes for the node
  45.         Field AttributeName$[MAX_ATTRIBUTES]    ;The attribute name array
  46.         Field AttributeValue$[MAX_ATTRIBUTES]   ;The attribute value array
  47.         Field Contents$                                                 ;The data contents of the node
  48.         Field Parent.xmlNode                                    ;This node's parent node. If this is set to Null, then this node is the "root" node
  49.         Field Level                                                             ;This is the node's level
  50.        
  51.         ;These fields are manipulated by xml_RegisterChild(), xml_GetChild(), and xml_UnregisterChild()
  52.         Field ChildCount                                                ;The number of the node's children
  53.         Field ChildBank                                                 ;A memory bank of handles to the node's children
  54. End Type
  55.  
  56.  
  57. ;**** Global Declarations ===============================================
  58.  
  59. Global xml_Error$[MAX_ERRORS]
  60. Global xml_ErrorPos[MAX_ERRORS]
  61. Global xml_ErrorCount
  62.  
  63.  
  64. ;**** Interface Functions ===============================================
  65. ;Interface functions are the functions the user (the programmer) uses
  66. ;in their program, unlike the internal functions, which are only called
  67. ;by these functions.
  68.  
  69. ;This function returns the level the node is at. If the node is the
  70. ;root node, it is at level 0. If it is a child of the root node,
  71. ;the level will be 1. If it is a child of a child of the root node,
  72. ;the level of 2 will be returned, etc.
  73. Function xmlNodeLevel(Node)
  74.         this.xmlNode = Object.xmlNode(Node)
  75.         Return thisLevel
  76. End Function
  77.  
  78. ;This returns a node's parent node. If the node has no parent (if it's
  79. ;the root node), 0 will be returned.
  80. Function xmlNodeParent(Node)
  81.         this.xmlNode = Object.xmlNode(Node)
  82.         If thisParent = Null Then Return 0
  83.         Return Handle(thisParent)
  84. End Function
  85.  
  86. ;This returns the number of children the node has. In many cases,
  87. ;the node will contain no children, therefore returning 0.
  88. Function xmlNodeChildCount(Node)
  89.         this.xmlNode = Object.xmlNode(Node)
  90.         Return thisChildCount
  91. End Function
  92.  
  93. ;This returns one of the node's children, specified by ChildIndex.
  94. ;ChildIndex may be set anywhere from 1 to the amount of children
  95. ;the node has, which can be obtained from the xmlNodeChildCount()
  96. ;function.
  97. Function xmlNodeChild(Node, ChildIndex)
  98.         this.xmlNode = Object.xmlNode(Node)
  99.         Return Handle(xml_GetChild(this, ChildIndex))
  100. End Function
  101.  
  102. ;This function will search for the first node matching the specified
  103. ;name and parent. Specifying a parent (optional) will only search
  104. ;nodes that are children of the specified parent node. If you only want
  105. ;to find a direct child of this node (not sub-childs), set Recurse to False.
  106. Function xmlNodeFind(Name$, Parent, Recurse = True)
  107.         parentnode.xmlNode = Object.xmlNode(Parent)
  108.         For this.xmlNode = Each xmlNode
  109.                 If thisParent = parentnode Then
  110.                         If Lower(thisName) = Lower(Name) Then
  111.                                 Return Handle(this)
  112.                         End If
  113.                         If Recurse = True And thisChildCount > 0 Then
  114.                                 ret = xmlNodeFind(Name, Handle(this), True)
  115.                                 If ret <> 0 Then Return ret
  116.                         End If
  117.                 End If
  118.         Next
  119. End Function
  120.  
  121. ;This function adds a new node to the "tree" of existing xml nodes. Set
  122. ;ParentNode to the node you would like this to be a child of, or set it
  123. ;to 0 if this is the "root" node. Note: only one root node is allowed.
  124. ;Optionally, Name$ can be set to a name the node will initially be given,
  125. ;although the node can be renamed later with xmlNodeNameSet()
  126. Function xmlNodeAdd(ParentNode, Name$="NewNode")
  127.         this.xmlNode = New xmlNode
  128.         parent.xmlNode = Object.xmlNode(ParentNode)
  129.        
  130.         thisParent = parent
  131.         If parent = Null Then
  132.                 thisLevel = 0
  133.         Else
  134.                 top.xmlNode = parent
  135.                 If parentChildCount = 0 Then
  136.                         top.xmlNode = parent
  137.                 Else
  138.                         top.xmlNode = Object.xmlNode( xmlNodeChild(ParentNode, 1) )
  139.                 End If
  140.                 Insert this After top
  141.                 thisLevel = parentLevel + 1
  142.                 xml_RegisterChild(parent, this)
  143.         End If
  144.        
  145.         thisName = Name
  146.         Return Handle(this)
  147. End Function
  148.  
  149. ;This function deletes the given node, including all of it's children
  150. ;(sub-nodes), if there are any. Ignore the ChildIndex variable, as it
  151. ;is used internally when recursively deleting the node's children.
  152. ;This can be used to delete an entire XML file in memory by deleting it's
  153. ;handle (root node)
  154. Function xmlNodeDelete(Node, ChildIndex = 0)
  155.         this.xmlNode = Object.xmlNode(Node)
  156.        
  157.         For i = 1 To thisChildCount
  158.                 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)
  159.         Next
  160.        
  161.         If thisParent <> Null Then
  162.                 If ChildIndex = 0 Then
  163.                         For i = 1 To thisParentChildCount
  164.                                 If xml_GetChild(thisParent, i) = this Then ChildIndex = i:Exit
  165.                         Next
  166.                 End If
  167.                 xml_UnregisterChild(thisParent, ChildIndex)
  168.         End If
  169.        
  170.         FreeBank thisChildBank
  171.         Delete this
  172. End Function
  173.  
  174. ;This sets a node's name. Note: A node's name must not be a blank string
  175. Function xmlNodeNameSet(Node, Name$)
  176.         this.xmlNode = Object.xmlNode(Node)
  177.         thisName = Name
  178. End Function
  179.  
  180. ;This returns the name of a node
  181. Function xmlNodeNameGet$(Node)
  182.         this.xmlNode = Object.xmlNode(Node)
  183.         Return thisName
  184. End Function
  185.  
  186. ;This sets the value of an attribute of a node. If the attribute does
  187. ;not exist, it will be created. The attribute's value may be any valid
  188. ;string of characters, not including double quotes. The value is allowed
  189. ;to be a blank string.
  190. ;Example:
  191. ;xmlNodeAttributeSet(node, "alpha", "0.7")
  192. Function xmlNodeAttributeValueSet(Node, Attribute$, Value$)
  193.         this.xmlNode = Object.xmlNode(Node)
  194.        
  195.         ;Check if the attribute exists or not
  196.         indx = 0
  197.         For i = 1 To thisAttributeCount
  198.                 If Attribute = thisAttributeName[i] Then indx = i:Exit
  199.         Next
  200.        
  201.         ;Create a new attribute if it doesn't exist
  202.         If indx = 0 Then
  203.                 thisAttributeCount = thisAttributeCount + 1
  204.                 thisAttributeName[thisAttributeCount] = Attribute
  205.                 indx = thisAttributeCount
  206.         End If
  207.        
  208.         ;Set the attribute's value
  209.         thisAttributeValue[indx] = Value
  210. End Function
  211.  
  212. ;This returns the value of the specified attribute, if it exists. If it
  213. ;doesn't exist, a blank string will be returned.
  214. ;Example:
  215. ;EntityAlpha EntityMesh, xmlNodeAttributeGet(EntityNode, "alpha")
  216. Function xmlNodeAttributeValueGet$(Node, Attribute$)
  217.         this.xmlNode = Object.xmlNode(Node)
  218.        
  219.         ;Find the attribute
  220.         indx=0
  221.         For i = 1 To thisAttributeCount
  222.                 If Attribute = thisAttributeName[i] Then indx = i:Exit
  223.         Next
  224.        
  225.         ;If the attribute exists, return it's value. If not, return a blank string
  226.         If indx = 0 Then
  227.                 Return ""
  228.         Else
  229.                 Return thisAttributeValue[indx]
  230.         End If
  231. End Function
  232.  
  233. ;This sets the name of an attribute (NOT it's value). Note: attribute
  234. ;names are case sensitive
  235. ;Example:
  236. ;xmlNodeAttributeNameSet(node,"pitch","Xang")
  237. Function xmlNodeAttributeNameSet(Node, Attribute$, NewName$)
  238.         this.xmlNode = Object.xmlNode(Node)
  239.        
  240.         ;Find the attribute
  241.         indx = 0
  242.         For i = 1 To thisAttributeCount
  243.                 If Attribute = thisAttributeName[i] Then indx = i:Exit
  244.         Next
  245.  
  246.         ;If the attribute exists, rename it
  247.         If indx <> 0 Then
  248.                 thisAttributeName[indx] = NewName
  249.         End If
  250. End Function
  251.  
  252. ;This deletes an attribute. Once a new attribute is created when
  253. ;using the xmlNodeAttributeSet() function, it will continue to
  254. ;reside in memory, and be saved to a file even if it's value is
  255. ;blank. To remove an un-used (or used) attribute of a node, use
  256. ;this function.
  257. ;Example:
  258. ;xmlNodeAttributeDelete(node, "hidden")
  259. Function xmlNodeAttributeDelete(Node, Attribute$)
  260.         this.xmlNode = Object.xmlNode(Node)
  261.        
  262.         ;Find the attribute
  263.         indx = 0
  264.         For i = 1 To thisAttributeCount
  265.                 If Attribute = thisAttributeName[i] Then indx = i:Exit
  266.         Next
  267.        
  268.         ;Delete the attribute, if it exists
  269.         If indx <> 0 Then
  270.                 thisAttributeName[indx] = thisAttributeName[thisAttributeCount]
  271.                 thisAttributeValue[indx] = thisAttributeValue[thisAttributeCount]
  272.                 thisAttributeCount = thisAttributeCount - 1
  273.         End If
  274. End Function
  275.  
  276. ;This sets a node's data string. A node's data is a string of
  277. ;text contained within the opening and closing node tags.
  278. ;Example:
  279. ;xmlNodeDataSet(titlenode, "BlitzXML")
  280. Function xmlNodeDataSet(Node, NodeData$)
  281.         this.xmlNode = Object.xmlNode(Node)
  282.         thisContents = NodeData
  283. End Function
  284.  
  285. ;This returns a node's data string. A node's data is a string
  286. ;of text contained within the opening and closing node tags.
  287. Function xmlNodeDataGet$(Node)
  288.         this.xmlNode = Object.xmlNode(Node)
  289.         Return thisContents
  290. End Function
  291.  
  292. ;This function saves all XML nodes to the specified file.
  293. ;If any errors occur, false will be returned, if not, true
  294. ;will be returned.
  295. Function xmlSave(FileName$, Node)
  296.         this.xmlNode = Object.xmlNode(Node)
  297.  
  298.         file = WriteFile(FileName)
  299.         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
  300.        
  301.         WriteLine file, "<?xml version="+Chr(34)+"1.0"+Chr(34)+" ?>"
  302.         xml_WriteNode(file, this)
  303.        
  304.         CloseFile file
  305. End Function
  306.  
  307. Function xml_WriteNode(File, Node.xmlNode)
  308.         Local NodeContents$, Indent$, Indent2$
  309.        
  310.         NodeContents = NodeName
  311.         For i = 1 To NodeAttributeCount
  312.                 NodeContents = NodeContents + " " + NodeAttributeName[i] + "=" + Chr$(34) + NodeAttributeValue[i] + Chr$(34)
  313.         Next
  314.        
  315.         Indent = String$(XML_INDENTATION$, NodeLevel)
  316.         Indent2 = String$(XML_INDENTATION$, NodeLevel+1)
  317.        
  318.         If NodeChildCount = 0 Then
  319.                 If NodeContents = "" Then
  320.                         WriteLine File, Indent + "<" + NodeContents + "/>"
  321.                 Else
  322.                     WriteLine File, Indent + "<" + NodeContents + ">" + NodeContents + "</" + NodeName + ">"
  323.                 End If
  324.         Else
  325.                 WriteLine File, Indent + "<" + NodeContents + ">"
  326.                 If NodeContents <> "" Then WriteLine File, Indent2 + NodeContents
  327.                
  328.                 For i = 1 To NodeChildCount
  329.                         xml_WriteNode(File, Object.xmlNode(xmlNodeChild(Handle(Node), i)))
  330.                 Next
  331.                
  332.                 WriteLine File, Indent + "</" + NodeName + ">"
  333.         End If
  334. End Function
  335.  
  336. ;This function loads and parses XML nodes from the specified XML file.
  337. ;Note: This (BlitzXML's xml parser) only supports xml files with standard
  338. ;xml tags and attributes with values enclosed in quotes. If the file
  339. ;is loaded successfully with no errors, a handle to the root node of the file
  340. ;will be returned. If not, 0 will be returned.
  341. ;Errors can be accessed using the xmlError$() and xmlErrorCount() functions.
  342. Function xmlLoad(FileName$)
  343.         Local attribute$[MAX_ATTRIBUTES]
  344.         Local value$[MAX_ATTRIBUTES]
  345.         Local nodestack[PARSER_RECURSE]
  346.         Local rootnode
  347.        
  348.         DebugLog "Loading XML file: " + FileName
  349.         xml_ClearErrors()
  350.         begintime = MilliSecs()
  351.        
  352.         ;Open the file
  353.         file = ReadFile(FileName)
  354.         If file = False Then
  355.                 xml_AddError("Error opening XML file: File does not exist.", 0)
  356.                 Return 0
  357.         End If
  358.         If Eof(file) = -1 Then
  359.                 xml_AddError("Error opening XML file: File is already in use by another program.", 0)
  360.                 Return 0
  361.         End If
  362.        
  363.         ;Read in all tags
  364.         stacklevel = 0
  365.         While Eof(file) = False
  366.                 ;Get the next tag or data section
  367.                 tag$ = xml_NextItem(file)
  368.                
  369.                 If tag$ <> "" Then
  370.                
  371.                         If xml_ItemType = 2 Then
  372.                                 ;Node contents
  373.                                 xmlNodeDataSet(nodestack[stacklevel - 1], Trim(Trim(xmlNodeDataGet(nodestack[stacklevel - 1])) + " " + Trim(tag)))
  374.                         Else
  375.                                 ;Check if it's a closing tag, opening tag, or stand-alone tag
  376.                                 If Left(tag,1) = "/" Then
  377.                                         ;Closing tag
  378.                                         stacklevel = stacklevel - 1
  379.                                        
  380.                                         tmp.xmlNode = Object.xmlNode(nodestack[stacklevel])
  381.                                         If tag <> "/" + tmpName Then xml_AddError("Unclosed tag (found <"+tag+">, expected </"+tmpName+">", FilePos(file))
  382.                                 Else
  383.                                         ;Create a new node
  384.                                         If stacklevel > 0 Then parent = nodestack[stacklevel - 1] Else parent = 0
  385.                                         node = xmlNodeAdd(parent)
  386.                                         If stacklevel = 0 Then rootnode = node
  387.                                        
  388.                                         ;Get the name and attributes from the tag
  389.                                         For i = 0 To attr:attribute[i] = "":value[i] = "":Next:attr = 0:opened = False:name$ = ""
  390.                                         length = Len(tag)
  391.                                         For i = 1 To length
  392.                                                 ch$ = Mid(tag, i, 1)
  393.                                                 If attr = 0 And ch = " " Then attr = attr + 1
  394.                                                 If ch = "=" Then attr = -attr
  395.                                                 If ch = Chr(34) Then
  396.                                                         If attr > 0 Then xml_AddError("Expecting equals symbol", FilePos(file))
  397.                                                         opened = 1 - opened
  398.                                                         If opened = False Then attr = Abs(attr):attr = attr + 1
  399.                                                 End If
  400.                                                 If ch <> Chr(34) And attr < 0 And opened Then value[-attr] = value[-attr] + ch
  401.                                                 If attr = 0 Then
  402.                                                         name = name + ch
  403.                                                 Else
  404.                                                         If attr > 0 And ch <> Chr(34) And ch<>" " Then attribute[attr] = attribute[attr] + ch
  405.                                                 End If
  406.                                         Next
  407.                                         For i = 1 To attr-1
  408.                                                 xmlNodeAttributeValueSet(node, attribute[i], value[i])
  409.                                         Next
  410.                                         xmlNodeNameSet(node, name)
  411.                                         nodestack[stacklevel] = node
  412.                                        
  413.                                         If Right(tag,1) = "/" Then
  414.                                                 ;Stand-alone tag
  415.                                         Else
  416.                                                 ;Opening tag
  417.                                                 stacklevel = stacklevel + 1
  418.                                         End If
  419.                                 End If
  420.                         End If
  421.                 End If
  422.         Wend
  423.        
  424.         CloseFile file
  425.        
  426.         endtime = MilliSecs()
  427.         parsetime# = (endtime - begintime) / 1000.0
  428.         If xmlErrorCount > 0 Then DebugLog "Parse failed" Else DebugLog "Parse completed in "+parsetime+" seconds."
  429.        
  430.         If xmlErrorCount > 0 Then Return 0 Else Return rootnode
  431. End Function
  432.  
  433. ;This function returns the number of errors and warnings from the last
  434. ;file parse performed.
  435. Function xmlErrorCount()
  436.         Return xml_ErrorCount
  437. End Function
  438.  
  439. ;This returns the position of the specified error (in characters from
  440. ;the beginning of the file
  441. Function xmlErrorPosition(ErrorNumber)
  442.         If ErrorNumber > xml_ErrorCount Then Return 0
  443.         Return xml_ErrorPos[ErrorNumber]
  444. End Function
  445.  
  446. ;This returns the description of the requested error.
  447. Function xmlError$(ErrorNumber)
  448.         If ErrorNumber > xml_ErrorCount Then Return ""
  449.         Return xml_Error[ErrorNumber]
  450. End Function
  451.  
  452.  
  453.  
  454. ;**** Internal Functions ================================================
  455. ;Internal functions should not be called from ANYWHERE but from other
  456. ;BlitzXML functions. These functions are undocumented, and you should
  457. ;NOT use them.
  458.  
  459. Global xml_ItemType
  460. Function xml_NextItem$(file)
  461.         Local tag$
  462.         While Eof(file) = False
  463.                 ch = ReadByte(file)
  464.                 If txt$ <> "" And (ch = 60 Or ch = 13) Then xml_ItemType = 2:SeekFile file,FilePos(file)-1:Return txt
  465.                 If ch <> 13 And ch <> 15 And ch <> 10 Then txt$ = txt$ + Chr(ch)
  466.                 If ch = 13 And txt <> "" Then txt = txt + " "
  467.                
  468.                 If ch = 60 Then ;<
  469.                         If opened = True Then xml_AddError("Expecting closing bracket (>)", FilePos(file))
  470.                         opened = True
  471.                 End If
  472.                 If ch = 62 Then ;>
  473.                         txt = ""
  474.                         If opened = False Then xml_AddError("Expecting opening bracket (<)", FilePos(file))
  475.                         opened = False
  476.                         If Left(tag,4) = "<!--" Or Left(tag,2) = "<?" Then
  477.                                 If Left(tag,4) = "<!--" And Right(tag,2) <> "--" Then xml_AddError("Expecting correct comment closure (-->)", FilePos(file))
  478.                                 If Left(tag,4) = "<?" And Right(tag,2) <> "?" Then xml_AddError("Expecting correct header closure (?>)", FilePos(file))
  479.                                 tag = ""
  480.                         Else
  481.                                 xml_ItemType = 1
  482.                                 Return Right(tag,Len(tag)-1)
  483.                         End If
  484.                 End If
  485.                 If opened Then tag = tag + Chr(ch)
  486.         Wend
  487. End Function
  488.  
  489. Function xml_RegisterChild(Node.xmlNode, Child.xmlNode)
  490.         ;Incriment the child count
  491.         NodeChildCount = NodeChildCount + 1
  492.        
  493.         ;Allocate memory for the data
  494.         If NodeChildBank = False Then
  495.                 NodeChildBank = CreateBank(4)
  496.         Else
  497.                 ResizeBank NodeChildBank, NodeChildCount * 4
  498.         End If
  499.        
  500.         ;Write the data
  501.         Value = Handle(Child)
  502.         PokeInt NodeChildBank, (NodeChildCount - 1) * 4, Value
  503. End Function
  504.  
  505. Function xml_GetChild.xmlNode(Node.xmlNode, ChildIndex)
  506.         ;Check if the ChildIndex is valid
  507.         If ChildIndex > NodeChildCount Then Return Null
  508.        
  509.         ;Get the child xmlNode object and return it
  510.         Value = PeekInt(NodeChildBank, (ChildIndex - 1) * 4)
  511.         this.xmlNode = Object.xmlNode(Value)
  512.         Return this
  513. End Function
  514.  
  515. Function xml_UnregisterChild(Node.xmlNode, ChildIndex)
  516.         ;Check if the ChildIndex is valid
  517.         If ChildIndex > NodeChildCount Then Return False
  518.  
  519.         ;"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
  520.         ;(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)
  521.         Value = PeekInt(NodeChildBank, (NodeChildCount - 1) * 4)
  522.         PokeInt NodeChildBank, (ChildIndex - 1) * 4, Value
  523.        
  524.         ;Downsize the bank, erasing the last child on the list which would be the child-to-be-deleted
  525.         ResizeBank NodeChildBank, (NodeChildCount - 1) * 4
  526.         NodeChildCount = NodeChildCount - 1
  527.        
  528.         Return True
  529. End Function
  530.  
  531. Function xml_ClearErrors()
  532.         xml_ErrorCount = 0
  533. End Function
  534.  
  535. Function xml_AddError(Description$, pos)
  536.         xml_ErrorCount = xml_ErrorCound + 1
  537.         xml_ErrorPos[xml_ErrorCount] = pos
  538.         xml_Error[xml_ErrorCount] = Description
  539.        
  540.         DebugLog "Error at char #"+pos+":  "+Description
  541. End Function


Comments :


Cold Harbour(Posted 1+ years ago)

 All looks great and I can see you to load an XML file.How about a sample of how to save an XML file?


Roland(Posted 1+ years ago)

 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


Jams(Posted 1+ years ago)

 Really great stuff! Using this alot in the project i'm working on!


@rtur(Posted 1+ years ago)

 Thanks! Great work.


John J.(Posted 1+ years ago)

 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.)


Cold Harbour(Posted 1+ years ago)

 Roland, thanks the penny's dropped now.Thanks for a great library John J.


Wayne(Posted 1+ years ago)

 The results were not what I expected for the material "logs" :
Code: [Select]
- <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:
Code: [Select]
Loading XML file: f:ogremediamodelshouse1.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



Axel Wheeler(Posted 1+ years ago)

 (Crossposted from showcase thread)Hate to bump this thread but:A: BlitzXML is sufficiently wonderful to merit it anyway, andB: 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 nodeName$ string, which generally makes it unusable.Here's my simple fix:
Code: [Select]
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
ame$
While Right$(name$,1)="/"
name$=Left$(name$,Len(name$)-1)
Wend
n
ame$=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.


Bobysait(Posted 1+ years ago)

 little bug on error count
Code: [Select]
Function xml_AddError(Description$, pos)
xml_ErrorCount = xml_ErrorCount + 1
xml_ErrorCount instead of xml_ErrorCoundNice job whatever ![edit]an other error in the function "xml_NextItem"1/ If txt$ <> "" And (ch = 60 Or ch = 13) Then [...]:Return txt2/ 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 happenother error in the same function :If Left(tag,4) = "<?" And Right(tag,2) <> "?" Then [...]length of words does not match conditions


tyoud(Posted 1+ years ago)

 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:
Code: [Select]
;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 thisContents
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:
Code: [Select]
Function xml_WriteNode(File, Node.xmlNode)
Local NodeContents$, Indent$, Indent2$

NodeContents = NodeName
For i = 1 To NodeAttributeCount
NodeContents = NodeContents + " " + NodeAttributeName[i] + "=" + Chr$(34) + NodeAttributeValue[i] + Chr$(34)
Next

Indent = String$(XML_INDENTATION$, NodeLevel)
Indent2 = String$(XML_INDENTATION$, NodeLevel+1)

If NodeChildCount = 0 Then
If NodeContents = "" Then
If (Right(NodeContents,1) <> "/") Then
WriteLine File, Indent + "<" + NodeContents + "/>"
Else
WriteLine File, Indent + "<" + NodeContents + ">"
EndIf
Else
   WriteLine File, Indent + "<" + NodeContents + ">" + NodeContents + "</" + NodeName + ">"
End If
Else
WriteLine File, Indent + "<" + NodeContents + ">"
If NodeContents <> "" Then WriteLine File, Indent2 + NodeContents

For i = 1 To NodeChildCount
xml_WriteNode(File, Object.xmlNode(xmlNodeChild(Handle(Node), i)))
Next

WriteLine File, Indent + "</" + NodeName + ">"
End If
End Function



tyoud(Posted 1+ years ago)

 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="&lt;test&gt;"> 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.
Code: [Select]
Function xmlNodeAttributeValueGet$(Node, Attribute$)
this.xmlNode = Object.xmlNode(Node)

;Find the attribute
indx=0
For i = 1 To thisAttributeCount
If Attribute = thisAttributeName[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 thisAttributeValue[indx]
; new code - substituate &lt; to < and &gt; to >
Return Replace$(Replace$(thisAttributeValue[indx], "&lt;", "<"), "&gt;", ">")
End If
End Function



Guy Fawkes(Posted 1+ years ago)

 Another preserved Library thanks to WaybackMachine! =Dhttp://web.archive.org/web/20070111180625/http://www.alsbonsai.com/john/BlitzXML_v1.71.zip [/i]

 

SimplePortal 2.3.6 © 2008-2014, SimplePortal