Param (
        [Int32[]]$EXEID,
        [parameter(DontShow)][Switch]$Worker
)

$GenerateAssembly = $false
$AutoRun = $true
$AutoExit = $true
$ToolchainProperties = @{
        'Apple Macintosh' = @{
                UseCommonSource = $true;
                StripConstantNames = $false;
                SourceReplacements = @{ "`r`n" = "`r" ; "JPSTRINGEND" = "\" }
                ImportScriptBlock = {
                        if (-not (Test-Path -Path "$OutputToolchainPath\ReDMCSB.dsk" -PathType Leaf)) {
                                $Global:ProgressPreference = 'SilentlyContinue'        # Hide the progress bar while expanding archive
                                Expand-Archive -Path "$OutputToolchainPath\ReDMCSB.zip" -DestinationPath $OutputToolchainPath
                        }

                        # Use a batch file to run multiple HFSUtils command lines for the best performance
                        # Prepare batch file content
                        $ImportBatchContent = @()
                        $ImportBatchContent += "SET HOME=$OutputToolchainPath\HFSUtils"
                        $ImportBatchContent += "SET HFSUTILS=$OutputToolchainPath\HFSUtils"
                        # Copy source code files and project files to Macintosh hard disk image
                        $ImportBatchContent += "`"$OutputToolchainPath\HFSUtils\hmount.exe`" `"$OutputToolchainPath\ReDMCSB.dsk`""
                        foreach ($Folder in (Get-ChildItem -Directory -Recurse -Path $OutputToolchainHardDiskPath)) {
                                $DestinationPath = "ReDMCSB:$($Folder.FullName.Replace(`"$OutputToolchainHardDiskPath\`", `"`").Replace(`"\`", `":`"))"
                                $ImportBatchContent += "`"$OutputToolchainPath\HFSUtils\hmkdir.exe`" `"$DestinationPath`""
                                $Files = Get-ChildItem -File -Path $Folder.FullName
                                if ($Files -ne $null) {
                                        $Extensions = $Files.Extension.ToUpper() | Sort-Object -Unique
                                        foreach ($Extension in $Extensions) {
                                                if ($Extension -eq ".HQX") {
                                                        $ImportBatchContent += "`"$OutputToolchainPath\HFSUtils\hcopy.exe`" -b `"$($Folder.FullName)\*.HQX`" `"$DestinationPath`""
                                                } else{
                                                        $ImportBatchContent += "`"$OutputToolchainPath\HFSUtils\hcopy.exe`" -r `"$($Folder.FullName)\*$Extension`" `"$DestinationPath`""
                                                        $ImportBatchContent += "`"$OutputToolchainPath\HFSUtils\hattrib.exe`" -t TEXT `"$($DestinationPath):*$Extension`""
                                                }
                                        }
                                }
                        }
                        $ImportBatchContent += "`"$OutputToolchainPath\HFSUtils\hrename.exe`" `"ReDMCSB:MPW:Startup.BAT`" `"ReDMCSB:MPW:Startup`""
                        $ImportBatchContent += "`"$OutputToolchainPath\HFSUtils\humount.exe`" `"ReDMCSB`""

                        # Save and run batch file
                        Set-Content -Path "$OutputToolchainPath\Import.cmd" -Value $ImportBatchContent
                        Start-Process -Wait -WindowStyle Hidden -FilePath "cmd.exe" -ArgumentList "/c `"$OutputToolchainPath\Import.cmd`""
                };
                RunScriptBlock = {
                        Write-Host "WARNING: Using the keyboard or mouse in the Macintosh emulator may break ReDMCSB build automation" -ForegroundColor Yellow -BackgroundColor Black
                        $Process = Start-Process -WorkingDirectory "$OutputToolchainPath\MinivMac" -FilePath "$OutputToolchainPath\MinivMac\Mini vMac.exe" -ArgumentList "`"$OutputToolchainPath\ReDMCSB.dsk`"" -PassThru
                        $Process.PriorityClass = [System.Diagnostics.ProcessPriorityClass]::AboveNormal
                        $Process.WaitForExit()
                };
                ExportScriptBlock = {
                        # Use a batch file to run multiple HFSUtils command lines for the best performance
                        # Prepare batch file content
                        $ExportBatchContent = @()
                        $ExportBatchContent += "SET HOME=$OutputToolchainPath\HFSUtils"
                        $ExportBatchContent += "SET HFSUTILS=$OutputToolchainPath\HFSUtils"

                        # Copy executable and project files from Macintosh hard disk image in BinHex format
                        $ExportBatchContent += "`"$OutputToolchainPath\HFSUtils\hmount.exe`" `"$OutputToolchainPath\ReDMCSB.dsk`""
                        foreach ($SelectedExecutable in $ExecutablesToBuild) {
                                $ExportBatchContent += "`"$OutputToolchainPath\HFSUtils\hcopy.exe`" -b `"ReDMCSB:OBJECT:$($SelectedExecutable.Media):$($SelectedExecutable.Name):$($SelectedExecutable.Media)_$($SelectedExecutable.Name).Project`" `"$OutputToolchainObjectPath\$($SelectedExecutable.Media)\$($SelectedExecutable.Name)`""
                                if ($SelectedExecutable.Format -eq "FTL") {
                                        $ExportBatchContent += "`"$OutputToolchainPath\HFSUtils\hcopy.exe`" -b `"ReDMCSB:BUILD:$($SelectedExecutable.Media):$($SelectedExecutable.Name).MAC`" `"$OutputToolchainBuildPath\$($SelectedExecutable.Media)`""
                                } else {
                                        $ExportBatchContent += "`"$OutputToolchainPath\HFSUtils\hcopy.exe`" -r `"ReDMCSB:BUILD:$($SelectedExecutable.Media):$($SelectedExecutable.Name)`" `"$OutputToolchainBuildPath\$($SelectedExecutable.Media)`""
                                }
                        }
                        $ExportBatchContent += "`"$OutputToolchainPath\HFSUtils\hcopy.exe`" -r `"ReDMCSB:OBJECT:G14ED:START:*`" `"$OutputToolchainObjectPath\G14ED\START`""
                        $ExportBatchContent += "`"$OutputToolchainPath\HFSUtils\hcopy.exe`" -r `"ReDMCSB:OBJECT:G14ED:DUNGEON.MASTER:*`" `"$OutputToolchainObjectPath\G14ED\DUNGEON.MASTER`""
                        $ExportBatchContent += "`"$OutputToolchainPath\HFSUtils\hcopy.exe`" -r `"ReDMCSB:OBJECT:G20E:GRAPH21.BIN:*`" `"$OutputToolchainObjectPath\G20E\GRAPH21.BIN`""
                        $ExportBatchContent += "`"$OutputToolchainPath\HFSUtils\hcopy.exe`" -r `"ReDMCSB:OBJECT:G20E:START:*`" `"$OutputToolchainObjectPath\G20E\START`""
                        $ExportBatchContent += "`"$OutputToolchainPath\HFSUtils\hcopy.exe`" -r `"ReDMCSB:OBJECT:G20E:DUNGEON.MASTER:*`" `"$OutputToolchainObjectPath\G20E\DUNGEON.MASTER`""
                        $ExportBatchContent += "`"$OutputToolchainPath\HFSUtils\hcopy.exe`" -r `"ReDMCSB:OBJECT:G21E:DUNGEON.MASTER:*`" `"$OutputToolchainObjectPath\G21E\DUNGEON.MASTER`""
                        $ExportBatchContent += "`"$OutputToolchainPath\HFSUtils\hcopy.exe`" -r `"ReDMCSB:BUILD:G14ED:*`" `"$OutputToolchainBuildPath\G14ED`""
                        $ExportBatchContent += "`"$OutputToolchainPath\HFSUtils\hcopy.exe`" -r `"ReDMCSB:BUILD:G20E:*`" `"$OutputToolchainBuildPath\G20E`""
                        $ExportBatchContent += "`"$OutputToolchainPath\HFSUtils\hcopy.exe`" -r `"ReDMCSB:BUILD:G21E:*`" `"$OutputToolchainBuildPath\G21E`""
                        $ExportBatchContent += "`"$OutputToolchainPath\HFSUtils\hcopy.exe`" -r `"ReDMCSB:OBJECT:SU1E:ANIMSND.o`" `"$OutputToolchainObjectPath\SU1E`""
                        $ExportBatchContent += "`"$OutputToolchainPath\HFSUtils\hcopy.exe`" -r `"ReDMCSB:Picture*`" `"$OutputToolchainBuildPath`""
                        $ExportBatchContent += "`"$OutputToolchainPath\HFSUtils\hcopy.exe`" -r `"ReDMCSB:BUILD:*.LOG`" `"$OutputToolchainBuildPath`""
                        $ExportBatchContent += "`"$OutputToolchainPath\HFSUtils\humount.exe`" `"ReDMCSB`""

                        # Save and run batch file
                        Set-Content -Path "$OutputToolchainPath\Export.cmd" -Value $ExportBatchContent
                        Start-Process -Wait -WindowStyle Hidden -FilePath "cmd.exe" -ArgumentList "/c `"$OutputToolchainPath\Export.cmd`""

                        # Prepare batch file content
                        $ConvertBatchContent = @()

                        # Convert PICT Macintosh images
                        $PictFiles = Get-ChildItem -File -Path "$OutputToolchainBuildPath\Picture*"
                        if ($PictFiles) {
                                foreach ($PictFile in $PictFiles) {
                                        $ConvertBatchContent += "`"$OutputToolchainPath\PictView\PVW32Con.exe`" `"$($PictFile.FullName)`" -w --o `"$OutputToolchainBuildPath\$($PictFile.Name).bmp`""
                                }
                        }

                        # Extract Resource Fork from BinHex files
                        $BinHexFiles = Get-ChildItem -File -Recurse -Path "$OutputToolchainBuildPath\*.hqx"
                        if ($BinHexFiles) {
                                foreach ($FolderName in ($BinHexFiles.DirectoryName | Sort-Object -Unique)) {
                                        $ConvertBatchContent += "`"$OutputToolchainPath\UUDeview\uudeview.exe`" -i -o -q -p `"$FolderName`" `"$FolderName\*.HQX`""
                                }
                        }

                        # Save and run batch file
                        if ($ConvertBatchContent.Count -gt 0) {
                                Set-Content -Path "$OutputToolchainPath\Convert.cmd" -Value $ConvertBatchContent
                                Start-Process -Wait -WindowStyle Hidden -FilePath "cmd.exe" -ArgumentList "/c `"$OutputToolchainPath\Convert.cmd`""
                        }

                        # Convert Macintosh executables to FTL executables
                        $FTLFiles = $ExecutablesToBuild | Where-Object { ($_.Format -eq "FTL") }
                        foreach ($FTLFile in $FTLFiles) {
                                if (Test-Path -Path "$OutputToolchainBuildPath\$($FTLFile.Media)\$($FTLFile.Name).MAC" -PathType Leaf) {
                                        Write-Host "Convert executable to FTL $OutputToolchainBuildPath\$($FTLFile.Media)\$($FTLFile.Name)"
                                        $Parameters = @{
                                                ResourceFileName   = "$OutputToolchainBuildPath\$($FTLFile.Media)\$($FTLFile.Name).MAC"
                                                OutputBaseFileName = "$OutputToolchainBuildPath\$($FTLFile.Media)\$($FTLFile.Name)"
                                                Year               = $($FTLFile.Year)
                                                Month              = $($FTLFile.Month)
                                                Day                = $($FTLFile.Day)
                                                # LogFilePath        = "$OutputToolchainBuildPath\ConvertTo-FTL.log"
                                        }
                                        $AdditionalCodePath = "$CommonBasePath\AdditionalCode\$($FTLFile.Media)_$($FTLFile.Name)_AdditionalCode.bin"
                                        if (Test-Path -Path $AdditionalCodePath -PathType Leaf) {
                                                $Parameters += @{
                                                        AdditionalCodeFileName = $AdditionalCodePath
                                                }
                                        }
                                        & "$CommonBasePath\ConvertTo-FTL.ps1" @Parameters
                                }
                        }
                };
        }
        'Atari ST' = @{
                UseCommonSource = $false;
                StripConstantNames = $false;
                SourceReplacements = @{}
                ImportScriptBlock = {};
                RunScriptBlock = {
                        $Process = Start-Process -WorkingDirectory "$OutputToolchainPath\Hatari" -FilePath "$OutputToolchainPath\Hatari\hatari.exe" -ArgumentList "-c `"$OutputToolchainPath\ReDMCSB.cfg`" `"$OutputToolchainHardDiskPath\PCOMMAND.PRG`"" -PassThru
                        $Process.PriorityClass = [System.Diagnostics.ProcessPriorityClass]::AboveNormal
                        $Process.WaitForExit()
                };
                ExportScriptBlock = {};
        }
        'Commodore Amiga' = @{
                UseCommonSource = $true;
                StripConstantNames = $false;
                SourceReplacements = @{ "`r`n" = "`n" ; "AztecCDirective_" = "#" }
                ImportScriptBlock = {};
                RunScriptBlock = {
                        Start-Process -Wait -WorkingDirectory "$OutputToolchainPath\WinUAE" -FilePath "$OutputToolchainPath\WinUAE\winuae.exe" -ArgumentList "-config=`"$OutputToolchainPath\ReDMCSB.UAE`""
                };
                ExportScriptBlock = {};
        }
        'Fujitsu FM-Towns' = @{
                UseCommonSource = $true;
                StripConstantNames = $false;
                SourceReplacements = @{ "JPSTRINGEND" = "" }
                ImportScriptBlock = {
                        if (-not (Test-Path -Path "$OutputToolchainPath\ReDMCSB.h0" -PathType Leaf)) {
                                $Global:ProgressPreference = 'SilentlyContinue'        # Hide the progress bar while expanding archive
                                Expand-Archive -Path "$OutputToolchainPath\ReDMCSB.zip" -DestinationPath $OutputToolchainPath
                        }

                        # Use a batch file to run multiple MTOOLS command lines for the best performance and to copy a folder tree (mcopy can copy a folder tree from disk image to host, but it can only copy files into image and not folders)
                        # Prepare batch file content
                        $ImportBatchContent = @()
                        $ImportBatchContent += "SET MTOOLS_SKIP_CHECK=1"
                        # Copy source code files and project files to Macintosh hard disk image
                        $ImportBatchContent += "`"$OutputToolchainPath\MTOOLS\mcopy.exe`" -s -n -o -i `"$OutputToolchainPath\ReDMCSB.h0`"@@1536 `"\\.\$OutputToolchainHardDiskPath\*`" ::"
                        foreach ($Folder in (Get-ChildItem -Directory -Recurse -Path $OutputToolchainHardDiskPath)) {
                                $ImportBatchContent += "`"$OutputToolchainPath\MTOOLS\mmd.exe`" -i `"$OutputToolchainPath\ReDMCSB.h0`"@@1536 ::$($Folder.FullName.Replace(`"$OutputToolchainHardDiskPath\`", `"`").Replace(`"\`", `"/`"))"
                                $ImportBatchContent += "`"$OutputToolchainPath\MTOOLS\mcopy.exe`" -s -n -o -i `"$OutputToolchainPath\ReDMCSB.h0`"@@1536 `"\\.\$($Folder.FullName)\*`" ::$($Folder.FullName.Replace($OutputToolchainHardDiskPath, `"`").Replace(`"\`", `"/`"))"
                        }

                        # Save and run batch file
                        Set-Content -Path "$OutputToolchainPath\Import.cmd" -Value $ImportBatchContent
                        Start-Process -Wait -WindowStyle Hidden -FilePath "cmd.exe" -ArgumentList "/c `"$OutputToolchainPath\Import.cmd`""
                };
                RunScriptBlock = {
                        Start-Process -Wait -WorkingDirectory "$OutputToolchainPath\UNZ" -FilePath "$OutputToolchainPath\UNZ\Unz.exe"
                };
                ExportScriptBlock = {
                        $Env:MTOOLS_SKIP_CHECK = 1
                        Start-Process -Wait -WindowStyle Hidden -FilePath "$OutputToolchainPath\MTOOLS\mcopy.exe" -ArgumentList "-s -n -o -i `"$OutputToolchainPath\ReDMCSB.h0`"@@1536 ::BUILD ::OBJECT `"\\.\$OutputToolchainHardDiskPath`""
                };
        }
        'IBM PC' = @{
                UseCommonSource = $false;
                StripConstantNames = $true;
                SourceReplacements = @{ "JPSTRINGEND" = "\" }
                ImportScriptBlock = {};
                RunScriptBlock = {
                        "MOUNT C `"$OutputToolchainHardDiskPath`"" | Out-File -FilePath "$OutputToolchainPath\ReDMCSB.conf" -Append -Encoding ASCII
                        "C:" | Out-File -FilePath "$OutputToolchainPath\ReDMCSB.conf" -Append -Encoding ASCII
                        "\SOURCE\REDMCSB.BAT" | Out-File -FilePath "$OutputToolchainPath\ReDMCSB.conf" -Append -Encoding ASCII
                        Start-Process -Wait -WorkingDirectory "$OutputToolchainPath\DOSBox" -FilePath "$OutputToolchainPath\DOSBox\DOSBox.exe" -ArgumentList "-conf `"$OutputToolchainPath\ReDMCSB.conf`""
                };
                ExportScriptBlock = {};
        }
        'Sharp X68000' = @{
                UseCommonSource = $true;
                StripConstantNames = $false;
                SourceReplacements = @{}
                ImportScriptBlock = {};
                RunScriptBlock = {
                        # There is no way to shut down the X68000 emulator "from the inside"
                        # This code will wait until either the emulator has been terminated (by the user) or until the file C:\EXIT.TXT exists (created at the end of the build script)
                        # If the file exists then the process is killed
                        $Process = Start-Process -WorkingDirectory "$OutputToolchainPath\XM6_TypeG" -FilePath "$OutputToolchainPath\XM6_TypeG\XM6g.exe" -ArgumentList "`"$OutputToolchainPath\ReDMCSB.xdf`"" -PassThru
                        $Process.PriorityClass = [System.Diagnostics.ProcessPriorityClass]::AboveNormal
                        while (-not $Process.HasExited) {
                                Start-Sleep -Seconds 2
                                if (Test-Path -Path "$OutputToolchainHardDiskPath\EXIT.TXT" -PathType Leaf) {
                                        $Process.Kill()
                                }
                        }
                };
                ExportScriptBlock = {};
        }
}

function Copy-ReDMCSBCustomSource {
Param(
        [Parameter(Mandatory=$true)][ValidateScript({Test-Path -Path $_ -PathType Container})][String]$InputPath,
        [Parameter(Mandatory=$true)][ValidateScript({Test-Path -Path $_ -PathType Container})][String]$OutputPath,
        [Parameter(Mandatory=$true)][String[]]$TagSet
)

        # Make sure MEDIA*.H files (containing '#define MEDIAxxx_' directives) are processed after all others
        $SourceFiles = @()
        $SourceFiles += (Get-Item "$InputPath\*.C")
        $SourceFiles += (Get-Item "$InputPath\*.H" -Exclude "MEDIA*.H")
        $SourceFiles += (Get-Item "$InputPath\MEDIA*.H")
        if ($SourceFiles -eq $null) {
                Write-Host "No source files found in input path [$InputPath], aborting."
                exit
        }

        # Create empty output folder
        $OutputFolderPath = "$OutputPath\$TagSet"
        if (Test-Path -Path $OutputFolderPath -PathType Container) {
                & "$PSScriptRoot\Remove-Folder.ps1" -Path $OutputFolderPath -TemporaryPath $OutputPath
                if (Test-Path -Path $OutputFolderPath -PathType Container) {
                        Write-Host "Failed to delete custom source folder [$OutputFolderPath], aborting."
                        exit
                }
        }
        $null = New-Item $OutputFolderPath -ItemType Directory -Force
        if (-not (Test-Path -Path $OutputFolderPath -PathType Container)) {
                Write-Host "Failed to create custom source folder [$OutputFolderPath], aborting."
                exit
        }

        $OutputLine = New-Object -TypeName Bool[] -ArgumentList 100
        $Tags = $TagSet -split "_"
        $CompileTagsToXTags = @{}
        $Encoding = [System.Text.Encoding]::GetEncoding("iso-8859-1")

        foreach ($SourceFile in $SourceFiles) {
                $StreamReader = New-Object -TypeName System.IO.StreamReader($SourceFile, $Encoding)
                $OutputFileContent = New-Object -TypeName System.Text.StringBuilder
                $CurrentDepth = 0
                $FileHasContent = $false
                $OutputLine[$CurrentDepth] = $true
                while (-not $StreamReader.EndOfStream ) {
                        $SourceFileLine = $StreamReader.ReadLine()
                        if ($SourceFileLine -match "(?<StartOfLine>^\s*#define )(MEDIA)(?<TagId>\d\d\d)(?<TagSet>_[a-zA-Z0-9_]*)(?<EndOfLine>.*)") {
                                $TagString = "MEDIA$($Matches.TagId)$($Matches.TagSet)"
                                # Translate MEDIA tag to X tag
                                if ($CompileTagsToXTags.Contains($TagString)) {
                                        if ($CompileTagsToXTags[$TagString] -ne $null) {
                                                $null = $OutputFileContent.AppendLine("$($Matches.StartOfLine)$($CompileTagsToXTags[$TagString])$($Matches.EndOfLine)")
                                        }
                                }
                                continue
                        }
                        if ($SourceFileLine.StartsWith("#endif")) {
                                if ($OutputLine[$CurrentDepth]) {
                                        $null = $OutputFileContent.AppendLine($SourceFileLine)
                                }
                                $CurrentDepth--
                                continue
                        }
                        if ($SourceFileLine.StartsWith("#if")) {
                                $CurrentDepth++
                                $OutputLine[$CurrentDepth] = $OutputLine[$CurrentDepth - 1]
                                if ($SourceFileLine -match "(^\s*#ifdef MEDIA)(?<TagId>\d\d\d)(?<TagSet>_[a-zA-Z0-9_]*)(?<EndOfLine>.*)") {
                                        $TagString = "MEDIA$($Matches.TagId)$($Matches.TagSet)"
                                        $CompileTagSet = $Matches.TagSet
                                        $CompileTags = $CompileTagSet.Split("_")
                                        $CommonTags = Compare-Object -ReferenceObject $CompileTags -DifferenceObject $Tags -PassThru -IncludeEqual -ExcludeDifferent
                                        if ($CommonTags -eq $null) {
                                                $OutputLine[$CurrentDepth] = $false
                                        } else {
                                                $JoinedCommonTags = $CommonTags -join '_'
                                                if ($OutputLine[$CurrentDepth]) {
                                                        if (-not $CompileTagsToXTags.Contains($TagString)) {
                                                                $CompileTagsToXTags.Add($TagString, "X$($Matches.TagId)_$($JoinedCommonTags)")
                                                        }
                                                        $null = $OutputFileContent.AppendLine("#ifdef $($CompileTagsToXTags[$TagString])$($Matches.EndOfLine)")
                                                }
                                        }
                                        continue
                                } else {
                                        # Search for MEDIA tags on an #if line and map them to their corresponding X tags
                                        if ($OutputLine[$CurrentDepth]) {
                                                $RegExMatches = ([regex]"(MEDIA)(?<TagId>\d\d\d)(?<TagSet>_[a-zA-Z0-9_]*)").Matches($SourceFileLine)
                                                foreach ($RegExMatch in $RegExMatches) {
                                                        $TagString = $RegExMatch.Value
                                                        $CompileTagSet = $RegExMatch.Groups["TagSet"].Value
                                                        $CompileTags = $CompileTagSet.Split("_")
                                                        $CommonTags = Compare-Object -ReferenceObject $CompileTags -DifferenceObject $Tags -PassThru -IncludeEqual -ExcludeDifferent
                                                        if ($CommonTags -ne $null) {
                                                                $JoinedCommonTags = $CommonTags -join '_'
                                                                if (-not $CompileTagsToXTags.Contains($TagString)) {
                                                                        $CompileTagsToXTags.Add($TagString, "X$($RegExMatch.Groups["TagId"].Value)_$($JoinedCommonTags)")
                                                                }
                                                                $SourceFileLine = $SourceFileLine -replace $TagString,$CompileTagsToXTags[$TagString]
                                                        }
                                                }
                                        }
                                }
                        }
                        if ($OutputLine[$CurrentDepth]) {
                                $null = $OutputFileContent.AppendLine($SourceFileLine)
                                if ($CurrentDepth -gt 0) {
                                        $FileHasContent = $true
                                }
                        }
                }
                $StreamReader.Close()
                if ($FileHasContent) {
                        $Content = $OutputFileContent.ToString()
                        if ($Content -ne "#ifndef COMPILE_H`r`n#include `"COMPILE.H`"`r`n#endif`r`n") {
                                [System.IO.File]::WriteAllText("$OutputFolderPath\$($SourceFile.Name)", $Content, $Encoding)
                        }
                }
        }
}

function Copy-ReDMCSBPreparedSource {
# Copy source code from source to destination path. Optional string replacements and long line splitting are performed before writing to the destination.
Param (
        [Parameter(Mandatory=$true)][ValidateScript({Test-Path -Path $_ -PathType Container})][String]$SourcePath,
        [Parameter(Mandatory=$true)][ValidateScript({Test-Path -Path $_ -PathType Container})][String]$DestinationPath,
        [Hashtable]$Replacements=@{},
        [Switch]$StripConstantNames
)

        # Create directory tree in destination
        $Directories = Get-ChildItem -Path $SourcePath -Directory -Recurse
        if ($Directories -ne $null) {
                $null = New-Item -Path $Directories.FullName.Replace($SourcePath, $DestinationPath) -ItemType Directory -Force
        }

        # Encoding must be an 8 bit encoding
        $Encoding = [System.Text.Encoding]::GetEncoding("iso-8859-1")

        # Read and process files
        $Files = Get-ChildItem -Path $SourcePath -File -Recurse
        if ($Files -eq $null) {
                return
        }
        $SourceFileFullNames = @()
        $SourceFileFullNames += $Files.FullName
        $FileCount = 0
        $TotalFileCount = $SourceFileFullNames.Count

        foreach ($SourceFileFullName in $SourceFileFullNames) {
                $FileCount++
                # Write-Progress -Activity "Prepare source code" -Status "Copying file $FileCount / $TotalFileCount" -PercentComplete ($FileCount * 100 / $TotalFileCount)

                # Read content from source file
                $StreamReader = New-Object -TypeName System.IO.StreamReader($SourceFileFullName, $Encoding)
                $StringBuilder = New-Object -TypeName System.Text.StringBuilder($StreamReader.ReadToEnd())
                $StreamReader.Close()

                # Perform string replacements in the file content
                if ($Replacements.Count -gt 0) {
                        foreach ($Replacement in $Replacements.Keys) {
                                $null = $StringBuilder.Replace($Replacement, $Replacements[$Replacement])
                        }
                }

                # Strip descriptive characters from constant names to minimize their memory footprint (ex: 'C12_ABC' => 'C12_'). Search is case-sensitive.
                $OutputString = $StringBuilder.ToString()
                if ($StripConstantNames) {
                        $OutputString = $OutputString -creplace "(C\d+_)[A-Z0-9_]+",'$1'
                }

                # Write updated file content to destination file
                [System.IO.File]::WriteAllText($SourceFileFullName.Replace($SourcePath,$DestinationPath), $OutputString, $Encoding)
        }
}

function Write-LimitedString {
Param (
        [String]$String,
        [String]$Padding,
        [Int]$MaxLength
)
        $FirstLine = $true
        while ($String -ne "") {
                if ($String.Length -gt $MaxLength) {
                        $Index = $MaxLength
                } else {
                        $Index = $String.Length
                }
                $TempString = $String.Substring(0, $Index)
                if ($String.Length -gt $MaxLength) {
                        while ($TempString[$Index] -ne ",") {
                                $Index--
                        }
                        $Index++
                }
                if (-not $FirstLine) {
                        Write-Host $Padding -NoNewline
                }
                Write-Host $TempString.Substring(0, $Index)
                $FirstLine = $false
                $String = $String.Substring($Index)
        }
}

if (($EXEID -eq $null) -or $Worker) {
        # Set console window dimensions
        [System.Console]::WindowWidth = 100
        [System.Console]::WindowHeight = 40
        [System.Console]::BufferWidth = [console]::WindowWidth
        [System.Console]::BufferHeight = 9999
        Clear-Host
}

$CommonBasePath = $PSScriptRoot
$ReDMCSBPath = (Get-Item $CommonBasePath).Parent.Parent.Parent.FullName
$ToolchainsPath = "$ReDMCSBPath\Toolchains"
$CommonSourcePath = "$ToolchainsPath\Common\Source"

# Load list of executables
$ExecutablesPath = "$CommonBasePath\Executables.txt"
if (-not (Test-Path -Path $ExecutablesPath -PathType Leaf)) {
        Write-Host "File not found: $ExecutablesPath" -ForegroundColor Red -BackgroundColor Black
        Read-Host -Prompt "Press enter to exit"
        exit
}
$Executables = Import-CSV -Delimiter "`t" -Path $ExecutablesPath

# Build list of valid toolchain names
# Start with supported toolchain names
$ToolchainNames = [String[]]$ToolchainProperties.Keys
# Remove toolchain names that do not exist as a folder
$ToolchainPaths = Get-ChildItem -Directory -Path $ToolchainsPath
if (($ToolchainNames -ne $null) -and ($ToolchainPaths -ne $null)) {
        $ToolchainNames = (Compare-Object -ReferenceObject $ToolchainNames -DifferenceObject $ToolchainPaths.Name -IncludeEqual -ExcludeDifferent).InputObject | Sort-Object
} else {
        $ToolchainNames = $null
}
# Remove toolchain names not required by any executable to build
$BuildExecutables = $Executables | Where-Object { ($_.Build -eq "TRUE") }
if (($ToolchainNames -ne $null) -and ($BuildExecutables -ne $null)) {
        $ToolchainNames = (Compare-Object -ReferenceObject $ToolchainNames -DifferenceObject ($BuildExecutables.Toolchain | Sort-Object -Unique) -IncludeEqual -ExcludeDifferent).InputObject | Sort-Object
} else {
        $ToolchainNames = $null
}

if ($ToolchainNames -eq $null) {
        Write-Host "No valid toolchain found" -ForegroundColor Red -BackgroundColor Black
        Read-Host -Prompt "Press enter to exit"
        exit
}

if ($EXEID -ne $null) {
        $ExecutablesToBuild = $Executables | Where-Object { $EXEID -contains $_.ID }
        $Toolchains = $ExecutablesToBuild.Toolchain | Sort-Object -Unique
        if (($ExecutablesToBuild -eq $null) -or ($Toolchains.Count -eq 0)) {
                Write-Host "Invalid list of executable IDs: $($EXEID -join ',')" -ForegroundColor Red -BackgroundColor Black
                Read-Host -Prompt "Press enter to exit"
                exit
        }
        if ($Worker) {
                if ($Toolchains.Count -gt 1) {
                        Write-Host "Specified executable IDs [$($EXEID -join ',')] refer to multiple toolchains [$($Toolchains -join ", ")]. A worker instance can process only one toolchain." -ForegroundColor Red -BackgroundColor Black
                        Read-Host -Prompt "Press enter to exit"
                        exit
                }
                $ToolchainName = $Toolchains
        }
}

if ($Worker -and ($ExecutablesToBuild -eq $null)) {
        Write-Host "No executables to build" -ForegroundColor Red -BackgroundColor Black
        Read-Host -Prompt "Press enter to exit"
        exit
}

# If no toolchain name was specified as a parameter then dislay selection menu
if (-not $Worker) {
        if ($EXEID -eq $null) {
                # Set console window title
                [System.Console]::Title = "Main menu - ReDMCSB"

                # Build list of menu choices
                $Choices = @{}
                $Choice = 1
                $ToolchainNames | foreach {
                        $Choices.Add("$Choice", @{ "Toolchain" = $_; "Label" = "Build executables on $_"})
                        $Choice++
                }
                $AllToolchains = $Choices.Values.Toolchain | Sort-Object
                $AllChoice = $Choice++
                $Choices.Add("$AllChoice", @{ "Toolchain" = $null; "Label" = "Build all executables" })
                $CustomChoice = $Choice
                $Choices.Add("$CustomChoice", @{ "Toolchain" = $null; "Label" = "Build custom choice of executables" })

                # Display menu
                do {
                        Clear-Host
                        Write-Host "+=================================================================================================+"
                        Write-Host "+ ReDMCSB                                                                                         +"
                        Write-Host "+=================================================================================================+"
                        Write-Host ""
                        Write-Host "Select which ReDMCSB executables to build or press Q to quit:"
                        Write-Host ""
                        foreach ($Choice in ($Choices.Keys | Sort-Object)) {
                                Write-Host "     $Choice : $($Choices[$Choice].Label)"
                        }
                        Write-Host ""
                        Write-Host "     Q : Quit"
                        Write-Host ""
                        $Selection = Read-Host "Please make a selection"
                        if ($Choices.ContainsKey($Selection)) {
                                if ($Selection -eq $AllChoice) {
                                        $SelectedToolchains = $AllToolchains
                                        $SelectedExecutables = $Executables | Where-Object { ($_.Build -eq "TRUE") }
                                } else {
                                        if ($Selection -eq $CustomChoice) {
                                                $SelectedExecutables = $Executables | Select-Object -Property ID,Toolchain,Format,Media,Name | Out-GridView -Title "Please select executables to build" -PassThru
                                                if ($SelectedExecutables -ne $null) {
                                                        $SelectedToolchains = $SelectedExecutables.Toolchain | Sort-Object -Unique
                                                } else {
                                                        $SelectedToolchains = $null
                                                }
                                        } else {
                                                $SelectedToolchains = @($Choices[$Selection].Toolchain)
                                                $SelectedExecutables = $Executables | Where-Object { ($_.Build -eq "TRUE") -and ($_.Toolchain -eq $Choices[$Selection].Toolchain) }
                                        }
                                }
                                $ToolchainToExecutables = $SelectedExecutables | Group-Object -Property Toolchain -AsHashTable
                                foreach ($Toolchain in $ToolchainToExecutables.Keys) {
                                        Start-Process -FilePath "powershell.exe" -ArgumentList "-ExecutionPolicy Bypass -Command `"& $PSCommandPath`" -Worker -EXEID $($ToolchainToExecutables[$Toolchain].ID -join ',')"
                                }
                        }
                } until ($Selection -eq 'q')
                [System.Console]::Title = "Windows PowerShell"
        } else {
                $ToolchainToExecutables = $ExecutablesToBuild | Group-Object -Property Toolchain -AsHashTable
                foreach ($Toolchain in $ToolchainToExecutables.Keys) {
                        Start-Process -FilePath "powershell.exe" -ArgumentList "-ExecutionPolicy Bypass -Command `"& $PSCommandPath`" -Worker -EXEID $($ToolchainToExecutables[$Toolchain].ID -join ',')"
                }
        }
        exit
}

# Fail if specified toolchain name does not exist
if ($ToolchainNames -notcontains $ToolchainName) {
        Write-Host "Invalid toolchain name specified: $ToolchainName" -ForegroundColor Red -BackgroundColor Black
        Read-Host -Prompt "Press enter to exit"
        exit
}

$BuildStartDateTime = Get-Date

# Set console window title
[System.Console]::Title = "$ToolchainName - ReDMCSB"

$OutputPath = "$ReDMCSBPath\Output"
$OutputBuildPath = "$OutputPath\Build"
$OutputObjectPath = "$OutputPath\Object"
$ToolchainPath = "$ToolchainsPath\$ToolchainName"
$ToolchainSourcePath = "$ToolchainPath\Source"
$OutputToolchainPath = "$OutputPath\$ToolchainName"
$OutputToolchainHardDiskPath = "$OutputToolchainPath\HARDDISK"
$OutputToolchainSourcePath = "$OutputToolchainHardDiskPath\SOURCE"
$OutputToolchainBuildPath = "$OutputToolchainHardDiskPath\BUILD"
$OutputToolchainObjectPath = "$OutputToolchainHardDiskPath\OBJECT"

$null = New-Item -Path $OutputPath -ItemType Directory -Force
$null = New-Item -Path $OutputBuildPath -ItemType Directory -Force
$null = New-Item -Path $OutputObjectPath -ItemType Directory -Force

Write-Host "+=================================================================================================+"
Write-Host "+ ReDMCSB                                                                                         +"
Write-Host "+=================================================================================================+"
Write-Host ""
Write-Host "---------------------------------------------------------------------------------------------------"
Write-Host "- Toolchain      : $ToolchainName"
Write-Host "- Folder         : $OutputToolchainPath"
Write-Host -NoNewLine "- Executable IDs : "
Write-LimitedString -String ($ExecutablesToBuild.ID -join ',') -Padding "                   " -MaxLength 80
Write-Host "---------------------------------------------------------------------------------------------------"

# Build custom source code for toolchains where compilation of the common source code fails because of too many #define directives
if ($ToolchainProperties[$ToolchainName].UseCommonSource) {
        Write-Host "----- Use common source code"
        $SourcePath = $CommonSourcePath
} else {
        $ToolchainCustomSource = (($Executables | Where-Object { $_.Toolchain -eq $ToolchainName }).Media | Sort-Object -Unique) -join "_"
        $SourcePath = "$OutputPath\$ToolchainCustomSource"
        if (Test-Path -Path $SourcePath -PathType Container) {
                $CustomSourceCreationTime = (Get-Item -Path $SourcePath).CreationTime
        } else {
                $CustomSourceCreationTime = [DateTime]0
        }

        # Copy custom source code if creation date of custom source folder is older than the file modification date of the latest modified source code file
        $LatestSourceFile = Get-ChildItem -File -Path $CommonSourcePath | Sort-Object -Property LastWriteTime | Select-Object -Last 1
        if ($LatestSourceFile.LastWriteTime -gt $CustomSourceCreationTime) {
                Write-Host "----- Copy custom source code"
                Copy-ReDMCSBCustomSource -InputPath $CommonSourcePath -OutputPath $OutputPath -TagSet $ToolchainCustomSource
                # & "$CommonBasePath\Copy-ReDMCSBCustomSource.ps1" -InputPath $CommonSourcePath -OutputPath $OutputPath -TagSet $ToolchainCustomSource #-LogPath "$OutputPath\$($ToolchainCustomSource)_Anomalies.txt"
        } else {
                Write-Host "----- Use existing custom source code"
        }
}

# Cleanup toolchain output folder to restart from scratch
if (Test-Path -Path $OutputToolchainPath -PathType Container) {
        Write-Host "----- Delete existing toolchain folder"
        & "$CommonBasePath\Remove-Folder.ps1" -Path $OutputToolchainPath -TemporaryPath $OutputPath
        if (Test-Path -Path $OutputToolchainPath -PathType Container) {
                Write-Host "Failed to delete folder [$OutputToolchainPath]. Make sure you don't have any file or program open in that location." -ForegroundColor Red -BackgroundColor Black
                Read-Host -Prompt "Press enter to exit"
                exit
        }
}

Write-Host "----- Create toolchain folders"
$null = New-Item -Path $OutputToolchainPath -ItemType Directory -Force
$null = New-Item -Path $OutputToolchainHardDiskPath -ItemType Directory -Force
$null = New-Item -Path $OutputToolchainSourcePath -ItemType Directory -Force
$null = New-Item -Path $OutputToolchainBuildPath -ItemType Directory -Force
$null = New-Item -Path $OutputToolchainObjectPath -ItemType Directory -Force
foreach ($Media in (($Executables | Where-Object { $_.Toolchain -eq $ToolchainName }).Media | Sort-Object -Unique)) {
        $null = New-Item -Path "$OutputToolchainBuildPath\$Media" -ItemType Directory -Force
        $null = New-Item -Path "$OutputToolchainObjectPath\$Media" -ItemType Directory -Force
        foreach ($Executable in ($Executables | Where-Object { ($_.Toolchain -eq $ToolchainName) -and ($_.'Media' -eq $Media) })) {
                $null = New-Item -Path "$OutputToolchainObjectPath\$Media\$($Executable.Name)" -ItemType Directory -Force
        }
}

Write-Host "----- Create toolchain control files"
foreach ($ExecutableToBuild in $ExecutablesToBuild) {
        $null = New-Item -Path "$OutputToolchainSourcePath\EXEID$($ExecutableToBuild.ID).TXT" -ItemType File
}

if ($GenerateAssembly) {
        $null = New-Item -Path "$OutputToolchainSourcePath\ASSEMBLY.TXT" -ItemType File
}
if ($AutoRun) {
        $null = New-Item -Path "$OutputToolchainSourcePath\AUTORUN.TXT" -ItemType File
}
if ($AutoExit) {
        $null = New-Item -Path "$OutputToolchainSourcePath\AUTOEXIT.TXT" -ItemType File
}

Write-Host "----- Copy toolchain base files"
Copy-Item -Path "$ToolchainPath\Base\*" -Destination $OutputToolchainPath -Recurse -Force

Write-Host "----- Copy toolchain source files"
$Parameters = @{
        SourcePath         = $ToolchainSourcePath
        DestinationPath    = $OutputToolchainSourcePath
        Replacements       = $ToolchainProperties[$ToolchainName].SourceReplacements
        StripConstantNames = $false
}
Copy-ReDMCSBPreparedSource @Parameters
Write-Host "----- Copy common source files"
$Parameters["SourcePath"] = $SourcePath
if ($ToolchainProperties[$ToolchainName].StripConstantNames) {
        $Parameters["StripConstantNames"] = $true
}
Copy-ReDMCSBPreparedSource @Parameters

if ($ToolchainProperties[$ToolchainName].ImportScriptBlock.ToString() -ne "") {
        Write-Host "----- Perform toolchain custom import actions"
        Invoke-Command $ToolchainProperties[$ToolchainName].ImportScriptBlock
}
Write-Host "----- Run toolchain and wait for completion"
Invoke-Command $ToolchainProperties[$ToolchainName].RunScriptBlock
if ($ToolchainProperties[$ToolchainName].ExportScriptBlock.ToString() -ne "") {
        Write-Host "----- Perform toolchain custom export actions"
        Invoke-Command $ToolchainProperties[$ToolchainName].ExportScriptBlock
}
Write-Host "----- Export Build and Object files"
Copy-Item -Path "$OutputToolchainBuildPath\*" -Destination $OutputBuildPath -Recurse -Force
Copy-Item -Path "$OutputToolchainObjectPath\*" -Destination $OutputObjectPath -Recurse -Force

Write-Host "----- Compare executable files"

# Analyse and compare generated executable files with reference executable files
# If any difference is found then compare
$DifferentExecutables = @()
foreach ($ExecutableToBuild in $ExecutablesToBuild) {
        $ReferenceOriginalExecutablePath = "$ReDMCSBPath\Reference\Original\$($ExecutableToBuild.Media)\$($ExecutableToBuild.Name)"
        $ReferenceReDMCSBExecutablePath = "$ReDMCSBPath\Reference\ReDMCSB\$($ExecutableToBuild.Media)\$($ExecutableToBuild.Name)"
        if (Test-Path -Path $ReferenceReDMCSBExecutablePath -PathType Leaf) {
                Add-Member -InputObject $ExecutableToBuild -NotePropertyName "Reference" -NotePropertyValue "ReDMCSB"
                $ReferenceExecutablePath = $ReferenceReDMCSBExecutablePath
        } else {
                Add-Member -InputObject $ExecutableToBuild -NotePropertyName "Reference" -NotePropertyValue "Original"
                $ReferenceExecutablePath = $ReferenceOriginalExecutablePath
        }

        $BuildExecutablePath = "$OutputToolchainBuildPath\$($ExecutableToBuild.Media)\$($ExecutableToBuild.Name)"

        if (-not (Test-Path -Path $BuildExecutablePath -PathType Leaf)) {
                Write-Host "Not found [$BuildExecutablePath]" -ForegroundColor Red -BackgroundColor Black
                continue
        }
        if (-not (Test-Path -Path $ReferenceExecutablePath -PathType Leaf)) {
                Write-Host "Not found [$ReferenceExecutablePath]" -ForegroundColor Red -BackgroundColor Black
                continue
        }
        if ((Get-FileHash -Path $BuildExecutablePath -Algorithm SHA1).Hash -eq (Get-FileHash -Path $ReferenceExecutablePath -Algorithm SHA1).Hash) {
                Write-Host "Identical [$ReferenceExecutablePath]" -ForegroundColor DarkGreen -BackgroundColor Black
                Write-Host "          [$BuildExecutablePath]" -ForegroundColor DarkGreen -BackgroundColor Black
        } else {
                Write-Host "Different [$ReferenceExecutablePath]" -ForegroundColor Red -BackgroundColor Black
                Write-Host "          [$BuildExecutablePath]" -ForegroundColor Red -BackgroundColor Black
                $DifferentExecutables += $ExecutableToBuild
        }
}

$BuildEndDateTime = Get-Date
Write-Host "Build duration: $([Math]::Round(($BuildEndDateTime - $BuildStartDateTime).TotalMinutes)) minutes and $([Math]::Round(($BuildEndDateTime - $BuildStartDateTime).Seconds)) seconds"

if ($DifferentExecutables.Count -gt 0) {
        Write-Host "----- Analyse differing executable files"
        $AnalysisScriptPath = "$CommonBasePath\Start-ReDMCSBAnalysis.ps1"

        foreach ($DifferentExecutable in $DifferentExecutables) {
                $ReferenceOriginalExecutablePath = "$ReDMCSBPath\Reference\Original\$($DifferentExecutable.Media)\$($DifferentExecutable.Name)"
                $ReferenceExpandedOriginalExecutablePath = "$ReDMCSBPath\Output\Expanded\Original\$($DifferentExecutable.Media)\$($DifferentExecutable.Name)"
                $ReferenceReDMCSBExecutablePath = "$ReDMCSBPath\Reference\ReDMCSB\$($DifferentExecutable.Media)\$($DifferentExecutable.Name)"
                $ReferenceExpandedReDMCSBExecutablePath = "$ReDMCSBPath\Output\Expanded\ReDMCSB\$($DifferentExecutable.Media)\$($DifferentExecutable.Name)"
                & $AnalysisScriptPath -EXEID $DifferentExecutable.ID -Instance $DifferentExecutable.Reference
                if ($DifferentExecutable.Reference -eq "ReDMCSB") {
                        if (Test-Path -Path $ReferenceExpandedReDMCSBExecutablePath -PathType Leaf) {
                                $ReferenceExecutablePath = $ReferenceExpandedReDMCSBExecutablePath
                        } else {
                                $ReferenceExecutablePath = $ReferenceReDMCSBExecutablePath
                        }
                } else {
                        if (Test-Path -Path $ReferenceExpandedOriginalExecutablePath -PathType Leaf) {
                                $ReferenceExecutablePath = $ReferenceExpandedOriginalExecutablePath
                        } else {
                                $ReferenceExecutablePath = $ReferenceOriginalExecutablePath
                        }
                }

                $BuildExecutablePath = "$OutputToolchainBuildPath\$($DifferentExecutable.Media)\$($DifferentExecutable.Name)"
                $ExpandedBuildExecutablePath = "$ReDMCSBPath\Output\Expanded\Build\$($DifferentExecutable.Media)\$($DifferentExecutable.Name)"
                & $AnalysisScriptPath -EXEID $DifferentExecutable.ID -Instance Build
                if (Test-Path -Path $ExpandedBuildExecutablePath -PathType Leaf) {
                        $BuildExecutablePath = $ExpandedBuildExecutablePath
                }

                if (-not (Test-Path -Path $BuildExecutablePath -PathType Leaf)) {
                        Write-Host "Not found [$BuildExecutablePath]" -ForegroundColor Red -BackgroundColor Black
                        continue
                }
                if (-not (Test-Path -Path $ReferenceExecutablePath -PathType Leaf)) {
                        Write-Host "Not found [$ReferenceExecutablePath]" -ForegroundColor Red -BackgroundColor Black
                        continue
                }
        }

        $AnalysisEndDateTime = Get-Date
        Write-Host "Analysis duration: $([Math]::Round(($AnalysisEndDateTime - $BuildEndDateTime).TotalMinutes)) minutes and $([Math]::Round(($AnalysisEndDateTime - $BuildEndDateTime).Seconds)) seconds"

        Write-Host ""
        Read-Host -Prompt "Press Enter to compare analysis text files or CTRL-C to exit"
        foreach ($DifferentExecutable in $DifferentExecutables) {
                $ReferenceAnalysis = "$ReDMCSBPath\Output\Analysis\TXT\$($DifferentExecutable.Media)_$($DifferentExecutable.Name)_$($DifferentExecutable.Reference).txt"
                $BuildAnalysis = "$ReDMCSBPath\Output\Analysis\TXT\$($DifferentExecutable.Media)_$($DifferentExecutable.Name)_Build.txt"
                if (-not (Test-Path -Path $ReferenceAnalysis -PathType Leaf)) {
                        Write-Host "Not found [$ReferenceAnalysis]" -ForegroundColor Red -BackgroundColor Black
                        continue
                }
                if (-not (Test-Path -Path $BuildAnalysis -PathType Leaf)) {
                        Write-Host "Not found [$BuildAnalysis]" -ForegroundColor Red -BackgroundColor Black
                        continue
                }
                $WinMergePath = "$ToolchainsPath\Common\Base\WinMerge\WinMergeU.exe"
                if (Test-Path -Path $WinMergePath -PathType Leaf) {
                        Start-Process -FilePath $WinMergePath -ArgumentList "`"$ReferenceAnalysis`" `"$BuildAnalysis`""
                } else {
                        Write-Host "Cannot run [$WinMergePath]: file not found" -ForegroundColor Red -BackgroundColor Black
                }
        }
}

Write-Host ""
Read-Host -Prompt "Press Enter to exit"
