Commands/Read-OpenPackage.ps1

function Read-OpenPackage
{
    <#
    .SYNOPSIS
        Reads Open Package Bytes
    .DESCRIPTION
        Reads Bytes within an Open Package
    .LINK
        Get-OpenPackage
    .LINK
        Write-OpenPackage
    .EXAMPLE
        $package = OP @{"hello.txt" = "Hello world"}
        $package |
            Read-OpenPackage -Uri /hello.txt
    .EXAMPLE
        $package = OP @{"hello.txt" = "Hello world"}
        ($package |
            Read-OpenPackage -Uri /hello.txt -RangeStart 0 -RangeEnd 5) -as 'char[]'
    .EXAMPLE
        $package = OP @{"hello.txt" = "Hello world"}
        ($package |
            Read-OpenPackage -Uri /hello.txt -RangeStart 0 -RangeEnd 5) -as 'char[]' -join ''
    #>

    [OutputType([byte[]])]
    [Alias('Read-OP', 'rdop', 'rdOpenPackage')]
    param(
    # One or more part uris
    [Parameter(Mandatory,ValueFromPipelineByPropertyName)]
    [Alias('PartUri')]
    [Uri[]]
    $Uri,

    # A start range
    [Parameter(ValueFromPipelineByPropertyName)]
    [Alias('PartStart')]
    [long]
    $RangeStart,

    # An ending range
    [Parameter(ValueFromPipelineByPropertyName)]
    [Alias('PartEnd')]
    [long]
    $RangeEnd,

    # The input object.
    # If this is not a package, it will be passed thru.
    [Parameter(ValueFromPipeline)]
    [Alias('Package')]
    [PSObject]
    $InputObject
    )

    process {
        # If the input was not a package,
        if ($InputObject -isnot [IO.Packaging.Package]) {
            return $InputObject # pass it thru.
        }

        # Read each part in the open package.
        foreach ($partUri in $uri) {
            # Make sure we have a leading slash
            $SlashPart = "$partUri" -replace '^/?', '/'

            # If the part does not exist
            if (-not $InputObject.PartExists($SlashPart)) {
                # continue to the next part.
                continue
            }

            # Try to get the part
            $part = $InputObject.GetPart($SlashPart)
            # if that failed, continue.
            if (-not $part) { 
                continue
            }

            # If we do not have a range start and end
            if (-not $RangeStart -and -not $RangeEnd) {
                # look for a reader
                if ($part.Reader -and $part.Read) {
                    # If we found one,
                    $part.Read() # read the content
                    continue # and continue.
                }
                # Otherwise, get our stream
                $partStream = $part.GetStream('Open', 'Read')
                # copy the output to a memory stream
                $memoryStream = [IO.MemoryStream]::new()
                $partStream.CopyTo($memoryStream)
            } else {
                # If a range was provided, get our stream
                $partStream = $part.GetStream('Open', 'Read')
                # If the range was smaller than the stream length
                if ($RangeStart -lt $partStream.Length) {
                    # seek to the start of the range
                    $null = $partStream.Seek($RangeStart, 'Begin')
                    # and adjust the end if needed
                    if ($rangeEnd -ge $partStream.Length) {
                        $rangeEnd = $partStream.Length 
                    }
                    # If the total range is positive
                    if ($RangeEnd - $RangeStart -gt 0) {
                        # get the range
                        $buffer = [byte[]]::new($RangeEnd - $RangeStart)
                        # read our byte range
                        $bytesRead = $partStream.Read($buffer, 0, $buffer.Length)
                        if ($bytesRead) {
                            $memoryStream = [IO.MemoryStream]::new()
                            # and write it to the memory stream
                            $null = $memoryStream.Write($buffer,0 , $bytesRead)
                        }                                                
                    }
                    
                } else {
                    # copy the output to a memory stream
                    $memoryStream = [IO.MemoryStream]::new()
                    $partStream.CopyTo($memoryStream)
                }        
            }
            
            $partStream.Close() # Close our part stream
            $partStream.Dispose() # and dispose of it.

            # If we had a memory stream
            if ($memoryStream) {
                # get it as an array
                [byte[]]$partBytes = $memoryStream.ToArray()
                            
                $memoryStream.Close() # then close
                $memoryStream.Dispose() # and dispose our stream.
        
                # and write the byte[] as a single object.
                $PSCmdlet.WriteObject($partBytes, $false)
            }
        }
    }
}