﻿Imports System
Imports System.IO
Imports System.Drawing
Imports System.Windows.Forms
Imports System.Threading
Imports System.Threading.Tasks
Imports System.Diagnostics
Imports System.Text

Public Class Form1

    Private Const FrmO_LC_OK As Integer = 0
    Private Const FrmO_LC_INFO As Integer = 1
    Private Const FrmO_LC_WARN As Integer = 2
    Private Const FrmO_LC_ERR As Integer = 3
    Private Const FrmO_LC_DIM As Integer = 4
    Private Const FrmO_LC_BDR As Integer = 5
    Private Const FrmO_LC_PROT As Integer = 6

    Private FrmV_lastOutputFolder As String = ""

    Private Shared ReadOnly DTD_VERSIONS As String() = {
        "dotfuscator_v2.5.dtd", "dotfuscator_v2.4.dtd",
        "dotfuscator_v2.3.dtd", "dotfuscator_v2.2.dtd",
        "dotfuscator_v2.1.dtd", "dotfuscator_v2.0.dtd"}

    Private Shared ReadOnly VS_EDITIONS As String() = {"Enterprise", "Professional", "Community", "BuildTools"}
    Private Shared ReadOnly VS_YEARS As String() = {"2022", "2019", "2017"}

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Me.BackColor = Color.FromArgb(13, 15, 25)
        Chk_ConfuserEx.Checked = True
        Cmb_Level.SelectedIndex = 2
        Txt_DotfuscatorPath.Enabled = False
        Btn_BrowseDotfuscator.Enabled = False
        Chk_RefProxy.Enabled = False
        Chk_Resources.Enabled = False
        Btn_OpenOutput.Enabled = False
        AutoFindDotfuscator()
        EnsureDotfuscatorDtd()
        Log("Dual Shield Obfuscator ready.", FrmO_LC_OK)
        Log("ConfuserEx: place ConfuserEx.CLI.exe next to this app.", FrmO_LC_INFO)
        Log("Dotfuscator: browse to dotfuscatorcli.exe below.", FrmO_LC_INFO)
    End Sub

    Private Sub EnsureDotfuscatorDtd()
        For Each ver As String In DTD_VERSIONS
            If File.Exists(Path.Combine(Application.StartupPath, ver)) Then Return
        Next

        Dim src As String = FindDtdOnDisk(Nothing)
        If src = "" Then
            Log("Dotfuscator DTD not found — Dotfuscator engine may not work.", FrmO_LC_DIM)
            Return
        End If

        Dim dest As String = Path.Combine(Application.StartupPath, Path.GetFileName(src))
        Try
            File.Copy(src, dest, False)
            Log("Dotfuscator DTD cached: " & Path.GetFileName(src), FrmO_LC_DIM)
        Catch

        End Try
    End Sub

    Private Function FindDtdOnDisk(dotExeDir As String) As String
        Dim pf As String = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles)
        Dim pf86 As String = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86)

        Dim searchDirs As New System.Collections.Generic.List(Of String)

        If dotExeDir IsNot Nothing AndAlso dotExeDir <> "" Then
            searchDirs.Add(Path.Combine(dotExeDir, "Common"))
        End If

        searchDirs.Add(Application.StartupPath)

        For Each root As String In {pf, pf86}
            For Each year As String In VS_YEARS
                For Each edition As String In VS_EDITIONS
                    searchDirs.Add(Path.Combine(root,
                        "Microsoft Visual Studio", year, edition,
                        "Common7\IDE\Extensions\PreEmptiveSolutions\DotfuscatorCE\Common"))
                Next
            Next
        Next

        For Each dir As String In searchDirs
            For Each ver As String In DTD_VERSIONS
                Dim candidate As String = Path.Combine(dir, ver)
                If File.Exists(candidate) Then Return candidate
            Next
        Next

        Return ""
    End Function

    Private Sub Chk_ConfuserEx_CheckedChanged(sender As Object, e As EventArgs) Handles Chk_ConfuserEx.CheckedChanged
        If Chk_ConfuserEx.Checked Then
            Chk_Dotfuscator.Checked = False
            Chk_Both.Checked = False
        End If
        SyncEngineState()
    End Sub

    Private Sub Chk_Dotfuscator_CheckedChanged(sender As Object, e As EventArgs) Handles Chk_Dotfuscator.CheckedChanged
        If Chk_Dotfuscator.Checked Then
            Chk_ConfuserEx.Checked = False
            Chk_Both.Checked = False
        End If
        SyncEngineState()
    End Sub

    Private Sub Chk_Both_CheckedChanged(sender As Object, e As EventArgs) Handles Chk_Both.CheckedChanged
        If Chk_Both.Checked Then
            Chk_ConfuserEx.Checked = False
            Chk_Dotfuscator.Checked = False
        End If
        SyncEngineState()
    End Sub

    Private Sub SyncEngineState()
        Dim needDot As Boolean = Chk_Dotfuscator.Checked OrElse Chk_Both.Checked
        Dim needCfx As Boolean = Chk_ConfuserEx.Checked OrElse Chk_Both.Checked
        Txt_DotfuscatorPath.Enabled = needDot
        Btn_BrowseDotfuscator.Enabled = needDot
        Chk_RefProxy.Enabled = needCfx
        Chk_Resources.Enabled = needCfx


        If needDot Then
            Dim found As Boolean = File.Exists(Txt_DotfuscatorPath.Text)
            Lbl_DotfuscatorStatus.Text = If(found, "Found OK", "Not found — please browse")
            Lbl_DotfuscatorStatus.ForeColor = If(found, Color.FromArgb(0, 212, 145), Color.FromArgb(248, 75, 75))
        Else
            Lbl_DotfuscatorStatus.Text = "Select Dotfuscator engine above to enable"
            Lbl_DotfuscatorStatus.ForeColor = Color.FromArgb(75, 98, 138)
        End If
        If Chk_ConfuserEx.Checked Then Log("Engine: ConfuserEx CLI", FrmO_LC_INFO)
        If Chk_Dotfuscator.Checked Then Log("Engine: Dotfuscator CLI", FrmO_LC_INFO)
        If Chk_Both.Checked Then Log("Engine: BOTH — double layer", FrmO_LC_WARN)
    End Sub

    Private Sub btnBrowseInput_Click(sender As Object, e As EventArgs) Handles Btn_BrowseInput.Click
        Using ofd As New OpenFileDialog()
            ofd.Title = "Select your .NET EXE or DLL"
            ofd.Filter = ".NET Assemblies|*.exe;*.dll|EXE files|*.exe|DLL files|*.dll"
            If ofd.ShowDialog() = DialogResult.OK Then
                Txt_InputFile.Text = ofd.FileName
                Txt_InputFile.ForeColor = Color.FromArgb(0, 218, 172)
                If Txt_OutputFolder.ForeColor = Color.FromArgb(68, 92, 128) Then
                    Txt_OutputFolder.Text = Path.Combine(Path.GetDirectoryName(ofd.FileName), "Obfuscated")
                    Txt_OutputFolder.ForeColor = Color.FromArgb(0, 168, 252)
                End If
                Dim fi As New FileInfo(ofd.FileName)
                Log("Input: " & ofd.FileName, FrmO_LC_OK)
                Log("  Size: " & Math.Round(fi.Length / 1024.0, 1) & " KB", FrmO_LC_DIM)
            End If
        End Using
    End Sub

    Private Sub btnBrowseOutput_Click(sender As Object, e As EventArgs) Handles Btn_BrowseOutput.Click
        Using fbd As New FolderBrowserDialog()
            fbd.Description = "Choose where to save the obfuscated file"
            If fbd.ShowDialog() = DialogResult.OK Then
                Txt_OutputFolder.Text = fbd.SelectedPath
                Txt_OutputFolder.ForeColor = Color.FromArgb(0, 168, 252)
                Log("Output folder: " & fbd.SelectedPath, FrmO_LC_INFO)
            End If
        End Using
    End Sub

    Private Sub btnBrowseDotfuscator_Click(sender As Object, e As EventArgs) Handles Btn_BrowseDotfuscator.Click
        Using ofd As New OpenFileDialog()
            ofd.Title = "Find dotfuscatorcli.exe"
            ofd.Filter = "Dotfuscator CLI|dotfuscatorcli.exe|EXE files|*.exe"
            If ofd.ShowDialog() = DialogResult.OK Then
                Txt_DotfuscatorPath.Text = ofd.FileName
                Txt_DotfuscatorPath.ForeColor = Color.FromArgb(0, 168, 252)
                Lbl_DotfuscatorStatus.Text = "Found OK"
                Lbl_DotfuscatorStatus.ForeColor = Color.FromArgb(0, 212, 145)
                Log("Dotfuscator CLI: " & ofd.FileName, FrmO_LC_OK)
            End If
        End Using
    End Sub

    Private Sub btnClearLog_Click(sender As Object, e As EventArgs) Handles btnClearLog.Click
        Rtb_Log.Clear()
    End Sub

    Private Sub btnOpenOutput_Click(sender As Object, e As EventArgs) Handles Btn_OpenOutput.Click
        If Directory.Exists(FrmV_lastOutputFolder) Then
            Process.Start("explorer.exe", FrmV_lastOutputFolder)
        End If
    End Sub

    Private Sub AutoFindDotfuscator()
        Dim pf As String = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles)
        Dim pf86 As String = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86)

        Dim candidates As New System.Collections.Generic.List(Of String)

        candidates.Add(Path.Combine(Application.StartupPath, "dotfuscatorcli.exe"))
        candidates.Add(Path.Combine(pf, "PreEmptive Solutions\Dotfuscator Professional\dotfuscatorcli.exe"))
        candidates.Add(Path.Combine(pf86, "PreEmptive Solutions\Dotfuscator Professional\dotfuscatorcli.exe"))

        For Each root As String In {pf, pf86}
            For Each year As String In VS_YEARS
                For Each edition As String In VS_EDITIONS
                    candidates.Add(Path.Combine(root,
                        "Microsoft Visual Studio", year, edition,
                        "Common7\IDE\Extensions\PreEmptiveSolutions\DotfuscatorCE\dotfuscatorcli.exe"))
                Next
            Next
        Next

        For Each p As String In candidates
            If File.Exists(p) Then
                Txt_DotfuscatorPath.Text = p
                Txt_DotfuscatorPath.ForeColor = Color.FromArgb(0, 168, 252)
                Log("Dotfuscator auto-found: " & p, FrmO_LC_OK)
                Return
            End If
        Next
        Log("Dotfuscator not auto-found. Browse manually if needed.", FrmO_LC_DIM)
    End Sub

    Private Async Sub btnObfuscate_Click(sender As Object, e As EventArgs) Handles Btn_Obfuscate.Click
        If Not File.Exists(Txt_InputFile.Text) Then
            Log("Please select a valid input file first.", FrmO_LC_ERR)
            MessageBox.Show("Please browse for your .exe or .dll file first.",
                "Missing Input", MessageBoxButtons.OK, MessageBoxIcon.Warning)
            Return
        End If
        If Txt_OutputFolder.ForeColor = Color.FromArgb(68, 92, 128) Then
            Log("Please select an output folder.", FrmO_LC_ERR)
            MessageBox.Show("Please browse for an output folder.",
                "Missing Output", MessageBoxButtons.OK, MessageBoxIcon.Warning)
            Return
        End If
        If (Chk_Dotfuscator.Checked OrElse Chk_Both.Checked) AndAlso
           Not File.Exists(Txt_DotfuscatorPath.Text) Then
            Log("Please browse to dotfuscatorcli.exe first.", FrmO_LC_ERR)
            MessageBox.Show("Please browse to dotfuscatorcli.exe.",
                "Dotfuscator Not Found", MessageBoxButtons.OK, MessageBoxIcon.Error)
            Return
        End If

        SetBusy(True)
        FrmV_lastOutputFolder = Txt_OutputFolder.Text
        Rtb_Log.Clear()

        Log("==========================================", FrmO_LC_BDR)
        Log("STARTING OBFUSCATION", FrmO_LC_OK)
        Log("Input:  " & Path.GetFileName(Txt_InputFile.Text), FrmO_LC_INFO)
        Log("Output: " & Txt_OutputFolder.Text, FrmO_LC_INFO)
        Log("Engine: " & GetEngineName(), FrmO_LC_INFO)
        Log("Level:  " & Cmb_Level.Text, FrmO_LC_INFO)
        Log("==========================================", FrmO_LC_BDR)

        Dim ok As Boolean = False
        Try
            If Chk_ConfuserEx.Checked Then
                ok = Await Task.Run(Function() RunCfx(Txt_InputFile.Text, Txt_OutputFolder.Text))
            ElseIf Chk_Dotfuscator.Checked Then
                ok = Await Task.Run(Function() RunDot(Txt_InputFile.Text, Txt_OutputFolder.Text))
            ElseIf Chk_Both.Checked Then
                Log("PHASE 1 of 2 — Dotfuscator...", FrmO_LC_WARN)
                Dim tmpFolder As String = Path.Combine(Path.GetTempPath(),
        "DS_" & DateTime.Now.Ticks.ToString().Substring(10))

                ok = Await Task.Run(Function() RunDot(Txt_InputFile.Text, tmpFolder))
                If ok Then
                    Log("PHASE 2 of 2 — ConfuserEx...", FrmO_LC_WARN)
                    Dim tmpFile As String = Path.Combine(tmpFolder, Path.GetFileName(Txt_InputFile.Text))
                    ok = Await Task.Run(Function() RunCfx(tmpFile, Txt_OutputFolder.Text))
                End If

                Try
                    If Directory.Exists(tmpFolder) Then Directory.Delete(tmpFolder, True)
                Catch
                End Try
            End If
        Catch ex As Exception
            Log("Unexpected error: " & ex.Message, FrmO_LC_ERR)
        End Try

        Log("==========================================", FrmO_LC_BDR)
        If ok Then
            Log("DONE! File protected successfully.", FrmO_LC_OK)
            Log("Saved to: " & Txt_OutputFolder.Text, FrmO_LC_OK)
            Log("NOTE: VirusTotal may show false positives — this is normal.", FrmO_LC_WARN)
            SetProgress(100)
            Lbl_Status.Text = "Complete!"
            Lbl_Status.ForeColor = Color.FromArgb(0, 218, 172)
            Btn_OpenOutput.Enabled = True
            Btn_OpenOutput.BackColor = Color.FromArgb(0, 96, 175)
        Else
            Log("FAILED. See log above for details.", FrmO_LC_ERR)
            SetProgress(0)
            Lbl_Status.Text = "Failed — see log"
            Lbl_Status.ForeColor = Color.FromArgb(248, 75, 75)
        End If
        Log("==========================================", FrmO_LC_BDR)
        SetBusy(False)
    End Sub

    Private Function RunCfx(inputFile As String, outputFolder As String) As Boolean
        Try
            Log("ConfuserEx: starting...", FrmO_LC_INFO)
            SetProgress(10)

            Dim cliPath As String = ""
            Dim desktop As String = Environment.GetFolderPath(Environment.SpecialFolder.Desktop)
            Dim docs As String = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)

            For Each p As String In {
                    Path.Combine(Application.StartupPath, "ConfuserEx.CLI.exe"),
                    Path.Combine(Application.StartupPath, "Confuser_CLI.exe"),
                    Path.Combine(Application.StartupPath, "ConfuserEx", "ConfuserEx.CLI.exe"),
                    Path.Combine(desktop, "ConfuserEx\ConfuserEx.CLI.exe"),
                    Path.Combine(docs, "ConfuserEx\ConfuserEx.CLI.exe"),
                    "C:\ConfuserEx\ConfuserEx.CLI.exe",
                    "D:\ConfuserEx\ConfuserEx.CLI.exe",
                    "E:\ConfuserEx\ConfuserEx.CLI.exe"}
                If File.Exists(p) Then cliPath = p : Exit For
            Next

            If cliPath = "" Then
                Log("ConfuserEx.CLI.exe not found!", FrmO_LC_ERR)
                Log("Download: github.com/mkbel/ConfuserEx/releases", FrmO_LC_WARN)
                Log("Place ConfuserEx.CLI.exe next to DualShieldObfuscator.exe", FrmO_LC_WARN)
                Return False
            End If

            If Not Directory.Exists(outputFolder) Then Directory.CreateDirectory(outputFolder)
            Dim xmlPath As String = Path.Combine(Path.GetTempPath(), "ds_cfx.crproj")
            WriteCfxXml(xmlPath, inputFile, outputFolder)
            Log("ConfuserEx: project XML created.", FrmO_LC_DIM)
            LogProtections()
            SetProgress(25)

            Log("ConfuserEx: running pipeline...", FrmO_LC_INFO)
            If Not RunCLI(cliPath, "-n """ & xmlPath & """", 25, 85) Then Return False

            Dim outFile As String = Path.Combine(outputFolder, Path.GetFileName(inputFile))
            If File.Exists(outFile) Then
                Log("ConfuserEx: output verified OK.", FrmO_LC_OK)
                SetProgress(90)
                Return True
            End If
            Log("ConfuserEx: output file not found — check log above.", FrmO_LC_WARN)
            Return False
        Catch ex As Exception
            Log("ConfuserEx error: " & ex.Message, FrmO_LC_ERR)
            Return False
        End Try
    End Function

    Private Sub WriteCfxXml(xmlPath As String, inputFile As String, outputFolder As String)
        Dim r, s, f, d, t, p, res As Boolean
        Me.Invoke(Sub()
                      r = Chk_Rename.Checked
                      s = Chk_Strings.Checked
                      f = Chk_ControlFlow.Checked
                      d = Chk_AntiDebug.Checked
                      t = Chk_AntiTamper.Checked
                      p = Chk_RefProxy.Checked
                      res = Chk_Resources.Checked
                  End Sub)
        Dim sb As New StringBuilder()
        sb.AppendLine("<?xml version=""1.0"" encoding=""utf-8""?>")
        sb.AppendLine("<project baseDir=""" & Path.GetDirectoryName(inputFile) & """")
        sb.AppendLine("         outputDir=""" & outputFolder & """")
        sb.AppendLine("         xmlns=""http://confuser.codeplex.com"">")
        sb.AppendLine("  <module path=""" & Path.GetFileName(inputFile) & """>")
        sb.AppendLine("    <rule pattern=""true"" inherit=""false"">")
        If r Then sb.AppendLine("      <protection id=""rename"" />")
        If s Then sb.AppendLine("      <protection id=""constants"" />")
        If f Then sb.AppendLine("      <protection id=""ctrl flow""><argument name=""intensity"" value=""" & GetIntensity() & """/></protection>")
        'If d Then sb.AppendLine("      <protection id=""anti debug"" />")
        'If t Then sb.AppendLine("      <protection id=""anti tamper"" />")
        If p Then sb.AppendLine("      <protection id=""ref proxy"" />")
        If res Then sb.AppendLine("      <protection id=""resources"" />")
        sb.AppendLine("      <protection id=""invalid metadata"" />")
        sb.AppendLine("    </rule>")
        sb.AppendLine("  </module>")
        sb.AppendLine("</project>")
        File.WriteAllText(xmlPath, sb.ToString())
    End Sub

    Private Function RunDot(inputFile As String, outputFolder As String) As Boolean
        Try
            Log("Dotfuscator: starting...", FrmO_LC_INFO)
            SetProgress(40)

            If Not Directory.Exists(outputFolder) Then Directory.CreateDirectory(outputFolder)

            Dim dotExe As String = ""
            Me.Invoke(Sub() dotExe = Txt_DotfuscatorPath.Text)
            Dim dotDir As String = Path.GetDirectoryName(dotExe)

            Dim tmpWork As String = Path.Combine(Path.GetTempPath(), "DualShield_Dot")
            If Not Directory.Exists(tmpWork) Then Directory.CreateDirectory(tmpWork)
            Dim xmlPath As String = Path.Combine(tmpWork, "ds_dot.xml")

            ' ── Find DTD ───────────────────────────────────────────
            Dim dtdSrc As String = FindDtdOnDisk(dotDir)
            If dtdSrc = "" Then
                Log("Dotfuscator: DTD not found — cannot continue.", FrmO_LC_ERR)
                Log("Install Visual Studio with Dotfuscator CE component.", FrmO_LC_WARN)
                Return False
            End If

            Dim dtdFileName As String = Path.GetFileName(dtdSrc)
            Dim dtdDest As String = Path.Combine(tmpWork, dtdFileName)
            File.Copy(dtdSrc, dtdDest, True)
            Log("Dotfuscator: DTD → " & dtdFileName, FrmO_LC_OK)

            WriteDotXml(xmlPath, inputFile, outputFolder, dtdDest)
            Log("Dotfuscator: project XML created.", FrmO_LC_DIM)
            Log("  Community Edition — Renaming only (Professional needed for more)", FrmO_LC_WARN)
            LogProtections()
            SetProgress(50)

            Dim args As String = """" & xmlPath & """"
            Log("Dotfuscator: running pipeline...", FrmO_LC_INFO)
            Dim ok As Boolean = RunCLI(dotExe, args, 50, 85)
            SetProgress(88)

            Try
                If Directory.Exists(tmpWork) Then Directory.Delete(tmpWork, True)
            Catch
            End Try

            If ok Then Log("Dotfuscator: finished OK.", FrmO_LC_OK)
            Return ok

        Catch ex As Exception
            Log("Dotfuscator error: " & ex.Message, FrmO_LC_ERR)
            Return False
        End Try
    End Function

    Private Sub WriteDotXml(xmlPath As String, inputFile As String,
                         outputFolder As String, dtdFullPath As String)
        Dim useRename As Boolean
        Me.Invoke(Sub() useRename = Chk_Rename.Checked)

        Dim dtdUri As String = "file:///" & dtdFullPath.Replace("\", "/")

        Dim sb As New StringBuilder()
        sb.AppendLine("<?xml version=""1.0"" encoding=""utf-8""?>")
        sb.AppendLine("<!DOCTYPE dotfuscator SYSTEM """ & dtdUri & """>")
        sb.AppendLine("<dotfuscator version=""2.0"">")
        sb.AppendLine("  <input>")
        sb.AppendLine("    <filelist>")
        sb.AppendLine("      <file dir=""" & Path.GetDirectoryName(inputFile) &
                  """ name=""" & Path.GetFileName(inputFile) & """/>")
        sb.AppendLine("    </filelist>")
        sb.AppendLine("  </input>")
        sb.AppendLine("  <output>")
        sb.AppendLine("    <file dir=""" & outputFolder & """/>")
        sb.AppendLine("  </output>")
        If useRename Then
            sb.AppendLine("  <renaming/>")
        End If

        sb.AppendLine("  <removal/>")
        sb.AppendLine("</dotfuscator>")
        File.WriteAllText(xmlPath, sb.ToString(), New UTF8Encoding(False))
    End Sub

    Private Function RunCLI(exePath As String, args As String,
                             startProgress As Integer, endProgress As Integer) As Boolean
        Try
            Dim psi As New ProcessStartInfo()
            psi.FileName = exePath
            psi.Arguments = args
            psi.RedirectStandardOutput = True
            psi.RedirectStandardError = True
            psi.UseShellExecute = False
            psi.CreateNoWindow = True

            Dim proc As New Process()
            proc.StartInfo = psi

            AddHandler proc.OutputDataReceived,
                Sub(s, ev)
                    If ev.Data IsNot Nothing AndAlso ev.Data.Trim() <> "" Then
                        Dim code As Integer = If(ev.Data.ToLower().Contains("error"), FrmO_LC_ERR,
                                              If(ev.Data.ToLower().Contains("warn"), FrmO_LC_WARN, FrmO_LC_DIM))
                        Log("  " & ev.Data, code)
                    End If
                End Sub

            AddHandler proc.ErrorDataReceived,
                Sub(s, ev)
                    If ev.Data IsNot Nothing AndAlso ev.Data.Trim() <> "" Then
                        Log("  " & ev.Data, FrmO_LC_WARN)
                    End If
                End Sub

            proc.Start()
            proc.BeginOutputReadLine()
            proc.BeginErrorReadLine()

            Dim prog As Integer = startProgress
            Do While Not proc.HasExited
                Thread.Sleep(500)
                If prog < endProgress - 2 Then prog += 2
                SetProgress(prog)
            Loop
            proc.WaitForExit()
            Return proc.ExitCode = 0

        Catch ex As Exception
            Log("Process error: " & ex.Message, FrmO_LC_ERR)
            Return False
        End Try
    End Function

    Private Function GetIntensity() As String
        Dim idx As Integer = 2
        Me.Invoke(Sub() idx = Cmb_Level.SelectedIndex)
        Select Case idx
            Case 0 : Return "1"
            Case 1 : Return "3"
            Case 2 : Return "7"
            Case 3 : Return "10"
            Case Else : Return "5"
        End Select
    End Function

    Private Function GetEngineName() As String
        If Chk_ConfuserEx.Checked Then Return "ConfuserEx CLI"
        If Chk_Dotfuscator.Checked Then Return "Dotfuscator CLI"
        If Chk_Both.Checked Then Return "BOTH (ConfuserEx + Dotfuscator)"
        Return "None"
    End Function

    Private Sub LogProtections()
        If Chk_Dotfuscator.Checked OrElse Chk_Both.Checked Then
            Log("  Note: Dotfuscator Community — Renaming only", FrmO_LC_WARN)
            Log("  Control Flow, Strings, AntiDebug require Professional", FrmO_LC_DIM)
        End If
        Dim list As New System.Collections.Generic.List(Of String)
        Me.Invoke(Sub()
                      If Chk_Rename.Checked Then list.Add("Rename")
                      If Chk_Strings.Checked Then list.Add("Strings")
                      If Chk_ControlFlow.Checked Then list.Add("ControlFlow")
                      If Chk_AntiDebug.Checked Then list.Add("AntiDebug")
                      If Chk_AntiTamper.Checked Then list.Add("AntiTamper")
                      If Chk_RefProxy.Checked Then list.Add("RefProxy")
                      If Chk_Resources.Checked Then list.Add("Resources")
                  End Sub)
        Log("  Protections: " & String.Join(", ", list), FrmO_LC_PROT)
    End Sub

    Private Sub Log(message As String, colorCode As Integer)
        If Rtb_Log Is Nothing Then Return
        If Rtb_Log.InvokeRequired Then
            Rtb_Log.Invoke(Sub() Log(message, colorCode))
            Return
        End If
        Dim c As Color
        Select Case colorCode
            Case FrmO_LC_OK : c = Color.FromArgb(0, 218, 172)
            Case FrmO_LC_INFO : c = Color.FromArgb(145, 175, 225)
            Case FrmO_LC_WARN : c = Color.FromArgb(252, 180, 0)
            Case FrmO_LC_ERR : c = Color.FromArgb(248, 72, 72)
            Case FrmO_LC_DIM : c = Color.FromArgb(65, 90, 128)
            Case FrmO_LC_BDR : c = Color.FromArgb(30, 44, 72)
            Case FrmO_LC_PROT : c = Color.FromArgb(178, 135, 252)
            Case Else : c = Color.FromArgb(145, 175, 225)
        End Select
        Rtb_Log.SelectionStart = Rtb_Log.TextLength
        Rtb_Log.SelectionColor = c
        Rtb_Log.AppendText(message & Environment.NewLine)
        Rtb_Log.ScrollToCaret()
    End Sub

    Private Sub SetProgress(value As Integer)
        If PgBar_Log Is Nothing Then Return
        If PgBar_Log.InvokeRequired Then
            PgBar_Log.Invoke(Sub() SetProgress(value))
            Return
        End If
        PgBar_Log.Value = Math.Max(0, Math.Min(100, value))
    End Sub

    Private Sub SetBusy(busy As Boolean)
        If Me.InvokeRequired Then
            Me.Invoke(Sub() SetBusy(busy))
            Return
        End If
        Dim en As Boolean = Not busy
        Btn_Obfuscate.Enabled = en
        Btn_BrowseInput.Enabled = en
        Btn_BrowseOutput.Enabled = en
        Chk_ConfuserEx.Enabled = en
        Chk_Dotfuscator.Enabled = en
        Chk_Both.Enabled = en
        Cmb_Level.Enabled = en
        For Each chk As CheckBox In {Chk_Rename, Chk_Strings, Chk_ControlFlow,
                                     Chk_AntiDebug, Chk_AntiTamper,
                                     Chk_RefProxy, Chk_Resources}
            chk.Enabled = en
        Next
        Btn_Obfuscate.Text = If(busy, "Processing...", "OBFUSCATE NOW")
        Btn_Obfuscate.BackColor = If(busy, Color.FromArgb(26, 76, 60), Color.FromArgb(0, 165, 125))
        Lbl_Status.Text = If(busy, "Running...", "Ready")
        Lbl_Status.ForeColor = If(busy, Color.FromArgb(252, 180, 0), Color.FromArgb(75, 98, 138))
    End Sub

End Class