Imports System.Text
Imports System.Text.RegularExpressions
Imports Microsoft.Win32
Imports System.ComponentModel
Imports System.Globalization
Imports System.Management
Imports System.Xml.Serialization
Imports System.Runtime.InteropServices
Imports System.Drawing.Design
Imports System.Runtime.Serialization
Imports System.Runtime.Serialization.Formatters.Binary
Imports System.Reflection
Imports System.Threading
Imports System.Resources
Imports System.Environment

Imports Stax
Imports Stax.UI
Imports Stax.DynamicObject
Imports Stax.Win32
Imports Stax.AppFramework
Imports Stax.ComponentModel

Public Module ShortcutModule
    Public g As New GlobalClass
    Public p As New Project
    Public s As New ApplicationSettings
End Module

Public Class Paths
    Public Shared BeSweet As New BeSweetPackage
    Public Shared AutoCrop As New AutoCropPackage
    Public Shared Deen As New DeenPackage
    Public Shared UnDot As New UnDotPackage
    Public Shared AviSynth As New AviSynthPackage
    Public Shared Decomb As New DecombPackage
    Public Shared DGIndex As New DGIndexPackage
    Public Shared DivX As New DivXPackage
    Public Shared FluxSmooth As New FluxSmoothPackage
    Public Shared VSFilter As New VSFilterPackage
    Public Shared Java As New JavaPackage
    Public Shared ProjectX As New ProjectXPackage
    Public Shared VirtualDubMod As New VirtualDubModPackage
    Public Shared MP4Box As New MP4BoxPackage
    Public Shared Mkvmerge As New MKVtoolnixPackage
    Public Shared VirtualDub As New VirtualDubPackage
    Public Shared XviD As New XviDPackage
    Public Shared x264 As New x264Package
    Public Shared DirectShowSource As New DirectShowSourcePackage
    Public Shared MediaInfo As New MediaInfoPackage
    Public Shared DivXMux As New DivXMuxPackage
    Public Shared DGDecode As New DGDecodePackage
    Public Shared Vorbis As New VorbisPackage
    Public Shared PMPMuxer As New PMPMuxerPackage

    Public Shared Packages As New Dictionary(Of String, Package)

    Shared Sub New()
        AddPackage(AutoCrop)
        AddPackage(AviSynth)
        AddPackage(BeSweet)
        AddPackage(Decomb)
        AddPackage(DGIndex)
        AddPackage(DivX)
        AddPackage(FluxSmooth)
        AddPackage(Java)
        AddPackage(ProjectX)
        AddPackage(VirtualDubMod)
        AddPackage(VirtualDub)
        AddPackage(XviD)
        AddPackage(MP4Box)
        AddPackage(x264)
        AddPackage(Deen)
        AddPackage(UnDot)
        AddPackage(DirectShowSource)
        AddPackage(Mkvmerge)
        AddPackage(MediaInfo)
        AddPackage(DivXMux)
        AddPackage(DGDecode)
        AddPackage(VSFilter)
        AddPackage(Vorbis)
        AddPackage(PMPMuxer)
    End Sub

    Public Shared Sub AddPackage(ByVal package As Package)
        Packages.Add(package.Name, package)
    End Sub

    Public Shared Function VerifyRequirements() As Boolean
        If Not File.Exists(CommonDirs.System + s.YV12Decoder.Value) Then
            Msg.Show("Most sources are YV12 and thus a YV12 decoder is required. The decoder defined in the settings '" + s.YV12Decoder.Value + "' could not be located. Please install the defined decoder.", MsgIcon.Warning)
            Return False
        End If

        Dim dec As String = RegistryHelp.LocalMachine.GetString("SOFTWARE\Microsoft\Windows NT\CurrentVersion\Drivers32", "VIDC.YV12")

        If dec <> s.YV12Decoder.Value Then
            If Msg.Show("Most sources are YV12 and thus a YV12 decoder is required. The decoder defined in the settings '" + s.YV12Decoder.Value + "' is not registered. Would you like to register now?", MsgIcon.Question, MessageBoxButtons.OKCancel) = DialogResult.OK Then
                RegistryHelp.LocalMachine.SetValue("SOFTWARE\Microsoft\Windows NT\CurrentVersion\Drivers32", "VIDC.YV12", s.YV12Decoder.Value)
            Else
                Return False
            End If
        End If

        If File.Exists(Paths.AviSynthPluginsDir + "MPEG2Dec3.dll") Then
            If Msg.Show("The AviSynth plugin MPEG2Dec3.dll conflicts with the AviSynth plugin DGDecode.dll." + vbCrLf + vbCrLf + "Please confirm to solve this conflict.", MsgIcon.Warning, MessageBoxButtons.OKCancel) = DialogResult.OK Then
                FileHelp.Delete(Paths.AviSynthPluginsDir + "MPEG2Dec3.dll")
            End If

            If File.Exists(Paths.AviSynthPluginsDir + "MPEG2Dec3.dll") Then
                Return False
            End If
        End If

        For Each i As String In Directory.GetFiles(Paths.AviSynthPluginsDir)
            If Regex.IsMatch(i, "(?i:decomb.+\.dll$)") Then
                If Msg.Show("A legacy Decomb version was found." + vbCrLf + vbCrLf + "Please confirm to solve this conflict.", MsgIcon.Warning, MessageBoxButtons.OKCancel) = DialogResult.OK Then
                    FileHelp.Delete(i)
                End If

                If File.Exists(i) Then
                    Return False
                End If
            End If
        Next

        For Each i As Package In Packages.Values
            If i.VerifyOK = False Then
                Return False
            End If
        Next

        Return True
    End Function

    Public Shared Function GetTempDir() As String
        If p.SourceFile = "" Then
            Return ""
        End If

        Dim ret As String = Filepath.GetDir(p.SourceFile) + "Temporary Files\"

        If Not Directory.Exists(ret) Then
            Directory.CreateDirectory(ret)
        End If

        Return ret
    End Function

    Public Shared Property AviSynthPluginsDir() As String
        Get
            Dim ret As String = RegistryHelp.LocalMachine.GetString( _
                "SOFTWARE\AviSynth", "plugindir2_5")

            If Not Directory.Exists(ret) Then
                ret = Paths.ExtractDir + "AviSynth Plugins"

                RegistryHelp.LocalMachine.SetValue("SOFTWARE\AviSynth", _
                    "plugindir2_5", ret)

                If Not Directory.Exists(ret) Then
                    Directory.CreateDirectory(ret)
                End If
            End If

            Return Filepath.AppendSeparator(ret)
        End Get
        Set(ByVal Value As String)
            RegistryHelp.LocalMachine.SetValue("SOFTWARE\AviSynth", _
                "plugindir2_5", Value)
        End Set
    End Property

    Public Shared ReadOnly Property ExtractDir() As String
        Get
            Dim ret As String

            If s.ExtractDir.Value <> "" Then
                ret = Filepath.AppendSeparator(CommonDirs.Startup + s.ExtractDir.Value)
                If Directory.Exists(ret) Then Return ret

                ret = Filepath.AppendSeparator(s.ExtractDir.Value)
                If Directory.Exists(ret) Then Return ret
            End If

            ret = CommonDirs.Startup + "Applications\"
            s.ExtractDir.Value = "Applications"

            If Not Directory.Exists(ret) Then
                Directory.CreateDirectory(ret)
            End If

            Return ret
        End Get
    End Property

    Public Shared ReadOnly Property DownloadDir() As String
        Get
            Dim ret As String

            If s.DownloadDir.Value <> "" Then
                ret = Filepath.AppendSeparator(CommonDirs.Startup + s.DownloadDir.Value)
                If Directory.Exists(ret) Then Return ret

                ret = Filepath.AppendSeparator(s.DownloadDir.Value)
                If Directory.Exists(ret) Then Return ret
            End If

            ret = CommonDirs.Startup + "Downloads\"
            s.DownloadDir.Value = "Downloads"

            If Not Directory.Exists(ret) Then
                Directory.CreateDirectory(ret)
            End If

            Return ret
        End Get
    End Property

    Private Shared SettingsDirValue As String

    Public Shared ReadOnly Property SettingsDir() As String
        Get
            If SettingsDirValue Is Nothing Then
                SettingsDirValue = RegistryHelp.CurrentUser.GetApplicationString("SettingsDir")
            End If

            Dim ret As String

            If Not String.IsNullOrEmpty(SettingsDirValue) Then
                ret = Filepath.AppendSeparator(CommonDirs.Startup + SettingsDirValue)
                If Directory.Exists(ret) Then Return ret

                ret = Filepath.AppendSeparator(SettingsDirValue)
                If Directory.Exists(ret) Then Return ret
            End If

            ret = CommonDirs.Startup + "Settings\"
            SettingsDirValue = "Settings"
            RegistryHelp.CurrentUser.SetApplicationValue("SettingsDir", "Settings")

            If Not Directory.Exists(ret) Then
                Directory.CreateDirectory(ret)
            End If

            Return ret
        End Get
    End Property

    Public Shared ReadOnly Property TemplateDir() As String
        Get
            Dim ret As String = SettingsDir + "Templates\"

            If Not Directory.Exists(ret) Then
                Directory.CreateDirectory(ret)

                Dim dvb As New Project
                dvb.Init()
                SafeSerialization.Serialize(dvb, ret + "DVB.rip")

                Dim dvd As New Project
                dvd.Init()
                dvd.Audio0 = New AudioSettings(AudioProfile.GetProfile("AAC - HE VBR 50-70 kbps"), New Language())
                dvd.Audio1 = New AudioSettings(AudioProfile.GetProfile("AAC - HE VBR 50-70 kbps"), New Language(New CultureInfo("en")))
                dvd.VideoEncoder.Muxer = New mkvmergeMuxer
                SafeSerialization.Serialize(dvd, ret + "DVD.rip")

                Dim psp As New Project
                psp.Init()
                psp.TargetWidth = 480
                psp.TargetHeight = 272
                psp.AutoResizeImage = 0
                psp.CompCheckAction = CompCheckAction.AdjustFileSize
                psp.AutoSmartCrop.Value = True
                psp.Audio0 = New AudioSettings(AudioProfile.GetProfile("MP3 - CBR 128 kbps 44100 Khz"), New Language())
                psp.VideoEncoder.Muxer = New PMPMuxer
                SafeSerialization.Serialize(psp, ret + "PlayStation Portable (PMP).rip")
            End If

            Return ret
        End Get
    End Property

    Public Shared ReadOnly Property DocumentsDir() As String
        Get
            Dim ret As String = CommonDirs.Startup + "Documents\"

            If Not Directory.Exists(ret) Then
                Directory.CreateDirectory(ret)
            End If

            Return ret
        End Get
    End Property

    Public Shared ReadOnly Property SettingsFile() As String
        Get
            Return SettingsDir + "Settings.bin"
        End Get
    End Property

    Public Shared ReadOnly Property DefaultProjectPath() As String
        Get
            Return SettingsDir + "Default.rip"
        End Get
    End Property
End Class

Public Class FileDialogFilter
    Public Shared Function GetVideo() As String
        Return "Source Files|*.avs;*.ts;*.avi;*.mpv;*.pva;*.vob;*.mpg;*.m2v;*.d2v;*.dat;*.rmvb;*.wmv|All Files|*.*"
    End Function

    Public Shared Function GetAudio() As String
        Return "Audio Files|*.wav;*.mp2;*.mp3;*.mpa;*.ac3;*.ogg;*.aac;*.mp4;*.dts|All Files|*.*"
    End Function
End Class

Public Class GlobalClass
    Public ProjectPath As String
    Public MainForm As MainForm
    Public ShutdownMode As ShutdownMode = ShutdownMode.Nothing
    Public PreviewLastPosition As Integer
    Public MinimizedWindows As Boolean
    Public SavedProject As New Project

    Public Function GetAutoSize(ByVal percentage As Integer) As Integer
        Dim ret As Integer
        Dim size As Integer = p.Size
        Dim bitrate As Integer = p.VideoBitrate

        For i As Integer = 1 To 10000
            p.Size = i
            p.VideoBitrate = CInt(Calc.GetTotalBitrate() - Calc.GetAudioBitrate())

            If Calc.GetPercent > percentage Then
                ret = i - 1
                Exit For
            End If
        Next

        p.Size = size
        p.VideoBitrate = bitrate

        If ret = 0 Then
            ret = size
        End If

        Return ret
    End Function

    Public Function WasProjectModified() As Boolean
        Return ObjectHelp.GetCompareString(g.SavedProject) <> ObjectHelp.GetCompareString(p)
    End Function

    Public Sub ShowCommandLinePreview(ByVal value As String)
        Using f As New StringEditorForm
            f.Text = "Command Line"
            f.cbWrap.Checked = False
            f.tb.Text = value
            f.tb.ReadOnly = True
            f.Width = 800
            f.Height = 200
            f.bOK.Visible = False
            f.bCancel.Text = "Close"
            f.ShowDialog()
        End Using
    End Sub

    Public Function IsValidSource() As Boolean
        If Not p.SourceAviSynthDocument.IsValid Then
            Msg.Show("Failed to detect a valid source.", "No Source", MsgIcon.Warning)
            Return False
        End If

        Return True
    End Function

    Public Sub ApplicationThreadException(ByVal sender As Object, ByVal e As ThreadExceptionEventArgs)
        If TypeOf e.Exception Is AbortException Then
            ProcessForm.CloseProcessForm()
        Else
            g.HandleException(e.Exception)
            Msg.Show("Please restart StaxRip.", MsgIcon.Warning)
        End If
    End Sub

    Public Function BelongsToSource(ByVal path As String) As Boolean
        If path.Contains("_") Then
            If StringHelp.DeleteRight(Filepath.GetNameNoExt(path), 2).StartsWith( _
                StringHelp.DeleteRight(Filepath.GetNameNoExt(p.SourceFile), 2)) Then

                Return True
            End If
        Else
            If Filepath.GetNameNoExt(path).Contains(Filepath.GetNameNoExt(p.SourceFile)) Then
                Return True
            End If
        End If

        Return False
    End Function

    Public Sub HandleException(ByVal ex As Exception)
        Log.Save()
        g.MainForm.SaveSettings()
        g.MainForm.IsSaveCanceled()

        Dim m As New Msg
        m.AddButton("Send Bug Report", DialogResult.OK)
        m.AddButton("Cancel", DialogResult.Cancel)
        m.Icon = MsgIcon.Error
        m.Title = "StaxRip Error"
        m.Text = "Something unexpected has happened:" + vbCrLf + vbCrLf + ex.Message + vbCrLf + vbCrLf + "If you can't solve this problem please send a bug report."

        If m.Show = DialogResult.OK Then
            SendBugReport(ex)
        End If

        Application.Exit()
    End Sub

    Public Sub SendBugReport(ByVal e As Exception)
        If Msg.Show("A zip file will be saved on the desktop containing diagnostic information. Please mail me this zip file.", "Send Bug Report", MsgIcon.Info, MessageBoxButtons.OKCancel) = DialogResult.OK Then
            Dim exceptions As New List(Of Exception)
            Dim dir As String = CommonDirs.Temp + "StaxRip Bug Report\"

            If Not e Is Nothing Then
                exceptions.Add(e)
            End If

            Try
                If Directory.Exists(dir) Then
                    Directory.Delete(dir, True)
                End If
            Catch ex As Exception
                exceptions.Add(ex)
            End Try

            Try
                Directory.CreateDirectory(dir)
            Catch ex As Exception
                exceptions.Add(ex)
            End Try

            Try
                Dim logfile As String = Filepath.GetDir(p.SourceFile) + p.Name + "_StaxRip.log"

                If File.Exists(logfile) Then
                    File.Copy(logfile, dir + "Log.txt")
                End If
            Catch ex As Exception
                exceptions.Add(ex)
            End Try

            Try
                If g.ProjectPath Is Nothing Then
                    g.ProjectPath = dir + "Project.rip"
                    g.MainForm.SaveProject(g.ProjectPath)
                Else
                    g.MainForm.SaveProject(g.ProjectPath)
                    File.Copy(g.ProjectPath, dir + "Project.rip")
                End If
            Catch ex As Exception
                exceptions.Add(ex)
            End Try

            Try
                StringHelp.WriteFile(dir + "Versions.txt", Info.GetVersions)
            Catch ex As Exception
                exceptions.Add(ex)
            End Try

            Try
                StringHelp.WriteFile(dir + "Macros.txt", Info.GetMacros)
            Catch ex As Exception
                exceptions.Add(ex)
            End Try

            Try
                StringHelp.WriteFile(dir + "AviSynth Plugins.txt", Info.GetAviSynthPlugins)
            Catch ex As Exception
                exceptions.Add(ex)
            End Try

            Dim targetPath As String = CommonDirs.UserDesktop + "StaxRip Diagnostic Files.zip"

            Try
                If File.Exists(targetPath) Then
                    File.Delete(targetPath)
                End If
            Catch ex As Exception
                exceptions.Add(ex)
            End Try

            Try
                Process.Start(CommonDirs.Startup + "Documents\support.htm")
            Catch ex As Exception
                exceptions.Add(ex)
            End Try

            Try
                If True Then
                    Throw New Exception
                End If
            Catch ex As Exception
                exceptions.Add(ex)
            End Try

            Try
                For Each i As Exception In exceptions
                    StringHelp.WriteFile(dir + "Exception" + exceptions.IndexOf(i).ToString + ".txt", i.ToString)
                Next
            Catch ex As Exception
            End Try

            Try
                Zip.Pack(dir, targetPath)
            Catch ex As Exception
                exceptions.Add(ex)
            End Try

            Try
                Directory.Delete(dir, True)
            Catch ex As Exception
            End Try
        End If
    End Sub

    Public Sub ShutdownPC()
        If g.ShutdownMode <> ShutdownMode.Nothing Then
            SavedProject = p
            g.MainForm.Close()

            Shutdown.Commit(g.ShutdownMode)
        End If
    End Sub

    Public Sub ShowHelpTopic(ByVal topic As String)
        Dim f As New HelpForm(Paths.DocumentsDir + topic + ".htm")
        f.Show()
    End Sub

    Public Sub Highlight(ByVal highlight As Boolean, ByVal c As Control)
        If highlight Then
            If Not c.BackColor.Equals(Color.Orange) Then
                c.BackColor = Color.Orange
            End If
        Else
            If TypeOf c Is Label OrElse TypeOf c Is GroupBox Then
                If Not c.BackColor.Equals(SystemColors.Control) Then
                    c.BackColor = SystemColors.Control
                End If
            Else
                If Not c.BackColor.Equals(SystemColors.Window) Then
                    c.BackColor = SystemColors.Window
                End If
            End If
        End If
    End Sub

    Public Sub SmartCrop()
        Dim tempLeft As Integer = p.CropLeft
        Dim tempRight As Integer = p.CropRight

        Dim ae As Double = Math.Abs(g.GetAspectError())

        While (p.SourceWidth - p.CropLeft - p.CropRight) > 64
            p.CropLeft += 2
            p.CropRight += 2

            If Math.Abs(g.GetAspectError()) < ae Then
                tempLeft = p.CropLeft
                tempRight = p.CropRight
                ae = Math.Abs(g.GetAspectError())
            End If
        End While

        p.CropLeft = tempLeft
        p.CropRight = tempRight

        Dim tempTop As Integer = p.CropTop
        Dim tempBottom As Integer = p.CropBottom

        While (p.SourceHeight - p.CropTop - p.CropBottom) > 64
            p.CropTop += 2
            p.CropBottom += 2

            If Math.Abs(g.GetAspectError()) < ae Then
                tempTop = p.CropTop
                tempBottom = p.CropBottom
                ae = Math.Abs(g.GetAspectError())
            End If
        End While

        p.CropTop = tempTop
        p.CropBottom = tempBottom
    End Sub

    Public Sub AutoResize()
        Dim w As Integer = 64
        Dim h As Integer = 16

        While w * h < p.AutoResizeImage
            w += 16
            h = Calc.FixMod(CInt(w / Calc.GetAR()), 16)

            If h = 0 Then
                w = p.TargetWidth
                h = p.TargetHeight
            End If
        End While

        g.MainForm.tbTargetWidth.Text = w.ToString()
        g.MainForm.tbTargetHeight.Text = h.ToString()
    End Sub

    Public Sub AutoCrop()
        Dim d As New AviSynthDocument
        d.Path = Paths.GetTempDir + p.Name + "_AutoCrop.avs"

        For Each i As AviSynthFilter In p.TargetAVSDoc.Filters
            If i.Category = "Source" Then
                d.Filters.Add(i.GetCopy)
            End If
        Next

        d.Filters.Add(New AviSynthFilter("AutoCrop", "AutoCrop", "ConvertToYV12()" + vbCrLf + "AutoCrop(mode=2,wmultof=4,hmultof=4,samples=10)", True))
        d.Synchronize()

        Dim logPath As String = Filepath.GetDir(d.Path) + "AutoCrop.log"
        Dim line As String = StringHelp.SplitLines(StringHelp.ReadFile(logPath))(0)

        Dim sa As String() = StringHelp.Split(StringHelp.GetRight( _
            StringHelp.GetLeft(line, ")"), "("), ",")

        p.CropLeft = CInt(sa(0))
        p.CropTop = CInt(sa(1))
        p.CropRight = p.SourceWidth - p.CropLeft - CInt(sa(2))
        p.CropBottom = p.SourceHeight - p.CropTop - CInt(sa(3))

        If (p.CropLeft + p.CropRight + 10) > p.SourceWidth Then
            p.CropLeft = 0
            p.CropRight = 0
        End If

        If (p.CropTop + p.CropBottom + 10) > p.SourceHeight Then
            p.CropTop = 0
            p.CropBottom = 0
        End If
    End Sub

    Public Function GetAspectError() As Double
        Return (Calc.GetAR / (p.TargetWidth / p.TargetHeight)) * 100 - 100
    End Function
End Class

Public Enum AudioMode
    DirectStreamCopy
    FullProcessingMode
End Enum

Public Enum RegistryRoot
    CurrentUser
    LocalMachine
    ClassesRoot
End Enum

Public Class VirtualDubScript
    Private LinesValue As New List(Of String)

    Public Property Lines() As List(Of String)
        Get
            Return LinesValue
        End Get
        Set(ByVal Value As List(Of String))
            LinesValue = Value
        End Set
    End Property

    Public Function GetStreamSetSourceParams(ByVal type As StreamType) As String
        Select Case type
            Case StreamType.MP3CBR
                Return "0x00000202,0"
            Case StreamType.MP3VBR
                Return "0x00000202,1"
            Case StreamType.WAV
                Return "0x00000201"
            Case StreamType.AC3
                Return "0x00000203,0"
            Case Else
                Return Nothing
        End Select
    End Function

    Public Sub SaveScript(ByVal path As String)
        Dim script As String = ""

        For Each i As String In Lines
            If i <> "" Then
                script += i + vbCrLf
            End If
        Next

        Lines.Clear()

        Dim newScript As String = ""

        For Each i As Char In script
            If Convert.ToByte(i) > 127 Then 'I don't know why even ANSII is not supported by VirtualDub
                For Each i2 As Byte In System.Text.Encoding.UTF8.GetBytes(i)
                    newScript += "\x" + System.Convert.ToString(i2, 16)
                Next
            Else
                newScript += i.ToString()
            End If
        Next

        StringHelp.WriteFile(path, newScript)
    End Sub

    Private Function EscapeSeparator(ByVal value As String) As String
        Return value.Replace("\\", "\").Replace("\", "\\")
    End Function

    Public Shared Function FixQuotes(ByVal st As String) As String
        Return st.Replace("""", "\""")
    End Function

    Public ReadOnly Property Text() As String
        Get
            Dim t As String = ""

            For Each i As String In Lines
                t += i + Environment.NewLine
            Next

            Return t
        End Get
    End Property

    Public Sub AddLine(ByVal search As String, ByVal line As String)
        Dim x As Integer
        Dim Item As String

        line = EscapeSeparator(line)

        For x = 0 To Lines.Count - 1
            Item = CStr(Lines.Item(x))

            If Item.IndexOf(search) > -1 AndAlso Not line.Contains(".Add") Then
                Lines.RemoveAt(x)
                Lines.Insert(x, line)

                Exit Sub
            End If
        Next

        Lines.Add(line)
    End Sub

    Public Sub SetLines(ByVal scriptText As String)
        For Each i As String In StringHelp.SplitLines(scriptText)
            If i.StartsWith("VirtualDub.") Then
                AddLine(StringHelp.GetLeft(i, "(") + "(", i)
            End If
        Next
    End Sub

    Public Sub Delete(ByVal search As String)
        Dim x As Integer

        For x = 0 To Lines.Count - 1
            If CType(Lines.Item(x), String).IndexOf(search) > -1 Then
                Lines.RemoveAt(x)
                Exit For
            End If
        Next
    End Sub

    'script functions

    Public Sub Open(ByVal path As String)
        Lines.Add("VirtualDub.Open(""" + EscapeSeparator(path) + ""","""",0);")
    End Sub

    Public Sub Close()
        Lines.Add("VirtualDub.Close();")
    End Sub

    Public Sub Append(ByVal path As String)
        Lines.Add("VirtualDub.Append(""" + EscapeSeparator(path) + """);")
    End Sub

    Public Sub Save(ByVal path As String)
        Lines.Add("VirtualDub.SaveAVI(""" + EscapeSeparator(path) + """);")
    End Sub

    Public Sub SaveWAV(ByVal path As String)
        Lines.Add("VirtualDub.SaveWAV(""" + EscapeSeparator(path) + """);")
    End Sub

    Public Sub SubsetClear()
        Lines.Add("VirtualDub.subset.Clear();")
    End Sub

    Public Sub SubsetAddRange(ByVal start As Integer, ByVal [end] As Integer)
        Lines.Add("VirtualDub.subset.AddRange(" + start.ToString + ", " + [end].ToString + ");")
    End Sub

    Public Sub RemoveInputStreams()
        Lines.Add("VirtualDub.RemoveInputStreams();")
    End Sub

    Public Sub VideoSetModeFastRecompress()
        AddLine("video.SetMode", "VirtualDub.video.SetMode(1);")
    End Sub

    Public Sub VideoSetRange(ByVal start As Integer, ByVal length As Integer)
        AddLine("video.SetRange", "VirtualDub.video.SetRange(" + start.ToString() + ", " + length.ToString() + ");")
    End Sub

    Public Sub VideoSetCompression(ByVal arguments As String, ByVal kbytes_per_sec As Double)
        AddLine("video.SetCompression", "VirtualDub.video.SetCompression(" + arguments.Replace("%kbytes_per_sec%", CInt(kbytes_per_sec).ToString()) + ");")
    End Sub

    Public Sub StreamSetSource(ByVal index As Integer, ByVal path As String, ByVal type As StreamType)
        Lines.Add("VirtualDub.stream[" + index.ToString() + "].SetSource(""" + EscapeSeparator(path) + """," + GetStreamSetSourceParams(type) + ");")
    End Sub

    Public Sub StreamDemux(ByVal index As Integer, ByVal path As String)
        Lines.Add("VirtualDub.stream[" + index.ToString() + "].Demux(""" + EscapeSeparator(path) + """);")
    End Sub

    Public Sub StreamSetMode(ByVal index As Integer, ByVal mode As AudioMode)
        Lines.Add("VirtualDub.stream[" + index.ToString() + "].SetMode(" + CInt(mode).ToString() + ");")
    End Sub

    Public Sub StreamSetInterleave(ByVal index As Integer, ByVal delay As Integer)
        Lines.Add("VirtualDub.stream[" + index.ToString() + "].SetInterleave(1,500,1,0," + delay.ToString + ");")
    End Sub
End Class

<Serializable()> _
Public Class Range
    Implements IComparable(Of Range)

    Public Start As Integer
    Public [End] As Integer

    Public Sub New(ByVal startPosition As Integer, ByVal endPosition As Integer)
        Me.Start = startPosition
        Me.End = endPosition
    End Sub

    Public Function GetLenght() As Integer
        Return [End] - Start
    End Function

    Public Function CompareTo(ByVal other As Range) As Integer Implements System.IComparable(Of Range).CompareTo
        Return Start.CompareTo(other.Start)
    End Function
End Class

Public Class Log
    Public Shared Event Update(ByVal sb As StringBuilder)

    Private Shared SB As New StringBuilder

    Public Shared Sub WriteLine(ByVal value As String)
        SB.Append(value + vbCrLf)
        RaiseUpdate()
    End Sub

    Public Shared Sub AddLines(ByVal value As String)
        SB.Append(StringHelp.FixBreak(value))

        If Not value.EndsWith(vbCrLf) Then
            SB.Append(vbCrLf)
        End If

        RaiseUpdate()
    End Sub

    Public Shared Sub WriteHeader(ByVal value As String)
        SB.Append(vbCrLf + "---------- " + value + " ----------" + vbCrLf + vbCrLf)
        RaiseUpdate()
    End Sub

    Public Shared Sub WriteStats(ByVal start As DateTime, ByVal frames As Integer)
        WriteStats(start, frames, True)
    End Sub

    Public Shared Sub WriteStats(ByVal start As DateTime, ByVal frames As Integer, ByVal showSpeed As Boolean)
        Dim n As TimeSpan = Now.Subtract(start)

        SB.Append("Start time: " + start.ToLongTimeString + vbCrLf)
        SB.Append("End time: " + Now.ToLongTimeString + vbCrLf)
        SB.Append("Duration: " + CInt(Math.Floor(n.TotalHours)).ToString("d2") + ":" + n.Minutes.ToString("d2") + ":" + n.Seconds.ToString("d2") + vbCrLf)

        If showSpeed Then
            SB.Append("Speed: " + CInt(frames / n.TotalSeconds).ToString() + " fps" + vbCrLf)
        End If

        RaiseUpdate()
    End Sub

    Private Shared Sub RaiseUpdate()
        RaiseEvent Update(SB)
    End Sub

    Public Shared Sub Save()
        Dim value As String = SB.ToString

        If StringHelp.ContainsValue(value, p.SourceFile) Then
            StringHelp.WriteFile(Filepath.GetDir(p.SourceFile) + p.Name + "_StaxRip.log", value)
        End If
    End Sub

    Public Shared Sub Clear()
        SB.Length = 0
        RaiseUpdate()
    End Sub
End Class

Public Class Calc
    Public Shared Function GetPercent() As Double
        If p.Compressibility = 0 Then
            Return 0
        End If

        Return (GetBPF() / p.Compressibility) * 100
    End Function

    Public Shared Function GetBPF() As Double
        Dim framerate As Double = p.TargetAVSDoc.GetFramerate

        If framerate = 0 Then
            Return 0
        End If

        If p.TargetWidth = 0 Then
            Return 0
        End If

        If p.TargetHeight = 0 Then
            Return 0
        End If

        Return (p.VideoBitrate * 1024) / (p.TargetWidth * p.TargetHeight * framerate)
    End Function

    Public Shared Function GetVideoBitrate() As Double
        Return Calc.GetTotalBitrate - Calc.GetAudioBitrate()
    End Function

    Public Shared Function GetTotalBitrate() As Double
        If p.TargetSeconds = 0 Then
            Return 0
        End If

        Dim kb As Integer = (p.Size * 1024) - GetOverheadAndSubtitlesKBytes()


        Return (kb * 8 * 1.024) / p.TargetSeconds
    End Function

    Public Shared Function GetVideoKBytes() As Double
        Return ((p.VideoBitrate * p.TargetSeconds) / 8) / 1.024
    End Function

    Public Shared Function GetOverheadAndSubtitlesKBytes() As Integer
        Dim ret As Double
        Dim frames As Integer = p.TargetAVSDoc.GetFrames

        If p.VideoEncoder.Muxer.OutputType = StreamType.AVI OrElse _
            p.VideoEncoder.Muxer.OutputType = StreamType.DMF Then

            ret += frames * 0.024

            If p.Audio0.File <> "" Then
                If p.Audio0.Profile.StreamType = StreamType.MP3VBR Then
                    ret += frames * 0.04
                Else
                    ret += frames * 0.02375
                End If
            End If

            If p.Audio1.File <> "" Then
                If p.Audio1.Profile.StreamType = StreamType.MP3VBR Then
                    ret += frames * 0.04
                Else
                    ret += frames * 0.02375
                End If
            End If
        ElseIf p.VideoEncoder.Muxer.OutputType = StreamType.MP4 Then
            ret += (10.4 / 1024) * frames
        ElseIf p.VideoEncoder.Muxer.OutputType = StreamType.MKV Then
            ret += frames * 0.013
        End If

        If p.VideoEncoder.Muxer.SupportsIDX Then
            For Each i As Subtitle In p.VideoEncoder.Muxer.IDX.Subtitles
                If i.Enabled Then
                    ret += CInt(p.TargetSeconds * 0.256)
                End If
            Next
        End If

        Return CInt(ret)
    End Function

    Public Shared Function GetAudioKBytes() As Double
        Return ((Calc.GetAudioBitrate() * p.TargetSeconds) / 8) / 1.024
    End Function

    Public Shared Function GetAudioBitrate() As Double
        Dim b0, b1 As Double

        If p.Audio0.File <> "" Then
            b0 = p.Audio0.Profile.Bitrate
        End If

        If p.Audio1.File <> "" Then
            b1 = p.Audio1.Profile.Bitrate
        End If

        Return b0 + b1
    End Function

    Public Shared Sub SetAudioBitrateFromFile(ByVal audioSettings As AudioSettings)
        If TypeOf audioSettings.Profile Is MuxAudioProfile Then
            audioSettings.Profile.Bitrate = GetBitrateFromFile(audioSettings.File, p.SourceSeconds)
        End If
    End Sub

    <DebuggerNonUserCode()> _
    Public Shared Function GetBitrateFromFile(ByVal path As String, ByVal seconds As Integer) As Double
        Try
            If path Is Nothing OrElse path = "" OrElse seconds = 0 Then
                Return 0
            End If

            Dim kBits As Double = ((New FileInfo(path).Length) * 8) / 1000

            Return kBits / seconds
        Catch
            Msg.Show("File doesn't exist: " + path, MsgIcon.Error)
        End Try
    End Function

    Public Shared Function GetKBytes(ByVal path As String) As Integer
        Return CInt((New FileInfo(path).Length) / 1024)
    End Function

    Public Shared Function GetAR() As Double
        Dim w As Integer = 720
        Dim h As Integer = 576

        If p.SourceWidth > 0 Then
            w = p.SourceWidth
            h = p.SourceHeight
        End If

        Dim cropWidth As Double = w - p.CropLeft - p.CropRight
        Dim cropHeight As Double = h - p.CropTop - p.CropBottom
        Dim format As Double = 4 / 3

        If p.Source169 Then format = 16 / 9
        If p.ITU.Value Then format = format * 1.02564

        If p.SourceHeight <> 576 AndAlso p.SourceHeight <> 480 Then
            format = w / h
        End If

        Return ((cropWidth / w) / (cropHeight / h)) * format
    End Function

    Public Shared Function GetMod(ByVal value As Integer) As Integer
        Dim ret As Integer

        For Each i As Integer In New Integer() {1, 2, 4, 8, 16}
            If value Mod i = 0 Then
                ret = i
            End If
        Next

        Return ret
    End Function

    Public Shared Function FixMod(ByVal value As Integer, ByVal modValue As Integer) As Integer
        If modValue = 0 Then
            Return value
        End If

        If value Mod modValue <> 0 Then
            Return CInt(value / modValue) * modValue
        Else
            Return value
        End If
    End Function
End Class

<Serializable()> _
Public Class Language
    Implements IComparable(Of Language)

    Public Sub New()
        Try
            CultureInfoValue = New CultureInfo(System.Globalization.CultureInfo.CurrentCulture.TwoLetterISOLanguageName)
        Catch ex As Exception
            CultureInfoValue = New CultureInfo("en")
        End Try
    End Sub

    Public Sub New(ByVal ci As CultureInfo)
        CultureInfoValue = ci
    End Sub

    Public Sub New(ByVal twoLetterCode As String)
        CultureInfoValue = New CultureInfo(twoLetterCode)
    End Sub

    Private CultureInfoValue As CultureInfo

    Public ReadOnly Property CultureInfo() As CultureInfo
        Get
            Return CultureInfoValue
        End Get
    End Property

    Public ReadOnly Property TwoLetterCode() As String
        Get
            Return CultureInfo.TwoLetterISOLanguageName
        End Get
    End Property

    Public ReadOnly Property ThreeLetterCode() As String
        Get
            Return CultureInfo.ThreeLetterISOLanguageName
        End Get
    End Property

    Public ReadOnly Property Name() As String
        Get
            Return CultureInfo.EnglishName
        End Get
    End Property

    Private Shared LanguagesValue As Language()

    Public Shared ReadOnly Property Languages() As Language()
        Get
            If LanguagesValue Is Nothing Then
                Dim l As New List(Of Language)

                For Each i As CultureInfo In System.Globalization.CultureInfo.GetCultures(CultureTypes.NeutralCultures)
                    If Not i.Equals(System.Globalization.CultureInfo.InvariantCulture) Then
                        l.Add(New Language(i))
                    End If
                Next

                l.Sort()

                LanguagesValue = l.ToArray
            End If

            Return LanguagesValue
        End Get
    End Property

    Public Overrides Function ToString() As String
        Return CultureInfo.EnglishName
    End Function

    Public Function CompareTo(ByVal other As Language) As Integer Implements System.IComparable(Of Language).CompareTo
        Return Name.CompareTo(other.Name)
    End Function

    Public Overrides Function Equals(ByVal o As Object) As Boolean
        If TypeOf o Is Language Then
            Return CultureInfo.Equals(DirectCast(o, Language).CultureInfo)
        End If
    End Function
End Class

Public Class CLTypeEditor
    Inherits UITypeEditor

    Public Overloads Overrides Function EditValue(ByVal context As ITypeDescriptorContext, ByVal provider As IServiceProvider, ByVal value As Object) As Object
        Using f As New CmdlScriptEditor
            f.SetCmdlDefaults()
            f.CmdlScriptControl.CommandLines = CStr(value)
            f.CmdlScriptControl.RemoveTools()

            If f.ShowDialog = DialogResult.OK Then
                Return f.CmdlScriptControl.CommandLines
            Else
                Return value
            End If
        End Using
    End Function

    Public Overloads Overrides Function GetEditStyle(ByVal context As ITypeDescriptorContext) As System.Drawing.Design.UITypeEditorEditStyle
        Return UITypeEditorEditStyle.Modal
    End Function
End Class

<Serializable()> _
Public MustInherit Class Profile
    Implements IComparable(Of Profile)

    Public MustOverride Function GetInfoValues() As Dictionary(Of String, String)

    Public Sub New(ByVal name As String, ByVal typeName As String)
        Me.Name = name
        Me.TypeName = typeName
    End Sub

    Private NameValue As String

    Public Property Name() As String
        Get
            Return NameValue
        End Get
        Set(ByVal Value As String)
            NameValue = Value
        End Set
    End Property

    Private TypeNameValue As String

    Public Property TypeName() As String
        Get
            Return TypeNameValue
        End Get
        Set(ByVal Value As String)
            TypeNameValue = Value
        End Set
    End Property

    Protected CanEditValue As Boolean

    Public ReadOnly Property CanEdit() As Boolean
        Get
            Return CanEditValue
        End Get
    End Property

    Public Overridable Function Edit() As DialogResult
    End Function

    <NonSerialized()> _
    Protected EditControlValue As Control

    Public Overridable ReadOnly Property EditControl() As Control
        Get
            Return EditControlValue
        End Get
    End Property

    Public Sub ShowInfoDialog()
        Dim l As Dictionary(Of String, String) = GetInfoValues()

        Using f As New StatusControlForm
            f.Text = Name
            f.StatusControl.AddIconTitle(Name, Nothing)
            Dim keys As New List(Of String)

            For Each i As String In l.Keys
                keys.Add(i)
            Next

            keys.Sort()

            For Each i As String In keys
                f.StatusControl.AddSection(i, l(i))
            Next

            f.SetHeight()
            f.ShowDialog()
        End Using
    End Sub

    Public Overridable Sub Clean()
    End Sub

    Public Overridable Function GetCopy() As Profile
        Return DirectCast(ObjectHelp.GetCopy(Me), Profile)
    End Function

    Public Overrides Function ToString() As String
        Return Name
    End Function

    Public Function CompareTo(ByVal other As Profile) As Integer Implements System.IComparable(Of Profile).CompareTo
        Return Name.CompareTo(other.Name)
    End Function
End Class

<Serializable()> _
Public Class AudioSettings
    Implements ISerializable

    Public Sub New(ByVal profile As AudioProfile, ByVal language As Language)
        Me.Profile = profile
        Me.Language = language
    End Sub

    Public Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext)
        For Each i As FieldInfo In Me.GetType().GetFields(BindingFlags.Instance Or BindingFlags.NonPublic Or BindingFlags.Public)
            Try
                Dim o As Object = info.GetValue(i.Name, i.FieldType)

                If i.Name = "ProfileValue" Then
                    DirectCast(o, AudioProfile).AudioSettings = Me
                End If

                i.SetValue(Me, o)
            Catch
            End Try
        Next
    End Sub

    Private FileValue As String = ""

    Public Property File() As String
        Get
            Return FileValue
        End Get
        Set(ByVal Value As String)
            FileValue = Value
        End Set
    End Property

    Private DelayValue As Integer

    Public Property Delay() As Integer
        Get
            Return DelayValue
        End Get
        Set(ByVal Value As Integer)
            DelayValue = Value
        End Set
    End Property

    Private LanguageValue As Language

    Public Property Language() As Language
        Get
            Return LanguageValue
        End Get
        Set(ByVal Value As Language)
            LanguageValue = Value
        End Set
    End Property

    Private ProfileValue As AudioProfile

    Public Property Profile() As AudioProfile
        Get
            Return ProfileValue
        End Get
        Set(ByVal Value As AudioProfile)
            ProfileValue = Value
            ProfileValue.AudioSettings = Me
        End Set
    End Property

    Public Overridable Sub GetObjectData(ByVal info As SerializationInfo, ByVal context As StreamingContext) Implements ISerializable.GetObjectData
        For Each i As FieldInfo In Me.GetType().GetFields(BindingFlags.Instance Or BindingFlags.NonPublic Or BindingFlags.Public)
            Try
                info.AddValue(i.Name, i.GetValue(Me))
            Catch
            End Try
        Next
    End Sub
End Class

Public Class Macro
    Implements IComparable(Of Macro)

    Public Sub New(ByVal shortname As String, ByVal description As String)
        Me.Shortname = shortname
        Me.Description = description
    End Sub

    Private ShortnameValue As String

    Public Property Shortname() As String
        Get
            Return ShortnameValue
        End Get
        Set(ByVal Value As String)
            ShortnameValue = Value
        End Set
    End Property

    Private FullnameValue As String

    Public Property Fullname() As String
        Get
            If FullnameValue Is Nothing Then
                FullnameValue = "%" + Shortname + "%"
            End If

            Return FullnameValue
        End Get
        Set(ByVal Value As String)
            FullnameValue = Value
        End Set
    End Property

    Private DescriptionValue As String

    Public Property Description() As String
        Get
            Return DescriptionValue
        End Get
        Set(ByVal Value As String)
            DescriptionValue = Value
        End Set
    End Property

    Public Function Solve() As String
        Return Solve(Fullname)
    End Function

    Public Shared Sub ShowHelp()
        ShowHelp(Nothing)
    End Sub

    Public Shared Sub ShowHelp(ByVal specialMacros As StringPairList)
        Dim f As New HelpForm()
        f.Document.WriteStart("Macros")
        f.Document.WriteParagraph("Macros are placeholders used in command lines, scripts and other customizable settings. Before a command line or script is executed containing macros get replaced with actual values.")

        If specialMacros Is Nothing Then
            f.Document.WriteTable("Macros", Macro.GetTips)
        Else
            f.Document.WriteTable("Special Macros", specialMacros)
            f.Document.WriteTable("Standard Macros", Macro.GetTips)
        End If

        f.Show()
    End Sub

    Public Shared Function GetTips() As StringPairList
        Dim l As New StringPairList

        For Each i As Macro In Macros
            l.Add(i.Fullname, i.Description)
        Next

        Return l
    End Function

    Public Overrides Function ToString() As String
        Return Fullname
    End Function

    Public Function CompareTo(ByVal other As Macro) As Integer Implements System.IComparable(Of Macro).CompareTo
        Return Shortname.CompareTo(other.Shortname)
    End Function

    Private Shared MacrosValue As List(Of Macro)

    Public Shared ReadOnly Property Macros() As List(Of Macro)
        Get
            If MacrosValue Is Nothing Then
                MacrosValue = New List(Of Macro)(New Macro() { _
                    New Macro("application_dir", "Directory of the application."), _
                    New Macro("audio_bitrate", "Overall audio bitrate."), _
                    New Macro("audio_file1", "File path of the first audio file."), _
                    New Macro("audio_file2", "File path of the second audio file."), _
                    New Macro("avisynth_plugin_dir", "AviSynth plugin directory."), _
                    New Macro("compressibility", "Compressibility value."), _
                    New Macro("crop_bottom", "Bottom crop value."), _
                    New Macro("crop_height", "Crop height."), _
                    New Macro("crop_left", "Left crop value."), _
                    New Macro("crop_right", "Right crop value."), _
                    New Macro("crop_top", "Top crop value."), _
                    New Macro("crop_width", "Crop width."), _
                    New Macro("encoder_ext", "File extension of the format the encoder of the active project outputs."), _
                    New Macro("encoder_out_file", "Output file of the encoder of the active project."), _
                    New Macro("pos_ms", "Current preview position in milliseconds."), _
                    New Macro("programs_dir", "Programs system directory."), _
                    New Macro("source_dir", "Directory of the source file."), _
                    New Macro("source_file", "File path of the source video."), _
                    New Macro("source_files_blank", "Source files in quotes separated by a blank."), _
                    New Macro("source_files_comma", "Source files in quotes separated by comma."), _
                    New Macro("source_files_comma_no_quotes", "Source files separated by comma."), _
                    New Macro("source_frames", "Length in frames of the source video."), _
                    New Macro("source_height", "Image height of the source video."), _
                    New Macro("source_name", "The name of the source file."), _
                    New Macro("source_rate", "Frame rate of the source video."), _
                    New Macro("source_seconds", "Length in seconds of the source video."), _
                    New Macro("source_temp_dir", "Directory for temporary files."), _
                    New Macro("source_width", "Image width of the source video."), _
                    New Macro("system_dir", "System directory."), _
                    New Macro("target_dir", "Directory of the target file."), _
                    New Macro("target_file", "File path of the target file."), _
                    New Macro("target_framerate", "Frame rate of the target video."), _
                    New Macro("target_frames", "Length in frames of the target video."), _
                    New Macro("target_height", "Image height of the target video."), _
                    New Macro("target_kilobyte", "Size of the target video in kilo bytes."), _
                    New Macro("target_megabyte", "Size of the target video in mega bytes."), _
                    New Macro("target_name", "Name of the target file."), _
                    New Macro("target_seconds", "Length in seconds of the target video."), _
                    New Macro("target_width", "Image width of the target video."), _
                    New Macro("version", "StaxRip version."), _
                    New Macro("video_bitrate", "Video bitrate")})

                For Each i As Package In Paths.Packages.Values
                    MacrosValue.Add(New Macro("application:" + i.Name, i.Description))
                Next

                MacrosValue.Sort()
            End If

            Return MacrosValue
        End Get
    End Property

    Public Shared Function Solve(ByVal value As String) As String
        If value Is Nothing Then Return ""
        If Not value.Contains("%") Then Return value

        If value.Contains("%version%") Then value = value.Replace("%version%", Application.ProductVersion)
        If Not value.Contains("%") Then Return value

        If value.Contains("%source_width%") Then value = value.Replace("%source_width%", p.SourceWidth.ToString)
        If Not value.Contains("%") Then Return value

        If value.Contains("%source_height%") Then value = value.Replace("%source_height%", p.SourceHeight.ToString)
        If Not value.Contains("%") Then Return value

        If value.Contains("%source_seconds%") Then value = value.Replace("%source_seconds%", p.SourceSeconds.ToString)
        If Not value.Contains("%") Then Return value

        If value.Contains("%source_frames%") Then value = value.Replace("%source_frames%", p.SourceFrames.ToString)
        If Not value.Contains("%") Then Return value

        If value.Contains("%source_rate%") Then value = value.Replace("%source_rate%", p.SourceFramerate.ToString("f2"))
        If Not value.Contains("%") Then Return value

        If value.Contains("%source_file%") Then value = value.Replace("%source_file%", p.SourceFile)
        If Not value.Contains("%") Then Return value

        If value.Contains("%source_dir%") Then value = value.Replace("%source_dir%", Filepath.GetDir(p.SourceFile))
        If Not value.Contains("%") Then Return value

        If value.Contains("%source_name%") Then value = value.Replace("%source_name%", Filepath.GetNameNoExt(p.SourceFile))
        If Not value.Contains("%") Then Return value

        If value.Contains("%target_width%") Then value = value.Replace("%target_width%", p.TargetWidth.ToString)
        If Not value.Contains("%") Then Return value

        If value.Contains("%target_height%") Then value = value.Replace("%target_height%", p.TargetHeight.ToString)
        If Not value.Contains("%") Then Return value

        If value.Contains("%target_seconds%") Then value = value.Replace("%target_seconds%", p.TargetSeconds.ToString)
        If Not value.Contains("%") Then Return value

        If value.Contains("%target_frames%") Then value = value.Replace("%target_frames%", p.TargetAVSDoc.GetFrames.ToString)
        If Not value.Contains("%") Then Return value

        If value.Contains("%target_framerate%") Then value = value.Replace("%target_framerate%", p.TargetAVSDoc.GetFramerate.ToString("f2"))
        If Not value.Contains("%") Then Return value

        If value.Contains("%target_megabyte%") Then value = value.Replace("%target_megabyte%", p.Size.ToString)
        If Not value.Contains("%") Then Return value

        If value.Contains("%target_kilobyte%") Then value = value.Replace("%target_kilobyte%", (p.Size * 1024).ToString)
        If Not value.Contains("%") Then Return value

        If value.Contains("%target_file%") Then value = value.Replace("%target_file%", p.TargetFile)
        If Not value.Contains("%") Then Return value

        If value.Contains("%target_dir%") Then value = value.Replace("%target_dir%", Filepath.GetDir(p.TargetFile))
        If Not value.Contains("%") Then Return value

        If value.Contains("%target_name%") Then value = value.Replace("%target_name%", p.Name)
        If Not value.Contains("%") Then Return value

        If value.Contains("%crop_width%") Then value = value.Replace("%crop_width%", (p.SourceWidth - p.CropLeft - p.CropRight).ToString)
        If Not value.Contains("%") Then Return value

        If value.Contains("%crop_height%") Then value = value.Replace("%crop_height%", (p.SourceHeight - p.CropTop - p.CropBottom).ToString)
        If Not value.Contains("%") Then Return value

        If value.Contains("%crop_left%") Then value = value.Replace("%crop_left%", p.CropLeft.ToString)
        If Not value.Contains("%") Then Return value

        If value.Contains("%crop_top%") Then value = value.Replace("%crop_top%", p.CropTop.ToString)
        If Not value.Contains("%") Then Return value

        If value.Contains("%crop_right%") Then value = value.Replace("%crop_right%", p.CropRight.ToString)
        If Not value.Contains("%") Then Return value

        If value.Contains("%crop_bottom%") Then value = value.Replace("%crop_bottom%", p.CropBottom.ToString)
        If Not value.Contains("%") Then Return value

        If value.Contains("%video_bitrate%") Then value = value.Replace("%video_bitrate%", p.VideoBitrate.ToString)
        If Not value.Contains("%") Then Return value

        If value.Contains("%audio_bitrate%") Then value = value.Replace("%audio_bitrate%", (p.Audio0.Profile.Bitrate + p.Audio1.Profile.Bitrate).ToString)
        If Not value.Contains("%") Then Return value

        If value.Contains("%audio_file1%") Then value = value.Replace("%audio_file1%", p.Audio0.File)
        If Not value.Contains("%") Then Return value

        If value.Contains("%audio_file2%") Then value = value.Replace("%audio_file2%", p.Audio1.File)
        If Not value.Contains("%") Then Return value

        If value.Contains("%application_dir%") Then value = value.Replace("%application_dir%", CommonDirs.Startup)
        If Not value.Contains("%") Then Return value

        If value.Contains("%system_dir%") Then value = value.Replace("%system_dir%", CommonDirs.System)
        If Not value.Contains("%") Then Return value

        If value.Contains("%programs_dir%") Then value = value.Replace("%programs_dir%", CommonDirs.Programs)
        If Not value.Contains("%") Then Return value

        If value.Contains("%avisynth_plugin_dir%") Then value = value.Replace("%avisynth_plugin_dir%", Paths.AviSynthPluginsDir)
        If Not value.Contains("%") Then Return value

        If value.Contains("%source_files_comma%") Then value = value.Replace("%source_files_comma%", """" + String.Join(""",""", p.SourceFiles.ToArray) + """")
        If Not value.Contains("%") Then Return value

        If value.Contains("%source_files_comma_no_quotes%") Then value = value.Replace("%source_files_comma_no_quotes%", String.Join(",", p.SourceFiles.ToArray))
        If Not value.Contains("%") Then Return value

        If value.Contains("%source_files_blank%") Then value = value.Replace("%source_files_blank%", """" + String.Join(""" """, p.SourceFiles.ToArray) + """")
        If Not value.Contains("%") Then Return value

        If value.Contains("%compressibility%") Then value = value.Replace("%compressibility%", Math.Round(p.Compressibility, 3).ToString.Replace(",", "."))
        If Not value.Contains("%") Then Return value

        If value.Contains("\\") Then value = value.Replace("\\", "\")
        If Not value.Contains("%") Then Return value

        If value.Contains("%encoder_out_file%") Then value = value.Replace("%encoder_out_file%", p.VideoEncoder.OutputPath)
        If Not value.Contains("%") Then Return value

        If value.Contains("%encoder_ext%") Then value = value.Replace("%encoder_ext%", p.VideoEncoder.GetExtension())
        If Not value.Contains("%") Then Return value

        If value.Contains("%source_temp_dir%") Then value = value.Replace("%source_temp_dir%", Paths.GetTempDir)
        If Not value.Contains("%") Then Return value

        If value.Contains("%pos_ms%") Then
            Dim r As Double = p.TargetAVSDoc.GetFramerate
            If r = 0 Then r = 25
            value = value.Replace("%pos_ms%", CInt((g.PreviewLastPosition / r) * 1000).ToString)
        End If

        If Not value.Contains("%") Then Return value

        If value.Contains("%application:") Then
            Dim mc As MatchCollection = Regex.Matches(value, "%application:(.+?)%")

            For Each i As Match In mc
                Dim path As String = Paths.Packages(i.Groups(1).Value).GetPath(False)

                If path Is Nothing Then
                    path = ""
                End If

                value = Regex.Replace(value, "%application:" + i.Groups(1).Value + "%", path)
            Next
        End If

        Return value
    End Function
End Class

Public Class Info
    Public Shared Function GetVersions() As String
        Dim sb As New StringBuilder

        sb.Append("StaxRip " + Application.ProductVersion + vbCrLf)
        sb.Append(Environment.OSVersion.ToString() + vbCrLf)
        sb.Append(".NET Framework " + Environment.Version.ToString() + vbCrLf)
        sb.Append("DirectX " + RegistryHelp.LocalMachine.GetString("Software\Microsoft\DirectX", "Version") + vbCrLf)

        sb.Append(vbCrLf)

        For Each i As Package In Paths.Packages.Values
            sb.AppendFormat("{0,-20}{1}" + vbCrLf, i.Name, i.IsCorrectVersion.ToString)
        Next

        Return sb.ToString
    End Function

    Public Shared Function GetMacros() As String
        Dim sb As New StringBuilder

        For Each i As Macro In Macro.Macros
            sb.AppendFormat("{0,-30}{1}" + vbCrLf, i.Shortname, i.Solve)
        Next

        Return sb.ToString
    End Function

    Public Shared Function GetAviSynthPlugins() As String
        Dim sb As New StringBuilder

        For Each i As String In Directory.GetFiles(Paths.AviSynthPluginsDir, "*.dll")
            sb.AppendFormat("{0,-25}{1}" + vbCrLf, Filepath.GetNameWithExt(i), File.GetLastWriteTimeUtc(i).ToString("g"))
        Next

        Return sb.ToString
    End Function
End Class

Public Enum StreamType
    <Extension(".mp3")> Ignore
    <Name("MP3 CBR"), Extension(".mp3")> MP3CBR
    <Name("MP3 VBR"), Extension(".mp3")> MP3VBR
    <Name("AAC-LC MP4"), Extension(".mp4")> AAC_LC_MP4
    <Name("AAC-LC RAW"), Extension(".aac")> AAC_LC_RAW
    <Name("AAC-HE MP4"), Extension(".mp4")> AAC_HE_MP4
    <Name("AAC-HE RAW"), Extension(".aac")> AAC_HE_RAW
    AC3
    WAV
    AVI
    MP4
    MPA
    MP2
    <Name("RAW 264")> <Extension(".264")> RAW264
    MKV
    <Name("DIVX"), Extension(".divx")> DMF
    <Extension(".ogg")> Vorbis
    M4V
    DTS
    PMP
End Enum

<AttributeUsage(AttributeTargets.Field)> _
Public Class ExtensionAttribute
    Inherits Attribute

    Public Sub New(ByVal value As String)
        Me.Value = value
    End Sub

    Private ValueValue As String

    Public Property Value() As String
        Get
            Return ValueValue
        End Get
        Set(ByVal Value As String)
            ValueValue = Value
        End Set
    End Property
End Class

Public Class StreamTypeUtils
    Public Shared Function GetExtension(ByVal type As StreamType) As String
        Dim fi As FieldInfo = GetType(StreamType).GetField(type.ToString)
        Dim attribs As Object() = fi.GetCustomAttributes(GetType(ExtensionAttribute), True)

        If attribs.Length = 0 Then
            Return "." + type.ToString.ToLower
        Else
            Return DirectCast(attribs(0), ExtensionAttribute).Value
        End If
    End Function

    Public Shared Function GetNames(ByVal types As StreamType()) As String
        Dim l As New List(Of String)

        For Each i As StreamType In types
            Dim fi As FieldInfo = GetType(StreamType).GetField(i.ToString)
            Dim a As Object() = fi.GetCustomAttributes(GetType(NameAttribute), True)

            If a.Length > 0 Then
                l.Add(DirectCast(a(0), NameAttribute).Value)
            Else
                l.Add(i.ToString)
            End If
        Next

        Return String.Join(", ", l.ToArray)
    End Function

    Public Shared Function GetName(ByVal st As StreamType) As String
        Dim fi As FieldInfo = GetType(StreamType).GetField(st.ToString)
        Dim a As Object() = fi.GetCustomAttributes(GetType(NameAttribute), True)

        If a.Length > 0 Then
            Return DirectCast(a(0), NameAttribute).Value
        Else
            Return st.ToString
        End If
    End Function
End Class

<Serializable()> _
Public Class ObjectStorage
    Private Hash As New Hashtable 'derived generic dic don't deserialize anything

    Public Function GetString(ByVal key As String, ByVal defaultValue As String) As String
        If Hash.Contains(key) AndAlso TypeOf Hash(key) Is String Then
            Return CStr(Hash(key))
        End If

        Return defaultValue
    End Function

    Public Function GetInt(ByVal key As String, ByVal defaultValue As Integer) As Integer
        If Hash.Contains(key) AndAlso TypeOf Hash(key) Is Integer Then
            Return CInt(Hash(key))
        End If

        Return defaultValue
    End Function

    Public Function GetBool(ByVal key As String) As Boolean
        If Hash.Contains(key) AndAlso TypeOf Hash(key) Is Boolean Then
            Return CBool(Hash(key))
        End If

        Return False
    End Function

    Public Sub SetValue(ByVal key As String, ByVal value As Object)
        Hash(key) = value
    End Sub

    Public Sub SetBool(ByVal key As String, ByVal value As Boolean)
        Hash(key) = value
    End Sub

    Public Sub Remove(ByVal key As String)
        If Hash.Contains(key) Then Hash.Remove(key)
    End Sub
End Class

Public Enum x264Mode
    <Name("Single Pass - Bitrate")> _
    SingleBitrate
    <Name("Single Pass - Quantizer")> _
    SingleQuant
    <Name("Single Pass - Quality")> _
    SingleCRF
    <Name("Two Pass")> _
    TwoPass
    <Name("Three Pass")> _
    ThreePass
End Enum

Public Enum x264OutputType
    RAW
    MP4
    MKV
End Enum

Public Enum x264TrellisMode
    Disabled
    <Name("Final MB")> _
    FinalMB
    Always
End Enum

Public Enum x264SubMEMode
    <Name("1 (fast)")> _
    V1 = 1
    <Name("2")> _
    V2
    <Name("3")> _
    V3
    <Name("4")> _
    V4
    <Name("5")> _
    V5
    <Name("6")> _
    V6
    <Name("7 (best)")> _
    V7
End Enum

Public Enum MEMethodMode
    <Name("Diamond")> _
    dia
    <Name("Hexagon")> _
    hex
    <Name("Multi Hex")> _
    umh
    <Name("Exhaustive")> _
    esa
End Enum

<Serializable()> _
Public Class SRTSubtitle
    Public Sub New(ByVal ci As CultureInfo)
        Language = New SettingBag(Of Language)(New Language(ci))
    End Sub

    Private PathValue As New SettingBag(Of String)("")

    Public Property Path() As SettingBag(Of String)
        Get
            Return PathValue
        End Get
        Set(ByVal Value As SettingBag(Of String))
            PathValue = Value
        End Set
    End Property

    Private LanguageValue As SettingBag(Of Language)

    Public Property Language() As SettingBag(Of Language)
        Get
            Return LanguageValue
        End Get
        Set(ByVal Value As SettingBag(Of Language))
            LanguageValue = Value
        End Set
    End Property

    Private NameValue As New SettingBag(Of String)("")

    Public Property Name() As SettingBag(Of String)
        Get
            Return NameValue
        End Get
        Set(ByVal Value As SettingBag(Of String))
            NameValue = Value
        End Set
    End Property

    Public Overrides Function ToString() As String
        Return Language.Value.ToString.PadRight(20) + Path.Value
    End Function
End Class

<Serializable()> _
Public Class Subtitle
    Private LanguageValue As Language

    Public Property Language() As Language
        Get
            Return LanguageValue
        End Get
        Set(ByVal Value As Language)
            LanguageValue = Value
        End Set
    End Property

    Private IndexValue As Integer

    Public Property Index() As Integer
        Get
            Return IndexValue
        End Get
        Set(ByVal Value As Integer)
            IndexValue = Value
        End Set
    End Property

    Private EnabledValue As Boolean

    Public Property Enabled() As Boolean
        Get
            Return EnabledValue
        End Get
        Set(ByVal Value As Boolean)
            EnabledValue = Value
        End Set
    End Property

    Public Overrides Function ToString() As String
        Return Language.ToString
    End Function
End Class

<Serializable()> _
Public Class IDX
    Private SubtitlesValue As New List(Of Subtitle)

    Private PathValue As String = ""

    Public Property Path() As String
        Get
            If PathValue <> "" AndAlso Not File.Exists(PathValue) Then
                PathValue = ""
            End If

            Return PathValue
        End Get
        Set(ByVal value As String)
            PathValue = value
            Subtitles.Clear()

            If File.Exists(PathValue) Then
                Dim enabledLanguages As New List(Of Language)

                For Each i As Match In Regex.Matches(StringHelp.ReadFile(PathValue), "id: (\w\w), index: (\d+)")
                    Dim a As New Subtitle

                    Try
                        a.Language = New Language(New CultureInfo(i.Groups(1).Value))
                    Catch ex As Exception
                        a.Language = Language.Languages(0)
                    End Try

                    a.Index = CInt(i.Groups(2).Value)

                    If Not enabledLanguages.Contains(a.Language) Then
                        For Each iLanguage As Language In p.PreselectionSubtitleLanguages
                            If iLanguage.Equals(a.Language) Then
                                enabledLanguages.Add(a.Language)
                                a.Enabled = True
                                Exit For
                            End If
                        Next
                    End If

                    Subtitles.Add(a)
                Next
            End If
        End Set
    End Property

    Public Property Subtitles() As List(Of Subtitle)
        Get
            If Path = "" Then
                SubtitlesValue.Clear()
            End If

            Return SubtitlesValue
        End Get
        Set(ByVal Value As List(Of Subtitle))
            SubtitlesValue = Value
        End Set
    End Property
End Class

<Serializable()> _
Public Class x264Params
    Implements ISerializable

    Public GOPSizeMin As New SettingBag(Of Integer)(25)
    Public GOPSizeMax As New SettingBag(Of Integer)(250)
    Public SceneCut As New SettingBag(Of Integer)(40)
    Public BFrames As New SettingBag(Of Integer)(4)
    Public BFramesAdaptive As New SettingBag(Of Boolean)(True)
    Public BFramesBias As New SettingBag(Of Integer)(0)
    Public BFramesPyramid As New SettingBag(Of Boolean)(False)
    Public BFramesWeightedPrediction As New SettingBag(Of Boolean)(False)
    Public CABAC As New SettingBag(Of Boolean)(True)
    Public ReferenceFrames As New SettingBag(Of Integer)(4)
    Public LoopFilter As New SettingBag(Of Boolean)(True)
    Public DeblockAlpha As New SettingBag(Of Integer)(-2)
    Public DeblockBeta As New SettingBag(Of Integer)(-2)
    Public Mode As New SettingBag(Of Integer)(x264Mode.SingleQuant)
    Public Quant As New SettingBag(Of Integer)(26)

    'IO
    Public Progress As New SettingBag(Of Boolean)(True)
    Public NoPSNR As New SettingBag(Of Boolean)(True)
    Public OutputType As New SettingBag(Of Integer)(x264OutputType.RAW)
    Public Threads As New SettingBag(Of Integer)(1)

    'analysis
    Public AdaptiveDCT As New SettingBag(Of Boolean)(False)
    Public I4x4 As New SettingBag(Of Boolean)(True)
    Public P4x4 As New SettingBag(Of Boolean)(True)
    Public I8x8 As New SettingBag(Of Boolean)(False)
    Public P8x8 As New SettingBag(Of Boolean)(True)
    Public B8x8 As New SettingBag(Of Boolean)(True)
    Public SubME As New SettingBag(Of Integer)(x264SubMEMode.V6)
    Public MixedRefs As New SettingBag(Of Boolean)(False)
    Public Trellis As New SettingBag(Of Integer)(x264TrellisMode.Disabled)
    Public MEMethod As New SettingBag(Of Integer)(MEMethodMode.hex)
    Public BFramesRDO As New SettingBag(Of Boolean)(False)

    'cli
    Public TurboRemove As New SettingBag(Of String)("--analyse --8x8dct --ref --mixed-refs --weightb --b-rdo --subme --trellis --me --bime --no-fast-pskip")
    Public TurboAdd As New SettingBag(Of String)("--subme 1 --analyse none --me dia --ref 1")
    Public AddAll As New SettingBag(Of String)("")

    Public Sub New()
    End Sub

    <DebuggerNonUserCode()> _
    Public Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext)
        For Each i As FieldInfo In Me.GetType.GetFields()
            Try
                i.SetValue(Me, info.GetValue(i.Name, i.FieldType))
            Catch
            End Try
        Next
    End Sub

    Public Sub GetObjectData(ByVal info As SerializationInfo, ByVal context As StreamingContext) Implements ISerializable.GetObjectData
        For Each i As FieldInfo In Me.GetType.GetFields()
            info.AddValue(i.Name, i.GetValue(Me))
        Next
    End Sub
End Class

Public Class ARCalc
    Public Shared Function IsARSignalingRequired() As Boolean
        Dim f As AviSynthFilter = p.TargetAVSDoc.GetFilter("Resize")
        Return p.AutoARSignaling.Value AndAlso (f Is Nothing OrElse Not f.Active)
    End Function

    Public Shared Function GetPARForY100() As Integer
        Dim dar As Double

        If p.SourceHeight = 576 OrElse p.SourceHeight = 480 Then
            If p.ITU.Value Then
                If p.Source169 Then
                    dar = 1.823
                Else
                    dar = 1.368
                End If
            Else
                If p.Source169 Then
                    dar = 16 / 9
                Else
                    dar = 4 / 3
                End If
            End If
        End If

        If dar = 0 Then
            Return 100
        Else
            Return CInt(((dar * p.SourceHeight) / p.SourceWidth) * 100)
        End If
    End Function
End Class

Public Class ScriptEditorTreeNodeSetting
    Inherits TextBoxButtonTreeNodeSetting

    Public Sub New(ByVal text As String, ByVal path As String, ByVal setting As SettingBag(Of String), ByVal description As String)
        MyBase.New(text, path, setting, description)
        ImageKey = "TextBox16"
    End Sub

    Protected Overrides Sub ButtonClick()
        Using f As New CmdlScriptEditor
            f.SetScriptDefaults()
            f.CmdlScriptControl.RemoveTools()
            f.CmdlScriptControl.CommandLines = Value

            If f.ShowDialog() = DialogResult.OK Then
                TextBox.Text = f.CmdlScriptControl.CommandLines
            End If
        End Using
    End Sub
End Class

Public Class CmdlEditorTreeNodeSetting
    Inherits TextBoxButtonTreeNodeSetting

    Public Sub New(ByVal text As String, ByVal path As String, ByVal setting As SettingBag(Of String), ByVal description As String)
        MyBase.New(text, path, setting, description)
        ImageKey = "TextBox16"
    End Sub

    Protected Overrides Sub ButtonClick()
        Using f As New CmdlScriptEditor
            f.SetCmdlDefaults()
            f.CmdlScriptControl.RemoveTools()
            f.CmdlScriptControl.CommandLines = Value

            If f.ShowDialog() = DialogResult.OK Then
                TextBox.Text = f.CmdlScriptControl.CommandLines
            End If
        End Using
    End Sub
End Class

Public Enum CompCheckAction
    [Nothing]
    <Name("image size")> AdjustImageSize
    <Name("file size")> AdjustFileSize
End Enum