Imports System.Text.RegularExpressions
Imports ICSharpCode.SharpZipLib.Zip

Public Class frmMain

#Region " MAP FILES "

    Private Sub btnBSP_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnBSP.Click
        Using ofd As New OpenFileDialog With {.Title = "Select BSP File", _
                                              .InitialDirectory = IO.Path.Combine(My.Settings.WolfPath, "maps"), _
                                              .CheckFileExists = True, _
                                              .Multiselect = False, _
                                              .Filter = "W:ET BSP Files (*.bsp)|*.bsp"}

            If ofd.ShowDialog = Windows.Forms.DialogResult.OK Then
                If ofd.FileName.Contains("\etmain\maps\") Then
                    txtBSP.Text = ofd.FileName

                    If My.Settings.AutoGenerate Then
                        'Auto-find other related files

                        lblLoading.Visible = True
                        lblLoading.Text = "Reading BSP File..."
                        Me.Enabled = False
                        'But first delete all files
                        txtLightmap.Clear()
                        txtTracemap.Clear()
                        txtScript.Clear()
                        txtObjData.Clear()
                        txtLoc.Clear()
                        txtArena.Clear()
                        txtLoadingImage.Clear()
                        txtCommandMap.Clear()
                        txtSounds.Clear()
                        txtSPS.Clear()

                        Dim st As My.MySettings = My.Settings


                        Dim found As New List(Of String)
                        Dim s As String = txtBSP.Text.Substring(0, txtBSP.Text.Length - ".BSP".Length)

                        If st.AG_Lightmap AndAlso IO.Directory.Exists(s) Then
                            txtLightmap.Text = s
                            found.Add("- Lightmap directory")
                        End If
                        If st.AG_Tracemap AndAlso IO.File.Exists(s & "_tracemap.tga") Then
                            txtTracemap.Text = s & "_tracemap.tga"
                            found.Add("- Tracemap")
                        End If
                        If st.AG_Script AndAlso IO.File.Exists(s & ".script") Then
                            txtScript.Text = s & ".script"
                            found.Add("- Script file")
                        End If
                        If st.AG_ObjData AndAlso IO.File.Exists(s & ".objdata") Then
                            txtObjData.Text = s & ".objdata"
                            found.Add("- Objective Data file")
                        End If
                        If st.AG_Loc AndAlso IO.File.Exists(s & "_loc.dat") Then
                            txtLoc.Text = s & "_loc.dat"
                            found.Add("- Loc file")
                        End If
                        If st.AG_Arena AndAlso IO.File.Exists(s.Replace("\etmain\maps\", "\etmain\scripts\") & ".arena") Then
                            txtArena.Text = s.Replace("\etmain\maps\", "\etmain\scripts\") & ".arena"
                            found.Add("- Arena file")
                        End If
                        If st.AG_LoadingImg AndAlso IO.File.Exists(s.Replace("\etmain\maps\", "\etmain\levelshots\") & ".tga") Then
                            txtLoadingImage.Text = s.Replace("\etmain\maps\", "\etmain\levelshots\") & ".tga"
                            found.Add("- Loading Image")
                        End If
                        If st.AG_CommandMapImg AndAlso IO.File.Exists(s.Replace("\etmain\maps\", "\etmain\levelshots\") & "_cc.tga") Then
                            txtCommandMap.Text = s.Replace("\etmain\maps\", "\etmain\levelshots\") & "_cc.tga"
                            found.Add("- CommandMap Image")
                        End If
                        If st.AG_SoundsFile AndAlso IO.File.Exists(s.Replace("\etmain\maps\", "\etmain\sound\scripts\") & ".sounds") Then
                            txtSounds.Text = s.Replace("\etmain\maps\", "\etmain\sound\scripts\") & ".sounds"
                            found.Add("- Sounds File")
                        End If
                        If st.AG_SPS AndAlso IO.File.Exists(s.Replace("\etmain\maps\", "\etmain\sound\maps\") & ".sps") Then
                            txtSPS.Text = s.Replace("\etmain\maps\", "\etmain\sound\maps\") & ".sps"
                            found.Add("- SPS File")
                        End If


                        If st.AG_Shaders Or st.AG_Textures Or st.AG_Models Then
                            lstTextFolders.Items.Clear()
                            lstTextFiles.Items.Clear()
                            lstModelFolders.Items.Clear()
                            lstModelFiles.Items.Clear()
                            lstShaders.Items.Clear()

                            GetTextures(txtBSP.Text)
                            found.Add("- Shaders (possibly)")
                            found.Add("- Textures (possibly)")
                            found.Add("- Models (possibly)")
                        End If

                        lblLoading.Visible = False
                        pb.Value = 0
                        Me.Enabled = True

                        If found.Count = 0 Then
                            MessageBox.Show("No other files have been found. Please check their location and add them manually.", "Not found", MessageBoxButtons.OK, MessageBoxIcon.Information)
                        Else
                            Dim msg As String = "The following files have been found and have been added automatically:" & Environment.NewLine & Environment.NewLine
                            msg &= String.Join(Environment.NewLine, found.ToArray)
                            MessageBox.Show(msg, "Auto-generated files", MessageBoxButtons.OK, MessageBoxIcon.Information)
                        End If

                    End If
                Else
                    MessageBox.Show("Please select a BSP file from your etmain\maps directory.", "Invalid Directory", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
                End If
            End If
        End Using
    End Sub

    Private Sub btnLightmap_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnLightmap.Click
        If txtBSP.Text = String.Empty Then
            MessageBox.Show("Please select a BSP file first.", "No BSP File found", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
            Exit Sub
        End If

        Using fbd As New FolderBrowserDialog With {.Description = "Select your Lightmap directory", _
                                                   .SelectedPath = IO.Path.Combine(My.Settings.WolfPath, "maps"), _
                                                   .ShowNewFolderButton = False}
            If fbd.ShowDialog = Windows.Forms.DialogResult.OK Then
                If fbd.SelectedPath.Contains("\etmain\maps\") Then
                    If fbd.SelectedPath.Contains(IO.Path.GetFileNameWithoutExtension(txtBSP.Text)) Then
                        txtLightmap.Text = fbd.SelectedPath
                    Else
                        MessageBox.Show("Lightmap directory name does not math with BSP file name.", "Invalid Directory", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
                    End If
                Else
                    MessageBox.Show("Please select a Lightmap directory from your etmain\maps directory.", "Invalid Directory", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
                End If
            End If
        End Using

    End Sub

    Private Sub btnTracemap_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnTracemap.Click
        If txtBSP.Text = String.Empty Then
            MessageBox.Show("Please select a BSP file first.", "No BSP File found", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
            Exit Sub
        End If

        Using ofd As New OpenFileDialog With {.Title = "Select Tracemap File", _
                                              .InitialDirectory = IO.Path.Combine(My.Settings.WolfPath, "maps"), _
                                              .CheckFileExists = True, _
                                              .Multiselect = False, _
                                              .Filter = "W:ET Tracemap Files (*_tracemap.tga)|*_tracemap.tga"}

            If ofd.ShowDialog = Windows.Forms.DialogResult.OK Then
                If ofd.FileName.Contains("\etmain\maps\") Then
                    If IO.Path.GetFileNameWithoutExtension(ofd.FileName) = IO.Path.GetFileNameWithoutExtension(txtBSP.Text) & "_tracemap" Then
                        txtTracemap.Text = ofd.FileName
                    Else
                        MessageBox.Show("Tracemap file name does not math with BSP file name.", "Invalid File", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
                    End If
                Else
                    MessageBox.Show("Please select a Tracemap file from your etmain\maps directory.", "Invalid Directory", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
                End If
            End If
        End Using
    End Sub

#Region " Controls "

    Private Sub btnOpenBSP_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnOpenBSP.Click
        Try
            OpenFile(txtBSP.Text)
        Catch ex As Exception
            MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
        End Try
    End Sub

    Private Sub btnHelpBSP_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnHelpBSP.Click
        Dim f As New frmHelp("BSP File")
        f.ShowDialog(Me)
    End Sub

    Private Sub btnClearLightmap_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnClearLightmap.Click
        txtLightmap.Clear()
    End Sub

    Private Sub btnOpenLightmap_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnOpenLightmap.Click
        Try
            OpenFile(txtLightmap.Text)
        Catch ex As Exception
            MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
        End Try
    End Sub

    Private Sub btnHelpLightmap_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnHelpLightmap.Click
        Dim f As New frmHelp("Lightmap Directory")
        f.ShowDialog(Me)
    End Sub

    Private Sub btnClearTracemap_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnClearTracemap.Click
        txtTracemap.Clear()
    End Sub

    Private Sub btnOpenTracemap_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnOpenTracemap.Click
        Try
            OpenFile(txtTracemap.Text)
        Catch ex As Exception
            MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
        End Try
    End Sub

    Private Sub btnHelpTracemap_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnHelpTracemap.Click
        Dim f As New frmHelp("Tracemap")
        f.ShowDialog(Me)
    End Sub

#End Region

#End Region

#Region " MAP SCRIPTS "

    Private Sub btnScript_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnScript.Click
        If txtBSP.Text = String.Empty Then
            MessageBox.Show("Please select a BSP file first.", "No BSP File found", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
            Exit Sub
        End If

        Using ofd As New OpenFileDialog With {.Title = "Select Script File", _
                                              .InitialDirectory = IO.Path.Combine(My.Settings.WolfPath, "maps"), _
                                              .CheckFileExists = True, _
                                              .Multiselect = False, _
                                              .Filter = "W:ET Script Files (*.script)|*.script"}

            If ofd.ShowDialog = Windows.Forms.DialogResult.OK Then
                If ofd.FileName.Contains("\etmain\maps\") Then
                    If IO.Path.GetFileNameWithoutExtension(ofd.FileName) = IO.Path.GetFileNameWithoutExtension(txtBSP.Text) Then
                        txtScript.Text = ofd.FileName
                    Else
                        MessageBox.Show("Script file name does not math with BSP file name.", "Invalid File", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
                    End If
                Else
                    MessageBox.Show("Please select a Script file from your etmain\maps directory.", "Invalid Directory", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
                End If
            End If
        End Using
    End Sub

    Private Sub btnObjData_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnObjData.Click
        If txtBSP.Text = String.Empty Then
            MessageBox.Show("Please select a BSP file first.", "No BSP File found", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
            Exit Sub
        End If

        Using ofd As New OpenFileDialog With {.Title = "Select Objective Data File", _
                                      .InitialDirectory = IO.Path.Combine(My.Settings.WolfPath, "maps"), _
                                      .CheckFileExists = True, _
                                      .Multiselect = False, _
                                      .Filter = "W:ET Objective Data Files (*.objdata)|*.objdata"}

            If ofd.ShowDialog = Windows.Forms.DialogResult.OK Then
                If ofd.FileName.Contains("\etmain\maps\") Then
                    If IO.Path.GetFileNameWithoutExtension(ofd.FileName) = IO.Path.GetFileNameWithoutExtension(txtBSP.Text) Then
                        txtObjData.Text = ofd.FileName
                    Else
                        MessageBox.Show("Objective Data file name does not math with BSP file name.", "Invalid File", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
                    End If
                Else
                    MessageBox.Show("Please select an Objective Data file from your etmain\maps directory.", "Invalid Directory", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
                End If
            End If
        End Using
    End Sub

    Private Sub btnArena_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnArena.Click
        If txtBSP.Text = String.Empty Then
            MessageBox.Show("Please select a BSP file first.", "No BSP File found", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
            Exit Sub
        End If

        Using ofd As New OpenFileDialog With {.Title = "Select Arena File", _
                                      .InitialDirectory = IO.Path.Combine(My.Settings.WolfPath, "scripts"), _
                                      .CheckFileExists = True, _
                                      .Multiselect = False, _
                                      .Filter = "W:ET Arena Files (*.arena)|*.arena"}

            If ofd.ShowDialog = Windows.Forms.DialogResult.OK Then
                If ofd.FileName.Contains("\etmain\scripts\") Then
                    If IO.Path.GetFileNameWithoutExtension(ofd.FileName) = IO.Path.GetFileNameWithoutExtension(txtBSP.Text) & "_loc" Then
                        txtArena.Text = ofd.FileName
                    Else
                        MessageBox.Show("Arena file name does not math with BSP file name.", "Invalid File", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
                    End If
                Else
                    MessageBox.Show("Please select an Arena file from your etmain\scripts directory.", "Invalid Directory", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
                End If
            End If
        End Using
    End Sub



#Region " Controls "

    Private Sub btnClearScript_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnClearScript.Click
        txtScript.Clear()
    End Sub

    Private Sub btnOpenScript_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnOpenScript.Click
        Try
            OpenFile(txtScript.Text)
        Catch ex As Exception
            MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
        End Try
    End Sub

    Private Sub btnHelpScript_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnHelpScript.Click
        Dim f As New frmHelp("Script File")
        f.ShowDialog(Me)
    End Sub

    Private Sub btnClearObjData_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnClearObjData.Click
        txtObjData.Clear()
    End Sub

    Private Sub btnOpenObjData_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnOpenObjData.Click
        Try
            OpenFile(txtObjData.Text)
        Catch ex As Exception
            MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
        End Try
    End Sub

    Private Sub btnHelpObjData_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnHelpObjData.Click
        Dim f As New frmHelp("Objective Data File")
        f.ShowDialog(Me)
    End Sub

    Private Sub btnClearArena_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnClearArena.Click
        txtArena.Clear()
    End Sub

    Private Sub btnOpenArena_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnOpenArena.Click
        Try
            OpenFile(txtArena.Text)
        Catch ex As Exception
            MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
        End Try
    End Sub

    Private Sub btnHelpArena_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnHelpArena.Click
        Dim f As New frmHelp("Arena File")
        f.ShowDialog(Me)
    End Sub

#End Region

#End Region

#Region " TEXTURES "

#Region " Texture Controls "

    Private Sub lstTextFolders_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lstTextFolders.SelectedIndexChanged
        RefreshTextureList()
    End Sub

    Private Sub lstTextFiles_DoubleClick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lstTextFiles.DoubleClick
        OpenTextureMenuItem.PerformClick()
    End Sub

    Private Sub lstTextFolders_DoubleClick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lstTextFolders.DoubleClick
        OpenTextureFolderMenuItem.PerformClick()
    End Sub

    Private Sub btnAddText_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAddText.Click
        Dim folderToSelect As clsTextureFolder = Nothing

        Using ofd As New OpenFileDialog With {.Title = "Select Texture File(s)", _
                                              .Multiselect = True, _
                                              .CheckFileExists = True, _
                                              .InitialDirectory = IO.Path.Combine(My.Settings.WolfPath, "textures"), _
                                              .Filter = "W:ET Textures|*.jpg;*.tga|JPG Files (*.jpg)|*.jpg|TGA Files (*.tga)|*.tga"}


            If ofd.ShowDialog = Windows.Forms.DialogResult.OK Then
                folderToSelect = AddTextures(ofd.FileNames)
            End If
        End Using

        If folderToSelect IsNot Nothing Then
            lstTextFolders.SelectedItem = folderToSelect
        End If
        RefreshTextureList()
    End Sub

    Private Sub btnRemoveText_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnRemoveText.Click
        If lstTextFiles.SelectedItems.Count > 0 Then
            Dim tf As clsTextureFolder = GetFolderFromShortTexture(lstTextFiles.SelectedItems(0).ToString)
            For Each tex As clsTexture In lstTextFiles.SelectedItems
                If tf IsNot Nothing Then
                    tf.Files.RemoveAt(tf.ShortFiles.IndexOf(tex.ShortFile))
                End If
            Next

            'If folder is now empty, remove that too
            If tf.Files.Count = 0 Then
                lstTextFolders.Items.Remove(tf)
            End If
        End If

        RefreshTextureList()
    End Sub

    Private Sub btnRemoveFolder_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnRemoveTextureFolder.Click
        Dim tf As clsTextureFolder = TryCast(lstTextFolders.SelectedItem, clsTextureFolder)
        If tf IsNot Nothing Then
            If MessageBox.Show("The selected texture folder contains " & tf.Files.Count & " textures." & Environment.NewLine & _
                               "Are you sure you want to remove them all from the current project?", "Remove Folder", MessageBoxButtons.YesNo, MessageBoxIcon.Question) = Windows.Forms.DialogResult.Yes Then
                lstTextFolders.Items.Remove(tf)
            End If
        End If

        RefreshTextureList()
    End Sub


#End Region

#Region " ContextMenuStrip "

    Private Sub OpenTextureFolderMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles OpenTextureFolderMenuItem.Click
        Dim f As clsTextureFolder = TryCast(lstTextFolders.SelectedItem, clsTextureFolder)
        Try
            OpenFile(f.FolderName)
        Catch ex As Exception
            MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
        End Try
    End Sub

    Private Sub DeleteTextureFolderMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles DeleteTextureFolderMenuItem.Click
        btnRemoveTextureFolder.PerformClick()
    End Sub

    Private Sub AddTextureMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles AddTextureMenuItem.Click
        btnAddText.PerformClick()
    End Sub

    Private Sub OpenTextureMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles OpenTextureMenuItem.Click
        Dim tex As clsTexture = TryCast(lstTextFiles.SelectedItem, clsTexture)
        Try
            OpenFile(tex.File)
        Catch ex As Exception
            MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
        End Try
    End Sub

    Private Sub DeleteTextureMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles DeleteTextureMenuItem.Click
        btnRemoveText.PerformClick()
    End Sub

    Private Sub cmsTextFolders_Opening(ByVal sender As System.Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles cmsTextFolders.Opening
        OpenTextureFolderMenuItem.Enabled = (lstTextFolders.SelectedItem IsNot Nothing)
        DeleteTextureFolderMenuItem.Enabled = (lstTextFolders.SelectedItem IsNot Nothing)
    End Sub

    Private Sub cmsTextures_Opening(ByVal sender As System.Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles cmsTextures.Opening
        OpenTextureMenuItem.Enabled = (lstTextFiles.SelectedItems.Count > 0)
        DeleteTextureMenuItem.Enabled = (lstTextFiles.SelectedItems.Count > 0)
    End Sub

#End Region

#Region " Functions "

    Private Function AddTextures(ByVal FileNames() As String, Optional ByVal CheckPAK As Boolean = False) As clsTextureFolder
        Dim folderToSelect As clsTextureFolder = Nothing
        If FileNames.Length > 0 Then

            'Check the first file for correct path; if not, show error.
            Dim parentFolder As String = IO.Path.GetDirectoryName(FileNames(0))
            If parentFolder.Contains("\etmain\textures\") Then

                For Each file As String In FileNames
                    If file IsNot Nothing Or file <> String.Empty Then
                        'Check each file for correct path; if not, skip without error (to avoid 10000 errors)
                        parentFolder = IO.Path.GetDirectoryName(file)
                        If parentFolder.Contains("\etmain\textures\") Then

                            Dim add As Boolean

                            If CheckPAK Then
                                If Not ExistsInPakxPK3(file) Then
                                    add = True
                                Else
                                    add = False
                                End If
                            Else
                                add = True
                            End If

                            If add Then
                                If Not TextureFolderExists(parentFolder) Then
                                    'This folder does not exist yet, add it
                                    Dim tf As New clsTextureFolder(parentFolder, New String() {file})
                                    lstTextFolders.Items.Add(tf)
                                    folderToSelect = tf
                                Else
                                    'Folder already exists, add file to existing
                                    Dim tf As clsTextureFolder = GetTextureFolderFromPath(parentFolder)

                                    'Only add the texture if it doesn't exist yet
                                    If tf IsNot Nothing Then
                                        Dim bln As Boolean = False
                                        For Each tex As clsTexture In tf.Files
                                            If tex.File = file Then
                                                bln = True
                                            End If
                                        Next
                                        If Not bln Then
                                            tf.Files.Add(New clsTexture(file))
                                        End If
                                    End If

                                    folderToSelect = tf
                                End If
                            End If
                        End If
                    End If
                Next

                ChangeProject()

            Else
                MessageBox.Show("Please select a texture from your etmain\textures directory.", "Invalid Directory", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
            End If
        End If

        Return folderToSelect
    End Function

    Private Sub RefreshTextureList()
        Dim tf As clsTextureFolder = TryCast(lstTextFolders.SelectedItem, clsTextureFolder)
        lstTextFiles.Items.Clear()
        If tf IsNot Nothing Then
            For Each tex As clsTexture In tf.Files
                lstTextFiles.Items.Add(tex)
            Next

            ChangeProject()
        End If
    End Sub

    Private Function TextureFolderExists(ByVal folder As String) As Boolean
        For Each c As clsTextureFolder In lstTextFolders.Items
            If c.FolderName = folder Then
                Return True
                Exit Function
            End If
        Next
        Return False
    End Function

    Private Function GetFolderFromTextureFile(ByVal file As String) As clsTextureFolder
        For Each f As clsTextureFolder In lstTextFolders.Items
            For Each tex As clsTexture In f.Files
                If tex.File = file Then
                    Return f
                    Exit Function
                End If
            Next
        Next
        Return Nothing
    End Function

    Private Function GetFolderFromTexture(ByVal tex As clsTexture) As clsTextureFolder
        For Each f As clsTextureFolder In lstTextFolders.Items
            If f.Files.Contains(tex) Then
                Return f
                Exit Function
            End If
        Next
        Return Nothing
    End Function

    Private Function GetFolderFromShortTexture(ByVal file As String) As clsTextureFolder
        For Each c As clsTextureFolder In lstTextFolders.Items
            If c.ShortFiles.Contains(file) Then
                Return c
                Exit Function
            End If
        Next
        Return Nothing
    End Function

    Private Function GetTextureFolderFromPath(ByVal folder As String) As clsTextureFolder
        For Each c As clsTextureFolder In lstTextFolders.Items
            If c.FolderName = folder Then
                Return c
                Exit Function
            End If
        Next
        Return Nothing
    End Function

#End Region

#Region " Extracting Textures from BSP "

    Private Sub GetTextures(ByVal bspFile As String)
        Try
            '-------------------------------------------------------------
            ' Read BSP file to obtain a list of texture shaders
            '-------------------------------------------------------------
            Dim enc As New System.Text.ASCIIEncoding
            Dim br As New IO.BinaryReader(IO.File.OpenRead(bspFile))

            br.ReadChars(4)         'Skip 'Magic Number' (IBSP)
            br.ReadInt32()          'Skip version

            Dim entityOffSet As Integer = br.ReadInt32()          'Read offset for Entities
            Dim entityLength As Integer = br.ReadInt32()          'Read length of entities

            Dim textureOffSet As Integer = br.ReadInt32    'Read position of texture chunk
            Dim textureLength As Integer = br.ReadInt32    'Read length of texture chunk

            'Read entity chunk (for models)
            br.BaseStream.Position = entityOffSet
            Dim strEntityBytes() As Byte = br.ReadBytes(entityLength)
            Dim entityChunk As String = enc.GetString(strEntityBytes).Replace(Chr(10), Environment.NewLine)


            'Read texture chunk
            Dim shaderDefs As New List(Of String)

            br.BaseStream.Position = textureOffSet
            Do Until br.BaseStream.Position > textureOffSet + textureLength
                Dim strTextureBytes() As Byte = br.ReadBytes(64)   'Read 64char texture string
                Dim s As String = enc.GetString(strTextureBytes).Trim.Replace(Chr(0), "")
                If s.StartsWith("textures/") Then  'it might also read misc_models, which we don't need
                    shaderDefs.Add(s)
                End If

                br.ReadInt32()  'Skip two integers
                br.ReadInt32()
            Loop

            pb.Maximum = 100
            pb.Minimum = 0
            pb.Style = ProgressBarStyle.Marquee
            pb.Value = 0


            '-------------------------------------------------------------
            'Get shader files containing the shaders
            '-------------------------------------------------------------
            Dim shaderFiles As List(Of String) = GetAllShaderFiles(shaderDefs)

            If My.Settings.AG_Shaders Then
                lstShaders.BeginUpdate()
                lstShaders.Items.Clear()
                For Each s As String In shaderFiles
                    AddShader(s, True)
                Next
                lstShaders.EndUpdate()
            End If

            '-------------------------------------------------------------
            ' Get texture images belonging to each shader
            '-------------------------------------------------------------
            If My.Settings.AG_Textures Then
                Dim textureImages As New List(Of String)
                For Each shaderDef As String In shaderDefs
                    For Each img As String In GetTextureImages(shaderDef)
                        If Not textureImages.Contains(img) Then textureImages.Add(img)
                    Next
                Next
                AddTextures(textureImages.ToArray, True)
            End If

            If My.Settings.AG_Models Then
                Dim models As List(Of String) = GetGameModels(entityChunk)
                AddModels(models.ToArray, True)
            End If

            pb.Value = 0
            pb.Style = ProgressBarStyle.Continuous
            Application.DoEvents()

        Catch ex As Exception
            MessageBox.Show("An error occured while reading the BSP file:" & Environment.NewLine & Environment.NewLine & ex.Message & _
                            Environment.NewLine & Environment.NewLine & "Details:" & Environment.NewLine & ex.ToString, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
            Me.Enabled = True
        End Try
    End Sub

    'Returns all shader FILES that contain a shader definition found in the BSP
    Private Function GetAllShaderFiles(ByVal shaderDefs As List(Of String)) As List(Of String)
        Dim lst As New List(Of String)
        Dim path As String
        Dim lines() As String

        path = IO.Path.Combine(My.Settings.WolfPath, "scripts")
        If IO.Directory.Exists(path) Then

            'Loop through each shader definition
            For Each shaderDef As String In shaderDefs

                'Loop through each shader file and read its lines into an array (lines)
                For Each file As String In IO.Directory.GetFiles(path, "*.shader")
                    lines = IO.File.ReadAllLines(file)

                    'Loop through each line and check if it equals the shader definition
                    For Each line As String In lines

                        'Remove any comments, tabs etc after the texture path
                        If line.Contains("//") Then
                            line = line.Substring(0, line.IndexOf("//")).Trim
                        End If
                        If line.Contains(ControlChars.Tab) Then
                            line = line.Substring(0, line.IndexOf(ControlChars.Tab)).Trim
                        End If
                        If line.Contains(" ") Then
                            line = line.Substring(0, line.IndexOf(" ")).Trim
                        End If

                        If line.Trim = shaderDef Then
                            'We found our shader, the current file should be added to our PK3
                            If Not lst.Contains(file) Then lst.Add(file)
                            Exit For
                        End If
                    Next

                Next
            Next
        End If

        Return lst
    End Function

    'Returns the shader FILES that contains the shader definition passed
    Private Function GetShaderFiles(ByVal shaderDef As String) As List(Of String)
        Dim lst As New List(Of String)
        Dim path As String = IO.Path.Combine(My.Settings.WolfPath, "scripts")
        Dim lines() As String

        If IO.Directory.Exists(path) Then
            For Each file As String In IO.Directory.GetFiles(path, "*.shader")
                lines = IO.File.ReadAllLines(file)

                'Loop through each line and check if it equals the shader definition
                For Each line As String In lines

                    'Remove any comments, tabs etc after the texture path
                    If line.Contains("//") Then
                        line = line.Substring(0, line.IndexOf("//")).Trim
                    End If
                    If line.Contains(ControlChars.Tab) Then
                        line = line.Substring(0, line.IndexOf(ControlChars.Tab)).Trim
                    End If
                    If line.Contains(" ") Then
                        line = line.Substring(0, line.IndexOf(" ")).Trim
                    End If

                    If line.Trim = shaderDef Then
                        'We found our shader, the current file should be added to our PK3
                        If Not lst.Contains(file) Then lst.Add(file)
                        Exit For
                    End If
                Next

            Next
        End If

        Return lst
    End Function

    'Returns the shader (between { and } characters) belonging to the passed shader definition
    Private Function GetShaderBlock(ByVal path As String, ByVal shaderDef As String) As String
        Dim block As String
        Dim shader As String = IO.File.ReadAllText(path)
        Dim texPos As Integer = shader.IndexOf(shaderDef)
        Dim openPos As Integer = shader.IndexOf("{", texPos)
        Dim closePos As Integer = 0
        Dim brackCount As Integer = 1
        Dim currentPos As Integer = openPos
        Do Until brackCount = 0
            If shader.Substring(currentPos + 1, 1) = "{" Then
                brackCount += 1
            ElseIf shader.Substring(currentPos + 1, 1) = "}" Then
                brackCount -= 1
            End If
            currentPos = currentPos + 1
        Loop
        block = shader.Substring(openPos, currentPos - 1 - openPos)

        Return block
    End Function

    'Returns texture IMAGES belonging to one shader definition
    Private Function GetTextureImages(ByVal shaderDef As String) As List(Of String)
        Dim lst As New List(Of String)

        Try
            Dim doesnotExist As List(Of String)

            'Find shader FILES belonging to shaderDef
            Dim files As List(Of String) = GetShaderFiles(shaderDef)

            If files.Count = 0 Then
                'No shader files found with shader definition, 
                'texture image should be just the shader def name + extension
                Dim texturePath As String = IO.Path.Combine(My.Settings.WolfPath, shaderDef)
                If IO.File.Exists(texturePath & ".tga") Then
                    lst.Add(texturePath & ".tga")
                ElseIf IO.File.Exists(texturePath & ".jpg") Then
                    lst.Add(texturePath & ".jpg")
                Else
                    MessageBox.Show("The following Texture Image should exist because no Shader File containing Shader Definition '" & shaderDef & "' has been found, but the Texture Image can not be found." & _
                                        Environment.NewLine & Environment.NewLine & texturePath & ".tga / .jpg", "Missing Texture", MessageBoxButtons.OK, MessageBoxIcon.Warning)
                End If
            End If

            For Each file As String In files

                doesnotExist = New List(Of String)

                'Read shader block
                Dim block As String = GetShaderBlock(file, shaderDef)
                If block <> String.Empty Then
                    'Parse block:

                    '1. Check if it contains "ImplicitMap -" which would mean it is an implicit shader:
                    'the texture image should be the shader definition name + .tga or .jpg
                    If block.ToLower.Contains("implicitmap -") Then
                        'Implicit shader
                        Dim texturePath As String = IO.Path.Combine(My.Settings.WolfPath, shaderDef)
                        If IO.File.Exists(texturePath & ".tga") Then
                            lst.Add(texturePath & ".tga")
                        ElseIf IO.File.Exists(texturePath & ".jpg") Then
                            lst.Add(texturePath & ".jpg")
                        Else
                            doesnotExist.Add(texturePath & ".tga")
                        End If

                    Else
                        'Explicit shader, look for texture maps / implicitMaps
                        Dim c() As Char = New Char() {CChar(Environment.NewLine), CChar(vbNewLine), CChar(vbCrLf), CChar(vbCr), CChar(vbLf), _
                                                      CChar(ControlChars.NewLine), CChar(ControlChars.CrLf), CChar(ControlChars.Cr), CChar(ControlChars.Lf)}
                        Dim lines() As String = block.Split(c)
                        For Each line As String In lines

                            If line.Trim.ToLower.StartsWith("map ") Then
                                Dim tex As String = ParseTexture("map ", line)
                                If tex <> String.Empty Then
                                    If tex.StartsWith("missingtexture|") Then
                                        doesnotExist.Add(tex.Split("|"c)(1))
                                    Else
                                        lst.Add(tex)
                                    End If
                                End If
                            ElseIf line.Trim.ToLower.StartsWith("implicitmap ") Then
                                Dim tex As String = ParseTexture("implicitmap ", line)
                                If tex <> String.Empty Then
                                    If tex.StartsWith("missingtexture|") Then
                                        doesnotExist.Add(tex.Split("|"c)(1))
                                    Else
                                        lst.Add(tex)
                                    End If
                                End If
                            ElseIf line.Trim.ToLower.StartsWith("implicitmask ") Then
                                Dim tex As String = ParseTexture("implicitmask ", line)
                                If tex <> String.Empty Then
                                    If tex.StartsWith("missingtexture|") Then
                                        doesnotExist.Add(tex.Split("|"c)(1))
                                    Else
                                        lst.Add(tex)
                                    End If
                                End If
                            ElseIf line.Trim.ToLower.StartsWith("implicitblend ") Then
                                Dim tex As String = ParseTexture("implicitblend ", line)
                                If tex <> String.Empty Then
                                    If tex.StartsWith("missingtexture|") Then
                                        doesnotExist.Add(tex.Split("|"c)(1))
                                    Else
                                        lst.Add(tex)
                                    End If
                                End If
                            End If
                        Next
                    End If

                    If doesnotExist.Count > 0 Then
                        MessageBox.Show("The following Texture Image(s) should exist according to Shader File '" & file & "' containing Shader Definition '" & shaderDef & "', but can not be found." & _
                                        Environment.NewLine & Environment.NewLine & String.Join(Environment.NewLine, doesnotExist.ToArray), "Missing Texture", MessageBoxButtons.OK, MessageBoxIcon.Warning)
                    End If
                End If
            Next

        Catch ex As Exception
            MessageBox.Show("An error occured while finding Texture Images:" & Environment.NewLine & Environment.NewLine & ex.Message & _
                            Environment.NewLine & Environment.NewLine & "Details:" & Environment.NewLine & ex.ToString, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)

        End Try

        Return lst
    End Function

    'Parses the texture from a "map <texture>", "implicitMap <texture>", "implicitBlend <texture>" etc, string.
    'Returns 'missingtexture|<texture>' if a texture has been parsed but does not exist (illegal)
    'Returns empty string if no texture parsed (legal)
    Private Function ParseTexture(ByVal parseFrom As String, ByVal line As String) As String
        Dim rtn As String = String.Empty

        Dim tex As String = line.Trim.ToLower.Substring(parseFrom.Length)
        If tex.StartsWith("textures\") Or tex.StartsWith("textures/") Then

            'Remove any comments, tabs etc after the texture path
            If tex.Contains("//") Then
                tex = tex.Substring(0, tex.IndexOf("//")).Trim
            End If
            If tex.Contains(ControlChars.Tab) Then
                tex = tex.Substring(0, tex.IndexOf(ControlChars.Tab)).Trim
            End If
            If tex.Contains(" ") Then
                tex = tex.Substring(0, tex.IndexOf(" ")).Trim
            End If

            tex = IO.Path.Combine(My.Settings.WolfPath, tex)

            If IO.Path.GetExtension(tex) = String.Empty Then
                'Very uncommon, no extension after an (implicit)Map stage..., try both
                tex = tex & ".tga"
            End If

            If IO.File.Exists(tex) Then
                rtn = tex
            Else
                'extension may be wrong
                If IO.Path.GetExtension(tex) = ".tga" Then
                    tex = IO.Path.ChangeExtension(tex, "jpg")
                ElseIf IO.Path.GetExtension(tex) = ".jpg" Then
                    tex = IO.Path.ChangeExtension(tex, "tga")
                End If

                If IO.File.Exists(tex) Then
                    rtn = tex
                Else
                    'even with other extension cannot be found: missing texture
                    rtn = "missingtexture|" & tex
                End If
            End If
        End If


        Return rtn
    End Function

#End Region

#End Region

#Region " SHADERS "

#Region " Shader Control "

    Private Sub lstShaders_DoubleClick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lstShaders.DoubleClick
        OpenShaderMenuItem.PerformClick()
    End Sub

    Private Sub btnAddShader_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAddShader.Click
        Using ofd As New OpenFileDialog With {.Title = "Select Shader File(s)", _
                                              .Multiselect = True, _
                                              .CheckFileExists = True, _
                                              .InitialDirectory = IO.Path.Combine(My.Settings.WolfPath, "scripts"), _
                                              .Filter = "W:ET Shaders (*.shader)|*.shader"}

            If ofd.ShowDialog = Windows.Forms.DialogResult.OK Then
                For Each file As String In ofd.FileNames
                    AddShader(file, False)
                Next
            End If
        End Using

    End Sub

    Private Sub btnRemoveShader_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnRemoveShader.Click
        Dim l As New List(Of clsShader)
        For Each s As clsShader In lstShaders.SelectedItems
            l.Add(s)
        Next
        For Each s As clsShader In l
            lstShaders.Items.Remove(s)
        Next
    End Sub

#End Region

#Region " ContextMenuStrip "
    Private Sub AddShaderMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles AddShaderMenuItem.Click
        btnAddShader.PerformClick()
    End Sub

    Private Sub OpenShaderMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles OpenShaderMenuItem.Click
        Dim s As clsShader = TryCast(lstShaders.SelectedItem, clsShader)
        Try
            OpenFile(s.File)
        Catch ex As Exception
            MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
        End Try
    End Sub

    Private Sub DeleteShaderMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles DeleteShaderMenuItem.Click
        btnRemoveShader.PerformClick()
    End Sub

    Private Sub cmsShaders_Opening(ByVal sender As System.Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles cmsShaders.Opening
        OpenShaderMenuItem.Enabled = (lstShaders.SelectedItems.Count > 0)
        DeleteShaderMenuItem.Enabled = (lstShaders.SelectedItems.Count > 0)
    End Sub

#End Region

#Region " FUNCTIONS "

    Private Sub AddShader(ByVal file As String, Optional ByVal checkPAK As Boolean = False)
        Dim shader As clsShader
        Dim bln As Boolean = False

        For Each s As clsShader In lstShaders.Items
            If s.File = file Then
                bln = True
            End If
        Next
        If Not bln Then
            shader = New clsShader(file)

            'Check if shader is standard
            If checkPAK Then
                If Not ExistsInPakxPK3(shader.File) Then
                    lstShaders.Items.Add(shader)
                End If
            Else
                lstShaders.Items.Add(shader)
            End If

            ChangeProject()
        End If
    End Sub

#End Region

#End Region

#Region " MODELS "

#Region " Model Controls "

    Private Sub lstModelFiles_DoubleClick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lstModelFiles.DoubleClick
        OpenModelMenuItem.PerformClick()
    End Sub

    Private Sub lstModelFolders_DoubleClick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lstModelFolders.DoubleClick
        OpenModelFolderMenuItem.PerformClick()
    End Sub

    Private Sub btnAddModel_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAddModel.Click
        Dim folderToSelect As clsModelFolder = Nothing

        Using ofd As New OpenFileDialog With {.Title = "Select Model File(s)", _
                                              .InitialDirectory = IO.Path.Combine(My.Settings.WolfPath, "models"), _
                                              .Multiselect = True, _
                                              .CheckFileExists = True, _
                                              .Filter = "W:ET Models (*.md3)|*.md3"}

            If ofd.ShowDialog = Windows.Forms.DialogResult.OK And ofd.FileNames.Length > 0 Then

                If ofd.FileNames.Length > 0 Then
                    folderToSelect = AddModels(ofd.FileNames)
                End If
            End If
        End Using

        If folderToSelect IsNot Nothing Then
            lstModelFolders.SelectedItem = folderToSelect
        End If
        RefreshModelList()
    End Sub

    Private Sub btnRemoveModel_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnRemoveModel.Click
        If lstModelFiles.SelectedItems.Count > 0 Then
            Dim mf As clsModelFolder = GetFolderFromShortModel(lstModelFiles.SelectedItems(0).ToString)
            For Each model As clsModel In lstModelFiles.SelectedItems
                If mf IsNot Nothing Then
                    mf.Files.RemoveAt(mf.ShortFiles.IndexOf(model.ShortFile))
                End If
            Next

            If mf.Files.Count = 0 Then
                lstModelFolders.Items.Remove(mf)
            End If
        End If

        RefreshModelList()
    End Sub

    Private Sub btnRemoveModelFolder_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnRemoveModelFolder.Click
        Dim mf As clsModelFolder = TryCast(lstModelFolders.SelectedItem, clsModelFolder)
        If mf IsNot Nothing Then
            If MessageBox.Show("The selected model folder contains " & mf.Files.Count & " models." & Environment.NewLine & _
                   "Are you sure you want to remove them all from the current project?", "Remove Folder", MessageBoxButtons.YesNo, MessageBoxIcon.Question) = Windows.Forms.DialogResult.Yes Then
                lstModelFolders.Items.Remove(mf)
            End If
        End If
    End Sub

    Private Sub lstModelFolders_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lstModelFolders.SelectedIndexChanged
        RefreshModelList()
    End Sub

#End Region

#Region " ContextMenuStrip "

    Private Sub OpenModelMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles OpenModelMenuItem.Click
        Dim m As clsModel = TryCast(lstModelFiles.SelectedItem, clsModel)
        Try
            OpenFile(m.File)
        Catch ex As Exception
            MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
        End Try
    End Sub

    Private Sub OpenModelFolderMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles OpenModelFolderMenuItem.Click
        Dim mf As clsModelFolder = TryCast(lstModelFolders.SelectedItem, clsModelFolder)
        Try
            OpenFile(mf.FolderName)
        Catch ex As Exception
            MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
        End Try
    End Sub

    Private Sub DeleteModelFolderMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles DeleteModelFolderMenuItem.Click
        btnRemoveModelFolder.PerformClick()
    End Sub

    Private Sub AddModelMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles AddModelMenuItem.Click
        btnAddModel.PerformClick()
    End Sub

    Private Sub DeleteModelMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles DeleteModelMenuItem.Click
        btnRemoveModel.PerformClick()
    End Sub

    Private Sub cmsModelFolders_Opening(ByVal sender As System.Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles cmsModelFolders.Opening
        OpenModelFolderMenuItem.Enabled = (lstModelFolders.SelectedItem IsNot Nothing)
        DeleteModelFolderMenuItem.Enabled = (lstModelFolders.SelectedItem IsNot Nothing)
    End Sub

    Private Sub cmsModels_Opening(ByVal sender As System.Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles cmsModels.Opening
        OpenModelMenuItem.Enabled = (lstModelFiles.SelectedItems.Count > 0)
        DeleteModelMenuItem.Enabled = (lstModelFiles.SelectedItems.Count > 0)
    End Sub

#End Region

#Region " Functions "

    Private Sub RefreshModelList()
        Dim mf As clsModelFolder = TryCast(lstModelFolders.SelectedItem, clsModelFolder)
        lstModelFiles.Items.Clear()
        If mf IsNot Nothing Then
            For Each model As clsModel In mf.Files
                lstModelFiles.Items.Add(model)
            Next

            ChangeProject()
        End If
    End Sub

    Private Function AddModels(ByVal fileNames() As String, Optional ByVal CheckPAK As Boolean = False) As clsModelFolder
        Dim folderToSelect As clsModelFolder = Nothing

        If fileNames.Length > 0 Then

            'Check the first file for correct path; if not, show error
            Dim parentFolder As String = IO.Path.GetDirectoryName(fileNames(0))
            If parentFolder.Contains("\etmain\models\") Then

                For Each file As String In fileNames
                    If file IsNot Nothing Or file <> String.Empty Then
                        'Check each file for correct path; if not, skip without error
                        parentFolder = IO.Path.GetDirectoryName(file)
                        If parentFolder.Contains("\etmain\models\") Then

                            Dim add As Boolean
                            If CheckPAK Then
                                If Not ExistsInPakxPK3(file) Then
                                    add = True
                                Else
                                    add = False
                                End If
                            Else
                                add = True
                            End If

                            If add Then
                                If Not ModelFolderExists(parentFolder) Then
                                    'Folder does not exist yet, add it
                                    Dim mf As New clsModelFolder(parentFolder, New String() {file})
                                    lstModelFolders.Items.Add(mf)
                                    folderToSelect = mf
                                Else
                                    'Folder exists, add file to existing
                                    Dim mf As clsModelFolder = GetModelFolderFromPath(parentFolder)
                                    If mf IsNot Nothing Then
                                        Dim bln As Boolean = False
                                        For Each model As clsModel In mf.Files
                                            If model.File = file Then
                                                bln = True
                                            End If
                                        Next
                                        If Not bln Then
                                            mf.Files.Add(New clsModel(file))
                                        End If
                                    End If

                                    folderToSelect = mf
                                End If
                            End If
                        End If
                    End If
                Next

                ChangeProject()
            Else
                MessageBox.Show("Please select a model from your etmain\models directory.", "Invalid Directory", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
            End If
        End If

        Return folderToSelect
    End Function

    Private Function ModelFolderExists(ByVal folder As String) As Boolean
        For Each mf As clsModelFolder In lstModelFolders.Items
            If mf.FolderName = folder Then
                Return True
                Exit Function
            End If
        Next
        Return False
    End Function

    Private Function GetFolderFromModelFile(ByVal file As String) As clsModelFolder
        For Each mf As clsModelFolder In lstModelFolders.Items
            For Each m As clsModel In mf.Files
                If m.File = file Then
                    Return mf
                    Exit Function
                End If
            Next
        Next
        Return Nothing
    End Function

    Private Function GetModelFolderFromPath(ByVal folder As String) As clsModelFolder
        For Each mf As clsModelFolder In lstModelFolders.Items
            If mf.FolderName = folder Then
                Return mf
                Exit Function
            End If
        Next
        Return Nothing
    End Function

    Private Function GetFolderFromShortModel(ByVal file As String) As clsModelFolder
        For Each mf As clsModelFolder In lstModelFolders.Items
            If mf.ShortFiles.Contains(file) Then
                Return mf
                Exit Function
            End If
        Next
        Return Nothing
    End Function

#End Region

#Region " Retrieving Models from BSP "

    'Uses the entity chunk (ec) from BSP file to check type of each model (misc_model or misc_gamemodel)
    'Returns only misc_gamemodels, since misc_models don't need to be in PK3
    Private Function GetGameModels(ByVal ec As String) As List(Of String)
        Dim lst As New List(Of String)

        'regex: retrieves [model path] from the string     "model" "[model path]"   or  "model2" "[model path]"
        Dim pattern As String = "(?<=""model2?""\s"").+(?="")"
        Dim r As New Regex(pattern)
        For Each m As Match In r.Matches(ec)
            If Not m.Value.StartsWith("*") Then
                Dim s As String = IO.Path.Combine(My.Settings.WolfPath, m.Value)
                If Not lst.Contains(s) Then lst.Add(s)
            End If
        Next

        Return lst
    End Function


#End Region

#End Region

#Region " SOUNDS "

#Region " SOUND CONTROLS "

    Private Sub btnSounds_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnSounds.Click
        If txtBSP.Text = String.Empty Then
            MessageBox.Show("Please select a BSP file first.", "No BSP File found", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
            Exit Sub
        End If

        Using ofd As New OpenFileDialog With {.Title = "Select Sounds File", _
                                              .InitialDirectory = IO.Path.Combine(My.Settings.WolfPath, "sound\scripts"), _
                                              .CheckFileExists = True, _
                                              .Multiselect = False, _
                                              .Filter = "W:ET Sounds Files (*.sounds)|*.sounds"}

            If ofd.ShowDialog = Windows.Forms.DialogResult.OK Then
                If ofd.FileName.Contains("\etmain\sound\scripts\") Then
                    If IO.Path.GetFileNameWithoutExtension(ofd.FileName) = IO.Path.GetFileNameWithoutExtension(txtBSP.Text) Then
                        txtSounds.Text = ofd.FileName
                    Else
                        MessageBox.Show("Sounds file name does not math with BSP file name.", "Invalid File", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
                    End If
                Else
                    MessageBox.Show("Please select a Sounds file from your etmain\sound\scripts directory.", "Invalid Directory", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
                End If
            End If
        End Using
    End Sub

    Private Sub btnSPS_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnSPS.Click
        If txtBSP.Text = String.Empty Then
            MessageBox.Show("Please select a BSP file first.", "No BSP File found", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
            Exit Sub
        End If

        Using ofd As New OpenFileDialog With {.Title = "Select SPS File", _
                                              .InitialDirectory = IO.Path.Combine(My.Settings.WolfPath, "sound\maps"), _
                                              .CheckFileExists = True, _
                                              .Multiselect = False, _
                                              .Filter = "W:ET SPS Files (*.sps)|*.sps"}

            If ofd.ShowDialog = Windows.Forms.DialogResult.OK Then
                If ofd.FileName.Contains("\etmain\sound\maps\") Then
                    If IO.Path.GetFileNameWithoutExtension(ofd.FileName) = IO.Path.GetFileNameWithoutExtension(txtBSP.Text) Then
                        txtSPS.Text = ofd.FileName
                    Else
                        MessageBox.Show("SPS file name does not math with BSP file name.", "Invalid File", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
                    End If
                Else
                    MessageBox.Show("Please select a SPS file from your etmain\sound\maps directory.", "Invalid Directory", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
                End If
            End If
        End Using
    End Sub

    Private Sub btnClearSPS_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnClearSPS.Click
        txtSPS.Clear()
    End Sub

    Private Sub btnOpenSPS_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnOpenSPS.Click
        Try
            OpenFile(txtSPS.Text)
        Catch ex As Exception
            MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
        End Try
    End Sub

    Private Sub btnHelpSPS_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnHelpSPS.Click
        Dim f As New frmHelp("SPS File")
        f.ShowDialog(Me)
    End Sub

    Private Sub btnAddSound_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAddSound.Click
        Dim folderToSelect As clsSoundFolder = Nothing

        Using ofd As New OpenFileDialog With {.Title = "Select Sound File(s)", _
                                              .InitialDirectory = IO.Path.Combine(My.Settings.WolfPath, "sound"), _
                                              .Multiselect = True, _
                                              .CheckFileExists = True, _
                                              .Filter = "W:ET Sounds (*.wav)|*.wav"}

            If ofd.ShowDialog = Windows.Forms.DialogResult.OK And ofd.FileNames.Length > 0 Then

                If ofd.FileNames.Length > 0 Then
                    folderToSelect = AddSounds(ofd.FileNames)
                End If
            End If
        End Using

        If folderToSelect IsNot Nothing Then
            lstSoundFolders.SelectedItem = folderToSelect
        End If
        RefreshSoundList()
    End Sub

    Private Sub btnRemoveSound_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnRemoveSound.Click
        If lstSoundFiles.SelectedItems.Count > 0 Then
            Dim sf As clsSoundFolder = GetFolderFromShortSound(lstSoundFiles.SelectedItems(0).ToString)
            For Each sound As clsSound In lstSoundFiles.SelectedItems
                If sf IsNot Nothing Then
                    sf.Files.RemoveAt(sf.ShortFiles.IndexOf(sound.ShortFile))
                End If
            Next

            If sf.Files.Count = 0 Then
                lstSoundFolders.Items.Remove(sf)
            End If
        End If

        RefreshSoundList()
    End Sub

    Private Sub btnRemoveSoundFolder_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnRemoveSoundFolder.Click
        Dim sf As clsSoundFolder = TryCast(lstSoundFolders.SelectedItem, clsSoundFolder)
        If sf IsNot Nothing Then
            If MessageBox.Show("The selected sound folder contains " & sf.Files.Count & " sounds." & Environment.NewLine & _
                   "Are you sure you want to remove them all from the current project?", "Remove Folder", MessageBoxButtons.YesNo, MessageBoxIcon.Question) = Windows.Forms.DialogResult.Yes Then
                lstSoundFolders.Items.Remove(sf)
            End If
        End If
    End Sub

    Private Sub lstSoundFolders_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lstSoundFolders.SelectedIndexChanged
        RefreshSoundList()
    End Sub

    Private Sub lstSoundFiles_DoubleClick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lstSoundFiles.DoubleClick
        OpenSoundMenuItem.PerformClick()
    End Sub

    Private Sub lstSoundFolders_DoubleClick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lstSoundFolders.DoubleClick
        OpenSoundFolderMenuItem.PerformClick()
    End Sub

    Private Sub btnClearSounds_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnClearSounds.Click
        txtSounds.Clear()
    End Sub

    Private Sub btnOpenSounds_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnOpenSounds.Click
        Try
            OpenFile(txtSounds.Text)
        Catch ex As Exception
            MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
        End Try
    End Sub
    Private Sub btnHelpSounds_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnHelpSounds.Click
        Dim f As New frmHelp("Sounds File")
        f.ShowDialog(Me)
    End Sub

#End Region

#Region " CONTEXT MENU STRIP "

    Private Sub cmsSounds_Opening(ByVal sender As System.Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles cmsSounds.Opening
        OpenSoundMenuItem.Enabled = (lstSoundFiles.SelectedItems.Count > 0)
        DeleteSoundMenuItem.Enabled = (lstSoundFiles.SelectedItems.Count > 0)
    End Sub

    Private Sub cmsSoundFolders_Opening(ByVal sender As System.Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles cmsSoundFolders.Opening
        OpenSoundFolderMenuItem.Enabled = (lstSoundFolders.SelectedItem IsNot Nothing)
        DeleteSoundFolderMenuItem.Enabled = (lstSoundFolders.SelectedItem IsNot Nothing)
    End Sub

    Private Sub OpenSoundMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles OpenSoundMenuItem.Click
        Dim s As clsSound = TryCast(lstSoundFiles.SelectedItem, clsSound)
        Try
            OpenFile(s.File)
        Catch ex As Exception
            MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
        End Try
    End Sub

    Private Sub OpenSoundFolderMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles OpenSoundFolderMenuItem.Click
        Dim sf As clsSoundFolder = TryCast(lstSoundFolders.SelectedItem, clsSoundFolder)
        Try
            OpenFile(sf.FolderName)
        Catch ex As Exception
            MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
        End Try
    End Sub

    Private Sub DeleteSoundFolderMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles DeleteSoundFolderMenuItem.Click
        btnRemoveSoundFolder.PerformClick()
    End Sub

    Private Sub AddSoundMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles AddSoundMenuItem.Click
        btnAddSound.PerformClick()
    End Sub

    Private Sub DeleteSoundMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles DeleteSoundMenuItem.Click
        btnRemoveSound.PerformClick()
    End Sub

#End Region

#Region " FUNCTIONS "

    Private Sub RefreshSoundList()
        Dim sf As clsSoundFolder = TryCast(lstSoundFolders.SelectedItem, clsSoundFolder)
        lstSoundFiles.Items.Clear()
        If sf IsNot Nothing Then
            For Each sound As clsSound In sf.Files
                lstSoundFiles.Items.Add(sound)
            Next

            ChangeProject()
        End If
    End Sub

    Private Function AddSounds(ByVal fileNames() As String) As clsSoundFolder
        Dim folderToSelect As clsSoundFolder = Nothing

        If fileNames.Length > 0 Then
            Dim parentFolder As String = IO.Path.GetDirectoryName(fileNames(0))
            If parentFolder.Contains("\etmain\sound\") Then

                For Each file As String In fileNames
                    If file IsNot Nothing And file <> String.Empty Then
                        parentFolder = IO.Path.GetDirectoryName(file)
                        If parentFolder.Contains("\etmain\sound\") Then

                            If Not SoundFolderExists(parentFolder) Then
                                Dim sf As New clsSoundFolder(parentFolder, New String() {file})
                                lstSoundFolders.Items.Add(sf)
                                folderToSelect = sf
                            Else
                                Dim sf As clsSoundFolder = GetSoundFolderFromPath(parentFolder)
                                If sf IsNot Nothing Then
                                    Dim bln As Boolean = False
                                    For Each sound As clsSound In sf.Files
                                        If sound.File = file Then
                                            bln = True
                                        End If
                                    Next
                                    If Not bln Then
                                        sf.Files.Add(New clsSound(file))
                                    End If
                                End If

                                folderToSelect = sf
                            End If
                        End If
                    End If
                Next

                ChangeProject()
            Else
                MessageBox.Show("Please select a model from your etmain\sound directory.", "Invalid Directory", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
            End If
        End If

        Return folderToSelect
    End Function

    Private Function GetFolderFromShortSound(ByVal file As String) As clsSoundFolder
        For Each sf As clsSoundFolder In lstSoundFolders.Items
            If sf.ShortFiles.Contains(file) Then
                Return sf
                Exit Function
            End If
        Next
        Return Nothing
    End Function

    Private Function SoundFolderExists(ByVal folder As String) As Boolean
        For Each sf As clsSoundFolder In lstSoundFolders.Items
            If sf.FolderName = folder Then
                Return True
                Exit Function
            End If
        Next
        Return False
    End Function

    Private Function GetSoundFolderFromPath(ByVal folder As String) As clsSoundFolder
        For Each sf As clsSoundFolder In lstSoundFolders.Items
            If sf.FolderName = folder Then
                Return sf
                Exit Function
            End If
        Next
        Return Nothing
    End Function

#End Region

#End Region

#Region " LEVELSHOTS "

    Private Sub btnLoadingImage_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnLoadingImage.Click
        If txtBSP.Text = String.Empty Then
            MessageBox.Show("Please select a BSP file first.", "No BSP File found", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
            Exit Sub
        End If

        Using ofd As New OpenFileDialog With {.Title = "Select Loading Image", _
                                      .InitialDirectory = IO.Path.Combine(My.Settings.WolfPath, "levelshots"), _
                                      .CheckFileExists = True, _
                                      .Multiselect = False, _
                                      .Filter = "TGA Files (*.tga)|*.tga"}

            If ofd.ShowDialog = Windows.Forms.DialogResult.OK Then
                If ofd.FileName.Contains("\etmain\levelshots\") Then
                    If IO.Path.GetFileNameWithoutExtension(ofd.FileName) = IO.Path.GetFileNameWithoutExtension(txtBSP.Text) Then
                        txtLoadingImage.Text = ofd.FileName
                    Else
                        MessageBox.Show("Loading Image file name does not math with BSP file name.", "Invalid File", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
                    End If
                Else
                    MessageBox.Show("Please select a Loading Image from your etmain\levelshots directory.", "Invalid Directory", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
                End If
            End If
        End Using
    End Sub

    Private Sub btnCommandMap_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCommandMap.Click
        If txtBSP.Text = String.Empty Then
            MessageBox.Show("Please select a BSP file first.", "No BSP File found", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
            Exit Sub
        End If

        Using ofd As New OpenFileDialog With {.Title = "Select CommandMap Image", _
                                      .InitialDirectory = IO.Path.Combine(My.Settings.WolfPath, "levelshots"), _
                                      .CheckFileExists = True, _
                                      .Multiselect = False, _
                                      .Filter = "TGA Files (*_cc.tga)|*_cc.tga"}

            If ofd.ShowDialog = Windows.Forms.DialogResult.OK Then
                If ofd.FileName.Contains("\etmain\levelshots\") Then
                    If IO.Path.GetFileNameWithoutExtension(ofd.FileName) = IO.Path.GetFileNameWithoutExtension(txtBSP.Text) & "_cc" Then
                        txtLoadingImage.Text = ofd.FileName
                    Else
                        MessageBox.Show("CommandMap Image file name does not math with BSP file name.", "Invalid File", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
                    End If
                Else
                    MessageBox.Show("Please select a CommandMap Image from your etmain\levelshots directory.", "Invalid Directory", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
                End If
            End If
        End Using
    End Sub

#Region " Controls "

    Private Sub btnClearLoadingImage_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnClearLoadingImage.Click
        txtLoadingImage.Clear()
    End Sub

    Private Sub btnOpenLoadingImage_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnOpenLoadingImage.Click
        Try
            If txtLoadingImage.Text <> String.Empty Then Process.Start(txtLoadingImage.Text)
        Catch ex As Exception
            MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
        End Try
    End Sub

    Private Sub btnHelpLoadingImg_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnHelpLoadingImg.Click
        Dim f As New frmHelp("Loading Image")
        f.ShowDialog(Me)
    End Sub

    Private Sub btnClearCommandMap_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnClearCommandMap.Click
        txtCommandMap.Clear()
    End Sub

    Private Sub btnOpenCommandMap_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnOpenCommandMap.Click
        Try
            If txtCommandMap.Text <> String.Empty Then Process.Start(txtCommandMap.Text)
        Catch ex As Exception
            MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
        End Try
    End Sub

    Private Sub btnHelpCommandMap_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnHelpCommandMap.Click
        Dim f As New frmHelp("Commandmap Image")
        f.ShowDialog(Me)
    End Sub

#End Region

#End Region

#Region " LIMBO MENU "

#End Region

#Region " MISC "

    Private Sub btnLoc_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnLoc.Click
        If txtBSP.Text = String.Empty Then
            MessageBox.Show("Please select a BSP file first.", "No BSP File found", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
            Exit Sub
        End If

        Using ofd As New OpenFileDialog With {.Title = "Select Location File", _
                                      .InitialDirectory = IO.Path.Combine(My.Settings.WolfPath, "maps"), _
                                      .CheckFileExists = True, _
                                      .Multiselect = False, _
                                      .Filter = "W:ET Location Files (*_loc.dat)|*_loc.dat"}

            If ofd.ShowDialog = Windows.Forms.DialogResult.OK Then
                If ofd.FileName.Contains("\etmain\maps\") Then
                    If IO.Path.GetFileNameWithoutExtension(ofd.FileName) = IO.Path.GetFileNameWithoutExtension(txtBSP.Text) & "_loc" Then
                        txtLoc.Text = ofd.FileName
                    Else
                        MessageBox.Show("Location file name does not math with BSP file name.", "Invalid File", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
                    End If
                Else
                    MessageBox.Show("Please select a Location file from your etmain\maps directory.", "Invalid Directory", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
                End If
            End If
        End Using
    End Sub

    Private Sub btnReadme_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnReadme.Click
        Using ofd As New OpenFileDialog With {.Title = "Select Readme File", _
                                      .InitialDirectory = My.Settings.WolfPath, _
                                      .CheckFileExists = True, _
                                      .Multiselect = False, _
                                      .Filter = "Document Files (*.txt, *.rtf)|*.txt;*.rtf"}

            If ofd.ShowDialog = Windows.Forms.DialogResult.OK Then
                txtReadme.Text = ofd.FileName
            End If
        End Using
    End Sub

#Region " Controls "

    Private Sub btnClearLoc_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnClearLoc.Click
        txtLoc.Clear()
    End Sub

    Private Sub btnOpenLoc_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnOpenLoc.Click
        Try
            OpenFile(txtLoc.Text)
        Catch ex As Exception
            MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
        End Try
    End Sub

    Private Sub btnHelpLoc_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnHelpLoc.Click
        Dim f As New frmHelp("Location File")
        f.ShowDialog(Me)
    End Sub

    Private Sub btnAddMiscFile_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAddMiscFile.Click
        Dim folderToSelect As clsMiscFolder = Nothing

        Using ofd As New OpenFileDialog With {.Title = "Select File(s)", _
                                              .InitialDirectory = My.Settings.WolfPath, _
                                              .Multiselect = True, _
                                              .CheckFileExists = True, _
                                              .Filter = "All Files|*.*"}

            If ofd.ShowDialog = Windows.Forms.DialogResult.OK And ofd.FileNames.Length > 0 Then

                If ofd.FileNames.Length > 0 Then
                    folderToSelect = AddMiscFiles(ofd.FileNames)
                End If
            End If
        End Using

        If folderToSelect IsNot Nothing Then
            lstMiscFolders.SelectedItem = folderToSelect
        End If
        RefreshMiscList()
    End Sub

    Private Sub btnRemoveMiscFile_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnRemoveMiscFile.Click
        If lstMiscFiles.SelectedItems.Count > 0 Then
            Dim mf As clsMiscFolder = GetFolderFromShortMisc(lstMiscFiles.SelectedItems(0).ToString)
            For Each Misc As clsMisc In lstMiscFiles.SelectedItems
                If mf IsNot Nothing Then
                    mf.Files.RemoveAt(mf.ShortFiles.IndexOf(Misc.ShortFile))
                End If
            Next

            If mf.Files.Count = 0 Then
                lstMiscFolders.Items.Remove(mf)
            End If
        End If

        RefreshMiscList()
    End Sub

    Private Sub btnRemoveMiscFolder_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnRemoveMiscFolder.Click
        Dim mf As clsMiscFolder = TryCast(lstMiscFolders.SelectedItem, clsMiscFolder)
        If mf IsNot Nothing Then
            If MessageBox.Show("The selected folder contains " & mf.Files.Count & " files." & Environment.NewLine & _
                   "Are you sure you want to remove them all from the current project?", "Remove Folder", MessageBoxButtons.YesNo, MessageBoxIcon.Question) = Windows.Forms.DialogResult.Yes Then
                lstMiscFolders.Items.Remove(mf)
            End If
        End If
    End Sub

    Private Sub lstMiscFolders_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lstMiscFolders.SelectedIndexChanged
        RefreshMiscList()
    End Sub

    Private Sub lstMiscFolders_DoubleClick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lstMiscFolders.DoubleClick
        OpenMiscFolderMenuItem.PerformClick()
    End Sub

    Private Sub lstMiscFiles_DoubleClick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lstMiscFiles.DoubleClick
        OpenMiscFileMenuItem.PerformClick()
    End Sub

#End Region

#Region " FUNCTIONS "

    Private Sub RefreshMiscList()
        Dim mf As clsMiscFolder = TryCast(lstMiscFolders.SelectedItem, clsMiscFolder)
        lstMiscFiles.Items.Clear()
        If mf IsNot Nothing Then
            For Each misc As clsMisc In mf.Files
                lstMiscFiles.Items.Add(misc)
            Next

            ChangeProject()
        End If
    End Sub

    Private Function AddMiscFiles(ByVal fileNames() As String) As clsMiscFolder
        Dim folderToSelect As clsMiscFolder = Nothing

        If fileNames.Length > 0 Then
            Dim parentFolder As String = IO.Path.GetDirectoryName(fileNames(0))
            If parentFolder.Contains("\etmain") Then

                For Each file As String In fileNames
                    If file IsNot Nothing And file <> String.Empty Then
                        parentFolder = IO.Path.GetDirectoryName(file)
                        If parentFolder.Contains("\etmain") Then

                            If Not MiscFolderExists(parentFolder) Then
                                Dim mf As New clsMiscFolder(parentFolder, New String() {file})
                                lstMiscFolders.Items.Add(mf)
                                folderToSelect = mf
                            Else
                                Dim mf As clsMiscFolder = GetMiscFolderFromPath(parentFolder)
                                If mf IsNot Nothing Then
                                    Dim bln As Boolean = False
                                    For Each misc As clsMisc In mf.Files
                                        If misc.File = file Then
                                            bln = True
                                        End If
                                    Next
                                    If Not bln Then
                                        mf.Files.Add(New clsMisc(file))
                                    End If
                                End If

                                folderToSelect = mf
                            End If
                        End If
                    End If
                Next

                ChangeProject()
            Else
                MessageBox.Show("Please select a file from your etmain directory, or any sub-directory.", "Invalid Directory", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
            End If
        End If

        Return folderToSelect
    End Function

    Private Function MiscFolderExists(ByVal folder As String) As Boolean
        For Each mf As clsMiscFolder In lstMiscFolders.Items
            If mf.FolderName = folder Then
                Return True
                Exit Function
            End If
        Next
        Return False
    End Function

    Private Function GetFolderFromMiscFile(ByVal file As String) As clsMiscFolder
        For Each mf As clsMiscFolder In lstMiscFolders.Items
            For Each m As clsMisc In mf.Files
                If m.File = file Then
                    Return mf
                    Exit Function
                End If
            Next
        Next
        Return Nothing
    End Function

    Private Function GetMiscFolderFromPath(ByVal folder As String) As clsMiscFolder
        For Each mf As clsMiscFolder In lstMiscFolders.Items
            If mf.FolderName = folder Then
                Return mf
                Exit Function
            End If
        Next
        Return Nothing
    End Function

    Private Function GetFolderFromShortMisc(ByVal file As String) As clsMiscFolder
        For Each mf As clsMiscFolder In lstMiscFolders.Items
            If mf.ShortFiles.Contains(file) Then
                Return mf
                Exit Function
            End If
        Next
        Return Nothing
    End Function

#End Region

#Region " ContextMenuStrip "

    Private Sub cmsMiscFolders_Opening(ByVal sender As System.Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles cmsMiscFolders.Opening
        OpenMiscFolderMenuItem.Enabled = (lstMiscFolders.SelectedItem IsNot Nothing)
        DeleteMiscFolderMenuItem.Enabled = (lstMiscFolders.SelectedItem IsNot Nothing)
    End Sub

    Private Sub OpenMiscFolderMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles OpenMiscFolderMenuItem.Click
        Dim mf As clsMiscFolder = TryCast(lstMiscFolders.SelectedItem, clsMiscFolder)
        Try
            OpenFile(mf.FolderName)
        Catch ex As Exception
            MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
        End Try
    End Sub

    Private Sub DeleteMiscFolderMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles DeleteMiscFolderMenuItem.Click
        btnRemoveMiscFolder.PerformClick()
    End Sub

    Private Sub cmsMisc_Opening(ByVal sender As System.Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles cmsMisc.Opening
        OpenMiscFileMenuItem.Enabled = (lstMiscFiles.SelectedItems.Count > 0)
        DeleteMiscFileMenuItem.Enabled = (lstMiscFiles.SelectedItems.Count > 0)
    End Sub

    Private Sub AddMiscFileMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles AddMiscFileMenuItem.Click
        btnAddMiscFile.PerformClick()
    End Sub

    Private Sub OpenMiscFileMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles OpenMiscFileMenuItem.Click
        Dim m As clsMisc = TryCast(lstMiscFiles.SelectedItem, clsMisc)
        Try
            OpenFile(m.File)
        Catch ex As Exception
            MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
        End Try
    End Sub

    Private Sub DeleteMiscFileMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles DeleteMiscFileMenuItem.Click
        btnRemoveMiscFile.PerformClick()
    End Sub

#End Region

#End Region





#Region " MENU & TOOLSTRIP "

    Private Sub GenerateArenaFileToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles GenerateArenaFileToolStripMenuItem.Click
        frmGenerateArena.Show(Me)
        frmGenerateArena.txtMapname.Text = IO.Path.GetFileNameWithoutExtension(txtBSP.Text)
    End Sub

    Private Sub GenerateObjectiveDataFileToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles GenerateObjectiveDataFileToolStripMenuItem.Click
        Try

            'Read script file for number of objectives
            If txtScript.Text <> String.Empty Then
                If IO.File.Exists(txtScript.Text) Then
                    Dim lines() As String = IO.File.ReadAllLines(txtScript.Text)
                    For Each line As String In lines
                        If line.Trim.StartsWith("wm_number_of_objectives") Then
                            Dim s As String = line.Trim
                            s = s.Substring("wm_number_of_objectives".Length).Trim

                            If s.Contains("//") Then
                                s = s.Substring(0, s.IndexOf("//")).Trim
                            End If
                            If s.Contains(ControlChars.Tab) Then
                                s = s.Substring(0, s.IndexOf(ControlChars.Tab)).Trim
                            End If
                            If s.Contains(" ") Then
                                s = s.Substring(0, s.IndexOf(" ")).Trim
                            End If

                            Dim i As Integer
                            Integer.TryParse(s, i)
                            If i > 0 Then
                                If i > 8 Then
                                    MessageBox.Show("Warning, PK3 Creator detected more than the maximum of 8 objectives in your Script File (wm_number_of_objectives)!", "Max Objectives", MessageBoxButtons.OK, MessageBoxIcon.Warning)
                                Else
                                    Dim f As New frmGenerateObjData(i, IO.Path.GetFileNameWithoutExtension(txtBSP.Text))
                                    f.Show(Me)
                                    Exit Sub
                                End If
                            End If
                        End If
                    Next
                    MessageBox.Show("Script File should contain a reference to the number of objectives (wm_number_of_objectives).", "Not Found", MessageBoxButtons.OK, MessageBoxIcon.Warning)

                End If
            Else
                MessageBox.Show("PK3 Creator reads the Script File for the number of objectives." & Environment.NewLine & "Please select a valid Script File first.", "No Script File found", MessageBoxButtons.OK, MessageBoxIcon.Information)
            End If

        Catch ex As Exception
            MessageBox.Show("Error while reading Script File for Number Of Objectives." & Environment.NewLine & ex.Message & Environment.NewLine & Environment.NewLine & "Details:" & Environment.NewLine & ex.ToString, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
        End Try
    End Sub

    Private Sub OptionsToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles OptionsToolStripMenuItem.Click
        frmOptions.Show()
    End Sub

    Private Sub ExitToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ExitToolStripMenuItem.Click
        Me.Close()
    End Sub

    Private Sub AboutToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles AboutToolStripMenuItem.Click
        frmAbout.Show(Me)
    End Sub

    Private Sub HelpToolStripMenuItem1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles HelpToolStripMenuItem1.Click
        Dim file As String = IO.Path.Combine(IO.Path.GetDirectoryName(Application.ExecutablePath), "Readme.rtf")
        If IO.File.Exists(file) Then OpenFile(file)
    End Sub

#End Region

#Region " PROJECT SAVING / LOADING "

    Dim ProjectSaved, ProjectChanged As Boolean
    Dim projectPath, projectTitle As String

    Private Function CreateProjectString() As String
        Dim nl As String = Environment.NewLine

        Dim bsp As String = "[BSP]" & txtBSP.Text
        Dim lightmap As String = "[LIGHTMAP]" & txtLightmap.Text
        Dim tracemap As String = "[TRACEMAP]" & txtTracemap.Text
        Dim script As String = "[SCRIPT]" & txtScript.Text
        Dim objdata As String = "[OBJDATA]" & txtObjData.Text
        Dim arena As String = "[ARENA]" & txtArena.Text
        Dim soundfile As String = "[SOUNDS]" & txtSounds.Text
        Dim spsfile As String = "[SPS]" & txtSPS.Text
        Dim loadingimg As String = "[LOADINGIMG]" & txtLoadingImage.Text
        Dim cmimg As String = "[COMMANDMAPIMG]" & txtCommandMap.Text
        Dim loc As String = "[LOCATIONS]" & txtLoc.Text
        Dim readme As String = "[README]" & txtReadme.Text

        Dim textList As New List(Of String)
        For Each tf As clsTextureFolder In lstTextFolders.Items
            For Each t As clsTexture In tf.Files
                If Not textList.Contains(t.File) Then textList.Add(t.File)
            Next
        Next
        Dim textures As String = "[TEXTURES BEGIN]" & nl & String.Join(nl, textList.ToArray) & nl & "[TEXTURES END]"

        Dim modList As New List(Of String)
        For Each mf As clsModelFolder In lstModelFolders.Items
            For Each m As clsModel In mf.Files
                If Not modList.Contains(m.File) Then modList.Add(m.File)
            Next
        Next
        Dim models As String = "[MODELS BEGIN]" & nl & String.Join(nl, modList.ToArray) & nl & "[MODELS END]"

        Dim soundList As New List(Of String)
        For Each sf As clsSoundFolder In lstSoundFolders.Items
            For Each s As clsSound In sf.Files
                If Not soundList.Contains(s.File) Then soundList.Add(s.File)
            Next
        Next
        Dim sounds As String = "[SOUNDS BEGIN]" & nl & String.Join(nl, soundList.ToArray) & nl & "[SOUNDS END]"

        Dim miscList As New List(Of String)
        For Each mf As clsMiscFolder In lstMiscFolders.Items
            For Each m As clsMisc In mf.Files
                If Not miscList.Contains(m.File) Then miscList.Add(m.File)
            Next
        Next
        Dim miscs As String = "[MISC BEGIN]" & nl & String.Join(nl, miscList.ToArray) & nl & "[MISC END]"

        Dim shaderList As New List(Of String)
        For Each s As clsShader In lstShaders.Items
            If Not shaderList.Contains(s.File) Then shaderList.Add(s.File)
        Next
        Dim shaders As String = "[SHADERS BEGIN]" & nl & String.Join(nl, shaderList.ToArray) & nl & "[SHADERS END]"


        Dim projectString As String = bsp & nl & lightmap & nl & tracemap & nl & script & _
                                       nl & objdata & nl & arena & nl & soundfile & nl & spsfile & nl & loadingimg & _
                                       nl & cmimg & nl & loc & nl & readme & nl & nl & _
                                       textures & nl & nl & shaders & nl & nl & models & nl & nl & sounds & _
                                       nl & nl & miscs

        Return projectString
    End Function

    Private Sub NewToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles NewToolStripMenuItem.Click, NewToolStripButton.Click
        If ProjectChanged Then
            Dim res As DialogResult = MessageBox.Show("Your project has changed since you last saved it. Do you want to save?", "Save", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Information)
            If res = Windows.Forms.DialogResult.Yes Then
                If ProjectSaved Then
                    SaveProject()
                Else
                    SaveProjectAs()
                End If
                NewProject()
            ElseIf res = Windows.Forms.DialogResult.No Then
                NewProject()
            End If
        Else
            NewProject()
        End If
    End Sub

    Private Sub OpenToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles OpenToolStripMenuItem.Click, OpenToolStripButton.Click
        If ProjectChanged Then
            Dim res As DialogResult = MessageBox.Show("Your project has changed since you last saved it. Do you want to save?", "Save", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Information)
            If res = Windows.Forms.DialogResult.Yes Then
                If ProjectSaved Then
                    SaveProject()
                Else
                    SaveProjectAs()
                End If
                OpenProject()
            ElseIf res = Windows.Forms.DialogResult.No Then
                OpenProject()
            End If
        Else
            OpenProject()
        End If
    End Sub

    Private Sub SaveToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles SaveToolStripMenuItem.Click, SaveToolStripButton.Click
        If ProjectSaved Then
            SaveProject()
        Else
            SaveProjectAs()
        End If
    End Sub

    Private Sub SaveAsToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles SaveAsToolStripMenuItem.Click
        SaveProjectAs()
    End Sub

    Private Sub NewProject()
        'Clear all 
        txtBSP.Clear()
        txtLightmap.Clear()
        txtTracemap.Clear()
        txtScript.Clear()
        txtObjData.Clear()
        txtSounds.Clear()
        txtSPS.Clear()
        txtLoc.Clear()
        txtArena.Clear()
        txtLoadingImage.Clear()
        txtCommandMap.Clear()
        txtReadme.Clear()

        lstTextFolders.Items.Clear()
        RefreshTextureList()

        lstModelFolders.Items.Clear()
        RefreshModelList()

        lstShaders.Items.Clear()

        lstSoundFolders.Items.Clear()
        RefreshSoundList()

        lstMiscFolders.Items.Clear()
        RefreshMiscList()

        projectPath = String.Empty
        projectTitle = "Untitled Project"

        ProjectChanged = False
        ProjectSaved = False
        UpdateTitle()
    End Sub

    Private Sub OpenProject()
        Using ofd As New OpenFileDialog With {.Title = "Open PK3 Project", _
                                              .Filter = "PK3 Project Files (*.pk3proj)|*.pk3proj"}
            If ofd.ShowDialog = Windows.Forms.DialogResult.OK Then
                OpenProject(ofd.FileName)
            End If
        End Using
    End Sub

    Private Sub OpenProject(ByVal file As String)
        projectPath = file
        projectTitle = IO.Path.GetFileNameWithoutExtension(file)

        LoadProject(file)

        ProjectChanged = False
        ProjectSaved = True
        UpdateTitle()
    End Sub

    Private Sub LoadProject(ByVal file As String)
        Dim text As String = IO.File.ReadAllText(file)
        Dim lines() As String = IO.File.ReadAllLines(file)

        'Read in the separate files
        For Each s As String In lines
            s = s.Trim

            If s <> String.Empty Then
                If s.StartsWith("[BSP]") Then
                    txtBSP.Text = s.Substring("[BSP]".Length)
                ElseIf s.StartsWith("[LIGHTMAP]") Then
                    txtLightmap.Text = s.Substring("[LIGHTMAP]".Length)
                ElseIf s.StartsWith("[TRACEMAP]") Then
                    txtTracemap.Text = s.Substring("[TRACEMAP]".Length)
                ElseIf s.StartsWith("[SCRIPT]") Then
                    txtScript.Text = s.Substring("[SCRIPT]".Length)
                ElseIf s.StartsWith("[OBJDATA]") Then
                    txtObjData.Text = s.Substring("[OBJDATA]".Length)
                ElseIf s.StartsWith("[ARENA]") Then
                    txtArena.Text = s.Substring("[ARENA]".Length)
                ElseIf s.StartsWith("[SOUNDS]") Then
                    txtSounds.Text = s.Substring("[SOUNDS]".Length)
                ElseIf s.StartsWith("[SPS]") Then
                    txtSPS.Text = s.Substring("[SPS]".Length)
                ElseIf s.StartsWith("[LOADINGIMG]") Then
                    txtLoadingImage.Text = s.Substring("[LOADINGIMG]".Length)
                ElseIf s.StartsWith("[COMMANDMAPIMG]") Then
                    txtCommandMap.Text = s.Substring("[COMMANDMAPIMG]".Length)
                ElseIf s.StartsWith("[LOCATIONS]") Then
                    txtLoc.Text = s.Substring("[LOCATIONS]".Length)
                ElseIf s.StartsWith("[README]") Then
                    txtReadme.Text = s.Substring("[README]".Length)
                End If
            End If
        Next

        'Read the texture/model/sound/shader block
        Dim textBlock As String = GetBlock(text, "TEXTURES")
        Dim shaderBlock As String = GetBlock(text, "SHADERS")
        Dim modelBlock As String = GetBlock(text, "MODELS")
        Dim soundsBlock As String = GetBlock(text, "SOUNDS")
        Dim miscBlock As String = GetBlock(text, "MISC")

        Dim c() As Char = New Char() {CChar(Environment.NewLine), CChar(vbCr), CChar(vbCrLf), CChar(vbNewLine)}
        Dim texturestemp() As String = textBlock.Split(c, StringSplitOptions.RemoveEmptyEntries)
        Dim shaderstemp() As String = shaderBlock.Split(c, StringSplitOptions.RemoveEmptyEntries)
        Dim modelstemp() As String = modelBlock.Split(c, StringSplitOptions.RemoveEmptyEntries)
        Dim soundstemp() As String = soundsBlock.Split(c, StringSplitOptions.RemoveEmptyEntries)
        Dim miscstemp() As String = miscBlock.Split(c, StringSplitOptions.RemoveEmptyEntries)

        Dim textures As New List(Of String)
        For Each s As String In texturestemp
            If s.Trim <> String.Empty Then textures.Add(s.Trim)
        Next

        Dim models As New List(Of String)
        For Each s As String In modelstemp
            If s.Trim <> String.Empty Then models.Add(s.Trim)
        Next

        Dim sounds As New List(Of String)
        For Each s As String In soundstemp
            If s.Trim <> String.Empty Then sounds.Add(s.Trim)
        Next

        Dim shaders As New List(Of String)
        For Each s As String In shaderstemp
            If s.Trim <> String.Empty Then shaders.Add(s.Trim)
        Next

        Dim miscs As New List(Of String)
        For Each s As String In miscstemp
            If s.Trim <> String.Empty Then miscs.Add(s.Trim)
        Next

        AddTextures(textures.ToArray)
        For Each s As String In shaders
            AddShader(s, False)
        Next
        AddModels(models.ToArray)
        AddSounds(sounds.ToArray)
        AddMiscFiles(miscs.ToArray)

    End Sub

    Private Function GetBlock(ByVal text As String, ByVal s As String) As String
        Dim beginStr As String = "[" & s & " BEGIN]"
        Dim endStr As String = "[" & s & " END]"
        Dim startPos As Integer = text.IndexOf(beginStr) + beginStr.Length
        Dim endPos As Integer = text.IndexOf(endStr)
        Dim len As Integer = endPos - startPos

        Return text.Substring(startPos, len)
    End Function

    Private Sub SaveProject()
        Try
            IO.File.WriteAllText(projectPath, CreateProjectString)

            ProjectChanged = False
            ProjectSaved = True
            UpdateTitle()
        Catch ex As Exception
            MessageBox.Show("An error occured while saving your project file." & Environment.NewLine & ex.Message & Environment.NewLine & Environment.NewLine & "Details:" & Environment.NewLine & ex.ToString, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
        End Try
    End Sub

    Private Sub SaveProjectAs()
        Using sfd As New SaveFileDialog With {.Title = "Save PK3 Project", _
                                              .Filter = "PK3 Project Files (*.pk3proj)|*.pk3proj"}
            If sfd.ShowDialog = Windows.Forms.DialogResult.OK Then
                projectPath = sfd.FileName
                projectTitle = IO.Path.GetFileNameWithoutExtension(sfd.FileName)

                SaveProject()
            End If
        End Using
    End Sub

    Private Sub ExportToPK3ToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ExportToPK3ToolStripMenuItem.Click, ExportToPK3ToolStripButton.Click
        If txtBSP.Text <> String.Empty Then
            Using sfd As New SaveFileDialog With {.Title = "Export project to PK3 File", _
                                                  .Filter = "PK3 Files (*.pk3)|*.pk3"}
                If sfd.ShowDialog = Windows.Forms.DialogResult.OK Then
                    Try
                        Using z As New ZipOutputStream(IO.File.Create(sfd.FileName))

                            z.SetLevel(My.Settings.CompressionQuality)



                            pb.Minimum = 0
                            pb.Maximum = 17
                            pb.Value = 0
                            pb.Style = ProgressBarStyle.Continuous
                            Me.Enabled = False

                            lblLoading.Text = "Zipping BSP File..."
                            lblLoading.Visible = True
                            SaveToPK3File(z, txtBSP.Text)

                            lblLoading.Text = "Zipping Lightmap Directory..."
                            For Each file As String In IO.Directory.GetFiles(txtLightmap.Text)
                                SaveToPK3File(z, file)
                            Next

                            lblLoading.Text = "Zipping Tracemap File..."
                            SaveToPK3File(z, txtTracemap.Text)

                            lblLoading.Text = "Zipping Script File..."
                            SaveToPK3File(z, txtScript.Text)

                            lblLoading.Text = "Zipping Objective Data File..."
                            SaveToPK3File(z, txtObjData.Text)

                            lblLoading.Text = "Zipping Arena File..."
                            SaveToPK3File(z, txtArena.Text)

                            lblLoading.Text = "Zipping Sounds File..."
                            SaveToPK3File(z, txtSounds.Text)

                            lblLoading.Text = "Zipping SPS File..."
                            SaveToPK3File(z, txtSPS.Text)

                            lblLoading.Text = "Zipping Loading Image..."
                            SaveToPK3File(z, txtLoadingImage.Text)

                            lblLoading.Text = "Zipping Commandmap Image..."
                            SaveToPK3File(z, txtCommandMap.Text)

                            lblLoading.Text = "Zipping Locations File..."
                            SaveToPK3File(z, txtLoc.Text)

                            lblLoading.Text = "Zipping Readme File..."
                            SaveToPK3File(z, txtReadme.Text)

                            lblLoading.Text = "Zipping Textures..."
                            For Each tf As clsTextureFolder In lstTextFolders.Items
                                For Each t As clsTexture In tf.Files
                                    SaveToPK3File(z, t.File)
                                Next
                            Next

                            lblLoading.Text = "Zipping Shaders..."
                            For Each s As clsShader In lstShaders.Items
                                SaveToPK3File(z, s.File)
                            Next

                            lblLoading.Text = "Zipping Models..."
                            For Each mf As clsModelFolder In lstModelFolders.Items
                                For Each m As clsModel In mf.Files
                                    SaveToPK3File(z, m.File)
                                Next
                            Next

                            lblLoading.Text = "Zipping Sounds..."
                            For Each sf As clsSoundFolder In lstSoundFolders.Items
                                For Each s As clsSound In sf.Files
                                    SaveToPK3File(z, s.File)
                                Next
                            Next

                            lblLoading.Text = "Zipping Misc Files..."
                            For Each mf As clsMiscFolder In lstMiscFolders.Items
                                For Each m As clsMisc In mf.Files
                                    SaveToPK3File(z, m.File)
                                Next
                            Next



                            z.Finish()
                            z.Close()

                            lblLoading.Visible = False
                            Me.Enabled = True
                            pb.Value = 0
                            MessageBox.Show("Project succesfully exported to PK3 file.", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information)

                        End Using

                    Catch ex As Exception
                        MessageBox.Show("An error occured while exporting to PK3 file:" & Environment.NewLine & ex.Message & Environment.NewLine & Environment.NewLine & _
                                        "Details:" & Environment.NewLine & ex.ToString)
                        Me.Enabled = True
                    End Try
                End If
            End Using
        Else
            MessageBox.Show("Please select a BSP file first.", "No BSP file", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
        End If
    End Sub

    Private Sub SaveToPK3File(ByRef z As ZipOutputStream, ByVal file As String)
        Application.DoEvents()

        Dim buffer(4096) As Byte
        Dim entry As ZipEntry

        If file <> String.Empty Then
            entry = New ZipEntry(GetShortFile(file))
            entry.DateTime = DateTime.Now
            z.PutNextEntry(entry)
            Using fs As IO.FileStream = IO.File.OpenRead(file)
                Dim sourceBytes As Integer
                Do
                    sourceBytes = fs.Read(buffer, 0, buffer.Length)
                    z.Write(buffer, 0, sourceBytes)
                Loop While sourceBytes > 0
            End Using
        End If

        pb.Increment(1)
        Application.DoEvents()
    End Sub

    Private Sub UpdateTitle()
        Me.Text = projectTitle & " - [ PK3 Creator (Alpha) ]"
        If ProjectChanged Then Me.Text &= "*"
    End Sub

    Private Function GetShortFile(ByVal file As String) As String
        If file.Contains("\etmain\") Then
            'Cut everything before '\etmain\'.
            Return file.Substring(file.IndexOf("\etmain\") + 8)
        Else
            Return String.Empty
        End If
    End Function

#End Region

#Region " Form related "

    Private Sub frmMain_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        Me.TopMost = True   'Workaround: otherwise the dialog box keeps popping up in the background

        If My.Settings.WolfPath = String.Empty Then
            MessageBox.Show("Please select your root 'Wolfenstein - Enemy Territory\etmain' mapping installation.", "Select W:ET Path", MessageBoxButtons.OK, MessageBoxIcon.Information)

            Using fbd As New FolderBrowserDialog With {.Description = "Select your 'W:ET\etmain' directory."}
                Dim bln As Boolean = False
                Dim res As DialogResult = Windows.Forms.DialogResult.Cancel

                Do While bln = False Or res <> Windows.Forms.DialogResult.OK
                    res = fbd.ShowDialog

                    If res = Windows.Forms.DialogResult.OK Then
                        If fbd.SelectedPath.EndsWith("\etmain") Then
                            bln = True
                        Else
                            bln = False
                            MessageBox.Show("Please select your 'etmain' directory.", "Select etmain Directory", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
                        End If
                    Else
                        Me.Close()
                        End
                    End If
                Loop

                My.Settings.WolfPath = fbd.SelectedPath
                My.Settings.Save()

            End Using
        End If

        Me.TopMost = False  'Workaround (see above)


        'Resize listview columns
        lstTextFolders.Columns(0).Width = lstTextFolders.Width - 4
        lstTextFiles.Columns(1).Width = CInt(lstTextFiles.Width / 4)
        lstTextFiles.Columns(0).Width = CInt(3 * lstTextFiles.Width / 4) - 4
        lstShaders.Columns(1).Width = CInt(lstShaders.Width / 4)
        lstShaders.Columns(0).Width = CInt(3 * lstShaders.Width / 4) - 4
        lstModelFolders.Columns(0).Width = lstModelFolders.Width - 4
        lstModelFiles.Columns(1).Width = CInt(lstModelFiles.Width / 4)
        lstModelFiles.Columns(0).Width = CInt(3 * lstModelFiles.Width / 4) - 4
        lstSoundFolders.Columns(0).Width = lstSoundFolders.Width - 4
        lstSoundFiles.Columns(1).Width = CInt(lstSoundFiles.Width / 4)
        lstSoundFiles.Columns(0).Width = CInt(3 * lstSoundFiles.Width / 4) - 4
        lstMiscFolders.Columns(0).Width = lstMiscFolders.Width - 4
        lstMiscFiles.Columns(1).Width = CInt(lstMiscFiles.Width / 4)
        lstMiscFiles.Columns(0).Width = CInt(3 * lstMiscFiles.Width / 4) - 4


        'Open new project or load project if commandline arguments contain a file
        NewProject()
        For Each arg As String In Environment.GetCommandLineArgs()
            If arg.EndsWith(".pk3proj") Then
                OpenProject(arg)
                Exit For
            End If
        Next

    End Sub

    Private Sub frmMain_FormClosing(ByVal sender As System.Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles MyBase.FormClosing
        If ProjectChanged Then
            Dim res As DialogResult = MessageBox.Show("Your project has changed since you last saved it. Do you want to save?", "Save", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Information)
            If res = Windows.Forms.DialogResult.Yes Then
                If ProjectSaved Then
                    SaveProject()
                Else
                    SaveProjectAs()
                End If
            ElseIf res = Windows.Forms.DialogResult.Cancel Then
                e.Cancel = True
            End If
        End If
    End Sub

#End Region

#Region " Misc functions "

    Private Sub OpenFile(ByVal path As String)
        If path <> String.Empty Then
            path = path.Replace("/", "\")
            Dim psi As New ProcessStartInfo
            psi.FileName = path
            psi.ErrorDialog = True

            Process.Start(psi)
        End If
    End Sub

    Private Sub ChangeProject()
        If Not ProjectChanged Then
            Me.Text &= "*"
            ProjectChanged = True
        End If
    End Sub

    Private Sub TextBoxes_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles txtTracemap.TextChanged, txtLightmap.TextChanged, txtBSP.TextChanged, txtScript.TextChanged, txtObjData.TextChanged, txtArena.TextChanged, txtSounds.TextChanged, txtLoadingImage.TextChanged, txtCommandMap.TextChanged, txtReadme.TextChanged, txtLoc.TextChanged, txtSPS.TextChanged
        ChangeProject()
    End Sub

#End Region

End Class
