Installer Generator 4 LBB EXE's

Code snippets illustrating how LBB can be used
xxgeek
Posts: 18
Joined: Tue Nov 28, 2023 1:36 pm

Installer Generator 4 LBB EXE's

Post by xxgeek »

A project started at the LB forum https://libertybasiccom.proboards.com/t ... cts?page=2

Instructions are in the program, no need for a help file.
It will take a folder of files, allow adding some more, then lump everything into a self extracting .EXE file for installation.
This installer Generator can be used with ANY EXE file

This was my final version.

Code: Select all

   'Installer Generator v3.24 for Just Basic v2.0 and Liberty Basic v4.5.1 and Pro projects
    ' xxgeek    July 2026
    nomainwin
    q$ = chr$(34)
    WindowWidth = 600
    WindowHeight = 560
    UpperLeftX = (DisplayWidth - WindowWidth) / 2
    UpperLeftY = (DisplayHeight - WindowHeight) / 2
    groupbox #main.cfgGroup, "Configuration Settings", 15, 10, 555, 165
    statictext #main.lbl1, "1.  Your Source Folder\EXE File:", 10, 38, 190, 20
    textbox    #main.txtSourceFolder, 205, 30, 260, 24
    button     #main.btnBrowseFolder, "Browse...", [browseSourceFolder], ul, 475, 29, 80, 25
    statictext #main.lbl2, "Output Installer EXE Filename:", 25, 70, 180, 20
    textbox    #main.txtFinalExe, 205, 65, 150, 24
    statictext #main.lbl3, "Your Program's EXE Filename:", 25, 103, 180, 20
    textbox    #main.txtTargetExe, 205, 100, 150, 24
    statictext #main.lbl4, "Installed Program's Shortcut Name:", 25, 138, 180, 40
    textbox    #main.txtShortcut, 205, 135, 150, 24
    button     #main.btnDropZone, "2 - Add Extra Files and Folders", [openDropZone], ul, 45, 185, 230, 35
    button     #main.btnCompile, "3 - Compile Application Installer", [buildInstaller], ul, 300, 185, 230, 35
    statictext #main.title, "I n s t a l l e r", 395, 80, 160, 40
    statictext #main.title2, "G e n e r a t o r", 385, 120, 180, 40
    texteditor #main.log, 15, 235, 555, 225
    checkbox #main.overWrite, "Over-Write Any Existing Installer of Same Name";folderName$;"-Installer.exe", [ovWrite], [novWrite], 25, 470, 285, 20
    open "Single EXE File Installer Compiler for JB, LB and LB Pro Projects" for window as #main
    print #main, "trapclose [quit]"
    #main "font Times New Roman 10 bold"
    #main.log, "!font arial 10 bold"
    #main.title2, "!font Times New Roman 20 bold"
    #main.title, "!font Times New Roman 20 bold"
    print #main.log, "System initialized."
    print #main.log, ""
    print #main.log, "Step #1 - 'Browse' to and Select Your Projects EXE File"
    print #main.log, "   (use generated defaults until familiar with Installer Generator - editing is allowed)"
    print #main.log, ""
    print #main.log, "Step #2 -  Use the 'Add Extra Files and Folders' Button  -  If Desired, not Necessary"
    print #main.log, ""
    print #main.log, "Step #3 - Use the 'Compile Application Installer' Button."
    print #main.log, ""
    print #main.log, "Step #4 - Wait Until Windows Explorer Opens Up With Your Installer.exe Selected"
    print #main.log, ""
    print #main.log, "System Ready."
    print #main.btnBrowseFolder, "!setfocus"
    wait

[browseSourceFolder]
    print #main.log, "SELECT YOUR PROJECT EXE FILE..."
    filedialog "SELECT YOUR PROJECT EXE FILE", "*.exe", assetFile$
    if assetFile$ <> "" then
        derivedFolder$ = ""
        lastSlash = 0
        for i = len(assetFile$) to 1 step -1
            if mid$(assetFile$, i, 1) = "\" then
                lastSlash = i
                derivedFolder$ = left$(assetFile$, i - 1)
                exit for
            end if
        next i
        if right$(derivedFolder$, 1) = ":" then derivedFolder$ = derivedFolder$ ; "\"

        folderName$ = ""
        if lastSlash > 0 then
            prevSlash = 0
            for i = (lastSlash - 1) to 1 step -1
                if mid$(derivedFolder$, i, 1) = "\" then
                    prevSlash = i
                    exit for
                end if
            next i
            if prevSlash > 0 then
                folderName$ = right$(derivedFolder$, len(derivedFolder$) - prevSlash)
            else
                if mid$(derivedFolder$, 2, 2) = ":\" then
                    folderName$ = right$(derivedFolder$, len(derivedFolder$) - 3)
                else
                    folderName$ = derivedFolder$
                end if
            end if
        end if

        if folderName$ = "" then folderName$ = "App"
        upperShortcut$ = upper$(folderName$)

        print #main.txtSourceFolder, derivedFolder$
        print #main.txtFinalExe, folderName$;"-Installer.exe"
        print #main.txtTargetExe, folderName$;".exe"
        print #main.txtShortcut, upperShortcut$
        print #main.log, "Configuration populated automatically."
        print #main.btnDropZone, "!setfocus"
    else
        print #main.log, "!cls"
        print #main.log, "Browse canceled."
        print #main.btnBrowseFolder, "!setfocus"
    end if
    wait

[openDropZone]
    workPath$ = DefaultDir$
    if right$(workPath$, 1) <> "\" then workPath$ = workPath$ ; "\"
    stagingFolder$ = workPath$ ; "StagingTemp"

    print #main.log, "!cls"
    print #main.log, "Opening Multi-Drop Files/Folders Staging Panel..."

    open workPath$ ; "stager.ps1" for output as #psOut
    print #psOut, "Add-Type -AssemblyName System.Windows.Forms"
    print #psOut, "$form = New-Object Windows.Forms.Form"
    print #psOut, "$form.Text = 'Asset Staging Drop Zone'"
    print #psOut, "$form.Width = 480; $form.Height = 290"
    print #psOut, "$form.StartPosition = 'CenterScreen'"
    print #psOut, "$form.TopMost = $true"
    print #psOut, "$form.AllowDrop = $true"

    ' Colors
    print #psOut, "$normalColor = 'LightGreen'"
    print #psOut, "$flashColor = 'Cyan'"

    ' Label
    print #psOut, "$label = New-Object Windows.Forms.Label"
    print #psOut, "$label.Text = '    DRAG  AND  DROP  FILES  AND  FOLDERS  HERE'"
    print #psOut, "$label.Location = New-Object System.Drawing.Point(15, 15)"
    print #psOut, "$label.Size = New-Object System.Drawing.Size(430, 130)"
    print #psOut, "$label.TextAlign = 'MiddleCenter'"
    print #psOut, "$form.BackColor = $normalColor"
    print #psOut, "$label.BackColor = 'Yellow'"
    print #psOut, "$label.Font = New-Object System.Drawing.Font('Segoe UI', 10, [System.Drawing.FontStyle]::Bold)"
    print #psOut, "$form.Controls.Add($label)"

    ' Button
    print #psOut, "$btn = New-Object Windows.Forms.Button"
    print #psOut, "$btn.Text = 'Finished Adding File/Folders'"
    print #psOut, "$btn.Location = New-Object System.Drawing.Point(140, 175)"
    print #psOut, "$btn.Size = New-Object System.Drawing.Size(180, 35)"
    print #psOut, "$btn.Add_Click({ Out-File -FilePath '" ; workPath$ ; "stage_done.txt' -InputObject 'done'; $form.Close() })"
    print #psOut, "$form.Controls.Add($btn)"

    ' FLASH TIMER: This triggers the reset after a flash
    print #psOut, "$flashTimer = New-Object Windows.Forms.Timer"
    print #psOut, "$flashTimer.Interval = 300"
    print #psOut, "$flashTimer.Add_Tick({ "
    print #psOut, "    $form.BackColor = $normalColor"
    print #psOut, "    $flashTimer.Stop()"
    print #psOut, "})"

    ' Drag Logic
    print #psOut, "$form.Add_DragEnter({ if ($_.Data.GetDataPresent([Windows.Forms.DataFormats]::FileDrop)) { $_.Effect = 'Copy' } })"
    print #psOut, "$form.Add_DragDrop({ "
    print #psOut, "    $files = $_.Data.GetData([Windows.Forms.DataFormats]::FileDrop)"
    print #psOut, "    $dest = '" ; stagingFolder$ ; "'"
    print #psOut, "    if (!(Test-Path $dest)) { New-Item -ItemType Directory -Path $dest | Out-Null }"
    print #psOut, "    $listFile = '" ; workPath$ ; "dropped_list.txt'"
    print #psOut, "    foreach ($file in $files) {"
    print #psOut, "        if (Test-Path $file -PathType Container) {"
    print #psOut, "            robocopy.exe " ; q$ ; "$file" ; q$ ; " " ; q$ ; "$dest\$(Split-Path $file -Leaf)" ; q$ ; " /E /IS /IT /R:0 /W:0"
    print #psOut, "            Add-Content -Path $listFile -Value $(Split-Path $file -Leaf)"
    print #psOut, "        } else {"
    print #psOut, "            robocopy.exe " ; q$ ; "$(Split-Path $file -Parent)" ; q$ ; " " ; q$ ; "$dest" ; q$ ; " " ; q$ ; "$(Split-Path $file -Leaf)" ; q$ ; " /IS /IT /R:0 /W:0"
    print #psOut, "            Add-Content -Path $listFile -Value $(Split-Path $file -Leaf)"
    print #psOut, "        }"
    print #psOut, "    }"

    ' Trigger the flash
    print #psOut, "    $form.BackColor = $flashColor"
    print #psOut, "    $flashTimer.Start()"
    print #psOut, "    $label.Text = 'Items processed! Drop more items, or click below when finished.'"
    print #psOut, "})"

    print #psOut, "[Windows.Forms.Application]::Run($form)"
    close #psOut
    if fileExists(workPath$, "stage_done.txt") > 0 then kill workPath$ ; "stage_done.txt"
    if fileExists(workPath$, "dropped_list.txt") > 0 then kill workPath$ ; "dropped_list.txt"

    run "powershell.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File " ; q$ ; workPath$ ; "stager.ps1" ; q$, hide

[waitForStager]
    scan
    if fileExists(workPath$, "stage_done.txt") > 0 then goto [stagerFinished]
    timer 250, [stageDelay]
    wait
[stageDelay]
    timer 0
    goto [waitForStager]

[stagerFinished]
    if fileExists(workPath$, "stager.ps1") > 0 then kill workPath$ ; "stager.ps1"
    if fileExists(workPath$, "stage_done.txt") > 0 then kill workPath$ ; "stage_done.txt"
    print #main.log, "Drop zone completed safely."

    if fileExists(workPath$, "dropped_list.txt") > 0 then
        print #main.log, "Staged Items Added:"
        open workPath$ ; "dropped_list.txt" for input as #listIn
[readLogLoop]
        if eof(#listIn) <> 0 then goto [readLogDone]
        line input #listIn, entry$
        if instr(entry$, ".") <> 0 then print #main.log, "File - ";entry$ else print #main.log, "Folder  - [";entry$;"]"
        goto [readLogLoop]
[readLogDone]
        close #listIn
        kill workPath$ ; "dropped_list.txt"
        print #main.log, ""
        print #main.log, "            ######  READY TO COMPILE THE INSTALLER  ######"
    end if
        print #main.btnCompile, "!setfocus"
    wait

[buildInstaller]
    print #main.txtSourceFolder, "!contents? sourceAppFolder$"
    sourceAppFolder$ = trim$(sourceAppFolder$)
    print #main.txtFinalExe, "!contents? finalExeName$"
    finalExeName$ = trim$(finalExeName$)
    print #main.txtTargetExe, "!contents? targetExecutable$"
    targetExecutable$ = trim$(targetExecutable$)
    print #main.txtShortcut, "!contents? shortcutName$"
    shortcutName$ = trim$(shortcutName$)

    if sourceAppFolder$ = "" or finalExeName$ = "" or targetExecutable$ = "" or shortcutName$ = "" then
        print #main.log, "ERROR: All settings must be defined before compiling."
        wait
    end if

    workPath$ = DefaultDir$
    if right$(workPath$, 1) <> "\" then workPath$ = workPath$ ; "\"
    stagingFolder$ = workPath$ ; "StagingTemp"

[generateEngine]
    print #main.log, "Generating specialized deployment layout scripts..."
    progFolder$ = targetExecutable$
    if lower$(right$(progFolder$, 4)) = ".exe" then progFolder$ = left$(progFolder$, len(progFolder$) - 4)

    open workPath$ ; "prompt.vbs" for output as #vbsPrompt
    print #vbsPrompt, "Set w = CreateObject("; q$; "WScript.Shell"; q$; ")"
    print #vbsPrompt, "Set f = CreateObject("; q$; "Scripting.FileSystemObject"; q$; ")"
    print #vbsPrompt, "p = f.GetParentFolderName(WScript.ScriptFullName)"
    print #vbsPrompt, "Set s = CreateObject("; q$; "Shell.Application"; q$; ")"
    print #vbsPrompt, "Set b = s.BrowseForFolder(0, "; q$; "Select Installation Destination Drive or Folder:"; q$; ", 1, 0)"
    print #vbsPrompt, "If b Is Nothing Then WScript.Quit"
    print #vbsPrompt, "targetDir = f.BuildPath(b.Self.Path, "; q$; progFolder$; q$; ")"
    print #vbsPrompt, "Set out = f.CreateTextFile(f.BuildPath(p, "; q$; "path.tmp"; q$; "), True)"
    print #vbsPrompt, "out.WriteLine targetDir"
    print #vbsPrompt, "out.Close"
    print #vbsPrompt, "w.Run "; q$; "cmd.exe /c powershell.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File "; q$; " & chr(34) & f.BuildPath(p, "; q$; "deploy.ps1"; q$; ") & chr(34), 0, True"
    close #vbsPrompt

    open workPath$ ; "deploy.ps1" for output as #psDeploy
    print #psDeploy, "param([string]$PassedPath)"
    print #psDeploy, "$scriptFile = $MyInvocation.MyCommand.Path"
    print #psDeploy, "$scriptDir  = Split-Path $scriptFile -Parent"
    print #psDeploy, "$tmpFile = Join-Path $scriptDir 'path.tmp'"
    print #psDeploy, "if ($PassedPath) { $InstallDir = $PassedPath } else {"
    print #psDeploy, "    if (Test-Path $tmpFile) { $InstallDir = Get-Content $tmpFile -First 1 } else { Exit }"
    print #psDeploy, "}"
    print #psDeploy, "$needsAdmin = $false"
    print #psDeploy, "if (!(Test-Path $InstallDir)) { try { New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null } catch { $needsAdmin = $true } }"
    print #psDeploy, "$testFile = [System.IO.Path]::Combine($InstallDir, 'perm_test.tmp')"
    print #psDeploy, "if (!$needsAdmin) { try { 'test' | Out-File $testFile -ErrorAction Stop; Remove-Item $testFile } catch { $needsAdmin = $true } }"
    print #psDeploy, "if ($needsAdmin) {"
    print #psDeploy, "    $currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())"
    print #psDeploy, "    if (!$currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {"
    print #psDeploy, "        $arguments = '-NoProfile -ExecutionPolicy Bypass -File ' + [char]34 + $scriptFile + [char]34 + ' -PassedPath ' + [char]34 + $InstallDir + [char]34"
    print #psDeploy, "        $process = Start-Process powershell.exe -ArgumentList $arguments -WorkingDirectory $scriptDir -Verb RunAs -WindowStyle Hidden -PassThru"
    print #psDeploy, "        $process.WaitForExit(); Exit"
    print #psDeploy, "    } else {"
    print #psDeploy, "        $ws = New-Object -ComObject WScript.Shell; $ws.Popup('Target directory write-protected.', 0, 'Access Error', 16); Exit"
    print #psDeploy, "    }"
    print #psDeploy, "}"
    print #psDeploy, "$archive = [System.IO.Path]::Combine($scriptDir, 'payload.tar')"
    print #psDeploy, "if (Test-Path $archive) {"
    print #psDeploy, "    Move-Item -Path $archive -Destination $InstallDir -Force; $oldDir = Get-Location; Set-Location $InstallDir"
    print #psDeploy, "    $tarProc = Start-Process tar.exe -ArgumentList '-xf payload.tar' -WindowStyle Hidden -PassThru"
    print #psDeploy, "    while (!$tarProc.HasExited) { Start-Sleep -Milliseconds 100 }"
    print #psDeploy, "    Remove-Item payload.tar -Force; Set-Location $oldDir"
    print #psDeploy, "}"
    print #psDeploy, "if (Test-Path $tmpFile) { Remove-Item $tmpFile -Force }"
    print #psDeploy, "$wsh = New-Object -ComObject WScript.Shell"
    print #psDeploy, "if ($wsh.Popup('Do you want to create a shortcut on your Desktop?', 0, 'Shortcut Setup', 36) -eq 6) {"
    print #psDeploy, "    $lnk = $wsh.CreateShortcut([System.IO.Path]::Combine([Environment]::GetFolderPath('Desktop'), '" ; shortcutName$ ; ".lnk'))"
    print #psDeploy, "    $lnk.TargetPath = [System.IO.Path]::Combine($InstallDir, '" ; targetExecutable$ ; "'); $lnk.WorkingDirectory = $InstallDir; $lnk.Save()"
    print #psDeploy, "}"
    print #psDeploy, "if ($wsh.Popup('Do you want to add this application to your Start Menu?', 0, 'Shortcut Setup', 36) -eq 6) {"
    print #psDeploy, "    $lnk = $wsh.CreateShortcut([System.IO.Path]::Combine([Environment]::GetFolderPath('Programs'), '" ; shortcutName$ ; ".lnk'))"
    print #psDeploy, "    $lnk.TargetPath = [System.IO.Path]::Combine($InstallDir, '" ; targetExecutable$ ; "'); $lnk.WorkingDirectory = $InstallDir; $lnk.Save()"
    print #psDeploy, "}"
    print #psDeploy, "$wsh.Popup('Installation Completed Successfully!', 0, 'Complete', 64) | Out-Null"
    close #psDeploy

[writeSED]
    print #main.log, "Building Self Extraction Directive file for Iexpress config..."
    open workPath$ ; "app.sed" for output as #sedOut
    print #sedOut, "[Version]"
    print #sedOut, "Class=IEXPRESS"
    print #sedOut, "SEDVersion=3"
    print #sedOut, "[Options]"
    print #sedOut, "PackagePurpose=InstallApp"
    print #sedOut, "ShowInstallProgramWindow=0"
    print #sedOut, "HideExtractAnimation=1"
    print #sedOut, "UseLongFileName=1"
    print #sedOut, "InsideCompressed=0"
    print #sedOut, "CAB_FixedSize=0"
    print #sedOut, "CAB_ReserVecZero=0"
    print #sedOut, "RebootMode=N"
    print #sedOut, "InstallPrompt="
    print #sedOut, "DisplayLicense="
    print #sedOut, "FinishMessage="
    print #sedOut, "TargetName="; finalExeName$
    print #sedOut, "AppLaunched=wscript.exe prompt.vbs"
    print #sedOut, "PostInstallCmd=<None>"
    print #sedOut, "UACExecutionLevel=AsInvoker"
    print #sedOut, "SourceFiles=SourceFiles"
    print #sedOut, "[SourceFiles]"
    print #sedOut, "SourceFiles0=."
    print #sedOut, "[SourceFiles0]"
    print #sedOut, "%FILE0%="
    print #sedOut, "%FILE1%="
    print #sedOut, "%FILE2%="
    print #sedOut, "[Strings]"
    print #sedOut, "FILE0="; q$; "prompt.vbs"; q$
    print #sedOut, "FILE1="; q$; "deploy.ps1"; q$
    print #sedOut, "FILE2="; q$; "payload.tar"; q$
    close #sedOut

    print #main.log, "Generating execution control block script..."
    if fileExists(workPath$, "comp_done.txt") > 0 then kill workPath$ ; "comp_done.txt"
    overWrite$=folderName$;"-Installer.exe"
    if fileExists(workPath$,overWrite$) and overWrite=1 then run "cmd.exe /c del /f /q ";q$;overWrite$;q$,hide

    open workPath$ ; "compile.bat" for output as #cBat
    print #cBat, "@echo off"
    print #cBat, "mode con: cols=45 lines=6"
    print #cBat, "title Pipeline Engine"
    print #cBat, "echo ==================================================="
    print #cBat, "echo  1. SYNCHRONIZING PRIMARY APPLICATION ASSETS..."
    print #cBat, "echo ==================================================="
    print #cBat, "robocopy.exe " ; q$ ; sourceAppFolder$ ; q$ ; " " ; q$ ; stagingFolder$ ; q$ ; " /E /IS /IT /R:0 /W:0"
    print #cBat, "echo."
    print #cBat, "echo ==================================================="
    print #cBat, "echo  2. RUNNING TAR PACKAGING COMPRESSION ENGINE..."
    print #cBat, "echo ==================================================="
    print #cBat, "tar.exe -cvf payload.tar -C "; q$; stagingFolder$; q$; " ."
    print #cBat, "echo."
    print #cBat, "echo ==================================================="
    print #cBat, "echo  3. RUNNING IEXPRESS EXECUTABLE COMPILER..."
    print #cBat, "echo ==================================================="
    print #cBat, "iexpress.exe /n app.sed"
    print #cBat, "echo done > comp_done.txt"
    print #cBat, "exit"
    close #cBat

    print #main.log, "Launching native console pipeline compilation stage..."
    run "cmd.exe /c compile.bat"

[waitForCompilation]
    scan
    if fileExists(workPath$, "comp_done.txt") > 0 then goto [cleanUpAll]
    timer 150, [cDelay]
    wait
[cDelay]
    timer 0
    goto [waitForCompilation]

[cleanUpAll]
    print #main.log, "" : print #main.log, ""

    if fileExists(workPath$, finalExeName$) > 0 then
        print #main.log, "###### SUCCESS! Master distribution EXE file generated. ######"
        print #main.btnBrowseFolder, "!setfocus"
        run "explorer.exe /select," ; q$ ; workPath$ ; finalExeName$ ; q$
    else
        notice "Installer file " + finalExeName$ + " was NOT created"
    end if

    if fileExists(workPath$, "prompt.vbs") > 0 then kill "prompt.vbs"
    if fileExists(workPath$, "deploy.ps1") > 0 then kill "deploy.ps1"
    if fileExists(workPath$, "payload.tar") > 0 then kill "payload.tar"
    if fileExists(workPath$, "app.sed") > 0 then kill "app.sed"
    if fileExists(workPath$, "compile.bat") > 0 then kill "compile.bat"
    if fileExists(workPath$, "comp_done.txt") > 0 then kill "comp_done.txt"
    run "cmd.exe /c rmdir /s /q " ; q$ ; workPath$ ; "StagingTemp" ; q$, hide
    wait

[ovWrite]
    overWrite = 1
    wait
[novWrite]
    overWrite = 0
    wait

[quit]
    timer 0
    #main.log "!cls"
    close #main
    end

function fileExists(path$, filename$)
    dim info$(10,10)
    files path$, filename$, info$()
    fileExists = val(info$(0,0))
end function

sub pause mil
    t=time$("ms")+mil
    while time$("ms")<t
        scan
    wend
end sub 
Another more intuitive version was written by atamose - an LB forum member

Give it a try too. You may prefer it.

Code: Select all

  nomainwin
    q$ = chr$(34)
    WindowWidth = 900
    WindowHeight = 765
    UpperLeftX = (DisplayWidth - WindowWidth) / 2
    UpperLeftY = (DisplayHeight - WindowHeight) / 2

    '--- Section 1 : Configuration Settings -------------------------------
    groupbox   #main.cfgGroup, "  1. CONFIGURATION SETTINGS  ", 15, 10, 853, 230

    statictext #main.lbl1,     "Your Source Folder\EXE File:", 25, 35, 200, 18
    statictext #main.lbl1d,    "Select the main EXE file to compile.", 25, 53, 200, 16
    textbox    #main.txtSourceFolder, 280, 40, 230, 24
    button     #main.btnBrowseFolder, " Browse...", [browseSourceFolder], ul, 520, 39, 70, 25

    statictext #main.lbl2,     "Output Installer EXE Filename:", 25, 88, 200, 18
    statictext #main.lbl2d,    "Name of the generated installer.", 25, 106, 200, 16
    textbox    #main.txtFinalExe, 280, 93, 230, 24

    statictext #main.lbl3,     "Your Program's EXE Filename:", 25, 138, 200, 18
    statictext #main.lbl3d,    "Name of the program being installed.", 25, 156, 200, 16
    textbox    #main.txtTargetExe, 280, 143, 230, 24

    statictext #main.lbl4,     "Installed Program's Shortcut Name:", 25, 188, 200, 18
    statictext #main.lbl4d,    "Shortcut name shown in Start Menu.", 25, 206, 200, 16
    textbox    #main.txtShortcut, 280, 193, 230, 24

    statictext #main.title,    "INSTALLER", 650, 55, 200, 30
    statictext #main.title2,   "GENERATOR", 640, 90, 200, 30
    statictext #main.tagline3,  "_____________", 680, 120, 110, 60
    statictext #main.tagline,  "Create professional installers         in just a few simple steps.", 645, 150, 180, 60
    statictext #main.tagline4,  "by xxgeek - 2026", 685, 190, 180, 20

    '--- Section 2 : Add Extra Files and Folders ---------------------------
    groupbox   #main.dropGroup, "  2. ADD EXTRA FILES AND FOLDERS  ", 15, 250, 420, 110
    statictext #main.dropDesc, "_____________________________________________________", 40, 267, 380, 30
    statictext #main.dropDesc, "Include additional files or folders in your installer. (optional)", 40, 300, 240, 40
    button     #main.btnDropZone, "Open File Manager", [openDropZone], ul, 285, 297, 125, 35

    '--- Section 3 : Compile Application Installer --------------------------
    groupbox   #main.compGroup, "  3. COMPILE APPLICATION INSTALLER  ", 445, 250, 425, 110
    statictext #main.dropDesc, "_____________________________________________________", 465, 267, 380, 30
    statictext #main.compDesc, "Compile your application into a single self-extracting installer EXE file.", 465, 300, 240, 40
    button     #main.btnCompile, "Compile Installer", [buildInstaller], ul, 715, 297, 125, 35

    '--- Status / Instructions ----------------------------------------------
    groupbox   #main.logGroup, "STATUS / INSTRUCTIONS", 15, 370, 853, 310
    texteditor #main.log, 25, 395, 835, 270

    '--- Footer ---------------------------------------------------------------
    checkbox #main.overWrite, "  Over-Write Any Existing Installer of Same Name";folderName$;"-Installer.exe", [ovWrite], [novWrite], 25, 685, 400, 20

    open "Vrs 3.25" for window_nf as #main
    print #main, "trapclose [quit]"
    #main "font Segoe UI 10"
    #main.log, "!font arial 10 bold"
    #main.title2, "!font Times New Roman 20 bold"
    #main.title, "!font Times New Roman 20 bold"
    #main.lbl1, "!font Segoe UI 10 bold"
    #main.lbl2, "!font Segoe UI 10 bold"
    #main.lbl3, "!font Segoe UI 10 bold"
    #main.lbl4, "!font Segoe UI 10 bold"
    #main.lbl1d, "!font Segoe UI 9"
    #main.lbl2d, "!font Segoe UI 9"
    #main.lbl3d, "!font Segoe UI 9"
    #main.lbl4d, "!font Segoe UI 9"
    #main.tagline, "!font Segoe UI 9"
    #main.dropDesc, "!font Segoe UI 9"
    #main.compDesc, "!font Segoe UI 9"

    #main.cfgGroup,"!font Segoe UI 10 bold"
    #main.dropGroup,"!font Segoe UI 10 bold"
    #main.compGroup, "!font Segoe UI 10 bold"
    #main.logGroup, "!font Segoe UI 10 bold"
    #main.tagline4, "!font Segoe UI 8 bold italic"

    print #main.log, "System initialized."
    print #main.log, ""
    print #main.log, "Step #1 - 'Browse' to and Select Your Projects EXE File"
    print #main.log, "   (use generated defaults until familiar with Installer Generator - editing is allowed)"
    print #main.log, ""
    print #main.log, "Step #2 -  Use the 'Add Extra Files and Folders' Button  -  If Desired, not Necessary"
    print #main.log, ""
    print #main.log, "Step #3 - Use the 'Compile Application Installer' Button."
    print #main.log, ""
    print #main.log, "Step #4 - Wait Until Windows Explorer Opens Up With Your Installer.exe Selected"
    print #main.log, ""
    print #main.log, "System Ready."
    print #main.btnBrowseFolder, "!setfocus"
    wait

[browseSourceFolder]
    print #main.log, "SELECT YOUR PROJECT EXE FILE..."
    filedialog "SELECT YOUR PROJECT EXE FILE", "*.exe", assetFile$
    if assetFile$ <> "" then
        derivedFolder$ = ""
        lastSlash = 0
        for i = len(assetFile$) to 1 step -1
            if mid$(assetFile$, i, 1) = "\" then
                lastSlash = i
                derivedFolder$ = left$(assetFile$, i - 1)
                exit for
            end if
        next i
        if right$(derivedFolder$, 1) = ":" then derivedFolder$ = derivedFolder$ ; "\"

        folderName$ = ""
        if lastSlash > 0 then
            prevSlash = 0
            for i = (lastSlash - 1) to 1 step -1
                if mid$(derivedFolder$, i, 1) = "\" then
                    prevSlash = i
                    exit for
                end if
            next i
            if prevSlash > 0 then
                folderName$ = right$(derivedFolder$, len(derivedFolder$) - prevSlash)
            else
                if mid$(derivedFolder$, 2, 2) = ":\" then
                    folderName$ = right$(derivedFolder$, len(derivedFolder$) - 3)
                else
                    folderName$ = derivedFolder$
                end if
            end if
        end if

        if folderName$ = "" then folderName$ = "App"
        upperShortcut$ = upper$(folderName$)

        print #main.txtSourceFolder, derivedFolder$
        print #main.txtFinalExe, folderName$;"-Installer.exe"
        print #main.txtTargetExe, folderName$;".exe"
        print #main.txtShortcut, upperShortcut$
        print #main.log, "Configuration populated automatically."
        print #main.btnDropZone, "!setfocus"
    else
        print #main.log, "!cls"
        print #main.log, "Browse canceled."
        print #main.btnBrowseFolder, "!setfocus"
    end if
    wait

[openDropZone]
    workPath$ = DefaultDir$
    if right$(workPath$, 1) <> "\" then workPath$ = workPath$ ; "\"
    stagingFolder$ = workPath$ ; "StagingTemp"

    print #main.log, "!cls"
    print #main.log, "Opening Multi-Drop Files/Folders Staging Panel..."

    open workPath$ ; "stager.ps1" for output as #psOut
    print #psOut, "Add-Type -AssemblyName System.Windows.Forms"
    print #psOut, "$form = New-Object Windows.Forms.Form"
    print #psOut, "$form.Text = 'Asset Staging Drop Zone'"
    print #psOut, "$form.Width = 480; $form.Height = 290"
    print #psOut, "$form.StartPosition = 'CenterScreen'"
    print #psOut, "$form.TopMost = $true"
    print #psOut, "$form.AllowDrop = $true"

    ' Colors
    print #psOut, "$normalColor = 'white'"
    print #psOut, "$flashColor = 'darkgray'"

    ' Label
    print #psOut, "$label = New-Object Windows.Forms.Label"
    print #psOut, "$label.Text = '    DRAG  AND  DROP  FILES  AND  FOLDERS  HERE'"
    print #psOut, "$label.Location = New-Object System.Drawing.Point(15, 15)"
    print #psOut, "$label.Size = New-Object System.Drawing.Size(430, 150)"
    print #psOut, "$label.TextAlign = 'MiddleCenter'"
    print #psOut, "$form.BackColor = $normalColor"
    print #psOut, "$label.BackColor = 'lightgray'"
    print #psOut, "$label.Font = New-Object System.Drawing.Font('Segoe UI', 10, [System.Drawing.FontStyle]::Bold)"
    print #psOut, "$form.Controls.Add($label)"

    ' Button
    print #psOut, "$btn = New-Object Windows.Forms.Button"
    print #psOut, "$btn.Text = 'Finished Adding File/Folders'"
    print #psOut, "$btn.Location = New-Object System.Drawing.Point(140, 190)"
    print #psOut, "$btn.Size = New-Object System.Drawing.Size(180, 35)"
    print #psOut, "$btn.Add_Click({ Out-File -FilePath '" ; workPath$ ; "stage_done.txt' -InputObject 'done'; $form.Close() })"
    print #psOut, "$form.Controls.Add($btn)"

    ' FLASH TIMER: This triggers the reset after a flash
    print #psOut, "$flashTimer = New-Object Windows.Forms.Timer"
    print #psOut, "$flashTimer.Interval = 300"
    print #psOut, "$flashTimer.Add_Tick({ "
    print #psOut, "    $form.BackColor = $normalColor"
    print #psOut, "    $flashTimer.Stop()"
    print #psOut, "})"

    ' Drag Logic
    print #psOut, "$form.Add_DragEnter({ if ($_.Data.GetDataPresent([Windows.Forms.DataFormats]::FileDrop)) { $_.Effect = 'Copy' } })"
    print #psOut, "$form.Add_DragDrop({ "
    print #psOut, "    $files = $_.Data.GetData([Windows.Forms.DataFormats]::FileDrop)"
    print #psOut, "    $dest = '" ; stagingFolder$ ; "'"
    print #psOut, "    if (!(Test-Path $dest)) { New-Item -ItemType Directory -Path $dest | Out-Null }"
    print #psOut, "    $listFile = '" ; workPath$ ; "dropped_list.txt'"
    print #psOut, "    foreach ($file in $files) {"
    print #psOut, "        if (Test-Path $file -PathType Container) {"
    print #psOut, "            robocopy.exe " ; q$ ; "$file" ; q$ ; " " ; q$ ; "$dest\$(Split-Path $file -Leaf)" ; q$ ; " /E /IS /IT /R:0 /W:0"
    print #psOut, "            Add-Content -Path $listFile -Value $(Split-Path $file -Leaf)"
    print #psOut, "        } else {"
    print #psOut, "            robocopy.exe " ; q$ ; "$(Split-Path $file -Parent)" ; q$ ; " " ; q$ ; "$dest" ; q$ ; " " ; q$ ; "$(Split-Path $file -Leaf)" ; q$ ; " /IS /IT /R:0 /W:0"
    print #psOut, "            Add-Content -Path $listFile -Value $(Split-Path $file -Leaf)"
    print #psOut, "        }"
    print #psOut, "    }"

    ' Trigger the flash
    print #psOut, "    $form.BackColor = $flashColor"
    print #psOut, "    $flashTimer.Start()"
    print #psOut, "    $label.Text = 'Items processed! Drop more items, or click below when finished.'"
    print #psOut, "})"

    print #psOut, "[Windows.Forms.Application]::Run($form)"
    close #psOut
    if fileExists(workPath$, "stage_done.txt") > 0 then kill workPath$ ; "stage_done.txt"
    if fileExists(workPath$, "dropped_list.txt") > 0 then kill workPath$ ; "dropped_list.txt"

    run "powershell.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File " ; q$ ; workPath$ ; "stager.ps1" ; q$, hide

[waitForStager]
    scan
    if fileExists(workPath$, "stage_done.txt") > 0 then goto [stagerFinished]
    timer 250, [stageDelay]
    wait
[stageDelay]
    timer 0
    goto [waitForStager]

[stagerFinished]
    if fileExists(workPath$, "stager.ps1") > 0 then kill workPath$ ; "stager.ps1"
    if fileExists(workPath$, "stage_done.txt") > 0 then kill workPath$ ; "stage_done.txt"
    print #main.log, "Drop zone completed safely."

    if fileExists(workPath$, "dropped_list.txt") > 0 then
        print #main.log, "Staged Items Added:"
        open workPath$ ; "dropped_list.txt" for input as #listIn
[readLogLoop]
        if eof(#listIn) <> 0 then goto [readLogDone]
        line input #listIn, entry$
        if instr(entry$, ".") <> 0 then print #main.log, "File - ";entry$ else print #main.log, "Folder  - [";entry$;"]"
        goto [readLogLoop]
[readLogDone]
        close #listIn
        kill workPath$ ; "dropped_list.txt"
        print #main.log, ""
        print #main.log, "            ######  READY TO COMPILE THE INSTALLER  ######"
    end if
        print #main.btnCompile, "!setfocus"
    wait

[buildInstaller]
    print #main.txtSourceFolder, "!contents? sourceAppFolder$"
    sourceAppFolder$ = trim$(sourceAppFolder$)
    print #main.txtFinalExe, "!contents? finalExeName$"
    finalExeName$ = trim$(finalExeName$)
    print #main.txtTargetExe, "!contents? targetExecutable$"
    targetExecutable$ = trim$(targetExecutable$)
    print #main.txtShortcut, "!contents? shortcutName$"
    shortcutName$ = trim$(shortcutName$)

    if sourceAppFolder$ = "" or finalExeName$ = "" or targetExecutable$ = "" or shortcutName$ = "" then
        print #main.log, "ERROR: All settings must be defined before compiling."
        wait
    end if

    workPath$ = DefaultDir$
    if right$(workPath$, 1) <> "\" then workPath$ = workPath$ ; "\"
    stagingFolder$ = workPath$ ; "StagingTemp"

[generateEngine]
    print #main.log, "Generating specialized deployment layout scripts..."
    progFolder$ = targetExecutable$
    if lower$(right$(progFolder$, 4)) = ".exe" then progFolder$ = left$(progFolder$, len(progFolder$) - 4)

    open workPath$ ; "prompt.vbs" for output as #vbsPrompt
    print #vbsPrompt, "Set w = CreateObject("; q$; "WScript.Shell"; q$; ")"
    print #vbsPrompt, "Set f = CreateObject("; q$; "Scripting.FileSystemObject"; q$; ")"
    print #vbsPrompt, "p = f.GetParentFolderName(WScript.ScriptFullName)"
    print #vbsPrompt, "Set s = CreateObject("; q$; "Shell.Application"; q$; ")"
    print #vbsPrompt, "Set b = s.BrowseForFolder(0, "; q$; "Select Installation Destination Drive or Folder:"; q$; ", 1, 0)"
    print #vbsPrompt, "If b Is Nothing Then WScript.Quit"
    print #vbsPrompt, "targetDir = f.BuildPath(b.Self.Path, "; q$; progFolder$; q$; ")"
    print #vbsPrompt, "Set out = f.CreateTextFile(f.BuildPath(p, "; q$; "path.tmp"; q$; "), True)"
    print #vbsPrompt, "out.WriteLine targetDir"
    print #vbsPrompt, "out.Close"
    print #vbsPrompt, "w.Run "; q$; "cmd.exe /c powershell.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File "; q$; " & chr(34) & f.BuildPath(p, "; q$; "deploy.ps1"; q$; ") & chr(34), 0, True"
    close #vbsPrompt

    open workPath$ ; "deploy.ps1" for output as #psDeploy
    print #psDeploy, "param([string]$PassedPath)"
    print #psDeploy, "$scriptFile = $MyInvocation.MyCommand.Path"
    print #psDeploy, "$scriptDir  = Split-Path $scriptFile -Parent"
    print #psDeploy, "$tmpFile = Join-Path $scriptDir 'path.tmp'"
    print #psDeploy, "if ($PassedPath) { $InstallDir = $PassedPath } else {"
    print #psDeploy, "    if (Test-Path $tmpFile) { $InstallDir = Get-Content $tmpFile -First 1 } else { Exit }"
    print #psDeploy, "}"
    print #psDeploy, "$needsAdmin = $false"
    print #psDeploy, "if (!(Test-Path $InstallDir)) { try { New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null } catch { $needsAdmin = $true } }"
    print #psDeploy, "$testFile = [System.IO.Path]::Combine($InstallDir, 'perm_test.tmp')"
    print #psDeploy, "if (!$needsAdmin) { try { 'test' | Out-File $testFile -ErrorAction Stop; Remove-Item $testFile } catch { $needsAdmin = $true } }"
    print #psDeploy, "if ($needsAdmin) {"
    print #psDeploy, "    $currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())"
    print #psDeploy, "    if (!$currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {"
    print #psDeploy, "        $arguments = '-NoProfile -ExecutionPolicy Bypass -File ' + [char]34 + $scriptFile + [char]34 + ' -PassedPath ' + [char]34 + $InstallDir + [char]34"
    print #psDeploy, "        $process = Start-Process powershell.exe -ArgumentList $arguments -WorkingDirectory $scriptDir -Verb RunAs -WindowStyle Hidden -PassThru"
    print #psDeploy, "        $process.WaitForExit(); Exit"
    print #psDeploy, "    } else {"
    print #psDeploy, "        $ws = New-Object -ComObject WScript.Shell; $ws.Popup('Target directory write-protected.', 0, 'Access Error', 16); Exit"
    print #psDeploy, "    }"
    print #psDeploy, "}"
    print #psDeploy, "$archive = [System.IO.Path]::Combine($scriptDir, 'payload.tar')"
    print #psDeploy, "if (Test-Path $archive) {"
    print #psDeploy, "    Move-Item -Path $archive -Destination $InstallDir -Force; $oldDir = Get-Location; Set-Location $InstallDir"
    print #psDeploy, "    $tarProc = Start-Process tar.exe -ArgumentList '-xf payload.tar' -WindowStyle Hidden -PassThru"
    print #psDeploy, "    while (!$tarProc.HasExited) { Start-Sleep -Milliseconds 100 }"
    print #psDeploy, "    Remove-Item payload.tar -Force; Set-Location $oldDir"
    print #psDeploy, "}"
    print #psDeploy, "if (Test-Path $tmpFile) { Remove-Item $tmpFile -Force }"
    print #psDeploy, "$wsh = New-Object -ComObject WScript.Shell"
    print #psDeploy, "if ($wsh.Popup('Do you want to create a shortcut on your Desktop?', 0, 'Shortcut Setup', 36) -eq 6) {"
    print #psDeploy, "    $lnk = $wsh.CreateShortcut([System.IO.Path]::Combine([Environment]::GetFolderPath('Desktop'), '" ; shortcutName$ ; ".lnk'))"
    print #psDeploy, "    $lnk.TargetPath = [System.IO.Path]::Combine($InstallDir, '" ; targetExecutable$ ; "'); $lnk.WorkingDirectory = $InstallDir; $lnk.Save()"
    print #psDeploy, "}"
    print #psDeploy, "if ($wsh.Popup('Do you want to add this application to your Start Menu?', 0, 'Shortcut Setup', 36) -eq 6) {"
    print #psDeploy, "    $lnk = $wsh.CreateShortcut([System.IO.Path]::Combine([Environment]::GetFolderPath('Programs'), '" ; shortcutName$ ; ".lnk'))"
    print #psDeploy, "    $lnk.TargetPath = [System.IO.Path]::Combine($InstallDir, '" ; targetExecutable$ ; "'); $lnk.WorkingDirectory = $InstallDir; $lnk.Save()"
    print #psDeploy, "}"
    print #psDeploy, "$wsh.Popup('Installation Completed Successfully!', 0, 'Complete', 64) | Out-Null"
    close #psDeploy

[writeSED]
    print #main.log, "Building Self Extraction Directive file for Iexpress config..."
    open workPath$ ; "app.sed" for output as #sedOut
    print #sedOut, "[Version]"
    print #sedOut, "Class=IEXPRESS"
    print #sedOut, "SEDVersion=3"
    print #sedOut, "[Options]"
    print #sedOut, "PackagePurpose=InstallApp"
    print #sedOut, "ShowInstallProgramWindow=0"
    print #sedOut, "HideExtractAnimation=1"
    print #sedOut, "UseLongFileName=1"
    print #sedOut, "InsideCompressed=0"
    print #sedOut, "CAB_FixedSize=0"
    print #sedOut, "CAB_ReserVecZero=0"
    print #sedOut, "RebootMode=N"
    print #sedOut, "InstallPrompt="
    print #sedOut, "DisplayLicense="
    print #sedOut, "FinishMessage="
    print #sedOut, "TargetName="; finalExeName$
    print #sedOut, "AppLaunched=wscript.exe prompt.vbs"
    print #sedOut, "PostInstallCmd=<None>"
    print #sedOut, "UACExecutionLevel=AsInvoker"
    print #sedOut, "SourceFiles=SourceFiles"
    print #sedOut, "[SourceFiles]"
    print #sedOut, "SourceFiles0=."
    print #sedOut, "[SourceFiles0]"
    print #sedOut, "%FILE0%="
    print #sedOut, "%FILE1%="
    print #sedOut, "%FILE2%="
    print #sedOut, "[Strings]"
    print #sedOut, "FILE0="; q$; "prompt.vbs"; q$
    print #sedOut, "FILE1="; q$; "deploy.ps1"; q$
    print #sedOut, "FILE2="; q$; "payload.tar"; q$
    close #sedOut

    print #main.log, "Generating execution control block script..."
    if fileExists(workPath$, "comp_done.txt") > 0 then kill workPath$ ; "comp_done.txt"
    overWrite$=folderName$;"-Installer.exe"
    if fileExists(workPath$,overWrite$) and overWrite=1 then run "cmd.exe /c del /f /q ";q$;overWrite$;q$,hide

    open workPath$ ; "compile.bat" for output as #cBat
    print #cBat, "@echo off"
    print #cBat, "mode con: cols=45 lines=6"
    print #cBat, "title Pipeline Engine"
    print #cBat, "echo ==================================================="
    print #cBat, "echo  1. SYNCHRONIZING PRIMARY APPLICATION ASSETS..."
    print #cBat, "echo ==================================================="
    print #cBat, "robocopy.exe " ; q$ ; sourceAppFolder$ ; q$ ; " " ; q$ ; stagingFolder$ ; q$ ; " /E /IS /IT /R:0 /W:0"
    print #cBat, "echo."
    print #cBat, "echo ==================================================="
    print #cBat, "echo  2. RUNNING TAR PACKAGING COMPRESSION ENGINE..."
    print #cBat, "echo ==================================================="
    print #cBat, "tar.exe -cvf payload.tar -C "; q$; stagingFolder$; q$; " ."
    print #cBat, "echo."
    print #cBat, "echo ==================================================="
    print #cBat, "echo  3. RUNNING IEXPRESS EXECUTABLE COMPILER..."
    print #cBat, "echo ==================================================="
    print #cBat, "iexpress.exe /n app.sed"
    print #cBat, "echo done > comp_done.txt"
    print #cBat, "exit"
    close #cBat

    print #main.log, "Launching native console pipeline compilation stage..."
    run "cmd.exe /c compile.bat"

[waitForCompilation]
    scan
    if fileExists(workPath$, "comp_done.txt") > 0 then goto [cleanUpAll]
    timer 150, [cDelay]
    wait
[cDelay]
    timer 0
    goto [waitForCompilation]

[cleanUpAll]
    print #main.log, "" : print #main.log, ""

    if fileExists(workPath$, finalExeName$) > 0 then
        print #main.log, "###### SUCCESS! Master distribution EXE file generated. ######"
        print #main.btnBrowseFolder, "!setfocus"
        run "explorer.exe /select," ; q$ ; workPath$ ; finalExeName$ ; q$
    else
        notice "Installer file " + finalExeName$ + " was NOT created"
    end if

    if fileExists(workPath$, "prompt.vbs") > 0 then kill "prompt.vbs"
    if fileExists(workPath$, "deploy.ps1") > 0 then kill "deploy.ps1"
    if fileExists(workPath$, "payload.tar") > 0 then kill "payload.tar"
    if fileExists(workPath$, "app.sed") > 0 then kill "app.sed"
    if fileExists(workPath$, "compile.bat") > 0 then kill "compile.bat"
    if fileExists(workPath$, "comp_done.txt") > 0 then kill "comp_done.txt"
    run "cmd.exe /c rmdir /s /q " ; q$ ; workPath$ ; "StagingTemp" ; q$, hide
    wait

[ovWrite]
    overWrite = 1
    wait
[novWrite]
    overWrite = 0
    wait

[quit]
    timer 0
    #main.log "!cls"
    close #main
    end

function fileExists(path$, filename$)
    dim info$(10,10)
    files path$, filename$, info$()
    fileExists = val(info$(0,0))
end function

sub pause mil
    t=time$("ms")+mil
    while time$("ms")<t
        scan
    wend
end sub