BaseEncoder.psm1
#Requires -Version 3.0 Set-StrictMode -Version Latest Function Get-RandomByteArray() { <# .SYNOPSIS A PowerShell function to generate an arbitrary number of cryptographically safe random bytes. .DESCRIPTION Takes a [System.UInt32] parameter specifying the number of bytes to generate and invokes [Security.Cryptography.RNGCryptoServiceProvider]::GetBytes() to return a [System.Byte[]] object containing that number of crypto-quality random bytes. .PARAMETER NumBytes [System.UInt32] object that specifies the number of random bytes to generate. Must be between 1 and [System.UInt32]::MaxValue (4294967295). .INPUTS Single [System.UInt32] parameter indicating the number of bytes to generate. .OUTPUTS [System.Byte[]] array containing the requested number of bytes that have been randomly generated by [Security.Cryptography.RNGCryptoServiceProvider]::GetBytes(). .EXAMPLE Get 64 random bytes: $Bytes = Get-RandomByteArray 64 .NOTES RNGCryptoServiceProvider is used over Get-Random because - when invoked inside a loop or other rapid iteration like the pipeline - Get-Random tends produce duplicate output on fast systems. This is because by default Get-Random uses [Environment]::TickCount (number of milliseconds elapsed since system startup) as a seed and repeated invocation over a loop on a fast system (i.e. sub-1ms execution time) effectively generates multiple calls to the same RNG using identical seeds; hence, identical outputs. RNGCryptoServiceProvider does not have this problem, therefore RNGCryptoServiceProvider was chosen to make the script safe for situations where random bytes might be generated via a loop or through pipeline operations. #> [CmdletBinding( SupportsShouldProcess=$False, DefaultParameterSetName="ByteOutput" )] [OutputType([System.Management.Automation.PSObject])] Param( [Parameter( ParameterSetName="ByteOutput", ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True, Mandatory=$True, Position=0, HelpMessage='Number of random bytes to generate, between 1 and [System.UInt32]::MaxValue (4294967295).' )] [ValidateNotNullOrEmpty()] [ValidateRange(1,[System.UInt32]::MaxValue)] [Alias('Value','Number')] [System.UInt32]$NumBytes ) [System.Byte[]]$Buffer = New-Object -TypeName System.Byte[] $NumBytes $RNG = [Security.Cryptography.RNGCryptoServiceProvider]::Create() $RNG.GetBytes($Buffer) Return ($Buffer) } Function Get-CompressedByteArray { <# .SYNOPSIS A PowerShell function to apply GZip compression to a byte array. .DESCRIPTION Takes a [System.Byte[]] array as input and returns a [System.Byte[]] array containing a GZip-compressed version of the input bytes. .PARAMETER ByteArray [System.Byte[]] object containing arbitrary bytes on which to apply compression. .PARAMETER CompressionLevel [System.IO.Compression.CompressionLevel] enum that determines the level of compression; valid values are "Fastest", "NoCompression" and "Optimal". By default if no CompressionLevel is specified, "Optimal" will be used. .INPUTS [System.Byte[]] object. .OUTPUTS [System.Byte[]] object. .EXAMPLE GZip compress a byte array using Optimal compression level: $GZBytes = Get-CompressedByteArray($Bytes) .EXAMPLE GZip compress a byte array using Fastest compression level: $GZBytes = Get-CompressedByteArray($Bytes) -CompressionLevel Fastest .NOTES This function provides a simple wrapper for the .NET System.IO.Compression.GzipStream class - no spectacular hand-rolled compression code here. #> [CmdletBinding( SupportsShouldProcess=$False, DefaultParameterSetName="ByteInput" )] [OutputType([System.Management.Automation.PSObject])] Param( [Parameter( ParameterSetName="ByteInput", ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True, Mandatory=$True, Position=0, HelpMessage='Byte array to GZip compress' )] [ValidateNotNullOrEmpty()] [Alias('Data','Bytes')] [System.Byte[]]$ByteArray, [Parameter( ParameterSetName="ByteInput", ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True, Mandatory=$False, Position=1, HelpMessage='GZip compression level, valid values are "Fastest", "NoCompression" and "Optimal"; default "Optimal"' )] [ValidateNotNullOrEmpty()] [System.IO.Compression.CompressionLevel]$CompressionLevel ) PROCESS { [System.IO.MemoryStream]$OutputStream = New-Object -TypeName System.IO.MemoryStream [System.Object]$GZStream = New-Object -TypeName System.IO.Compression.GzipStream $OutputStream,([IO.Compression.CompressionMode]::Compress),$CompressionLevel Try { $GZStream.Write($ByteArray,0,$ByteArray.Length) [System.Byte[]]$ResultObject = $OutputStream.ToArray() Return ($ResultObject) } Catch { Write-Error "Exception: $($_.Exception.Message)" Break } Finally { $GZStream.Close() $GZStream.Dispose() $OutputStream.Close() $OutputStream.Dispose() } } } Function Get-DecompressedByteArray { <# .SYNOPSIS A PowerShell function decompress GZip-compressed byte arrays. .DESCRIPTION Takes a [System.Byte[]] array containing a GZip-compressed data as input and returns a [System.Byte[]] array of the corresponding decompressed data. .PARAMETER ByteArray [System.Byte[]] object containing arbitrary bytes to decompress. .INPUTS [System.Byte[]] object. .OUTPUTS [System.Byte[]] object. .EXAMPLE Decompress a byte array:: $Bytes = Get-DecompressedByteArray($GZBytes) .NOTES The function will check the first two bytes of the input object for the GZip "magic number" 0x1F,0x8B - if the magic number is not present the function assumes the byte array is not compressed and returns the original input object without modification. #> [CmdletBinding( SupportsShouldProcess=$False, DefaultParameterSetName="ByteInput" )] [OutputType([System.Management.Automation.PSObject])] Param( [Parameter( ParameterSetName="ByteInput", ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True, Mandatory=$True, Position=0, HelpMessage='Byte array to GZip decompress' )] [ValidateNotNullOrEmpty()] [Alias('Data','Bytes')] [System.Byte[]]$ByteArray ) PROCESS { If (Compare-Object -ReferenceObject ([System.Byte[]]$ByteArray[0,1]) -DifferenceObject ([System.Byte[]](0x1F,0x8B))) { Write-Warning "Data stream is missing GZip magic number, bytes are probably not compressed." Return ($ByteArray) #TODO: determine best way to handle missing GZip header - returning the original byte array isn't elegant } [System.Object]$InputStream = New-Object -TypeName System.IO.MemoryStream(,$ByteArray) [System.Object]$OutputStream = New-Object -TypeName System.IO.MemoryStream [System.Object]$GZStream = New-Object -TypeName System.IO.Compression.GzipStream $InputStream,([IO.Compression.CompressionMode]::Decompress) Try { $GZStream.CopyTo($OutputStream) [System.Byte[]]$ResultObject = $OutputStream.ToArray() Return ($ResultObject) } Catch { Write-Error "Exception: $($_.Exception.Message)" Break } Finally { $GZStream.Close() $GZStream.Dispose() $InputStream.Close() $InputStream.Dispose() $OutputStream.Close() $OutputStream.Dispose() } } } Function ConvertTo-Ascii85() { <# .SYNOPSIS A PowerShell function to convert arbitrary data into an Ascii85 encoded string. .DESCRIPTION Takes a string, byte array or file object as input and returns a Ascii85 encoded string or location of the Ascii85 result output file object. The default input and output type if positional parameters are used is [System.String]. .PARAMETER Bytes [System.Byte[]] object containing a byte array to be encoded as Ascii85 string. Accepts pipeline input. .PARAMETER String [System.String] object containing plain text to be encoded as Ascii85 string. Accepts pipeline input. .PARAMETER InFile [System.IO.Fileinfo] object containing the details of a file on disk to be converted to Ascii85 string and output as a new file; output files are written as UTF-8 no BOM. Accepts pipeline input. .PARAMETER OutFile Optional [System.IO.Fileinfo] object containing the details of the new file to write to disk containing Ascii85 encoded data from the input file. Can be used with any input mode (Bytes, String, or InFile). .PARAMETER Unormatted By default the function adds line breaks to output string every 64 characters and adds A85 start/end delimiters (<~ / ~>); this parameter suppresses formatting and returns the Ascii85 string result as a single, unbroken string object with no delimiters. .PARAMETER AutoSave [System.String] containing a new file extension to use to automatically generate files. When paired with -InFile, automatically create an output filename of in the form of the original file name plus the suffix specified after the parameter, for example -AutoSave "A85" would create the OutFile name <InFile>.A85. Useful if piping the output of Get-ChildItem to the function to convert files as a bulk operation. Cannot be used with input methods other than -InFile. .PARAMETER Raw Optional switch parameter that when present will produce raw string output instead of a PSObject. This parameter limits the functionality of the pipeline but is convenient for simple encoding operations. .INPUTS Any single object or collection of strings, bytes, or files (such as those from Get-ChildItem) can be piped to the function for processing into Ascii85 encoded data. .OUTPUTS The output is always an ASCII string; if any input method is used with -OutFile or -InFile is used with -AutoSave, the output is a [System.IO.FileInfo] object containing details of a UTF8 no BOM text file with the Ascii85 encoded data as contents. Unless -Unformatted is specified, the console or file string data is formatted with start and end delimiters (<~ / ~>) and line breaks are added every 64 character. If -Unformatted is present, the output is a [System.String] with no line breaks or delimiters. If outputting to the console, the string is returned within a PSObject with a single member named Ascii85EncodedData as [System.String]; if -Raw is specified, the [System.String] is not wrapped in a PSObject and returned directly. This means that output using -Raw cannot easily use the pipeline, but makes it a useful option for quick encoding operations. The -Verbose parameter will return the function's total execution time. .EXAMPLE Convert a string directly into Ascii85: ConvertTo-Ascii85 "This is a plaintext string" .EXAMPLE Pipe an object (string or array of strings, byte array or array of byte arrays, file info or array of file info objects) to the function for encoding as Ascii85: $MyObject | ConvertTo-Ascii85 .EXAMPLE Convert a byte array to Ascii85 and return the output with block formatting and not wrapped in a PSObject (as a raw [System.String]): ConvertTo-Ascii85 -ByteArray $Bytes -Raw .EXAMPLE Pipe the results of a directory listing from Get-ChildItem and generate a new Ascii85 encoded file with block formatting for each input file: Get-ChildItem C:\Text\*.txt | ConvertTo-Ascii85 -AutoSave "A85" .EXAMPLE Use file based input to Ascii85 encode an input file and output the results as new file C:\Text\Ascii85.txt with no line breaks or delimiters: ConvertTo-Ascii85 -File C:\Text\file.txt -OutFile C:\Text\Ascii85.txt -Unformatted .NOTES Ascii85 is similar but slightly different than the Base85 encoding scheme from which it is derived. More info on Ascii85 can be found here: https://en.wikipedia.org/wiki/Ascii85 #> [CmdletBinding( SupportsShouldProcess=$True, ConfirmImpact="High", DefaultParameterSetName="StringInput" )] [OutputType([System.Management.Automation.PSObject])] Param( [Parameter( ParameterSetName="ByteInput", ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True, Mandatory=$True, Position=0, HelpMessage='Byte array to Ascii85 encode.' )] [ValidateNotNullOrEmpty()] [Alias('ByteArray','Data')] [System.Byte[]]$Bytes, [Parameter( ParameterSetName="StringInput", ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True, Mandatory=$True, Position=0, HelpMessage='String to Ascii85 encode.' )] [ValidateNotNullOrEmpty()] [Alias('Plaintext','Text')] [System.String]$String, [Parameter( ParameterSetName="FileInput", ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True, Mandatory=$True,Position=0, HelpMessage='File to Ascii85 encode.' )] [ValidateNotNullOrEmpty()] [Alias('Filename','FullName','File')] [ValidateScript({ If (-Not($_ | Test-Path -PathType Leaf)) { throw ("Invalid input file name specified.") }Else{ $True } })] [ValidateScript({ Try { $_.Open([System.IO.FileMode]::Open,[System.IO.FileAccess]::Read,[System.IO.FileShare]::None).Close() $True } Catch { throw ("Input file is locked for reading or could not obtain read access.") } })] [System.IO.Fileinfo]$InFile, [Parameter( ParameterSetName="StringInput", ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True, Mandatory=$False, Position=1, HelpMessage='Output result as UTF8-NoBOM encoded text to specified file instead of writing to console.' )] [Parameter( ParameterSetName="ByteInput" )] [Parameter( ParameterSetName="FileInput" )] [ValidateNotNullOrEmpty()] [System.IO.Fileinfo]$OutFile, [Parameter( ParameterSetName="ByteInput", ValueFromPipeline=$False, ValueFromPipelineByPropertyName=$False, Mandatory=$False, HelpMessage='Do not format output string using header, footer or line breaks.' )] [Parameter( ParameterSetName="StringInput" )] [Parameter( ParameterSetName="FileInput" )] [ValidateNotNullOrEmpty()] [Switch]$Unformatted, [Parameter( ParameterSetName="FileInput", ValueFromPipeline=$False, ValueFromPipelineByPropertyName=$False, Mandatory=$False, HelpMessage='When in file input mode automatically select output file name using the specified suffix as the file extension; not valid with any other input mode (String or Bytes).' )] [ValidateNotNullOrEmpty()] [ValidateScript({ If (-Not(($_.IndexOfAny([System.IO.Path]::GetInvalidFileNameChars()) -eq -1))) { throw ("AutoSave suffix contains illegal characters.") } Else { $True } })] [System.String]$AutoSave, [Parameter( ParameterSetName="ByteInput", ValueFromPipeline=$False, ValueFromPipelineByPropertyName=$False, Mandatory=$False, HelpMessage='When returning a string instead of a file, return a raw string instead of PSObject; applies to both console and file output modes.' )] [Parameter( ParameterSetName="StringInput" )] [Parameter( ParameterSetName="FileInput" )] [ValidateNotNullOrEmpty()] [Switch]$Raw ) BEGIN { } PROCESS { If ($PSBoundParameters.ContainsKey('InFile') -and $PSBoundParameters.ContainsKey('AutoSave')) { $OutFile = ($InFile.FullName.ToString()) + ".$($AutoSave)" } If ($OutFile) { If ((Test-Path $OutFile -PathType Leaf) -and ($PSCmdlet.ShouldProcess($OutFile,'Overwrite'))) { Remove-Item $OutFile -Confirm:$False } If (Test-Path $OutFile -PathType Leaf) { Write-Error "Could not overwrite existing output file '$($Outfile)'" -ErrorAction Stop } $Null = New-Item -Path $OutFile -ItemType File If ($Raw) { Write-Warning "File output mode specified; Parameter '-Raw' will be ignored." } } Switch ($PSCmdlet.ParameterSetName) { "ByteInput" { [System.IO.Stream]$InputStream = New-Object -TypeName System.IO.MemoryStream(,$Bytes) Break } "StringInput" { [System.IO.Stream]$InputStream = New-Object -TypeName System.IO.MemoryStream(,[System.Text.Encoding]::ASCII.GetBytes($String)) Break } "FileInput" { [System.IO.Stream]$InputStream = [System.IO.File]::Open($InFile.FullName,[System.IO.FileMode]::Open,[System.IO.FileAccess]::Read,[System.IO.FileShare]::ReadWrite) Break } } [System.Object]$Timer = [System.Diagnostics.Stopwatch]::StartNew() [System.Object]$BinaryReader = New-Object -TypeName System.IO.BinaryReader($InputStream) [System.Object]$Ascii85Output = New-Object -TypeName System.Text.StringBuilder If (-Not $Unformatted) { [void]$Ascii85Output.Append("<~") [System.UInt16]$LineLen = 2 } Try { While ([System.Byte[]]$BytesRead = $BinaryReader.ReadBytes(4)) { [System.UInt16]$ByteLength = $BytesRead.Length If ($ByteLength -lt 4) { [System.Byte[]]$WorkingBytes = ,0x00 * 4 [System.Buffer]::BlockCopy($BytesRead,0,$WorkingBytes,0,$ByteLength) [System.Array]::Resize([ref]$BytesRead,4) [System.Buffer]::BlockCopy($WorkingBytes,0,$BytesRead,0,4) } If ([BitConverter]::IsLittleEndian) { [Array]::Reverse($BytesRead) } [System.Char[]]$A85Chars = ,0x00 * 5 [System.UInt32]$Sum = [BitConverter]::ToUInt32($BytesRead,0) [System.UInt16]$ByteLen = [Math]::Ceiling(($ByteLength / 4) * 5) If ($ByteLength -eq 4 -And $Sum -eq 0) { [System.Char[]]$A85Chunk = "z" } Else { [System.Char[]]$A85Chunk = ,0x00 * $ByteLen $A85Chars[0] = [System.Text.Encoding]::ASCII.GetChars([Math]::Floor(($Sum / [Math]::Pow(85,4)) % 85) + 33)[0] $A85Chars[1] = [System.Text.Encoding]::ASCII.GetChars([Math]::Floor(($Sum / [Math]::Pow(85,3)) % 85) + 33)[0] $A85Chars[2] = [System.Text.Encoding]::ASCII.GetChars([Math]::Floor(($Sum / [Math]::Pow(85,2)) % 85) + 33)[0] $A85Chars[3] = [System.Text.Encoding]::ASCII.GetChars([Math]::Floor(($Sum / 85) % 85) + 33)[0] $A85Chars[4] = [System.Text.Encoding]::ASCII.GetChars([Math]::Floor($Sum % 85) + 33)[0] [System.Array]::Copy($A85Chars,$A85Chunk,$ByteLen) } ForEach ($A85Char in $A85Chunk) { [void]$Ascii85Output.Append($A85Char) If (-Not $Unformatted) { If ($LineLen -eq 64) { [void]$Ascii85Output.Append("`r`n") $LineLen = 0 } Else { $LineLen++ } } } } If (-Not $Unformatted) { If ($LineLen -le 62) { [void]$Ascii85Output.Append("~>") } Else { [void]$Ascii85Output.Append("~`r`n>") } } [System.String]$Ascii85Result = $Ascii85Output.ToString() $Ascii85ResultObject = New-Object -TypeName PSObject If ($OutFile) { [System.IO.File]::WriteAllLines($OutFile.FullName,$Ascii85Result,(New-Object -TypeName System.Text.UTF8Encoding $False)) $Ascii85ResultObject = $OutFile } Else { If ($Raw) { $Ascii85ResultObject = $Ascii85Result } Else { Add-Member -InputObject $Ascii85ResultObject -MemberType 'NoteProperty' -Name 'Ascii85EncodedData' -Value $Ascii85Result } } Return ($Ascii85ResultObject) } Catch { Write-Error "Exception: $($_.Exception.Message)" Break } Finally { $BinaryReader.Close() $BinaryReader.Dispose() $InputStream.Close() $InputStream.Dispose() $Timer.Stop() [System.String]$TimeLapse = "Ascii85 encode completed in $($Timer.Elapsed.Hours) hours, $($Timer.Elapsed.Minutes) minutes, $($Timer.Elapsed.Seconds) seconds, $($Timer.Elapsed.Milliseconds) milliseconds" Write-Verbose $TimeLapse } } } Function ConvertFrom-Ascii85() { <# .SYNOPSIS A PowerShell function to convert an arbitrary Ascii85 encoded string into a byte array or binary file. .DESCRIPTION Takes a string of Ascii85 formatted data and decodes into the original ASCII string or byte array. Input includes a Ascii85 string or a file containing an Ascii85 string. Both formatted (line breaks) and unformatted Ascii85 data are supported. The default input and output type if positional parameters are used is [System.String]; it is also possible to write a binary file from the Ascii85 input using -OutFile. .PARAMETER Ascii85EncodedString [System.String] object containing Ascii85 encoded data. Accepts pipeline input. .PARAMETER InFile [System.IO.Fileinfo] object containing the details of a file on disk to be loaded as a string object and decoded from Ascii85 string; accepts pipeline input. .PARAMETER OutFile Optional [System.IO.Fileinfo] object containing the details of the new file to write to disk containing Ascii85 decoded data from the input file. Can be used with any input mode (Bytes, String, or InFile); file content will be raw decoded bytes. .PARAMETER OutBytes Return the decoded data as [System.Byte[]] to the console instead of the default ASCII string. .PARAMETER AutoSave When paired with -InFile, automatically create an output filename of in the form of the original file name plus the suffix specified after the parameter; for example, -AutoSave "BIN" will result in OutFile name <InFile>.bin. Useful if piping the output of Get-ChildItem to the function to convert files as a bulk operation. Cannot be used with input methods other than -InFile. .PARAMETER Raw Optional switch parameter that when present will produce raw output instead of a PSObject. Depending on the parameters used, the return object could be of type [System.String] or [System.Byte[]]. .INPUTS Any single object, array or collection of strings or files (such as those from Get-ChildItem) can be piped to the function for processing from Ascii85 encoded data. Input data from file is always processed as ASCII text regardless of source file text encoding. .OUTPUTS In the case of direct string input, a [System.String] containing the decoded data as ASCII text is returned within a PSObject with a single member named Ascii85DecodedData. If any input method is used with -OutFile or -InFile is used with -AutoSave, the output is a [System.IO.FileInfo] object containing details of a binary file with the Ascii85 decoded data as contents. If -OutBytes is specified, data is returned to the console as [System.Byte[]] wrapped in a PSObject. If -Raw is specified, the [System.String] or [System.Byte[]] is not wrapped in a PSObject and is returned directly. This means that output using -Raw cannot easily use the pipeline. The -Verbose parameter will return the function's total execution time. .EXAMPLE Convert a Ascii85 string to a decoded byte array: [System.Byte[]]$Bytes = ConvertFrom-Ascii85 "6uQRXD.RU,@<?4%DBS" -OutBytes -Raw .EXAMPLE Decode a Ascii85 encoded string: ConvertFrom-Ascii85 -Ascii85EncodedString "6uQRXD.RU,@<?4%DBS" .EXAMPLE Pipe an object (string or array of strings, file info or array of file info objects) to the function for decoding from Ascii85: $MyObject | ConvertFrom-Ascii85 .EXAMPLE Pipe the results of a directory listing from Get-ChildItem and generate a new Ascii85 decoded file for each input file: Get-ChildItem C:\Text\*.A85 | ConvertFrom-Ascii85 -AutoSave "BIN" .EXAMPLE Use file based input to decode an input file and output the results as new file C:\Text\file.txt: ConvertFrom-Ascii85 -File C:\Text\file.A85 -OutFile C:\Text\file.txt .NOTES Ascii85 is similar but slightly different than the Base85 encoding scheme from which it is derived. More info on Ascii85 can be found here: https://en.wikipedia.org/wiki/Ascii85. Because Ascii85 encoded text uses some PowerSehll reserved characters in its output (notably ` and $), when using string input it is sometimes necessary to escape certain characters in order for input to be read correctly. File input does not suffer from this limitation. #> [CmdletBinding( SupportsShouldProcess=$True, ConfirmImpact="High", DefaultParameterSetName="StringInput" )] [OutputType([System.Management.Automation.PSObject])] Param( [Parameter( ParameterSetName="StringInput", ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True, Mandatory=$True, Position=0, HelpMessage='Input A85encoded string.' )] [ValidateNotNullOrEmpty()] [Alias('String','Plaintext','Text','A85EncodedData')] [System.String]$Ascii85EncodedString, [Parameter( ParameterSetName="FileInput", ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True, Mandatory=$True,Position=0, HelpMessage='File to A85decode' )] [ValidateNotNullOrEmpty()] [Alias('Filename','FullName')] [ValidateScript({ If (-Not($_ | Test-Path -PathType Leaf)) { throw ("Invalid input file name specified.") }Else{ $True } })] [ValidateScript({ Try { $_.Open([System.IO.FileMode]::Open,[System.IO.FileAccess]::Read,[System.IO.FileShare]::None).Close() $True } Catch { throw ("Input file is locked for reading or could not obtain read access.") } })] [System.IO.Fileinfo]$InFile, [Parameter( ParameterSetName="FileInput", ValueFromPipeline=$False, ValueFromPipelineByPropertyName=$False, Mandatory=$False, Position=1, HelpMessage='Path to output file when decoding in file mode.' )] [ValidateNotNullOrEmpty()] [System.IO.Fileinfo]$OutFile, [Parameter( ParameterSetName="ByteInput", ValueFromPipeline=$False, ValueFromPipelineByPropertyName=$False, Mandatory=$False, HelpMessage='Output decoded data as raw bytes instead of ASCII text.' )] [Parameter( ParameterSetName="StringInput" )] [Parameter( ParameterSetName="FileInput" )] [ValidateNotNullOrEmpty()] [Switch]$OutBytes, [Parameter( ParameterSetName="ByteInput", ValueFromPipeline=$False, ValueFromPipelineByPropertyName=$False, Mandatory=$False, HelpMessage='Determine the output file name from the A85encoded data header; only applies to file mode. Can be mixed with -AutoSave or -OutFile when using pipeline to extract the file name if it exists, and rely on -AutoSave / -OutFile if it does not.' )] [Parameter( ParameterSetName="StringInput" )] [Parameter( ParameterSetName="FileInput" )] [ValidateNotNullOrEmpty()] [Switch]$Auto, [Parameter( ParameterSetName="FileInput", ValueFromPipeline=$False, ValueFromPipelineByPropertyName=$False, Mandatory=$False, HelpMessage='When in file input mode, automatically select output file name using the specified suffix as the file extension; not valid with any other input mode (String or Bytes).' )] [ValidateNotNullOrEmpty()] [ValidateScript({ If (-Not(($_.IndexOfAny([System.IO.Path]::GetInvalidFileNameChars()) -eq -1))) { throw ("AutoSave suffix contains illegal characters.") } Else { $True } })] [System.String]$AutoSave, [Parameter( ParameterSetName="StringInput", ValueFromPipeline=$False, ValueFromPipelineByPropertyName=$False, Mandatory=$False, HelpMessage='When returning to console, return a raw byte array instead of PSObject.' )] [Parameter( ParameterSetName="FileInput" )] [ValidateNotNullOrEmpty()] [Switch]$Raw ) BEGIN { If ($PSBoundParameters.ContainsKey("AutoSave") -and $PSCmdlet.ParameterSetName -ne "FileInput") { Write-Error "-AutoSave can only be used in file input mode." -ErrorAction Stop } [System.String]$NON_A85_Pattern = "[^\x21-\x75]" [System.String]$OFS = "" } PROCESS { If ($PSBoundParameters.ContainsKey('InFile') -and $PSBoundParameters.ContainsKey('AutoSave')) { $OutFile = ($InFile.FullName.ToString()) + ".$($AutoSave)" } Switch ($PSCmdlet.ParameterSetName) { "StringInput" { [System.String]$A85String = $Ascii85EncodedString.Replace("<~","").Replace("~>","").Replace(" ","").Replace("`r`n","").Replace("`n","") Break } "FileInput" { [System.String]$A85String = ([System.IO.File]::ReadAllText($InFile.FullName)).Replace("<~","").Replace("~>","").Replace(" ","").Replace("`r`n","").Replace("`n","") Break } } If ($A85String -match $NON_A85_Pattern) { Throw "Invalid Ascii85 data detected in input stream." } [System.Object]$InputStream = New-Object -TypeName System.IO.MemoryStream([System.Text.Encoding]::ASCII.GetBytes($A85String),0,$A85String.Length) [System.Object]$BinaryReader = New-Object -TypeName System.IO.BinaryReader($InputStream) [System.Object]$OutputStream = New-Object -TypeName System.IO.MemoryStream [System.Object]$BinaryWriter = New-Object -TypeName System.IO.BinaryWriter($OutputStream) If ($OutFile) { If ((Test-Path $OutFile -PathType Leaf) -and ($PSCmdlet.ShouldProcess($OutFile,'Overwrite'))) { Remove-Item $OutFile -Confirm:$False } If (Test-Path $OutFile -PathType Leaf) { Write-Error "Could not overwrite existing output file '$($Outfile)'" -ErrorAction Stop } $Null = New-Item -Path $OutFile -ItemType File } [System.Object]$Timer = [System.Diagnostics.Stopwatch]::StartNew() Try { While ([System.Byte[]]$BytesRead = $BinaryReader.ReadBytes(5)) { [System.Boolean]$AtEnd = ($BinaryReader.BaseStream.Length -eq $BinaryReader.BaseStream.Position) [System.UInt16]$ByteLength = $BytesRead.Length If ($ByteLength -lt 5) { [System.Byte[]]$WorkingBytes = ,0x75 * 5 [System.Buffer]::BlockCopy($BytesRead,0,$WorkingBytes,0,$ByteLength) [System.Array]::Resize([ref]$BytesRead,5) [System.Buffer]::BlockCopy($WorkingBytes,0,$BytesRead,0,5) } [System.UInt16]$ByteLen = [Math]::Floor(($ByteLength * 4) / 5) [System.Byte[]]$BinChunk = ,0x00 * $ByteLen If ($BytesRead[0] -eq 0x7A) { $BinaryWriter.Write($BinChunk) If (-Not $AtEnd) { $BinaryReader.BaseStream.Position = $BinaryReader.BaseStream.Position - 4 Continue } } Else { [System.UInt32]$Sum = 0 $Sum += ($BytesRead[0] - 33) * [Math]::Pow(85,4) $Sum += ($BytesRead[1] - 33) * [Math]::Pow(85,3) $Sum += ($BytesRead[2] - 33) * [Math]::Pow(85,2) $Sum += ($BytesRead[3] - 33) * 85 $Sum += ($BytesRead[4] - 33) [System.Byte[]]$A85Bytes = [System.BitConverter]::GetBytes($Sum) If ([BitConverter]::IsLittleEndian) { [Array]::Reverse($A85Bytes) } [System.Buffer]::BlockCopy($A85Bytes,0,$BinChunk,0,$ByteLen) $BinaryWriter.Write($BinChunk) } } $ResultObject = New-Object -TypeName PSObject If ($OutFile) { [System.IO.File]::WriteAllBytes($OutFile,($OutputStream.ToArray())) $ResultObject = $OutFile } Else { If ($OutBytes -and $Raw) { $ResultObject = $OutputStream.ToArray() } ElseIf ($OutBytes) { Add-Member -InputObject $ResultObject -MemberType 'NoteProperty' -Name 'Ascii85DecodedData' -Value $OutputStream.ToArray() } ElseIf ($Raw) { [System.String]$Results = [System.Text.Encoding]::ASCII.GetString(($OutputStream.ToArray())) $ResultObject = $Results } Else { [System.String]$Results = [System.Text.Encoding]::ASCII.GetString(($OutputStream.ToArray())) Add-Member -InputObject $ResultObject -MemberType 'NoteProperty' -Name 'AsciiDecodedString' -Value $Results } } Return ($ResultObject) } Catch { Write-Error "Exception: $($_.Exception.Message)" Break } Finally { $BinaryReader.Close() $BinaryReader.Dispose() $BinaryWriter.Close() $BinaryWriter.Dispose() $InputStream.Close() $InputStream.Dispose() $OutputStream.Close() $OutputStream.Dispose() $Timer.Stop() [System.String]$TimeLapse = "Ascii85 decode completed after $($Timer.Elapsed.Hours) hours, $($Timer.Elapsed.Minutes) minutes, $($Timer.Elapsed.Seconds) seconds, $($Timer.Elapsed.Milliseconds) milliseconds" Write-Verbose $TimeLapse } } } Function ConvertTo-Base16() { <# .SYNOPSIS A PowerShell function to convert arbitrary data into a Base16 (hexadecimal) encoded string. .DESCRIPTION Takes a string, byte array or file object as input and returns a Base16 encoded string or location of the Base16 result output file object. The default input and output type if positional parameters are used is [System.String]. .PARAMETER Bytes [System.Byte[]] object containing a byte array to be encoded as Base16 string. Accepts pipeline input. .PARAMETER String [System.String] object containing plain text to be encoded as Base16 string. Accepts pipeline input. .PARAMETER InFile [System.IO.Fileinfo] object containing the details of a file on disk to be converted to Base16 string and output as a new file; output files are written as UTF-8 no BOM. Accepts pipeline input. .PARAMETER OutFile Optional [System.IO.Fileinfo] object containing the details of the new file to write to disk containing Base16 encoded data from the input file. Can be used with any input mode (Bytes, String, or InFile). .PARAMETER Unormatted By default the function adds line breaks to output string every 64 characters and block style header / footer (-----BEGIN BASE16 ENCODED DATA-----/-----END BASE16 ENCODED DATA-----); this parameter suppresses formatting and returns the Base16 string result as a single, unbroken string object with no header or footer. .PARAMETER AutoSave [System.String] containing a new file extension to use to automatically generate files. When paired with -InFile, automatically create an output filename of in the form of the original file name plus the suffix specified after the parameter, for example -AutoSave "B16" would create the OutFile name <InFile>.B16. Useful if piping the output of Get-ChildItem to the function to convert files as a bulk operation. Cannot be used with input methods other than -InFile. .PARAMETER Raw Optional switch parameter that when present will produce raw string output instead of a PSObject. This parameter limits the functionality of the pipeline but is convenient for simple encoding operations. .INPUTS Any single object or collection of strings, bytes, or files (such as those from Get-ChildItem) can be piped to the function for processing into Base16 encoded data. .OUTPUTS The output is always an ASCII string; if any input method is used with -OutFile or -InFile is used with -AutoSave, the output is a [System.IO.FileInfo] object containing details of a UTF8 no BOM text file with the Base16 encoded data as contents. Unless -Unformatted is specified, the console or file string data is formatted with block headers (-----BEGIN BASE16 ENCODED DATA-----/-----END BASE16 ENCODED DATA-----) and line breaks are added every 64 character. If -Unformatted is present, the output is a [System.String] with no line breaks or header / footer. If outputting to the console, the string is returned within a PSObject with a single member named Base16EncodedData as [System.String]; if -Raw is specified, the [System.String] is not wrapped in a PSObject and returned directly. This means that output using -Raw cannot easily use the pipeline, but makes it a useful option for quick encoding operations. The -Verbose parameter will return the function's total execution time. .EXAMPLE Convert a string directly into Base16: ConvertTo-Base16 "This is a plaintext string" .EXAMPLE Pipe an object (string or array of strings, byte array or array of byte arrays, file info or array of file info objects) to the function for encoding as Base16: $MyObject | ConvertTo-Base16 .EXAMPLE Convert a byte array to Base16 and return the output with block formatting and not wrapped in a PSObject (as a raw [System.String]): ConvertTo-Base16 -ByteArray $Bytes -Raw .EXAMPLE Pipe the results of a directory listing from Get-ChildItem and generate a new Base16 encoded file with block formatting for each input file: Get-ChildItem C:\Text\*.txt | ConvertTo-Base16 -AutoSave "B16" .EXAMPLE Use file based input to Base16 encode an input file and output the results as new file C:\Text\Base16.txt with no line breaks or header / footer: ConvertTo-Base16 -File C:\Text\file.txt -OutFile C:\Text\Base16.txt -Unformatted .NOTES This function uses the built-in ToString("X2") method for converting arbitrary bytes into hexadecimal string; more information on the Base16, Base16, and Base16 Data Encoding standard can be found on the IETF web site: https://tools.ietf.org/html/rfc4648 #> [CmdletBinding( SupportsShouldProcess=$True, ConfirmImpact="High", DefaultParameterSetName="StringInput" )] [OutputType([System.Management.Automation.PSObject])] Param( [Parameter( ParameterSetName="ByteInput", ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True, Mandatory=$True, Position=0, HelpMessage='Byte array to Base16 encode.' )] [ValidateNotNullOrEmpty()] [Alias('ByteArray','Data')] [System.Byte[]]$Bytes, [Parameter( ParameterSetName="StringInput", ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True, Mandatory=$True, Position=0, HelpMessage='String to Base16 encode.' )] [ValidateNotNullOrEmpty()] [Alias('Plaintext','Text')] [System.String]$String, [Parameter( ParameterSetName="FileInput", ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True, Mandatory=$True,Position=0, HelpMessage='File to Base16 encode.' )] [ValidateNotNullOrEmpty()] [Alias('Filename','FullName','File')] [ValidateScript({ If (-Not($_ | Test-Path -PathType Leaf)) { throw ("Invalid input file name specified.") }Else{ $True } })] [ValidateScript({ Try { $_.Open([System.IO.FileMode]::Open,[System.IO.FileAccess]::Read,[System.IO.FileShare]::None).Close() $True } Catch { throw ("Input file is locked for reading or could not obtain read access.") } })] [System.IO.Fileinfo]$InFile, [Parameter( ParameterSetName="StringInput", ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True, Mandatory=$False, Position=1, HelpMessage='Output result to specified file as UTF8-NoBOM text instead of console.' )] [Parameter( ParameterSetName="ByteInput" )] [Parameter( ParameterSetName="FileInput" )] [ValidateNotNullOrEmpty()] [System.IO.Fileinfo]$OutFile, [Parameter( ParameterSetName="ByteInput", ValueFromPipeline=$False, ValueFromPipelineByPropertyName=$False, Mandatory=$False, HelpMessage='Do not format output string using header/footer and line breaks.' )] [Parameter( ParameterSetName="StringInput" )] [Parameter( ParameterSetName="FileInput" )] [ValidateNotNullOrEmpty()] [Switch]$Unformatted, [Parameter( ParameterSetName="FileInput", ValueFromPipeline=$False, ValueFromPipelineByPropertyName=$False, Mandatory=$False, HelpMessage='When in file input mode, automatically select output file name using the specified suffix as the file extension; not valid with any other input mode (String or Bytes).' )] [ValidateNotNullOrEmpty()] [ValidateScript({ If (-Not(($_.IndexOfAny([System.IO.Path]::GetInvalidFileNameChars()) -eq -1))) { throw ("AutoSave suffix contains illegal characters.") } Else { $True } })] [System.String]$AutoSave, [Parameter( ParameterSetName="ByteInput", ValueFromPipeline=$False, ValueFromPipelineByPropertyName=$False, Mandatory=$False, HelpMessage='When returning a string instead of a file, return a raw string instead of PSObject; applies to both console and file output modes.' )] [Parameter( ParameterSetName="StringInput" )] [Parameter( ParameterSetName="FileInput" )] [ValidateNotNullOrEmpty()] [Switch]$Raw ) BEGIN { [System.String]$B16Header = "-----BEGIN BASE16 ENCODED DATA-----" [System.String]$B16Footer = "-----END BASE16 ENCODED DATA-----" [System.String]$OFS = "" } PROCESS { If ($PSBoundParameters.ContainsKey('InFile') -and $PSBoundParameters.ContainsKey('AutoSave')) { $OutFile = ($InFile.FullName.ToString()) + ".$($AutoSave)" } If ($OutFile) { If ((Test-Path $OutFile -PathType Leaf) -and ($PSCmdlet.ShouldProcess($OutFile,'Overwrite'))) { Remove-Item $OutFile -Confirm:$False } If (Test-Path $OutFile -PathType Leaf) { Write-Error "Could not overwrite existing output file '$($Outfile)'" -ErrorAction Stop } $Null = New-Item -Path $OutFile -ItemType File If ($Raw) { Write-Warning "File output mode specified; Parameter '-Raw' will be ignored." } } Switch ($PSCmdlet.ParameterSetName) { "ByteInput" { [System.IO.Stream]$InputStream = New-Object -TypeName System.IO.MemoryStream(,$Bytes) Break } "StringInput" { [System.IO.Stream]$InputStream = New-Object -TypeName System.IO.MemoryStream(,[System.Text.Encoding]::ASCII.GetBytes($String)) Break } "FileInput" { [System.IO.Stream]$InputStream = [System.IO.File]::Open($InFile.FullName,[System.IO.FileMode]::Open,[System.IO.FileAccess]::Read,[System.IO.FileShare]::ReadWrite) Break } } [System.Object]$Timer = [System.Diagnostics.Stopwatch]::StartNew() [System.Object]$BinaryReader = New-Object -TypeName System.IO.BinaryReader($InputStream) [System.Object]$Base16Output = New-Object -TypeName System.Text.StringBuilder If (-Not $Unformatted) { [void]$Base16Output.Append("$($B16Header)`r`n") } [System.String]$B16Line = [String]::Empty Try { While ([System.Byte[]]$BytesRead = $BinaryReader.ReadBytes(2)) { [System.Boolean]$AtEnd = ($BinaryReader.BaseStream.Length -eq $BinaryReader.BaseStream.Position) [System.String]$B16 = ForEach ($Byte in $BytesRead) { $Byte.ToString("X2") } $B16Line += $B16 If ($B16Line.Length -eq 64 -and -not $Unformatted) { [void]$Base16Output.Append($B16Line + "`r`n") [System.String]$B16Line = [String]::Empty } ElseIf ($AtEnd) { [void]$Base16Output.Append($B16Line) } } If (-Not $Unformatted) { [void]$Base16Output.Append("`r`n$($B16Footer)") } [System.String]$Base16Result = $Base16Output.ToString() $Base16ResultObject = New-Object -TypeName PSObject If ($OutFile) { [System.IO.File]::WriteAllLines($OutFile.FullName,$Base16Result,(New-Object -TypeName System.Text.UTF8Encoding $False)) $Base16ResultObject = $OutFile } Else { If ($Raw) { $Base16ResultObject = $Base16Result } Else { Add-Member -InputObject $Base16ResultObject -MemberType 'NoteProperty' -Name 'Base16EncodedData' -Value $Base16Result } } Return ($Base16ResultObject) } Catch { Write-Error "Exception: $($_.Exception.Message)" Break } Finally { $BinaryReader.Close() $BinaryReader.Dispose() $InputStream.Close() $InputStream.Dispose() $Timer.Stop() [System.String]$TimeLapse = "Base16 encode completed after $($Timer.Elapsed.Hours) hours, $($Timer.Elapsed.Minutes) minutes, $($Timer.Elapsed.Seconds) seconds, $($Timer.Elapsed.Milliseconds) milliseconds" Write-Verbose $TimeLapse } } } Function ConvertFrom-Base16() { <# .SYNOPSIS A PowerShell function to convert an arbitrary Base16 encoded string into a byte array or binary file. .DESCRIPTION Takes a string of Base16 formatted data and decodes into the original ASCII string or byte array. Input includes a Base16 string or a file containing Base16 string. Both formatted (line breaks) and unformatted Base16 data are supported. The default input and output type if positional parameters are used is [System.String]; it is also possible to write a binary file from the Base16 input using -OutFile. .PARAMETER Base16EncodedString [System.String] object containing Base16 encoded data. Accepts pipeline input. .PARAMETER InFile [System.IO.Fileinfo] object containing the details of a file on disk to be loaded as a string object and decoded from Base16 string; accepts pipeline input. .PARAMETER OutFile Optional [System.IO.Fileinfo] object containing the details of the new file to write to disk containing Base16 decoded data from the input file. Can be used with any input mode (Bytes, String, or InFile); file content will be raw decoded bytes. .PARAMETER OutBytes Return the decoded data as [System.Byte[]] to the console instead of the default ASCII string. .PARAMETER AutoSave When paired with -InFile, automatically create an output filename of in the form of the original file name plus the suffix specified after the parameter; for example, -AutoSave "BIN" will result in OutFile name <InFile>.bin. Useful if piping the output of Get-ChildItem to the function to convert files as a bulk operation. Cannot be used with input methods other than -InFile. .PARAMETER Raw Optional switch parameter that when present will produce raw output instead of a PSObject. Depending on the parameters used, the return object could be of type [System.String] or [System.Byte[]]. .INPUTS Any single object, array or collection of strings or files (such as those from Get-ChildItem) can be piped to the function for processing from Base16 encoded data. Input data from file is always processed as ASCII text regardless of source file text encoding. .OUTPUTS In the case of direct string input, a [System.String] containing the decoded data as ASCII text is returned within a PSObject with a single member named Base16DecodedData. If any input method is used with -OutFile or -InFile is used with -AutoSave, the output is a [System.IO.FileInfo] object containing details of a binary file with the Base16 decoded data as contents. If -OutBytes is specified, data is returned to the console as [System.Byte[]] wrapped in a PSObject. If -Raw is specified, the [System.String] or [System.Byte[]] is not wrapped in a PSObject and is returned directly. This means that output using -Raw cannot easily use the pipeline. The -Verbose parameter will return the function's total execution time. .EXAMPLE Pipe an object (string or array of strings, file info or array of file info objects) to the function for decoding from Base16: $MyObject | ConvertFrom-Base16 .EXAMPLE Pipe the results of a directory listing from Get-ChildItem and generate a new Base16 decoded file for each input file: Get-ChildItem C:\Text\*.B16 | ConvertFrom-Base16 -AutoSave "BIN" .EXAMPLE Use file based input to decode an input file and output the results as new file C:\Text\file.txt: ConvertFrom-Base16 -File C:\Text\file.B16 -OutFile C:\Text\file.txt .NOTES This function uses the built-in [System.Convert]::ToByte() method to convert bytes to Base 16 (hex); more information on the Base16, Base16, and Base16 Data Encoding standard can be found on the IETF web site: https://tools.ietf.org/html/rfc4648 #> [CmdletBinding( SupportsShouldProcess=$True, ConfirmImpact="High", DefaultParameterSetName="StringInput" )] [OutputType([System.Management.Automation.PSObject])] Param( [Parameter( ParameterSetName="StringInput", ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True, Mandatory=$True, Position=0, HelpMessage='Base16 encoded string.' )] [ValidateNotNullOrEmpty()] [Alias('String','Plaintext','Text','Base16EncodedData')] [System.String]$Base16EncodedString, [Parameter( ParameterSetName="FileInput", ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True, Mandatory=$True,Position=0, HelpMessage='File to Base16 decode.' )] [ValidateNotNullOrEmpty()] [Alias('Filename','FullName')] [ValidateScript({ If (-Not($_ | Test-Path -PathType Leaf)) { throw ("Invalid input file name specified.") }Else{ $True } })] [ValidateScript({ Try { $_.Open([System.IO.FileMode]::Open,[System.IO.FileAccess]::Read,[System.IO.FileShare]::None).Close() $True } Catch { throw ("Input file is locked for reading or could not obtain read access.") } })] [System.IO.Fileinfo]$InFile, [Parameter( ParameterSetName="FileInput", ValueFromPipeline=$False, ValueFromPipelineByPropertyName=$False, Mandatory=$False, Position=1, HelpMessage='Path to output file when decoding in file mode.' )] [ValidateNotNullOrEmpty()] [System.IO.Fileinfo]$OutFile, [Parameter( ParameterSetName="ByteInput", ValueFromPipeline=$False, ValueFromPipelineByPropertyName=$False, Mandatory=$False, HelpMessage='Output decoded data as raw bytes instead of ASCII text.' )] [Parameter( ParameterSetName="StringInput" )] [Parameter( ParameterSetName="FileInput" )] [ValidateNotNullOrEmpty()] [Switch]$OutBytes, [Parameter( ParameterSetName="FileInput", ValueFromPipeline=$False, ValueFromPipelineByPropertyName=$False, Mandatory=$False, HelpMessage='When in file input mode, automatically select output file name using the specified suffix as the file extension; not valid with any other input mode (String or Bytes).' )] [ValidateNotNullOrEmpty()] [ValidateScript({ If (-Not(($_.IndexOfAny([System.IO.Path]::GetInvalidFileNameChars()) -eq -1))) { throw ("AutoSave suffix contains illegal characters.") } Else { $True } })] [System.String]$AutoSave, [Parameter( ParameterSetName="StringInput", ValueFromPipeline=$False, ValueFromPipelineByPropertyName=$False, Mandatory=$False, HelpMessage='When returning to console, return a raw byte array instead of PSObject.' )] [Parameter( ParameterSetName="FileInput" )] [ValidateNotNullOrEmpty()] [Switch]$Raw ) BEGIN { If ($PSBoundParameters.ContainsKey("AutoSave") -and $PSCmdlet.ParameterSetName -ne "FileInput") { Write-Error "-AutoSave can only be used in file input mode." -ErrorAction Stop } [System.String]$B16CHARSET_Pattern = "^[A-F0-9 ]*$" [System.String]$B16Header = "-----BEGIN BASE16 ENCODED DATA-----" [System.String]$B16Footer = "-----END BASE16 ENCODED DATA-----" } PROCESS { If ($PSBoundParameters.ContainsKey('InFile') -and $PSBoundParameters.ContainsKey('AutoSave')) { $OutFile = ($InFile.FullName.ToString()) + ".$($AutoSave)" } If ($OutFile) { If ((Test-Path $OutFile -PathType Leaf) -and ($PSCmdlet.ShouldProcess($OutFile,'Overwrite'))) { Remove-Item $OutFile -Confirm:$False } If (Test-Path $OutFile -PathType Leaf) { Write-Error "Could not overwrite existing output file '$($Outfile)'" -ErrorAction Stop } $Null = New-Item -Path $OutFile -ItemType File } Switch ($PSCmdlet.ParameterSetName) { "StringInput" { [System.String]$Base16String = $Base16EncodedString.Replace($B16Header,"").Replace($B16Footer,"").Replace(" ","").Replace("`r`n","").Replace("`n","") Break } "FileInput" { [System.String]$Base16String = ([System.IO.File]::ReadAllText($InFile.FullName)).Replace($B16Header,"").Replace($B16Footer,"").Replace(" ","").Replace("`r`n","").Replace("`n","") Break } } If (-not ($Base16String -imatch $B16CHARSET_Pattern)) { Throw "Invalid Base16 data encountered." } [System.Object]$Timer = [System.Diagnostics.Stopwatch]::StartNew() Try { [System.Byte[]]$B16Results = [System.Byte[]]::new($Base16String.Length / 2) For($i = 0; $i -lt $Base16String.Length; $i += 2){ $B16Results[$i / 2] = [System.Convert]::ToByte($Base16String.Substring($i, 2), 16) } $ResultObject = New-Object -TypeName PSObject If ($OutFile) { [System.IO.File]::WriteAllBytes($OutFile,$B16Results) $ResultObject = $OutFile } Else { If ($OutBytes -and $Raw) { $ResultObject = $B16Results } ElseIf ($OutBytes) { Add-Member -InputObject $ResultObject -MemberType 'NoteProperty' -Name 'Base16DecodedData' -Value $B16Results } ElseIf ($Raw) { [System.String]$Results = [System.Text.Encoding]::ASCII.GetString($B16Results) $ResultObject = $Results } Else { [System.String]$Results = [System.Text.Encoding]::ASCII.GetString($B16Results) Add-Member -InputObject $ResultObject -MemberType 'NoteProperty' -Name 'Base16DecodedString' -Value $Results } } Return ($ResultObject) } Catch { Write-Error "Exception: $($_.Exception.Message)" Break } Finally { $Timer.Stop() [System.String]$TimeLapse = "Base16 decode completed after $($Timer.Elapsed.Hours) hours, $($Timer.Elapsed.Minutes) minutes, $($Timer.Elapsed.Seconds) seconds, $($Timer.Elapsed.Milliseconds) milliseconds" Write-Verbose $TimeLapse } } } Function ConvertTo-Base32() { <# .SYNOPSIS A PowerShell function to convert arbitrary data into a Base32 encoded string. .DESCRIPTION Takes a string, byte array or file object as input and returns a Base32 encoded string or location of the Base32 result output file object. The default input and output type if positional parameters are used is [System.String]. .PARAMETER Bytes [System.Byte[]] object containing a byte array to be encoded as Base32 string. Accepts pipeline input. .PARAMETER String [System.String] object containing plain text to be encoded as Base32 string. Accepts pipeline input. .PARAMETER InFile [System.IO.Fileinfo] object containing the details of a file on disk to be converted to Base32 string and output as a new file; output files are written as UTF-8 no BOM. Accepts pipeline input. .PARAMETER OutFile Optional [System.IO.Fileinfo] object containing the details of the new file to write to disk containing Base32 encoded data from the input file. Can be used with any input mode (Bytes, String, or InFile). .PARAMETER Unormatted By default the function adds line breaks to output string every 64 characters and block style header / footer (-----BEGIN BASE32 ENCODED DATA-----/-----END BASE32 ENCODED DATA-----); this parameter suppresses formatting and returns the Base32 string result as a single, unbroken string object with no header or footer. .PARAMETER Base32Hex Use the alternative charset described in RFC4648 for "Base32 Hex" (0123456789ABCDEFGHIJKLMNOPQRSTUV) instead of the typical Base32 charset (ABCDEFGHIJKLMNOPQRSTUVWXYZ234567). .PARAMETER AutoSave [System.String] containing a new file extension to use to automatically generate files. When paired with -InFile, automatically create an output filename of in the form of the original file name plus the suffix specified after the parameter, for example -AutoSave "B32" would create the OutFile name <InFile>.b32. Useful if piping the output of Get-ChildItem to the function to convert files as a bulk operation. Cannot be used with input methods other than -InFile. .PARAMETER Raw Optional switch parameter that when present will produce raw string output instead of a PSObject. This parameter limits the functionality of the pipeline but is convenient for simple encoding operations. .INPUTS Any single object or collection of strings, bytes, or files (such as those from Get-ChildItem) can be piped to the function for processing into Base32 encoded data. .OUTPUTS The output is always an ASCII string; if any input method is used with -OutFile or -InFile is used with -AutoSave, the output is a [System.IO.FileInfo] object containing details of a UTF8 no BOM text file with the Base32 encoded data as contents. Unless -Unformatted is specified, the console or file string data is formatted with block headers (-----BEGIN BASE32 ENCODED DATA-----/-----END BASE32 ENCODED DATA-----) and line breaks are added every 64 character. If -Unformatted is present, the output is a [System.String] with no line breaks or header / footer. If outputting to the console, the string is returned within a PSObject with a single member named Base32EncodedData as [System.String]; if -Raw is specified, the [System.String] is not wrapped in a PSObject and returned directly. This means that output using -Raw cannot easily use the pipeline, but makes it a useful option for quick encoding operations. The -Verbose parameter will return the function's total execution time. .EXAMPLE Convert a string directly into Base32: ConvertTo-Base32 "This is a plaintext string" .EXAMPLE Pipe an object (string or array of strings, byte array or array of byte arrays, file info or array of file info objects) to the function for encoding as Base32: $MyObject | ConvertTo-Base32 .EXAMPLE Convert a byte array to Base32 and return the output with block formatting and not wrapped in a PSObject (as a raw [System.String]): ConvertTo-Base32 -ByteArray $Bytes -Raw .EXAMPLE Load the contents of a file as byte array and convert directly into Base32-Hex: ConvertTo-Base32 -Base32Hex -ByteArray ([System.IO.File]::ReadAllBytes('C:\File.txt')) .EXAMPLE Pipe the results of a directory listing from Get-ChildItem and generate a new Base32 encoded file with block formatting for each input file: Get-ChildItem C:\Text\*.txt | ConvertTo-Base32 -AutoSave "B32" .EXAMPLE Use file based input to Base32 encode an input file and output the results as new file C:\Text\base32.txt with no line breaks or header / footer: ConvertTo-Base32 -File C:\Text\file.txt -OutFile C:\Text\base32.txt -Unformatted .NOTES More information on the Base16, Base32, and Base64 Data Encoding standard can be found on the IETF web site: https://tools.ietf.org/html/rfc4648 #> [CmdletBinding( SupportsShouldProcess=$True, ConfirmImpact="High", DefaultParameterSetName="StringInput" )] [OutputType([System.Management.Automation.PSObject])] Param( [Parameter( ParameterSetName="ByteInput", ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True, Mandatory=$True, Position=0, HelpMessage='Byte array to Base32 encode.' )] [ValidateNotNullOrEmpty()] [Alias('ByteArray','Data')] [System.Byte[]]$Bytes, [Parameter( ParameterSetName="StringInput", ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True, Mandatory=$True, Position=0, HelpMessage='String to Base32 encode.' )] [ValidateNotNullOrEmpty()] [Alias('Plaintext','Text')] [System.String]$String, [Parameter( ParameterSetName="FileInput", ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True, Mandatory=$True,Position=0, HelpMessage='File to Base32 encode.' )] [ValidateNotNullOrEmpty()] [Alias('Filename','FullName','File')] [ValidateScript({ If (-Not($_ | Test-Path -PathType Leaf)) { throw ("Invalid input file name specified.") }Else{ $True } })] [ValidateScript({ Try { $_.Open([System.IO.FileMode]::Open,[System.IO.FileAccess]::Read,[System.IO.FileShare]::None).Close() $True } Catch { throw ("Input file is locked for reading or could not obtain read access.") } })] [System.IO.Fileinfo]$InFile, [Parameter( ParameterSetName="StringInput", ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True, Mandatory=$False, Position=1, HelpMessage='Output result to specified file as UTF8-NoBOM text instead of console.' )] [Parameter( ParameterSetName="ByteInput" )] [Parameter( ParameterSetName="FileInput" )] [ValidateNotNullOrEmpty()] [System.IO.Fileinfo]$OutFile, [Parameter( ParameterSetName="ByteInput", ValueFromPipeline=$False, ValueFromPipelineByPropertyName=$False, Mandatory=$False, HelpMessage='Do not format output string using header/footer and line breaks.' )] [Parameter( ParameterSetName="StringInput" )] [Parameter( ParameterSetName="FileInput" )] [ValidateNotNullOrEmpty()] [Switch]$Unformatted, [Parameter( ParameterSetName="ByteInput", ValueFromPipeline=$False, ValueFromPipelineByPropertyName=$False, Mandatory=$False, HelpMessage='Use extended Base32 Hex charset instead of standard Base32 charset.' )] [Parameter( ParameterSetName="StringInput" )] [Parameter( ParameterSetName="FileInput" )] [ValidateNotNullOrEmpty()] [Switch]$Base32Hex, [Parameter( ParameterSetName="FileInput", ValueFromPipeline=$False, ValueFromPipelineByPropertyName=$False, Mandatory=$False, HelpMessage='When in file input mode, automatically select output file name using the specified suffix as the file extension; not valid with any other input mode (String or Bytes).' )] [ValidateNotNullOrEmpty()] [ValidateScript({ If (-Not(($_.IndexOfAny([System.IO.Path]::GetInvalidFileNameChars()) -eq -1))) { throw ("AutoSave suffix contains illegal characters.") } Else { $True } })] [System.String]$AutoSave, [Parameter( ParameterSetName="ByteInput", ValueFromPipeline=$False, ValueFromPipelineByPropertyName=$False, Mandatory=$False, HelpMessage='When returning a string instead of a file, return a raw string instead of PSObject; applies to both console and file output modes.' )] [Parameter( ParameterSetName="StringInput" )] [Parameter( ParameterSetName="FileInput" )] [ValidateNotNullOrEmpty()] [Switch]$Raw ) BEGIN { If ($Base32Hex) { [System.String]$B32CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUV" [System.String]$B32Name = "Base32-Hex" } Else { [System.String]$B32CHARSET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567" [System.String]$B32Name = "Base32" } [System.String]$B32Header = "-----BEGIN $($B32Name.ToUpper()) ENCODED DATA-----" [System.String]$B32Footer = "-----END $($B32Name.ToUpper()) ENCODED DATA-----" } PROCESS { If ($PSBoundParameters.ContainsKey('InFile') -and $PSBoundParameters.ContainsKey('AutoSave')) { $OutFile = ($InFile.FullName.ToString()) + ".$($AutoSave)" } If ($OutFile) { If ((Test-Path $OutFile -PathType Leaf) -and ($PSCmdlet.ShouldProcess($OutFile,'Overwrite'))) { Remove-Item $OutFile -Confirm:$False } If (Test-Path $OutFile -PathType Leaf) { Write-Error "Could not overwrite existing output file '$($Outfile)'" -ErrorAction Stop } $Null = New-Item -Path $OutFile -ItemType File If ($Raw) { Write-Warning "File output mode specified; Parameter '-Raw' will be ignored." } } Switch ($PSCmdlet.ParameterSetName) { "ByteInput" { [System.IO.Stream]$InputStream = New-Object -TypeName System.IO.MemoryStream(,$Bytes) Break } "StringInput" { [System.IO.Stream]$InputStream = New-Object -TypeName System.IO.MemoryStream(,[System.Text.Encoding]::ASCII.GetBytes($String)) Break } "FileInput" { [System.IO.Stream]$InputStream = [System.IO.File]::Open($InFile.FullName,[System.IO.FileMode]::Open,[System.IO.FileAccess]::Read,[System.IO.FileShare]::ReadWrite) Break } } [System.Object]$Timer = [System.Diagnostics.Stopwatch]::StartNew() [System.Object]$BinaryReader = New-Object -TypeName System.IO.BinaryReader($InputStream) [System.Object]$Base32Output = New-Object -TypeName System.Text.StringBuilder If (-Not $Unformatted) { [void]$Base32Output.Append("$($B32Header)`r`n") } Try { While ([System.Byte[]]$BytesRead = $BinaryReader.ReadBytes(5)) { [System.Boolean]$AtEnd = ($BinaryReader.BaseStream.Length -eq $BinaryReader.BaseStream.Position) [System.UInt16]$ByteLength = $BytesRead.Length If ($ByteLength -lt 5) { [System.Byte[]]$WorkingBytes = ,0x00 * 5 [System.Buffer]::BlockCopy($BytesRead,0,$WorkingBytes,0,$ByteLength) [System.Array]::Resize([ref]$BytesRead,5) [System.Buffer]::BlockCopy($WorkingBytes,0,$BytesRead,0,5) } [System.Char[]]$B32Chars = ,0x00 * 8 [System.Char[]]$B32Chunk = ,"=" * 8 $B32Chars[0] = ($B32CHARSET[($BytesRead[0] -band 0xF8) -shr 3]) $B32Chars[1] = ($B32CHARSET[(($BytesRead[0] -band 0x07) -shl 2) -bor (($BytesRead[1] -band 0xC0) -shr 6)]) $B32Chars[2] = ($B32CHARSET[($BytesRead[1] -band 0x3E) -shr 1]) $B32Chars[3] = ($B32CHARSET[(($BytesRead[1] -band 0x01) -shl 4) -bor (($BytesRead[2] -band 0xF0) -shr 4)]) $B32Chars[4] = ($B32CHARSET[(($BytesRead[2] -band 0x0F) -shl 1) -bor (($BytesRead[3] -band 0x80) -shr 7)]) $B32Chars[5] = ($B32CHARSET[($BytesRead[3] -band 0x7C) -shr 2]) $B32Chars[6] = ($B32CHARSET[(($BytesRead[3] -band 0x03) -shl 3) -bor (($BytesRead[4] -band 0xE0) -shr 5)]) $B32Chars[7] = ($B32CHARSET[$BytesRead[4] -band 0x1F]) [System.Array]::Copy($B32Chars,$B32Chunk,([Math]::Ceiling(($ByteLength / 5) * 8))) If ($BinaryReader.BaseStream.Position % 8 -eq 0 -and -Not $Unformatted -and -not $AtEnd) { [void]$Base32Output.Append($B32Chunk) [void]$Base32Output.Append("`r`n") } Else { [void]$Base32Output.Append($B32Chunk) } } If (-Not $Unformatted) { [void]$Base32Output.Append("`r`n$($B32Footer)") } [System.String]$Base32Result = $Base32Output.ToString() $Base32ResultObject = New-Object -TypeName PSObject If ($OutFile) { [System.IO.File]::WriteAllLines($OutFile.FullName,$Base32Result,(New-Object -TypeName System.Text.UTF8Encoding $False)) $Base32ResultObject = $OutFile } Else { If ($Raw) { $Base32ResultObject = $Base32Result } Else { Add-Member -InputObject $Base32ResultObject -MemberType 'NoteProperty' -Name 'Base32EncodedData' -Value $Base32Result } } Return ($Base32ResultObject) } Catch { Write-Error "Exception: $($_.Exception.Message)" Break } Finally { $BinaryReader.Close() $BinaryReader.Dispose() $InputStream.Close() $InputStream.Dispose() $Timer.Stop() [System.String]$TimeLapse = "Base32 encode completed after $($Timer.Elapsed.Hours) hours, $($Timer.Elapsed.Minutes) minutes, $($Timer.Elapsed.Seconds) seconds, $($Timer.Elapsed.Milliseconds) milliseconds" Write-Verbose $TimeLapse } } } Function ConvertFrom-Base32() { <# .SYNOPSIS A PowerShell function to convert an arbitrary Base32 encoded string into a byte array or binary file. .DESCRIPTION Takes a string of Base32 formatted data and decodes into the original ASCII string or byte array. Input includes a Base32 string or a file containing Base32 string. Both formatted (line breaks) and unformatted Base32 data are supported. The default input and output type if positional parameters are used is [System.String]; it is also possible to write a binary file from the Base32 input using -OutFile. .PARAMETER Base32EncodedString [System.String] object containing Base32 encoded data. Accepts pipeline input. .PARAMETER InFile [System.IO.Fileinfo] object containing the details of a file on disk to be loaded as a string object and decoded from Base32 string; accepts pipeline input. .PARAMETER OutFile Optional [System.IO.Fileinfo] object containing the details of the new file to write to disk containing Base32 decoded data from the input file. Can be used with any input mode (Bytes, String, or InFile); file content will be raw decoded bytes. .PARAMETER Base32Hex Use the alternative charset described in RFC4648 for "Base32 Hex" (0123456789ABCDEFGHIJKLMNOPQRSTUV) instead of the typical Base32 charset (ABCDEFGHIJKLMNOPQRSTUVWXYZ234567) when decoding. .PARAMETER OutBytes Return the decoded data as [System.Byte[]] to the console instead of the default ASCII string. .PARAMETER AutoSave When paired with -InFile, automatically create an output filename of in the form of the original file name plus the suffix specified after the parameter; for example, -AutoSave "BIN" will result in OutFile name <InFile>.bin. Useful if piping the output of Get-ChildItem to the function to convert files as a bulk operation. Cannot be used with input methods other than -InFile. .PARAMETER Raw Optional switch parameter that when present will produce raw output instead of a PSObject. Depending on the parameters used, the return object could be of type [System.String] or [System.Byte[]]. .INPUTS Any single object, array or collection of strings or files (such as those from Get-ChildItem) can be piped to the function for processing from Base32 encoded data. Input data from file is always processed as ASCII text regardless of source file text encoding. .OUTPUTS In the case of direct string input, a [System.String] containing the decoded data as ASCII text is returned within a PSObject with a single member named Base32DecodedData. If any input method is used with -OutFile or -InFile is used with -AutoSave, the output is a [System.IO.FileInfo] object containing details of a binary file with the Base32 decoded data as contents. If -OutBytes is specified, data is returned to the console as [System.Byte[]] wrapped in a PSObject. If -Raw is specified, the [System.String] or [System.Byte[]] is not wrapped in a PSObject and is returned directly. This means that output using -Raw cannot easily use the pipeline. The -Verbose parameter will return the function's total execution time. .EXAMPLE Convert a Base32 string to a decoded byte array: [System.Byte[]]$Bytes = ConvertFrom-Base32 "IIAGCADTABSQAMYAGIAA====" -OutBytes -Raw .EXAMPLE Decode a Base32 encoded string: ConvertFrom-Base32 -Base32EncodedString "IIAGCADTABSQAMYAGIAA====" .EXAMPLE Pipe an object (string or array of strings, file info or array of file info objects) to the function for decoding from Base32: $MyObject | ConvertFrom-Base32 .EXAMPLE Pipe the results of a directory listing from Get-ChildItem and generate a new Base32 decoded file for each input file: Get-ChildItem C:\Text\*.b32 | ConvertFrom-Base32 -AutoSave "BIN" .EXAMPLE Use file based input to decode an input file and output the results as new file C:\Text\file.txt: ConvertFrom-Base32 -File C:\Text\file.b32 -OutFile C:\Text\file.txt .NOTES More information on the Base16, Base32, and Base64 Data Encoding standard can be found on the IETF web site: https://tools.ietf.org/html/rfc4648 #> [CmdletBinding( SupportsShouldProcess=$True, ConfirmImpact="High", DefaultParameterSetName="StringInput" )] [OutputType([System.Management.Automation.PSObject])] Param( [Parameter( ParameterSetName="StringInput", ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True, Mandatory=$True, Position=0, HelpMessage='Base32 encoded string.' )] [ValidateNotNullOrEmpty()] [Alias('String','Plaintext','Text','Base32EncodedData')] [System.String]$Base32EncodedString, [Parameter( ParameterSetName="FileInput", ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True, Mandatory=$True,Position=0, HelpMessage='File to Base32 decode.' )] [ValidateNotNullOrEmpty()] [Alias('Filename','FullName')] [ValidateScript({ If (-Not($_ | Test-Path -PathType Leaf)) { throw ("Invalid input file name specified.") }Else{ $True } })] [ValidateScript({ Try { $_.Open([System.IO.FileMode]::Open,[System.IO.FileAccess]::Read,[System.IO.FileShare]::None).Close() $True } Catch { throw ("Input file is locked for reading or could not obtain read access.") } })] [System.IO.Fileinfo]$InFile, [Parameter( ParameterSetName="FileInput", ValueFromPipeline=$False, ValueFromPipelineByPropertyName=$False, Mandatory=$False, Position=1, HelpMessage='Path to output file when decoding in file mode.' )] [ValidateNotNullOrEmpty()] [System.IO.Fileinfo]$OutFile, [Parameter( ParameterSetName="ByteInput", ValueFromPipeline=$False, ValueFromPipelineByPropertyName=$False, Mandatory=$False, HelpMessage='Use extended Base32 Hex charset instead of standard Base32 charset.' )] [Parameter( ParameterSetName="StringInput" )] [Parameter( ParameterSetName="FileInput" )] [ValidateNotNullOrEmpty()] [Switch]$Base32Hex, [Parameter( ParameterSetName="ByteInput", ValueFromPipeline=$False, ValueFromPipelineByPropertyName=$False, Mandatory=$False, HelpMessage='Output decoded data as raw bytes instead of ASCII text.' )] [Parameter( ParameterSetName="StringInput" )] [Parameter( ParameterSetName="FileInput" )] [ValidateNotNullOrEmpty()] [Switch]$OutBytes, [Parameter( ParameterSetName="FileInput", ValueFromPipeline=$False, ValueFromPipelineByPropertyName=$False, Mandatory=$False, HelpMessage='When in file input mode, automatically select output file name using the specified suffix as the file extension; not valid with any other input mode (String or Bytes).' )] [ValidateNotNullOrEmpty()] [ValidateScript({ If (-Not(($_.IndexOfAny([System.IO.Path]::GetInvalidFileNameChars()) -eq -1))) { throw ("AutoSave suffix contains illegal characters.") } Else { $True } })] [System.String]$AutoSave, [Parameter( ParameterSetName="StringInput", ValueFromPipeline=$False, ValueFromPipelineByPropertyName=$False, Mandatory=$False, HelpMessage='When returning to console, return a raw byte array instead of PSObject.' )] [Parameter( ParameterSetName="FileInput" )] [ValidateNotNullOrEmpty()] [Switch]$Raw ) BEGIN { If ($PSBoundParameters.ContainsKey("AutoSave") -and $PSCmdlet.ParameterSetName -ne "FileInput") { Write-Error "-AutoSave can only be used in file input mode." -ErrorAction Stop } If ($Base32Hex) { [System.String]$B32CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUV" [System.String]$B32CHARSET_Pattern = "^[A-V0-9 ]+=*$" [System.String]$B32Name = "Base32-Hex" } Else { [System.String]$B32CHARSET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567" [System.String]$B32CHARSET_Pattern = "^[A-Z2-7 ]+=*$" [System.String]$B32Name = "Base32" } [System.String]$B32Header = "-----BEGIN $($B32Name.ToUpper()) ENCODED DATA-----" [System.String]$B32Footer = "-----END $($B32Name.ToUpper()) ENCODED DATA-----" } PROCESS { If ($PSBoundParameters.ContainsKey('InFile') -and $PSBoundParameters.ContainsKey('AutoSave')) { $OutFile = ($InFile.FullName.ToString()) + ".$($AutoSave)" } If ($OutFile) { If ((Test-Path $OutFile -PathType Leaf) -and ($PSCmdlet.ShouldProcess($OutFile,'Overwrite'))) { Remove-Item $OutFile -Confirm:$False } If (Test-Path $OutFile -PathType Leaf) { Write-Error "Could not overwrite existing output file '$($Outfile)'" -ErrorAction Stop } $Null = New-Item -Path $OutFile -ItemType File } Switch ($PSCmdlet.ParameterSetName) { "StringInput" { [System.String]$Base32String = $Base32EncodedString.Replace($B32Header,"").Replace($B32Footer,"").Replace(" ","").Replace("`r`n","").Replace("`n","").ToUpper() Break } "FileInput" { [System.String]$Base32String = ([System.IO.File]::ReadAllText($InFile.FullName)).Replace($B32Header,"").Replace($B32Footer,"").Replace(" ","").Replace("`r`n","").Replace("`n","").ToUpper() Break } } If (-not ($Base32String -match $B32CHARSET_Pattern)) { Throw ("Invalid Base32 data encountered in input stream.") } [System.Object]$Timer = [System.Diagnostics.Stopwatch]::StartNew() [System.Object]$InputStream = New-Object -TypeName System.IO.MemoryStream([System.Text.Encoding]::ASCII.GetBytes($Base32String),0,$Base32String.Length) [System.Object]$BinaryReader = New-Object -TypeName System.IO.BinaryReader($InputStream) [System.Object]$OutputStream = New-Object -TypeName System.IO.MemoryStream [System.Object]$BinaryWriter = New-Object -TypeName System.IO.BinaryWriter($OutputStream) Try { While ([System.Char[]]$CharsRead = $BinaryReader.ReadChars(8)) { [System.Byte[]]$B32Bytes = ,0x00 * 5 [System.UInt16]$CharLen = 8 - ($CharsRead -Match "=").Count [System.UInt16]$ByteLen = [Math]::Floor(($CharLen * 5) / 8) [System.Byte[]]$BinChunk = ,0x00 * $ByteLen If ($CharLen -lt 8) { [System.Char[]]$WorkingChars = ,"A" * 8 [System.Array]::Copy($CharsRead,$WorkingChars,$CharLen) [System.Array]::Resize([ref]$CharsRead,8) [System.Array]::Copy($WorkingChars,$CharsRead,8) } $B32Bytes[0] = (($B32CHARSET.IndexOf($CharsRead[0]) -band 0x1F) -shl 3) -bor (($B32CHARSET.IndexOf($CharsRead[1]) -band 0x1C) -shr 2) $B32Bytes[1] = (($B32CHARSET.IndexOf($CharsRead[1]) -band 0x03) -shl 6) -bor (($B32CHARSET.IndexOf($CharsRead[2]) -band 0x1F) -shl 1) -bor (($B32CHARSET.IndexOf($CharsRead[3]) -band 0x10) -shr 4) $B32Bytes[2] = (($B32CHARSET.IndexOf($CharsRead[3]) -band 0x0F) -shl 4) -bor (($B32CHARSET.IndexOf($CharsRead[4]) -band 0x1E) -shr 1) $B32Bytes[3] = (($B32CHARSET.IndexOf($CharsRead[4]) -band 0x01) -shl 7) -bor (($B32CHARSET.IndexOf($CharsRead[5]) -band 0x1F) -shl 2) -bor (($B32CHARSET.IndexOf($CharsRead[6]) -band 0x18) -shr 3) $B32Bytes[4] = (($B32CHARSET.IndexOf($CharsRead[6]) -band 0x07) -shl 5) -bor ($B32CHARSET.IndexOf($CharsRead[7]) -band 0x1F) [System.Buffer]::BlockCopy($B32Bytes, 0, $BinChunk, 0, $ByteLen) $BinaryWriter.Write($BinChunk) } $ResultObject = New-Object -TypeName PSObject If ($OutFile) { [System.IO.File]::WriteAllBytes($OutFile,($OutputStream.ToArray())) $ResultObject = $OutFile } Else { If ($OutBytes -and $Raw) { $ResultObject = $OutputStream.ToArray() } ElseIf ($OutBytes) { Add-Member -InputObject $ResultObject -MemberType 'NoteProperty' -Name 'ByteArray' -Value $OutputStream.ToArray() } ElseIf ($Raw) { [System.String]$Results = [System.Text.Encoding]::ASCII.GetString(($OutputStream.ToArray())) $ResultObject = $Results } Else { [System.String]$Results = [System.Text.Encoding]::ASCII.GetString(($OutputStream.ToArray())) Add-Member -InputObject $ResultObject -MemberType 'NoteProperty' -Name 'Base32DecodedString' -Value $Results } } Return ($ResultObject) } Catch { Write-Error "Exception: $($_.Exception.Message)" Break } Finally { $BinaryReader.Close() $BinaryReader.Dispose() $BinaryWriter.Close() $BinaryWriter.Dispose() $InputStream.Close() $InputStream.Dispose() $OutputStream.Close() $OutputStream.Dispose() $Timer.Stop() [System.String]$TimeLapse = "Base32 decode completed after $($Timer.Elapsed.Hours) hours, $($Timer.Elapsed.Minutes) minutes, $($Timer.Elapsed.Seconds) seconds, $($Timer.Elapsed.Milliseconds) milliseconds" Write-Verbose $TimeLapse } } } Function ConvertTo-Base64() { <# .SYNOPSIS A PowerShell function to convert arbitrary data into a Base64 encoded string. .DESCRIPTION Takes a string, byte array or file object as input and returns a Base64 encoded string or location of the Base64 result output file object. The default input and output type if positional parameters are used is [System.String]. .PARAMETER Bytes [System.Byte[]] object containing a byte array to be encoded as Base64 string. Accepts pipeline input. .PARAMETER String [System.String] object containing plain text to be encoded as Base64 string. Accepts pipeline input. .PARAMETER InFile [System.IO.Fileinfo] object containing the details of a file on disk to be converted to Base64 string and output as a new file; output files are written as UTF-8 no BOM. Accepts pipeline input. .PARAMETER OutFile Optional [System.IO.Fileinfo] object containing the details of the new file to write to disk containing Base64 encoded data from the input file. Can be used with any input mode (Bytes, String, or InFile). .PARAMETER Unormatted By default the function adds line breaks to output string every 64 characters and block style header / footer (-----BEGIN BASE64 ENCODED DATA-----/-----END BASE64 ENCODED DATA-----); this parameter suppresses formatting and returns the Base64 string result as a single, unbroken string object with no header or footer. .PARAMETER AutoSave [System.String] containing a new file extension to use to automatically generate files. When paired with -InFile, automatically create an output filename of in the form of the original file name plus the suffix specified after the parameter, for example -AutoSave "B64" would create the OutFile name <InFile>.B64. Useful if piping the output of Get-ChildItem to the function to convert files as a bulk operation. Cannot be used with input methods other than -InFile. .PARAMETER Raw Optional switch parameter that when present will produce raw string output instead of a PSObject. This parameter limits the functionality of the pipeline but is convenient for simple encoding operations. .INPUTS Any single object or collection of strings, bytes, or files (such as those from Get-ChildItem) can be piped to the function for processing into Base64 encoded data. .OUTPUTS The output is always an ASCII string; if any input method is used with -OutFile or -InFile is used with -AutoSave, the output is a [System.IO.FileInfo] object containing details of a UTF8 no BOM text file with the Base64 encoded data as contents. Unless -Unformatted is specified, the console or file string data is formatted with block headers (-----BEGIN BASE64 ENCODED DATA-----/-----END BASE64 ENCODED DATA-----) and line breaks are added every 64 character. If -Unformatted is present, the output is a [System.String] with no line breaks or header / footer. If outputting to the console, the string is returned within a PSObject with a single member named Base64EncodedData as [System.String]; if -Raw is specified, the [System.String] is not wrapped in a PSObject and returned directly. This means that output using -Raw cannot easily use the pipeline, but makes it a useful option for quick encoding operations. The -Verbose parameter will return the function's total execution time. .EXAMPLE Convert a string directly into Base64: ConvertTo-Base64 "This is a plaintext string" .EXAMPLE Pipe an object (string or array of strings, byte array or array of byte arrays, file info or array of file info objects) to the function for encoding as Base64: $MyObject | ConvertTo-Base64 .EXAMPLE Convert a byte array to Base64 and return the output with block formatting and not wrapped in a PSObject (as a raw [System.String]): ConvertTo-Base64 -ByteArray $Bytes -Raw .EXAMPLE Load the contents of a file as byte array and convert directly into Base64-Hex: ConvertTo-Base64 -Base64Hex -ByteArray ([System.IO.File]::ReadAllBytes('C:\File.txt')) .EXAMPLE Pipe the results of a directory listing from Get-ChildItem and generate a new Base64 encoded file with block formatting for each input file: Get-ChildItem C:\Text\*.txt | ConvertTo-Base64 -AutoSave "B64" .EXAMPLE Use file based input to Base64 encode an input file and output the results as new file C:\Text\Base64.txt with no line breaks or header / footer: ConvertTo-Base64 -File C:\Text\file.txt -OutFile C:\Text\Base64.txt -Unformatted .NOTES This function uses the built-in [System.Convert]::ToBase64String() method so it is obscenely fast compared to the hand-rolled methods used in other functions from this module. More information on the Base16, Base64, and Base64 Data Encoding standard can be found on the IETF web site: https://tools.ietf.org/html/rfc4648 #> [CmdletBinding( SupportsShouldProcess=$True, ConfirmImpact="High", DefaultParameterSetName="StringInput" )] [OutputType([System.Management.Automation.PSObject])] Param( [Parameter( ParameterSetName="ByteInput", ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True, Mandatory=$True, Position=0, HelpMessage='Byte array to Base64 encode.' )] [ValidateNotNullOrEmpty()] [Alias('ByteArray','Data')] [System.Byte[]]$Bytes, [Parameter( ParameterSetName="StringInput", ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True, Mandatory=$True, Position=0, HelpMessage='String to Base64 encode.' )] [ValidateNotNullOrEmpty()] [Alias('Plaintext','Text')] [System.String]$String, [Parameter( ParameterSetName="FileInput", ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True, Mandatory=$True,Position=0, HelpMessage='File to Base64 encode.' )] [ValidateNotNullOrEmpty()] [Alias('Filename','FullName','File')] [ValidateScript({ If (-Not($_ | Test-Path -PathType Leaf)) { throw ("Invalid input file name specified.") }Else{ $True } })] [ValidateScript({ Try { $_.Open([System.IO.FileMode]::Open,[System.IO.FileAccess]::Read,[System.IO.FileShare]::None).Close() $True } Catch { throw ("Input file is locked for reading or could not obtain read access.") } })] [System.IO.Fileinfo]$InFile, [Parameter( ParameterSetName="StringInput", ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True, Mandatory=$False, Position=1, HelpMessage='Output result to specified file as UTF8-NoBOM text instead of console.' )] [Parameter( ParameterSetName="ByteInput" )] [Parameter( ParameterSetName="FileInput" )] [ValidateNotNullOrEmpty()] [System.IO.Fileinfo]$OutFile, [Parameter( ParameterSetName="ByteInput", ValueFromPipeline=$False, ValueFromPipelineByPropertyName=$False, Mandatory=$False, HelpMessage='Do not format output string using header/footer and line breaks.' )] [Parameter( ParameterSetName="StringInput" )] [Parameter( ParameterSetName="FileInput" )] [ValidateNotNullOrEmpty()] [Switch]$Unformatted, [Parameter( ParameterSetName="FileInput", ValueFromPipeline=$False, ValueFromPipelineByPropertyName=$False, Mandatory=$False, HelpMessage='When in file input mode, automatically select output file name using the specified suffix as the file extension; not valid with any other input mode (String or Bytes).' )] [ValidateNotNullOrEmpty()] [ValidateScript({ If (-Not(($_.IndexOfAny([System.IO.Path]::GetInvalidFileNameChars()) -eq -1))) { throw ("AutoSave suffix contains illegal characters.") } Else { $True } })] [System.String]$AutoSave, [Parameter( ParameterSetName="ByteInput", ValueFromPipeline=$False, ValueFromPipelineByPropertyName=$False, Mandatory=$False, HelpMessage='When returning a string instead of a file, return a raw string instead of PSObject; applies to both console and file output modes.' )] [Parameter( ParameterSetName="StringInput" )] [Parameter( ParameterSetName="FileInput" )] [ValidateNotNullOrEmpty()] [Switch]$Raw ) BEGIN { [System.String]$B64Header = "-----BEGIN BASE64 ENCODED DATA-----" [System.String]$B64Footer = "-----END BASE64 ENCODED DATA-----" } PROCESS { If ($PSBoundParameters.ContainsKey('InFile') -and $PSBoundParameters.ContainsKey('AutoSave')) { $OutFile = ($InFile.FullName.ToString()) + ".$($AutoSave)" } If ($OutFile) { If ((Test-Path $OutFile -PathType Leaf) -and ($PSCmdlet.ShouldProcess($OutFile,'Overwrite'))) { Remove-Item $OutFile -Confirm:$False } If (Test-Path $OutFile -PathType Leaf) { Write-Error "Could not overwrite existing output file '$($Outfile)'" -ErrorAction Stop } $Null = New-Item -Path $OutFile -ItemType File If ($Raw) { Write-Warning "File output mode specified; Parameter '-Raw' will be ignored." } } Switch ($PSCmdlet.ParameterSetName) { "ByteInput" { [System.IO.Stream]$InputStream = New-Object -TypeName System.IO.MemoryStream(,$Bytes) Break } "StringInput" { [System.IO.Stream]$InputStream = New-Object -TypeName System.IO.MemoryStream(,[System.Text.Encoding]::ASCII.GetBytes($String)) Break } "FileInput" { [System.IO.Stream]$InputStream = [System.IO.File]::Open($InFile.FullName,[System.IO.FileMode]::Open,[System.IO.FileAccess]::Read,[System.IO.FileShare]::ReadWrite) Break } } [System.Object]$Timer = [System.Diagnostics.Stopwatch]::StartNew() [System.Object]$BinaryReader = New-Object -TypeName System.IO.BinaryReader($InputStream) [System.Object]$Base64Output = New-Object -TypeName System.Text.StringBuilder If (-Not $Unformatted) { [void]$Base64Output.Append("$($B64Header)`r`n") } [System.String]$B64Line = [String]::Empty Try { While ([System.Byte[]]$BytesRead = $BinaryReader.ReadBytes(3)) { [System.Boolean]$AtEnd = ($BinaryReader.BaseStream.Length -eq $BinaryReader.BaseStream.Position) $B64Line += [System.Convert]::ToBase64String($BytesRead) If ($B64Line.Length -eq 64 -and -not $Unformatted) { [void]$Base64Output.Append($B64Line + "`r`n") [System.String]$B64Line = [String]::Empty } ElseIf ($AtEnd) { [void]$Base64Output.Append($B64Line) } } If (-Not $Unformatted) { [void]$Base64Output.Append("`r`n$($B64Footer)") } [System.String]$Base64Result = $Base64Output.ToString() $Base64ResultObject = New-Object -TypeName PSObject If ($OutFile) { [System.IO.File]::WriteAllLines($OutFile.FullName,$Base64Result,(New-Object -TypeName System.Text.UTF8Encoding $False)) $Base64ResultObject = $OutFile } Else { If ($Raw) { $Base64ResultObject = $Base64Result } Else { Add-Member -InputObject $Base64ResultObject -MemberType 'NoteProperty' -Name 'Base64EncodedData' -Value $Base64Result } } Return ($Base64ResultObject) } Catch { Write-Error "Exception: $($_.Exception.Message)" Break } Finally { $BinaryReader.Close() $BinaryReader.Dispose() $InputStream.Close() $InputStream.Dispose() $Timer.Stop() [System.String]$TimeLapse = "Base64 encode completed after $($Timer.Elapsed.Hours) hours, $($Timer.Elapsed.Minutes) minutes, $($Timer.Elapsed.Seconds) seconds, $($Timer.Elapsed.Milliseconds) milliseconds" Write-Verbose $TimeLapse } } } Function ConvertFrom-Base64() { <# .SYNOPSIS A PowerShell function to convert an arbitrary Base64 encoded string into a byte array or binary file. .DESCRIPTION Takes a string of Base64 formatted data and decodes into the original ASCII string or byte array. Input includes a Base64 string or a file containing Base64 string. Both formatted (line breaks) and unformatted Base64 data are supported. The default input and output type if positional parameters are used is [System.String]; it is also possible to write a binary file from the Base64 input using -OutFile. .PARAMETER Base64EncodedString [System.String] object containing Base64 encoded data. Accepts pipeline input. .PARAMETER InFile [System.IO.Fileinfo] object containing the details of a file on disk to be loaded as a string object and decoded from Base64 string; accepts pipeline input. .PARAMETER OutFile Optional [System.IO.Fileinfo] object containing the details of the new file to write to disk containing Base64 decoded data from the input file. Can be used with any input mode (Bytes, String, or InFile); file content will be raw decoded bytes. .PARAMETER OutBytes Return the decoded data as [System.Byte[]] to the console instead of the default ASCII string. .PARAMETER AutoSave When paired with -InFile, automatically create an output filename of in the form of the original file name plus the suffix specified after the parameter; for example, -AutoSave "BIN" will result in OutFile name <InFile>.bin. Useful if piping the output of Get-ChildItem to the function to convert files as a bulk operation. Cannot be used with input methods other than -InFile. .PARAMETER Raw Optional switch parameter that when present will produce raw output instead of a PSObject. Depending on the parameters used, the return object could be of type [System.String] or [System.Byte[]]. .INPUTS Any single object, array or collection of strings or files (such as those from Get-ChildItem) can be piped to the function for processing from Base64 encoded data. Input data from file is always processed as ASCII text regardless of source file text encoding. .OUTPUTS In the case of direct string input, a [System.String] containing the decoded data as ASCII text is returned within a PSObject with a single member named Base64DecodedData. If any input method is used with -OutFile or -InFile is used with -AutoSave, the output is a [System.IO.FileInfo] object containing details of a binary file with the Base64 decoded data as contents. If -OutBytes is specified, data is returned to the console as [System.Byte[]] wrapped in a PSObject. If -Raw is specified, the [System.String] or [System.Byte[]] is not wrapped in a PSObject and is returned directly. This means that output using -Raw cannot easily use the pipeline. The -Verbose parameter will return the function's total execution time. .EXAMPLE Convert a Base64 string to a decoded byte array: [System.Byte[]]$Bytes = ConvertFrom-Base64 "VGhpcyBpcyBCYXNlNjQgdGV4dA==" -OutBytes -Raw .EXAMPLE Decode a Base64 encoded string: ConvertFrom-Base64 -Base64EncodedString "VGhpcyBpcyBCYXNlNjQgdGV4dA==" .EXAMPLE Pipe an object (string or array of strings, file info or array of file info objects) to the function for decoding from Base64: $MyObject | ConvertFrom-Base64 .EXAMPLE Pipe the results of a directory listing from Get-ChildItem and generate a new Base64 decoded file for each input file: Get-ChildItem C:\Text\*.B64 | ConvertFrom-Base64 -AutoSave "BIN" .EXAMPLE Use file based input to decode an input file and output the results as new file C:\Text\file.txt: ConvertFrom-Base64 -File C:\Text\file.B64 -OutFile C:\Text\file.txt .NOTES This function uses the built-in [System.Convert]::FromBase64String() method so it is obscenely fast compared to the hand-rolled methods used in other functions from this module. More information on the Base16, Base64, and Base64 Data Encoding standard can be found on the IETF web site: https://tools.ietf.org/html/rfc4648 #> [CmdletBinding( SupportsShouldProcess=$True, ConfirmImpact="High", DefaultParameterSetName="StringInput" )] [OutputType([System.Management.Automation.PSObject])] Param( [Parameter( ParameterSetName="StringInput", ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True, Mandatory=$True, Position=0, HelpMessage='Base64 encoded string.' )] [ValidateNotNullOrEmpty()] [Alias('String','Plaintext','Text','Base64EncodedData')] [System.String]$Base64EncodedString, [Parameter( ParameterSetName="FileInput", ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True, Mandatory=$True,Position=0, HelpMessage='File to Base64 decode.' )] [ValidateNotNullOrEmpty()] [Alias('Filename','FullName')] [ValidateScript({ If (-Not($_ | Test-Path -PathType Leaf)) { throw ("Invalid input file name specified.") }Else{ $True } })] [ValidateScript({ Try { $_.Open([System.IO.FileMode]::Open,[System.IO.FileAccess]::Read,[System.IO.FileShare]::None).Close() $True } Catch { throw ("Input file is locked for reading or could not obtain read access.") } })] [System.IO.Fileinfo]$InFile, [Parameter( ParameterSetName="FileInput", ValueFromPipeline=$False, ValueFromPipelineByPropertyName=$False, Mandatory=$False, Position=1, HelpMessage='Path to output file when decoding in file mode.' )] [ValidateNotNullOrEmpty()] [System.IO.Fileinfo]$OutFile, [Parameter( ParameterSetName="ByteInput", ValueFromPipeline=$False, ValueFromPipelineByPropertyName=$False, Mandatory=$False, HelpMessage='Output decoded data as raw bytes instead of ASCII text.' )] [Parameter( ParameterSetName="StringInput" )] [Parameter( ParameterSetName="FileInput" )] [ValidateNotNullOrEmpty()] [Switch]$OutBytes, [Parameter( ParameterSetName="FileInput", ValueFromPipeline=$False, ValueFromPipelineByPropertyName=$False, Mandatory=$False, HelpMessage='When in file input mode, automatically select output file name using the specified suffix as the file extension; not valid with any other input mode (String or Bytes).' )] [ValidateNotNullOrEmpty()] [ValidateScript({ If (-Not(($_.IndexOfAny([System.IO.Path]::GetInvalidFileNameChars()) -eq -1))) { throw ("AutoSave suffix contains illegal characters.") } Else { $True } })] [System.String]$AutoSave, [Parameter( ParameterSetName="StringInput", ValueFromPipeline=$False, ValueFromPipelineByPropertyName=$False, Mandatory=$False, HelpMessage='When returning to console, return a raw byte array instead of PSObject.' )] [Parameter( ParameterSetName="FileInput" )] [ValidateNotNullOrEmpty()] [Switch]$Raw ) BEGIN { If ($PSBoundParameters.ContainsKey("AutoSave") -and $PSCmdlet.ParameterSetName -ne "FileInput") { Write-Error "-AutoSave can only be used in file input mode." -ErrorAction Stop } [System.String]$B64CHARSET_Pattern = "^[a-zA-Z0-9/+]*={0,2}$" [System.String]$B64Header = "-----BEGIN BASE64 ENCODED DATA-----" [System.String]$B64Footer = "-----END BASE64 ENCODED DATA-----" } PROCESS { If ($PSBoundParameters.ContainsKey('InFile') -and $PSBoundParameters.ContainsKey('AutoSave')) { $OutFile = ($InFile.FullName.ToString()) + ".$($AutoSave)" } If ($OutFile) { If ((Test-Path $OutFile -PathType Leaf) -and ($PSCmdlet.ShouldProcess($OutFile,'Overwrite'))) { Remove-Item $OutFile -Confirm:$False } If (Test-Path $OutFile -PathType Leaf) { Write-Error "Could not overwrite existing output file '$($Outfile)'" -ErrorAction Stop } $Null = New-Item -Path $OutFile -ItemType File } Switch ($PSCmdlet.ParameterSetName) { "StringInput" { [System.String]$Base64String = $Base64EncodedString.Replace($B64Header,"").Replace($B64Footer,"").Replace(" ","").Replace("`r`n","").Replace("`n","") Break } "FileInput" { [System.String]$Base64String = ([System.IO.File]::ReadAllText($InFile.FullName)).Replace($B64Header,"").Replace($B64Footer,"").Replace(" ","").Replace("`r`n","").Replace("`n","") Break } } If (-not ($Base64String -imatch $B64CHARSET_Pattern)) { Throw "Invalid Base64 data encountered." Return } [System.Object]$Timer = [System.Diagnostics.Stopwatch]::StartNew() Try { [System.Byte[]]$B64Results = [System.Byte[]][Convert]::FromBase64String($Base64String) $ResultObject = New-Object -TypeName PSObject If ($OutFile) { [System.IO.File]::WriteAllBytes($OutFile,$B64Results) $ResultObject = $OutFile } Else { If ($OutBytes -and $Raw) { $ResultObject = $B64Results } ElseIf ($OutBytes) { Add-Member -InputObject $ResultObject -MemberType 'NoteProperty' -Name 'Base64DecodedData' -Value $B64Results } ElseIf ($Raw) { [System.String]$Results = [System.Text.Encoding]::ASCII.GetString($B64Results) $ResultObject = $Results } Else { [System.String]$Results = [System.Text.Encoding]::ASCII.GetString($B64Results) Add-Member -InputObject $ResultObject -MemberType 'NoteProperty' -Name 'Base64DecodedString' -Value $Results } } Return ($ResultObject) } Catch { Write-Error "Exception: $($_.Exception.Message)" Break } Finally { $Timer.Stop() [System.String]$TimeLapse = "Base64 decode completed after $($Timer.Elapsed.Hours) hours, $($Timer.Elapsed.Minutes) minutes, $($Timer.Elapsed.Seconds) seconds, $($Timer.Elapsed.Milliseconds) milliseconds" Write-Verbose $TimeLapse } } } Function ConvertTo-UUEncoding() { <# .SYNOPSIS A PowerShell function to convert an arbitrary byte array into a UNIX-to-UNIX (UU) encoded string. .DESCRIPTION Takes a string, byte array or file as input and returns a UUencoded string or file info for a new UUencoded copy of the input file as a result. The default input and output type if positional parameters are used is [System.String]. .PARAMETER Bytes [System.Byte[]] object containing a byte array to be UUencoded. Accepts pipeline input. .PARAMETER String [System.String] object containing plain text to be UUencoded. Accepts pipeline input. .PARAMETER InFile [System.IO.Fileinfo] object containing the details of a file on disk to be converted to UUencoded string and output as a new file; output files are written as UTF-8 no BOM. Accepts pipeline input. .PARAMETER OutFile Optional [System.IO.Fileinfo] object containing the details of the new file to write to disk containing UUencoded data from the input file. Can be used with any input mode (Bytes, String, or InFile). .PARAMETER AutoSave [System.String] containing a new file extension to use to automatically generate files. When paired with -InFile, automatically create an output filename of in the form of the original file name plus the suffix specified after the parameter. For example, -AutoSave "UU" will produce OutFile name <InFile>.UU. Useful if piping the output of Get-ChildItem to the function to convert files as a bulk operation. Cannot be used with input methods other than -InFile. .PARAMETER Unformatted When the -Unformatted switch is specified, the returned uuencoded string has no header, footer, line breaks or byte length indicators; a single unbroken [System.String] is returned instead. Using this option will produce output that is considered invalid by most uudecode applications and such data cannot be decoded by utilities like the Linux uuencode binary. However, unformatted uuencoded strings can still be successfully decoded by this module's ConvertFrom-UUEncoding function. .PARAMETER Raw Optional switch parameter that when present will produce raw string output instead of a PSObject. This parameter limits the functionality of the pipeline but is convenient for simple encoding operations within a script. .INPUTS Any single object or collection of strings, bytes, or files (such as those from Get-ChildItem) can be piped to the function for processing into UUencoded data. .OUTPUTS The output is always an ASCII string; if any input method is used with -OutFile or -InFile is used with -AutoSave, the output is a [System.IO.FileInfo] object containing details of a UTF8 no BOM text file with the UUencoded data as contents. String data is formatted with uuencode-compliant header, footer, line length and line breaks - it is possible to decode UUencoded data from this function using the Linux UUdecode utility. The function adds required uuencoding header row (begin nnnn filename) and ending row (end); the function also adds a line break every 60 characters per the specification, and a UUencoded value containing the line length is also inserted at the beginning of each line for a total of 61 characters per line on full line input. This format is required by the specification and is not mutable. If outputting to the console, the string is returned within a PSObject as [System.String] with a single member named UUEncodedData; if -Raw is specified, the [System.String] is not wrapped in a PSObject and returned directly. This means that output using -Raw cannot easily use the pipeline, but makes it a useful option for quick encoding operations. The -Verbose parameter will return the function's total execution time. .EXAMPLE Convert a string directly into UUencoded string: ConvertTo-UUEncoding "This is a plaintext string" -Raw .EXAMPLE Pipe an object (string or array of strings, byte array or array of byte arrays, file info or array of file info objects) to the function for uuencoding: $MyObject | ConvertTo-UUEncoding .EXAMPLE Convert a byte array to UUencoded string and return the output with block formatting and not wrapped in a PSObject (as a raw [System.String]): ConvertTo-UUEncoding -ByteArray $Bytes -Raw .EXAMPLE Load the contents of a file as byte array and convert directly UUencoded string: ConvertTo-UUEncoding -ByteArray ([System.IO.File]::ReadAllBytes('C:\File.txt')) .EXAMPLE Pipe the results of a directory listing from Get-ChildItem and generate a new UUencoded file with block formatting for each input file: Get-ChildItem C:\Text\*.txt | ConvertTo-UUEncoding -AutoSave "UU" .EXAMPLE Use file based input to uuencode an input file and output the results as new file C:\Text\UU.txt: ConvertTo-UUEncoding -File C:\Text\file.txt -OutFile C:\Text\UU.txt .NOTES UNIX-style line breaks (\n) are used as opposed to Windows-style line breaks (\r\n) on output string data; this is to maintain compatibility with Linux UUdecode utilities. This function is really geared more toward encoding / decoding files vs. string or pipeline input. UUencoding in general has been largely replaced by Base64, which does not suffer from the same technical limitations. #> [CmdletBinding( SupportsShouldProcess=$True, ConfirmImpact="High", DefaultParameterSetName="StringInput" )] [OutputType([System.Management.Automation.PSObject])] Param( [Parameter( ParameterSetName="ByteInput", ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True, Mandatory=$True, Position=0, HelpMessage='Byte array to uuencode.' )] [ValidateNotNullOrEmpty()] [Alias('ByteArray','Data')] [System.Byte[]]$Bytes, [Parameter( ParameterSetName="StringInput", ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True, Mandatory=$True, Position=0, HelpMessage='String to uuencode.' )] [ValidateNotNullOrEmpty()] [Alias('Plaintext','Text')] [System.String]$String, [Parameter( ParameterSetName="FileInput", ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True, Mandatory=$True,Position=0, HelpMessage='File to uuencode' )] [ValidateNotNullOrEmpty()] [Alias('Filename','FullName','File')] [ValidateScript({ If (-Not($_ | Test-Path -PathType Leaf)) { throw ("Invalid input file name specified.") }Else{ $True } })] [ValidateScript({ Try { $_.Open([System.IO.FileMode]::Open,[System.IO.FileAccess]::Read,[System.IO.FileShare]::None).Close() $True } Catch { throw ("Input file is locked for reading or could not obtain read access.") } })] [System.IO.Fileinfo]$InFile, [Parameter( ParameterSetName="StringInput", ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True, Mandatory=$False, Position=1, HelpMessage='Output result to file instead of console.' )] [Parameter( ParameterSetName="ByteInput" )] [Parameter( ParameterSetName="FileInput" )] [ValidateNotNullOrEmpty()] [System.IO.Fileinfo]$OutFile, [Parameter( ParameterSetName="FileInput", ValueFromPipeline=$False, ValueFromPipelineByPropertyName=$False, Mandatory=$False, HelpMessage='When in file input mode, automatically select output file name using the specified suffix as the file extension; not valid with any other input mode (String or Bytes).' )] [ValidateNotNullOrEmpty()] [ValidateScript({ If (-Not(($_.IndexOfAny([System.IO.Path]::GetInvalidFileNameChars()) -eq -1))) { throw ("AutoSave suffix contains illegal characters.") } Else { $True } })] [System.String]$AutoSave, [Parameter( ParameterSetName="ByteInput", ValueFromPipeline=$False, ValueFromPipelineByPropertyName=$False, Mandatory=$False, HelpMessage='Do not format output string using header/footer and line breaks.' )] [Parameter( ParameterSetName="StringInput" )] [Parameter( ParameterSetName="FileInput" )] [ValidateNotNullOrEmpty()] [Switch]$Unformatted, [Parameter( ParameterSetName="ByteInput", ValueFromPipeline=$False, ValueFromPipelineByPropertyName=$False, Mandatory=$False, HelpMessage='When returning a string instead of a file, return a raw string instead of PSObject.' )] [Parameter( ParameterSetName="StringInput" )] [Parameter( ParameterSetName="FileInput" )] [ValidateNotNullOrEmpty()] [Switch]$Raw ) BEGIN { [System.String]$OFS = "" } PROCESS { If ($PSBoundParameters.ContainsKey('InFile') -and $PSBoundParameters.ContainsKey('AutoSave')) { $OutFile = ($InFile.FullName.ToString()) + ".$($AutoSave)" } If ($OutFile) { If ((Test-Path $OutFile -PathType Leaf) -and ($PSCmdlet.ShouldProcess($OutFile,'Overwrite'))) { Remove-Item $OutFile -Confirm:$False } If (Test-Path $OutFile -PathType Leaf) { Write-Error "Could not overwrite existing output file '$($Outfile)'" -ErrorAction Stop } $Null = New-Item -Path $OutFile -ItemType File If ($Raw) { Write-Warning "File output mode specified; Parameter '-Raw' will be ignored." } } Switch ($PSCmdlet.ParameterSetName) { "ByteInput" { [System.String]$UUName = "byteinput" [System.IO.Stream]$InputStream = New-Object -TypeName System.IO.MemoryStream(,$Bytes) Break } "StringInput" { [System.String]$UUName = "stringinput" [System.IO.Stream]$InputStream = New-Object -TypeName System.IO.MemoryStream(,[System.Text.Encoding]::ASCII.GetBytes($String)) Break } "FileInput" { [System.String]$UUName = $InFile.Name.ToString() [System.IO.Stream]$InputStream = [System.IO.File]::Open($InFile.FullName,[System.IO.FileMode]::Open,[System.IO.FileAccess]::Read,[System.IO.FileShare]::ReadWrite) Break } } [System.Object]$Timer = [System.Diagnostics.Stopwatch]::StartNew() [System.Object]$BinaryReader = New-Object -TypeName System.IO.BinaryReader($InputStream) [System.String]$UULine = [String]::Empty [System.Object]$UUOutput = New-Object -TypeName System.Text.StringBuilder If (-Not $Unformatted) { [void]$UUOutput.Append("begin 0744 $($UUName)`n") } Try { While ([System.Byte[]]$BytesRead = $BinaryReader.ReadBytes(3)) { [System.Boolean]$AtEnd = ($BinaryReader.BaseStream.Length -eq $BinaryReader.BaseStream.Position) [System.UInt16]$ByteLength = $BytesRead.Length If ($ByteLength -lt 3) { [System.Byte[]]$WorkingBytes = ,0x01 * 3 [System.Buffer]::BlockCopy($BytesRead,0,$WorkingBytes,0,$ByteLength) [System.Array]::Resize([ref]$BytesRead,3) [System.Buffer]::BlockCopy($WorkingBytes,0,$BytesRead,0,3) } [System.Char[]]$UUChars = ,0x00 * 4 [System.Char[]]$UUChunk = ,0x00 * 4 $UUChars[0] = [System.Text.Encoding]::ASCII.GetChars((($BytesRead[0] -band 0xFC) -shr 2) + 32)[0] $UUChars[1] = [System.Text.Encoding]::ASCII.GetChars(((($BytesRead[0] -band 0x03) -shl 4 ) -bor (($BytesRead[1] -band 0xF0) -shr 4 )) + 32)[0] $UUChars[2] = [System.Text.Encoding]::ASCII.GetChars(((($BytesRead[1] -band 0x0F) -shl 2 ) -bor (($BytesRead[2] -band 0xC0) -shr 6 )) + 32)[0] $UUChars[3] = [System.Text.Encoding]::ASCII.GetChars(($BytesRead[2] -band 0x3F) + 32)[0] [System.Array]::Copy($UUChars,$UUChunk,([Math]::Ceiling(($ByteLength / 3) * 4))) $UULine += $UUChunk If ($UULine.Length -eq 60) { Switch ($Unformatted) { $False { [void]$UUOutput.Append(([System.Text.Encoding]::ASCII.GetChars((($UULine.Length / 4) * 3) + 32))[0] + $UULine + "`n") [System.String]$UULine = [String]::Empty Break } $True { [void]$UUOutput.Append($UULine) [System.String]$UULine = [String]::Empty Break } } } If ($AtEnd) { [System.UInt16]$TrimEnd = 3 - $ByteLength Switch ($Unformatted) { $False { [void]$UUOutput.Append(([System.Text.Encoding]::ASCII.GetChars(((($UULine.Length / 4) * 3) - $TrimEnd) + 32))[0] + $UULine.SubString(0,($UULine.Length - $TrimEnd))) Break } $True { [void]$UUOutput.Append($UULine.SubString(0,($UULine.Length - $TrimEnd))) Break } } } } If (-Not $Unformatted) { [void]$UUOutput.Append("`n```nend") } [System.String]$UUResult = $UUOutput.ToString() $UUResultObject = New-Object -TypeName PSObject If ($OutFile) { [System.IO.File]::WriteAllLines($OutFile.FullName,$UUResult,(New-Object -TypeName System.Text.UTF8Encoding $False)) $UUResultObject = $OutFile } Else { If ($Raw) { $UUResultObject = $UUResult } Else { Add-Member -InputObject $UUResultObject -MemberType 'NoteProperty' -Name 'UUEncodedData' -Value $UUResult } } Return ($UUResultObject) } Catch { Write-Error "Exception: $($_.Exception.Message)" Break } Finally { $BinaryReader.Close() $BinaryReader.Dispose() $InputStream.Close() $InputStream.Dispose() $Timer.Stop() [System.String]$TimeLapse = "UUencode completed after $($Timer.Elapsed.Hours) hours, $($Timer.Elapsed.Minutes) minutes, $($Timer.Elapsed.Seconds) seconds, $($Timer.Elapsed.Milliseconds) milliseconds" Write-Verbose $TimeLapse } } } Function ConvertFrom-UUEncoding() { <# .SYNOPSIS A PowerShell function to convert UUencoded string data into a byte array or binary file. .DESCRIPTION Takes a string of UUencoded data and decodes into the original ASCII string or byte array. Input includes a UUencoded string or a file containing UUencoded data. The default input and output type if positional parameters are used is [System.String]. It is also possible to write a binary file using -OutFile. .PARAMETER UUEncodedString [System.String] object containing UUencoded data. Accepts pipeline input. .PARAMETER InFile [System.IO.Fileinfo] object containing the details of a file on disk to be loaded as a string object and UUdecoded; accepts pipeline input. .PARAMETER OutFile Optional [System.IO.Fileinfo] object containing the details of the new file to write to disk containing UUencoded data from the input file. Can be used with any input mode (Bytes, String, or InFile); file content will be raw decoded bytes. .PARAMETER OutBytes Return the decoded data as [System.Byte[]] to the console instead of the default ASCII string. .PARAMETER Auto If the input data contains the header row, automatically extrapolates the destination file name. If -Auto is specified in file output mode and the input data does not contain a header row or is not formatted and -AutoSave or -OutFile are not present, the function will return an error. .PARAMETER AutoSave When paired with -InFile, automatically create an output filename of in the form of the original file name plus the suffix specified after the parameter. For example, -AutoSave "UUDEC" will produce OutFile name <InFile>.UUDEC. Useful if piping the output of Get-ChildItem to the function to convert files as a bulk operation. Cannot be used with input methods other than -InFile. .PARAMETER Raw Optional switch parameter that when present will produce raw output instead of a PSObject. Depending on the parameters used, the return object could be of type [System.String] or [System.Byte[]]. .INPUTS Any single object, array or collection of strings or files (such as those from Get-ChildItem) can be piped to the function for processing from UUencoded data. Input data from file is always processed as ASCII text regardless of source file text encoding. .OUTPUTS In the case of direct string input, a [System.String] containing the decoded data as ASCII text is returned within a PSObject with a single member named UUDecodedData. If any input method is used with -OutFile or -InFile is used with -AutoSave, the output is a [System.IO.FileInfo] object containing details of a binary file with the decoded data as contents. If -OutBytes is specified, data is returned to the console as [System.Byte[]] wrapped in a PSObject. If -Raw is specified, the [System.String] or [System.Byte[]] is not wrapped in a PSObject and is returned directly. This means that output using -Raw cannot easily use the pipeline. The -Verbose parameter will return the function's total execution time. .EXAMPLE Pipe an object (string or array of strings, file info or array of file info objects) to the function for decoding from Base32: $MyObject | ConvertFrom-UUEncoding .EXAMPLE Pipe the results of a directory listing from Get-ChildItem and generate a new Base32 decoded file for each input file: Get-ChildItem C:\Text\*.UU | ConvertFrom-UUEncoding -AutoSave "UUDEC" .EXAMPLE Use file based input to decode an input file and output the results as new file C:\Text\file.txt: ConvertFrom-UUEncoding -File C:\Text\file.UU -OutFile C:\Text\file.txt .NOTES If the input data contains the header row, the output file name can be automatically extracted from the UUencoded data. If this row is absent and -Auto is specified, the function will return an error. This function is really geared more toward encoding / decoding files vs. string or pipeline input; it is really just for historical reference as UUencoding was superseded by Base64 encoding which is technically superior. #> [CmdletBinding( SupportsShouldProcess=$True, ConfirmImpact="High", DefaultParameterSetName="StringInput" )] [OutputType([System.Management.Automation.PSObject])] Param( [Parameter( ParameterSetName="StringInput", ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True, Mandatory=$True, Position=0, HelpMessage='Input uuencoded string.' )] [ValidateNotNullOrEmpty()] [Alias('String','Plaintext','Text','UUEncodedData')] [System.String]$UUEncodedString, [Parameter( ParameterSetName="FileInput", ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True, Mandatory=$True,Position=0, HelpMessage='File to uudecode' )] [ValidateNotNullOrEmpty()] [Alias('Filename','FullName')] [ValidateScript({ If (-Not($_ | Test-Path -PathType Leaf)) { throw ("Invalid input file name specified.") }Else{ $True } })] [ValidateScript({ Try { $_.Open([System.IO.FileMode]::Open,[System.IO.FileAccess]::Read,[System.IO.FileShare]::None).Close() $True } Catch { throw ("Input file is locked for reading or could not obtain read access.") } })] [System.IO.Fileinfo]$InFile, [Parameter( ParameterSetName="FileInput", ValueFromPipeline=$False, ValueFromPipelineByPropertyName=$False, Mandatory=$False, Position=1, HelpMessage='Path to output file when decoding in file mode.' )] [ValidateNotNullOrEmpty()] [System.IO.Fileinfo]$OutFile, [Parameter( ParameterSetName="ByteInput", ValueFromPipeline=$False, ValueFromPipelineByPropertyName=$False, Mandatory=$False, HelpMessage='Output decoded data as raw bytes instead of ASCII text.' )] [Parameter( ParameterSetName="StringInput" )] [Parameter( ParameterSetName="FileInput" )] [ValidateNotNullOrEmpty()] [Switch]$OutBytes, [Parameter( ParameterSetName="ByteInput", ValueFromPipeline=$False, ValueFromPipelineByPropertyName=$False, Mandatory=$False, HelpMessage='Determine the output file name from the uuencoded data header; only applies to file mode. Can be mixed with -AutoSave or -OutFile when using pipeline to extract the file name if it exists, and rely on -AutoSave / -OutFile if it does not.' )] [Parameter( ParameterSetName="StringInput" )] [Parameter( ParameterSetName="FileInput" )] [ValidateNotNullOrEmpty()] [Switch]$Auto, [Parameter( ParameterSetName="FileInput", ValueFromPipeline=$False, ValueFromPipelineByPropertyName=$False, Mandatory=$False, HelpMessage='When in file input mode, automatically select output file name using the specified suffix as the file extension; not valid with any other input mode (String or Bytes).' )] [ValidateNotNullOrEmpty()] [ValidateScript({ If (-Not(($_.IndexOfAny([System.IO.Path]::GetInvalidFileNameChars()) -eq -1))) { throw ("AutoSave suffix contains illegal characters.") } Else { $True } })] [System.String]$AutoSave, [Parameter( ParameterSetName="StringInput", ValueFromPipeline=$False, ValueFromPipelineByPropertyName=$False, Mandatory=$False, HelpMessage='When returning to console, return a raw byte array instead of PSObject.' )] [Parameter( ParameterSetName="FileInput" )] [ValidateNotNullOrEmpty()] [Switch]$Raw ) BEGIN { If ($PSBoundParameters.ContainsKey("AutoSave") -and $PSCmdlet.ParameterSetName -ne "FileInput") { Write-Error "-AutoSave can only be used in file input mode." -ErrorAction Stop } [System.String]$NON_UU_Pattern = "[^\u0000-\u007F“”]" [System.String]$OFS = "" } PROCESS { If ($PSBoundParameters.ContainsKey('InFile') -and $PSBoundParameters.ContainsKey('AutoSave')) { $OutFile = ($InFile.FullName.ToString()) + ".$($AutoSave)" } Switch ($PSCmdlet.ParameterSetName) { "StringInput" { [System.Object]$InputStream = New-Object -TypeName System.IO.MemoryStream(,[System.Text.Encoding]::ASCII.GetBytes($UUEncodedString)) [System.IO.StreamReader]$StreamReader = New-Object -TypeName System.IO.StreamReader($InputStream) Break } "FileInput" { [System.Object]$InputStream = [System.IO.File]::Open($InFile.FullName,[System.IO.FileMode]::Open,[System.IO.FileAccess]::Read,[System.IO.FileShare]::ReadWrite) [System.IO.StreamReader]$StreamReader = New-Object -TypeName System.IO.StreamReader($InputStream) Break } } [System.Boolean]$Unformatted = $False [System.Object]$OutputStream = New-Object -TypeName System.IO.MemoryStream [System.Object]$BinaryWriter = New-Object -TypeName System.IO.BinaryWriter($OutputStream) [System.Char[]]$UUHeaderProbe = ,0x00 * 5 Try { [void]$StreamReader.Read($UUHeaderProbe,0,5) If (($UUHeaderProbe -join "").ToLower() -ne "begin") { [System.Char[]]$UUProbe = ,0x00 * 65535 [System.UInt16]$UUProbeLength = $StreamReader.Read($UUProbe,0,65535) [System.String]$UUProbeLine = ($UUProbe -join "").Substring(0,$UUProbeLength) If ($UUProbeLine -match $NON_UU_Pattern) { Throw "Invalid UUencoded data encountered first 64KB of file - file may not be UUencoded." } $Unformatted = $True } $StreamReader.BaseStream.Position = 0 $StreamReader.DiscardBufferedData() If (-Not $Unformatted) { [System.String[]]$UUHeader = ($StreamReader.ReadLine()) -split " " } Else { [System.String[]]$UUHeader = [System.String]::Empty } If ($PSBoundParameters.ContainsKey('Auto') -And $Unformatted) { $StreamReader.Close() $StreamReader.Dispose() $BinaryWriter.Close() $BinaryWriter.Dispose() $OutputStream.Close() $OutputStream.Dispose() Write-Error "The -Auto parameter was used but the input stream does not appear to contain a header row. You will need to specify -OutFile or -AutoSave in order to produce file output." -ErrorAction Stop } ElseIf ($PSBoundParameters.ContainsKey('Auto') -And -Not $Unformatted -And $UUHeader[2]) { $OutFile = Join-Path (Get-Location).path $UUHeader[2] } If ($OutFile) { If ((Test-Path $OutFile -PathType Leaf) -and ($PSCmdlet.ShouldProcess($OutFile,'Overwrite'))) { Remove-Item $OutFile -Confirm:$False } If (Test-Path $OutFile -PathType Leaf) { Write-Error "Could not overwrite existing output file '$($Outfile)'" -ErrorAction Stop } $Null = New-Item -Path $OutFile -ItemType File } [System.Object]$Timer = [System.Diagnostics.Stopwatch]::StartNew() While ([System.Char[]]$UULine = $StreamReader.ReadLine()) { If (-Not $Unformatted) { If (($UULine -join "") -eq "end" -or $UULine[0] -eq "``") { Break } } If (($UULine -join "") -match $NON_UU_Pattern) { Throw "Invalid UUencode data encountered in input stream." } [System.Object]$ProcessStream = New-Object -TypeName System.IO.MemoryStream($UULine,0,$UULine.Length) [System.Object]$BinaryReader = New-Object -TypeName System.IO.BinaryReader($ProcessStream) If (-Not $Unformatted) { [void]$BinaryReader.ReadByte() } While ([System.Byte[]]$BytesRead = $BinaryReader.ReadBytes(4)) { [System.UInt16]$ByteLength = $BytesRead.Length If ($ByteLength -lt 4) { [System.Byte[]]$WorkingBytes = ,0x00 * 4 [System.Buffer]::BlockCopy($BytesRead,0,$WorkingBytes,0,$ByteLength) [System.Array]::Resize([ref]$BytesRead,4) [System.Buffer]::BlockCopy($WorkingBytes,0,$BytesRead,0,4) } [System.Byte[]]$UUBytes = ,0x00 * 3 [System.UInt16]$ByteLen = [Math]::Floor(($ByteLength * 3) / 4) [System.Byte[]]$BinChunk = ,0x00 * $ByteLen $UUBytes[0] = ((($BytesRead[0] - 32) -band 0x3F) -shl 2) -bor ((($BytesRead[1] - 32) -band 0x30) -shr 4) $UUBytes[1] = ((($BytesRead[1] - 32) -band 0x0F) -shl 4) -bor ((($BytesRead[2] - 32) -band 0x3C) -shr 2) $UUBytes[2] = ((($BytesRead[2] - 32) -band 0x03) -shl 6) -bor (($BytesRead[3] - 32) -band 0x3F) [System.Buffer]::BlockCopy($UUBytes,0,$BinChunk,0,$ByteLen) $BinaryWriter.Write($BinChunk) } } $ResultObject = New-Object -TypeName PSObject If ($OutFile) { [System.IO.File]::WriteAllBytes($OutFile,($OutputStream.ToArray())) $ResultObject = $OutFile } Else { If ($OutBytes -and $Raw) { $ResultObject = $OutputStream.ToArray() } ElseIf ($OutBytes) { Add-Member -InputObject $ResultObject -MemberType 'NoteProperty' -Name 'UUDecodedData' -Value $OutputStream.ToArray() } ElseIf ($Raw) { [System.String]$Results = [System.Text.Encoding]::ASCII.GetString(($OutputStream.ToArray())) $ResultObject = $Results } Else { [System.String]$Results = [System.Text.Encoding]::ASCII.GetString(($OutputStream.ToArray())) Add-Member -InputObject $ResultObject -MemberType 'NoteProperty' -Name 'UUDecodedString' -Value $Results } } Return ($ResultObject) } Catch { Write-Error "Exception: $($_.Exception.Message)" Break } Finally { $BinaryWriter.Close() $BinaryWriter.Dispose() $InputStream.Close() $InputStream.Dispose() $OutputStream.Close() $OutputStream.Dispose() $StreamReader.Close() $StreamReader.Dispose() $Timer.Stop() [System.String]$TimeLapse = "UUdecode completed after $($Timer.Elapsed.Hours) hours, $($Timer.Elapsed.Minutes) minutes, $($Timer.Elapsed.Seconds) seconds, $($Timer.Elapsed.Milliseconds) milliseconds" Write-Verbose $TimeLapse } } } Export-ModuleMember -Function Get-RandomByteArray Export-ModuleMember -Function Get-CompressedByteArray Export-ModuleMember -Function Get-DecompressedByteArray Export-ModuleMember -Function ConvertTo-Ascii85 Export-ModuleMember -Function ConvertFrom-Ascii85 Export-ModuleMember -Function ConvertTo-Base16 Export-ModuleMember -Function ConvertFrom-Base16 Export-ModuleMember -Function ConvertTo-Base32 Export-ModuleMember -Function ConvertFrom-Base32 Export-ModuleMember -Function ConvertTo-Base64 Export-ModuleMember -Function ConvertFrom-Base64 Export-ModuleMember -Function ConvertTo-UUEncoding Export-ModuleMember -Function ConvertFrom-UUEncoding |