FileFormat/FileFormat.psm1

using namespace System.Collections.Generic

function Get-FileList {
    param (
        [Parameter(Mandatory)]
        [string]$Files
    )

    $fileList = [List[string]]::new()

    foreach ($file in $Files.Split(",")) {
        $file = $file.Trim()

        if ([string]::IsNullOrEmpty($file)) {
            continue
        }

        if (-Not [System.IO.Path]::IsPathRooted($file)) {
            throw "The file path '$file' is not rooted."
        }

        $fileList.Add($file)
    }

    return , $fileList
}

function Get-FilteredFileList {
    param (
        [Parameter(Mandatory)]
        [string]$Path,

        [Parameter()]
        [AllowEmptyString()]
        [string]$Filter,

        [Parameter()]
        [switch]$Recurse = $false,

        [Parameter()]
        [AllowNull()]
        [int]$Depth
    )

    $filteredFiles = [List[string]]::new()

    # Get the List of files from the specified folder path.
    $getChildItemParams = @{
        Path = $Path
        File = $true
    }

    if ($Recurse) {
        $getChildItemParams["Recurse"] = $Recurse
    }

    if ($Depth) {
        $getChildItemParams["Depth"] = $Depth
    }

    $fileList = Get-ChildItem @getChildItemParams

    $fileFilterList = [List[string]]::new()

    if ($Filter) {
        foreach ($fileFilter in $Filter.Split(",")) {
            if (-not $fileFilter.Contains(".")) {
                $fileFilter = "*." + $fileFilter
            }
            $fileFilterList.Add($fileFilter)
        }
    }

    foreach ($file in $fileList) {
        if (Test-FileFilterMatch -FileFilterList $fileFilterList -FileName $file.Name) {
            $fullPath = Join-Path -Path $file.DirectoryName -ChildPath $file.Name
            $filteredFiles.Add($fullPath)
        }
    }

    return , $filteredFiles
}

function Test-FileFilterMatch {
    param (
        [Parameter(Mandatory)]
        [AllowEmptyCollection()]
        [List[string]]$FileFilterList,

        [Parameter(Mandatory)]
        [string]$FileName
    )

    if ($FileFilterList.Count -eq 0) {
        return $true
    }

    foreach ($fileFilter in $FileFilterList) {
        if ($FileName -Like $fileFilter) {
            return $true
        }
    }
}

function Get-CatalogFileList {
    param (
        [Parameter(Mandatory)]
        [string]$Path
    )

    $fileList = [List[string]]::new()

    $folderPath = Split-Path -Parent $Path
    foreach ($line in Get-Content (Resolve-Path $Path)) {
        $filePath = Join-Path -Path $folderPath -ChildPath $line
        $filePath = Resolve-Path $filePath
        $fileList.Add($filePath)
    }

    return , $fileList
}

function Format-FileList {
    param (
        [Parameter(Mandatory)]
        [AllowEmptyCollection()]
        [List[string]]$ListedFiles,

        [Parameter(Mandatory)]
        [AllowEmptyCollection()]
        [List[string]]$FilteredFiles,

        [Parameter(Mandatory)]
        [AllowEmptyCollection()]
        [List[string]]$CatalogFiles
    )

    $formattedSignToolFiles = [List[string]]::new()
    $formattedSignCliFiles = [List[string]]::new()
    $fileChecker = [HashSet[string]]::new()

    $printMsixWarning = $true
    $fileList = $ListedFiles + $FilteredFiles + $CatalogFiles
    foreach ($file in $fileList) {
        if (($file.EndsWith(".msix")) -and $printMsixWarning) {
            Write-Information -MessageData (-join @(
                "`tNote: To successfully sign an .msix/.appx package using a Trusted"
                " Signing certificate, the certificate's subject name must be the"
                " same as the 'Publisher' listed in the app's manifest."
            )) -InformationAction Continue
            $printMsixWarning = $false
        }

        if (-not $fileChecker.Contains($file)) {
            if ($file.EndsWith(".application") -or $file.EndsWith(".manifest") -or $file.EndsWith(".vsto")) {
                $formattedSignCliFiles.Add("`"${file}`"")
            } else {
                $formattedSignToolFiles.Add("`"${file}`"")
            }
            $fileChecker.Add($file) | Out-Null
        }
    }

    return @{ SignToolFiles = $formattedSignToolFiles; SignCliFiles = $formattedSignCliFiles }
}


function Split-FileList {
    param (
        [Parameter(Mandatory)]
        [AllowEmptyCollection()]
        [List[string]]$FormattedFiles,

        [Parameter(Mandatory)]
        [ValidateRange(0, 30000)]
        [int]$BatchSize
    )

    $batchedFileLists = [List[List[string]]]::new()

    $batchLength = 0
    $batchedFileList = [List[string]]::new()
    foreach ($file in $FormattedFiles) {
        if ($batchLength + $file.Length + 1 -lt $BatchSize) {
            $batchedFileList.Add($file)
            $batchLength += $file.Length + 1
        } else {
            if ($batchedFileList.Count -gt 0) {
                $batchedFileLists.Add($batchedFileList)
            }

            $batchedFileList = [List[string]]::new()
            $batchedFileList.Add($file)
            $batchLength = $file.Length
        }
    }

    if ($batchedFileList.Count -gt 0) {
        $batchedFileLists.Add($batchedFileList)
    }

    return , $batchedFileLists
}