# encoding: ascii
# api: powershell
# title: Get-ZipContent
# description: Original post at http://www.cyberforum.ru/post8426907.html
# version: 0.1
# type: function
# author: govnyakha
# license: CC0
# function: Get-ZipContent
# x-poshcode-id: 6128
# x-archived: 2016-03-19T02:53:33
# x-published: 2016-12-02T12:02:00
#
#
#requires -version 4
function Get-ZipContent {
  <#
    .SYNOPSIS
        Reads ZIP file data.
    .DESCRIPTION
        Function gets data with BinaryReader instead COM Shell.Application.
    .EXAMPLE
        PS E:\downloads> Get-ZipContent SysinternalsSuite.zip | Format-Table -a

        Crc32      Attributes Modified            Method    Path                 Packed    Size
        -----      ---------- --------            ------    ----                 ------    ----
        0x0A1AC525          0 01.11.2006 14:06:36 {Deflate} AccessEnum.exe        48346  174968
        0x5A4797E5          0 12.07.2007 6:26:48  {Deflate} AdExplorer.chm        42823   50379
        0xAFDD017F          0 14.11.2012 11:22:40 {Deflate} ADExplorer.exe       203304  479832
        ...
  #>
  param(
    [Parameter(Mandatory=$true)]
    [ValidateScript({Test-Path $_})]
    [String]$PathName
  )
  
  begin {
    $PathName = Convert-Path $Pathname
    $Methods  = @{
      Store   = 0
      Deflate = 8
      BZIP2   = 12
      LZMA    = 14
    }
  }
  process {
    try {
      $fs = [IO.File]::OpenRead($PathName)
      $br = New-Object IO.BinaryReader($fs)
      $fs.Position = $fs.Length - 22
      # locate of start of central directory
      $fs.Position += 16
      $fs.Position = $br.ReadUInt32()
      # read certral directories data
      while ($true) {
        if ($br.ReadUInt32() -ne 33639248) {break}
        $fs.Position += 6 # skip next three fields
        # compressed method and last modification datetime
        $a, $b, $c = $br.ReadUInt16(), $br.ReadUInt16(), $br.ReadUInt16()
        # crc32 and [un]compressed size(s)
        $d, $e, $f = $br.ReadUInt32(), $br.ReadUInt32(), $br.ReadUInt32()
        $g = $br.ReadUInt16() # file name length
        $h = $br.ReadUInt16() # extra field length
        $i = $br.ReadUInt16() # file comment length
        $fs.Position += 4     # skip next two fields
        $j = $br.ReadUInt32() # external file attributes
        $fs.Position += 4     # skip next field
        New-Object PSObject -Property @{
          Path = -join $br.ReadChars($g)
          Attributes = [IO.FileAttributes]$j
          Crc32 = "0x$($d.ToString('X8'))"
          Modified = New-Object DateTime(
            (($c -shr 9) + 1980), (($c -shr 5) -band 0xf), ($c -band 0x1f),
            ($b -shr 11), (($b -shr 5) -band 0x3f), (($b -band 0x1f) * 2)
          )
          Size = $f
          Packed = $e
          Method = $Methods.Keys.Where({$Methods.Item($_) -eq $a})
        }
        $fs.Position += $h + $i
      }
    }
    catch {
      $_.Exception.InnerException
    }
    finally {
      if ($br -ne $bull) {$br.Close()}
      if ($fs -ne $null) {$fs.Close()}
    }
  }
  end {}
}
