#requires -version 5.1 <# .SYNOPSIS A PowerShell script with a GUI to merge and deduplicate the contents of hash files. .DESCRIPTION This script provides a user-friendly graphical interface to: 1. Select multiple text-based hash files. 2. Read all lines from the selected files. 3. Remove all duplicate lines efficiently. 4. Save the merged, unique list of hashes to a new file specified by the user. .AUTHOR Gemini #> # Load necessary .NET assemblies for the GUI Add-Type -AssemblyName System.Windows.Forms Add-Type -AssemblyName System.Drawing # Suppress errors for a cleaner user experience in the console $ErrorActionPreference = 'SilentlyContinue' # --- GUI FORM SETUP --- $form = New-Object System.Windows.Forms.Form $form.Text = 'Hash File Merger & Deduplicator' $form.Size = New-Object System.Drawing.Size(600, 450) $form.StartPosition = 'CenterScreen' $form.FormBorderStyle = 'FixedSingle' $form.MaximizeBox = $false $form.Icon = [System.Drawing.SystemIcons]::Application # --- GUI CONTROLS --- # 1. "Select Files" Button $selectButton = New-Object System.Windows.Forms.Button $selectButton.Text = '1. Select Hash Files...' $selectButton.Location = New-Object System.Drawing.Point(20, 20) $selectButton.Size = New-Object System.Drawing.Size(550, 40) $selectButton.Font = New-Object System.Drawing.Font('Segoe UI', 10, [System.Drawing.FontStyle]::Bold) # 2. ListBox to display selected files $fileListBox = New-Object System.Windows.Forms.ListBox $fileListBox.Location = New-Object System.Drawing.Point(20, 70) $fileListBox.Size = New-Object System.Drawing.Size(550, 200) $fileListBox.HorizontalScrollbar = $true # 3. "Merge and Save" Button $mergeButton = New-Object System.Windows.Forms.Button $mergeButton.Text = '2. Merge and Save As...' $mergeButton.Location = New-Object System.Drawing.Point(20, 280) $mergeButton.Size = New-Object System.Drawing.Size(550, 40) $mergeButton.Enabled = $false # Disabled until files are selected $mergeButton.Font = New-Object System.Drawing.Font('Segoe UI', 10, [System.Drawing.FontStyle]::Bold) # 4. Status Label for feedback $statusLabel = New-Object System.Windows.Forms.Label $statusLabel.Text = 'Please select one or more hash files to begin.' $statusLabel.Location = New-Object System.Drawing.Point(20, 330) $statusLabel.Size = New-Object System.Drawing.Size(550, 25) $statusLabel.Font = New-Object System.Drawing.Font('Segoe UI', 9) # 5. Final Result Label $resultLabel = New-Object System.Windows.Forms.Label $resultLabel.Text = '' $resultLabel.Location = New-Object System.Drawing.Point(20, 355) $resultLabel.Size = New-Object System.Drawing.Size(550, 25) $resultLabel.Font = New-Object System.Drawing.Font('Segoe UI', 9, [System.Drawing.FontStyle]::Bold) # --- EVENT HANDLERS (Button Click Actions) --- # Action for the "Select Files" button $selectButton.Add_Click({ $openFileDialog = New-Object System.Windows.Forms.OpenFileDialog $openFileDialog.Title = "Select Hash Files" $openFileDialog.Filter = "Text Files (*.txt, *.dic)|*.txt;*.dic|All Files (*.*)|*.*" $openFileDialog.Multiselect = $true if ($openFileDialog.ShowDialog() -eq 'OK') { # Clear previous list and add new files $fileListBox.Items.Clear() $openFileDialog.FileNames | ForEach-Object { $fileListBox.Items.Add($_) } # Update status and enable the merge button $statusLabel.Text = "$($fileListBox.Items.Count) file(s) selected." $resultLabel.Text = "" $mergeButton.Enabled = $true } }) # Action for the "Merge and Save" button $mergeButton.Add_Click({ # Disable buttons during processing $mergeButton.Enabled = $false $selectButton.Enabled = $false $form.Cursor = [System.Windows.Forms.Cursors]::WaitCursor try { $statusLabel.Text = "Processing... Reading files and calculating hashes." $form.Refresh() # Force the UI to update # Using a HashSet for high-performance deduplication, especially with large files $uniqueHashes = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase) $totalLines = 0 # Read each file and add its lines to the HashSet foreach ($filePath in $fileListBox.Items) { $lines = [System.IO.File]::ReadAllLines($filePath) $totalLines += $lines.Count foreach ($line in $lines) { # Only add non-empty lines if (-not [string]::IsNullOrWhiteSpace($line)) { $uniqueHashes.Add($line) | Out-Null } } } $statusLabel.Text = "Read $totalLines total lines. Found $($uniqueHashes.Count) unique lines." $resultLabel.Text = "Please choose where to save the merged file." $form.Refresh() # Prompt user for save location $saveFileDialog = New-Object System.Windows.Forms.SaveFileDialog $saveFileDialog.Title = "Save Merged Hash File" $saveFileDialog.Filter = "Text File (*.txt)|*.txt" $saveFileDialog.FileName = "merged_hashes.txt" if ($saveFileDialog.ShowDialog() -eq 'OK') { # Write the unique hashes to the new file [System.IO.File]::WriteAllLines($saveFileDialog.FileName, $uniqueHashes) $resultLabel.Text = "Success! Saved $($uniqueHashes.Count) unique hashes to $($saveFileDialog.SafeFileName)." $statusLabel.Text = "Process complete. Select more files or close the window." } else { $resultLabel.Text = "Save operation was cancelled." } } catch { [System.Windows.Forms.MessageBox]::Show("An error occurred: $($_.Exception.Message)", "Error", "OK", "Error") $resultLabel.Text = "An error occurred during processing." } finally { # Re-enable buttons and restore cursor $mergeButton.Enabled = $true $selectButton.Enabled = $true $form.Cursor = [System.Windows.Forms.Cursors]::Default } }) # --- ADD CONTROLS TO FORM and SHOW --- $form.Controls.Add($selectButton) $form.Controls.Add($fileListBox) $form.Controls.Add($mergeButton) $form.Controls.Add($statusLabel) $form.Controls.Add($resultLabel) # Display the form to the user [void]$form.ShowDialog()