xmlvb.netprocedural

Cross reference xml files that have been loaded into a list as strings, break them apart and retrieve specific data from them


Main Form Code: https://pastebin.com/31gjpzm6 < Outdated
Blueprint Input Code W/ Mods: https://pastebin.com/KsF2vAWw < Outdated

Mods from "%AppData%\Roaming\SpaceEngineers\Mods" are extracted to "C:\SETools\unpacked_mods\" into a folder named by an integer which is equal to the number that it was extracted so the first mod is 0 second is 1 and so on.

A typical mod will have its main data file like so "C:\SETools\unpacked_mods\0\data\CubeBlocks.sbc" and the internal data is in XML as well.

CubeBlocks.sbc Mod Info File: https://pastebin.com/UhhGtEhN

So what i want to do is cross-reference the modded subtype id in the blueprint file with the one in all the mod files and check if they match and if they do then grab their Icon which is in DDS format and send it to a picturebox which has already been generated in the Form1 code above. And i want to do this by retrieving the path to a string.

Screenshot: http://imgur.com/a/3bIfN

So my question is how do i go about writing a function to do this without writing an IF function for each mod, basically i want to make a list of vanilla blocktypes and do like an if blockname is = to any item in the list of vanilla blocks then run the check for mods function.

What ive got so far:

Public Sub LoadModDefinitions()
    Dim modpath As String = "C:\SETools\unpacked_mods\"

    For Each entry In Directory.GetFiles(modpath, "CubeBlocks.sbc") 'For each zipfilename in the given directory extract them to the given folder
        Dim filereader As StreamReader = New StreamReader(modpath)
        For Each block In entry
            ModdedCubeblockDefinitions.Add(filereader.ReadToEnd(modpath))
        Next block
    Next
End Sub

^ This loads all the modded block information into a list.

And the function in question is the one below how can i search each block of data in the list of modded block data, find out which one contains the input "subtypesearch" & return a specific chunk of that files contents which is a file path between "" and "/icon"

Public Function SearchModdedCubeDefinitions(subtypesearch As String) 'WIP WIP WIP WIP WIP WIP
    Dim BlockToSearch As String = subtypesearch

    Return Nothing
End Function

And the automatic extracting of the mods files

'Check if program has been ran before and extract all mods if it hasnt
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles Me.Load
    Try
        'Check if the program directory exists & if not then create it
        If Not Directory.Exists("C:\SETools") Then
            Try
                Directory.CreateDirectory("C:\SETools")
            Catch ex As Exception
            End Try
        End If
        'Check if the mod extraction directory exists in the C drive & if not then create it
        If Not Directory.Exists("C:\SETools\unpacked_mods") Then
            Try
                Directory.CreateDirectory("C:\SETools\unpacked_mods")
            Catch ex As Exception
            End Try
        End If
        'Check if the file that determins whether or not the program has been run before exists or not & if not then create it
        If Not File.Exists("C:\SETools\Activated.txt") Then
            Try
                File.CreateText("C:\SETools\Activated.txt")
                'Unpack all mods
                unpackAll(sepath, extractto)
            Catch ex As Exception
            End Try
        End If
    Catch ex As Exception
    End Try
End Sub

Currently it seems that all the files are being read into the string list with no issue but i have no idea where to even start with the search function. I have a function written already for something i was doing before to search textboxes and was thinking of trying to implement it here but I'm still going to be concerned on how to return the text between the icon tags, i was thinking maybe using regex string splitting but I'm not quite sure any pointers?

Current Search Function

Private Function FindWords(ByVal TextSearched As String, ByVal Paragraph As String) As Integer
    Dim location As Integer = 0
    Dim occurances As Integer = 0
    Do
        location = TextSearched.IndexOf(Paragraph, location)
        If location <> -1 Then
            occurances += 1
            location += Paragraph.Length
        End If
    Loop Until location = -1
    Return occurances
End Function

Solution

  • Using serialization to solve the problem

    XMLCleaner.vb

    Public Class XMLCleaner
        Public Function CleanXML(inputfile As String)
            Dim originalxml As String = inputfile
            Dim outputxml As String = Nothing
    
            'only keep requested xml lines and remove xsi:type from <definition> element
            For Each line In originalxml.Split(vbNewLine)
                If line.ToString().Contains("<?xml") Then
                    outputxml += line.ToString() + vbNewLine
                ElseIf line.ToString().Contains("<Definitions xmlns:xsi") Then
                    outputxml += line.ToString() + vbNewLine
                ElseIf line.ToString().Contains("<CubeBlocks>") Then
                    outputxml += line.ToString() + vbNewLine
                ElseIf line.ToString().Contains("<Definition xsi:type") Then
                    outputxml += "<Definition>" + vbNewLine
                ElseIf line.ToString().Contains("<Definition>") Then
                    outputxml += line.ToString() + vbNewLine
                ElseIf line.ToString().Contains("<Id>") Then
                    outputxml += line.ToString() + vbNewLine
                ElseIf line.ToString().Contains("</Id>") Then
                    outputxml += line.ToString() + vbNewLine
                ElseIf line.ToString().Contains("<TypeId>") Then
                    outputxml += line.ToString() + vbNewLine
                ElseIf line.ToString().Contains("<SubtypeId>") Then
                    outputxml += line.ToString() + vbNewLine
                ElseIf line.ToString().Contains("<DisplayName>") Then
                    outputxml += line.ToString() + vbNewLine
                ElseIf line.ToString().Contains("<Icon>") Then
                    outputxml += line.ToString() + vbNewLine
                ElseIf line.ToString().Contains("<CubeSize>") Then
                    outputxml += line.ToString() + vbNewLine
                ElseIf line.ToString().Contains("<BlockPairName>") Then
                    outputxml += line.ToString() + vbNewLine
                ElseIf line.ToString().Contains("<BuildTimeSeconds>") Then
                    outputxml += line.ToString() + vbNewLine
                ElseIf line.ToString().Contains("<Components>") Then
                    outputxml += line.ToString() + vbNewLine
                ElseIf line.ToString().Contains("<Component Subtype") Then
                    outputxml += line.ToString() + vbNewLine
                ElseIf line.ToString().Contains("</Components>") Then
                    outputxml += line.ToString() + vbNewLine
                ElseIf line.ToString().Contains("</Definition>") Then
                    outputxml += line.ToString() + vbNewLine
                ElseIf line.ToString().Contains("</CubeBlocks>") Then
                    outputxml += line.ToString() + vbNewLine
                ElseIf line.ToString().Contains("</Definitions>") Then
                    outputxml += line.ToString() + vbNewLine
                End If
            Next
    
            Return outputxml
        End Function
    End Class
    

    This method extracts only the wanted data from the cubeblocks file of every mod to make it serializable by the universal cubeblocks deserializer I made to answer another question I had.

    CubeBlocksSerializer.vb

    Option Strict Off
    Option Explicit On
    
    Imports System.Xml.Serialization
    
    <System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.7.3081.0"),
     System.SerializableAttribute(),
     System.Diagnostics.DebuggerStepThroughAttribute(),
     System.ComponentModel.DesignerCategoryAttribute("code"),
     System.Xml.Serialization.XmlTypeAttribute(AnonymousType:=True),
     System.Xml.Serialization.XmlRootAttribute([Namespace]:="", IsNullable:=False)>
    Partial Public Class Definitions
        Private cubeBlocksField() As DefinitionsDefinition
        <System.Xml.Serialization.XmlArrayItemAttribute("Definition", IsNullable:=False)>
        Public Property CubeBlocks() As DefinitionsDefinition()
            Get
                Return Me.cubeBlocksField
            End Get
            Set
                Me.cubeBlocksField = Value
            End Set
        End Property
    End Class
    
    <System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.7.3081.0"),
     System.SerializableAttribute(),
     System.Diagnostics.DebuggerStepThroughAttribute(),
     System.ComponentModel.DesignerCategoryAttribute("code"),
     System.Xml.Serialization.XmlTypeAttribute(AnonymousType:=True)>
    Partial Public Class DefinitionsDefinition
        Private idField As DefinitionsDefinitionID
        Private displayNameField As String
        Private iconField As String
        Private cubeSizeField As String
        Private componentsField() As DefinitionsDefinitionComponent
        Private blockPairNameField As String
        Private buildTimeSecondsField As Integer
    
        Public Property Id() As DefinitionsDefinitionID
            Get
                Return Me.idField
            End Get
            Set
                Me.idField = Value
            End Set
        End Property
    
        Public Property DisplayName() As String
            Get
                Return Me.displayNameField
            End Get
            Set
                Me.displayNameField = Value
            End Set
        End Property
    
        Public Property Icon() As String
            Get
                Return Me.iconField
            End Get
            Set
                Me.iconField = Value
            End Set
        End Property
    
        Public Property CubeSize() As String
            Get
                Return Me.cubeSizeField
            End Get
            Set
                Me.cubeSizeField = Value
            End Set
        End Property
    
        <System.Xml.Serialization.XmlArrayItemAttribute("Component", IsNullable:=False)>
        Public Property Components() As DefinitionsDefinitionComponent()
            Get
                Return Me.componentsField
            End Get
            Set
                Me.componentsField = Value
            End Set
        End Property
    
        Public Property BlockPairName() As String
            Get
                Return Me.blockPairNameField
            End Get
            Set
                Me.blockPairNameField = Value
            End Set
        End Property
    
        Public Property BuildTimeSeconds() As Integer
            Get
                Return Me.buildTimeSecondsField
            End Get
            Set
                Me.buildTimeSecondsField = Value
            End Set
        End Property
    End Class
    
    <System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.7.3081.0"),
     System.SerializableAttribute(),
     System.Diagnostics.DebuggerStepThroughAttribute(),
     System.ComponentModel.DesignerCategoryAttribute("code"),
     System.Xml.Serialization.XmlTypeAttribute(AnonymousType:=True)>
    Partial Public Class DefinitionsDefinitionID
        Private typeIdField As String
        Private subtypeIdField As String
    
        Public Property TypeId() As String
            Get
                Return Me.typeIdField
            End Get
            Set
                Me.typeIdField = Value
            End Set
        End Property
    
        Public Property SubtypeId() As String
            Get
                Return Me.subtypeIdField
            End Get
            Set
                Me.subtypeIdField = Value
            End Set
        End Property
    End Class
    
    <System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.7.3081.0"),
     System.SerializableAttribute(),
     System.Diagnostics.DebuggerStepThroughAttribute(),
     System.ComponentModel.DesignerCategoryAttribute("code"),
     System.Xml.Serialization.XmlTypeAttribute(AnonymousType:=True)>
    Partial Public Class DefinitionsDefinitionComponent
        Private subtypeField As String
        Private countField As Integer
        Private countFieldSpecified As Boolean
    
        <System.Xml.Serialization.XmlAttributeAttribute()>
        Public Property Subtype() As String
            Get
                Return Me.subtypeField
            End Get
            Set
                Me.subtypeField = Value
            End Set
        End Property
    
        <System.Xml.Serialization.XmlAttributeAttribute()>
        Public Property Count() As Integer
            Get
                Return Me.countField
            End Get
            Set
                Me.countField = Value
            End Set
        End Property
    
        <System.Xml.Serialization.XmlIgnoreAttribute()>
        Public Property CountSpecified() As Boolean
            Get
                Return Me.countFieldSpecified
            End Get
            Set
                Me.countFieldSpecified = Value
            End Set
        End Property
    End Class
    

    This class deserializes the cubeblocks file to a dictionary of DefinitionsDefinition class see below.

    Main.vb

    Public ModBlockDefinitionDictionary As New Dictionary(Of String, DefinitionsDefinition)
    
    Public Sub LoadModDefinitions()
            Dim modpath As String = My.Settings.SpaceEngineersWorkingDirectory
            Dim id As Integer = 0
            Dim Cleaner As New XMLCleaner()
    
            For Each folder In Directory.GetDirectories(modpath)
                Dim folder_complete As String = folder.ToString() + "\Data"
    
                For Each file In Directory.GetFiles(folder_complete, "CubeBlocks.sbc") 'For each zipfilename in the given directory extract them to the given folder
                    Dim moddedxml As String = Cleaner.CleanXML(System.IO.File.ReadAllText(file))
                    Dim DefinitionData As New Definitions()
                    Dim xmlSerializer As XmlSerializer = New XmlSerializer(GetType(Definitions))
    
                    Try
                        Dim streamread As New StringReader(moddedxml)
                        DefinitionData = xmlSerializer.Deserialize(streamread)
                    Catch ex As Exception
    
                    End Try
    
                    For Each BlockDefinition In DefinitionData.CubeBlocks()
                        Try
                            If Not ModBlockDefinitionDictionary(BlockDefinition.Id.SubtypeId.ToString()) Is Nothing Then
                                'Block already exists dont add it
                            Else
                                ModBlockDefinitionDictionary.Add(BlockDefinition.Id.SubtypeId.ToString(), BlockDefinition)
                            End If
                        Catch ex As Exception
    
                        End Try
                    Next
                Next
    
                id += 1
            Next
        End Sub
    

    Then for each mod block you want to check you can fetch that block from the ModBlockDefintionsDictonary using its subtype id as the key.