7Z.psm1

#region New-7ZFile
Function New-7ZFile
{
    Param
    (
        [CmdletBinding()]

        [Parameter(Position = 0, Mandatory = $true)]
        [string]$Path,

        [Parameter(Position = 1, Mandatory = $false )] 
        [string]$FilePath,

        [Parameter(Position = 2, Mandatory = $false )]
        [ValidateSet(“7z”,”Zip”,”Tar”, "GZip", "BZip2", "Exe")] 
        [string]$FileType = "7Z",

        [int]$Threads,

        [string]$Split,

        [switch]$Encrypt,
        [switch]$EncryptFileNames,

        [System.Security.SecureString]$Password
    )

    # Get the path to the 7Z.exe
    $7ZPath = Get-7ZPath

    # Test the 7Z files
    Test-7ZInstallation

    # Test if source path exists
    if(-Not (Test-Path $Path))
    {
        Write-Output "Could not find path $Path!"
        return
    }

    # Get the full path to the source
    $sourcePath = (Resolve-Path $Path).Path

    # Check if the FilePath is null and create a path
    if(-Not $FilePath)
    {
        $base = (Get-Location).Path + "\"
        $filename = (Get-Item -Path $Path).Name + "." + $FileType.ToLower()
        $FilePath = $base + $filename
    }
    else
    {
        $FilePath = (Resolve-NonExistentPath -Path $FilePath).Fullname
    }

    # Check the extension of the file path
    if( -Not ($FilePath.ToLower().EndsWith("."+$FileType.ToLower())))
    {
        # Find the last . on the file name
        $index = $FilePath.LastIndexOf('.')
        $FilePath = $FilePath.Substring(0, $index + 1) + $FileType.ToLower()
    }

    # Form the arguments
    $arguments = @()

    $arguments += "a"

    if($Threads)
    {
        $arguments += "-mmt=$Threads"
    }

    if( $FileType.ToLower() -eq "exe" )
    {
        $arguments += "-sfx7z.sfx"
    }

    if($Split)
    {
        # Get the size to split the files
        switch ($split.ToLower().Substring($split.length - 2, 2))
        {
            'mb' {$SplitSize = 'm'; $Size = $Split.Substring(0, $Split.Length - 2)}
            'kb' {$SplitSize = 'k'; $Size = $Split.Substring(0, $Split.Length - 2)}
            'gb' {$SplitSize = 'g'; $Size = $Split.Substring(0, $Split.Length - 2)}
            'b' {$SplitSize = 'b'; $Size = $Split.Substring(0, $Split.Length - 1)}
            Default {$SplitSize = "b"; $Size = $Split.Substring(0, $Split.Length - 1)}
        }

        $arguments += "-v$Size$SplitSize"
    }

    # Check if we should encrypt the file
    if($Encrypt)
    {
        if($FileType -ne "7z")
        {
            Throw "Encryption is only supported with the 7z file format."
        }

        # Chech if the password was provided
        if(!$Password)
        {
            # Read the password from the console
            $Password = Read-Host -Prompt "Enter the password for the archive: " -AsSecureString
        }

        # Get the plain text password
        $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Password)
        $PlainPassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)

        $arguments += "-p$PlainPassword"

        if($EncryptFileNames)
        {
            $arguments += "-mhe=on"
        }
    }

    $arguments += '"' + $FilePath + '"'
    $arguments += '"' + $sourcePath + '"'
    
    # Start 7z
    $pinfo = New-Object System.Diagnostics.ProcessStartInfo
    $pinfo.FileName = $7ZPath
    $pinfo.RedirectStandardError = $true
    $pinfo.RedirectStandardOutput = $true
    $pinfo.CreateNoWindow = $true
    $pinfo.UseShellExecute = $false
    $pinfo.Arguments = $arguments
    $p = New-Object System.Diagnostics.Process
    $p.StartInfo = $pinfo
    $p.Start() | Out-Null
    $p.WaitForExit()
    $stdout = $p.StandardOutput.ReadToEnd()
    $stderr = $p.StandardError.ReadToEnd()

    Write-Verbose $stdout

    if($p.ExitCode -ne 0)
    {
        Write-Error $stderr
    }
}
#endregion

#region Get-7ZFile
Function Get-7ZFile
{
    Param
    (
        [CmdletBinding()]

        [Parameter(Position = 0, Mandatory = $true)]
        [string]$Path
    )

    # Get the path to the 7Z.exe
    $7ZPath = Get-7ZPath

    # Test the 7Z files
    Test-7ZInstallation

    # Test if source path exists
    if(-Not (Test-Path $Path))
    {
        Write-Output "Could not find path $Path!"
        return
    }

    # Get the full path to the source
    $sourcePath = (Resolve-Path $Path).Path

    # Form the arguments
    $arguments = @()

    $arguments += "l"
    $arguments += "-ba"
    $arguments += "-slt"
    $arguments += '"' + $sourcePath + '"'

    
    # Start 7z
    $pinfo = New-Object System.Diagnostics.ProcessStartInfo
    $pinfo.FileName = $7ZPath
    $pinfo.RedirectStandardError = $true
    $pinfo.RedirectStandardOutput = $true
    $pinfo.CreateNoWindow = $true
    $pinfo.UseShellExecute = $false
    $pinfo.Arguments = $arguments
    $p = New-Object System.Diagnostics.Process
    $p.StartInfo = $pinfo
    $p.Start() | Out-Null
    $p.WaitForExit()
    $stdout = $p.StandardOutput.ReadToEnd()
    $stderr = $p.StandardError.ReadToEnd()

    #Process output
    $output = $stdout.Split("`n`r") | Where-Object {$_ -ne ""}

    $currentLine = 0

    $propertyCounter = 0
    foreach($l in $output)
    {
        if($propertyCounter -gt 8)
        {
            $propertyCounter = 0
        }

        if($propertyCounter -eq 0)
        {
            $obj = New-Object psobject
        }

        $items = $l.Split("=")

        $propertyName = $items[0].Trim()
        $propertyValue = $items[1].Trim()

        
        if($propertyName -eq "Modified")
        {
            $propertyName = "Date"
            $propertyValue = [datetime]$items[1].Trim()
        }

        if($propertyName -eq "Size")
        {
            $propertyName = "Size (MB)"

            try
            {
                $propertyValue = [int]::Parse($propertyValue)
            }
            catch
            {
                $propertyValue = 0
            }

            if($propertyValue -gt 0)
            {
                $propertyValue = $propertyValue / 1MB
                $propertyValue = [Math]::Round($propertyValue,2)
            }
        }

        if($propertyName -eq "Packed Size")
        {
            $propertyName = "Compressed Size (MB)"

            try
            {
                $propertyValue = [int]::Parse($propertyValue)
            }
            catch
            {
                $propertyValue = 0
            }

            if($propertyValue -gt 0)
            {
                $propertyValue = $propertyValue / 1MB
                $propertyValue = [Math]::Round($propertyValue,2)
            }
        }

        if($propertyName -eq "Block")
        {
            try
            {
                $propertyValue = [int]::Parse($propertyValue)
            }
            catch
            {
                $propertyValue = 0
            }
        }

        if($propertyName -eq "Attributes")
        {
            $propertyName = "Type"

            if($propertyValue -eq "A")
            {
                $propertyValue = "File"
            }
            else
            {
                $propertyValue = "Directory"
            }
        }
            
        # Add the property to the object
        $obj | Add-Member -MemberType NoteProperty -Name $propertyName -Value $propertyValue

        # Reset if needed
        if($propertyCounter -eq 8)
        {
            Write-Output $obj
        }

        $propertyCounter++
    }



    <#
    $start = 0
    foreach($l in $output)
    {
        if( $l.Length -le 1 )
        {
            continue
        }
 
        if( ($l.StartsWith("--------")) -and ($start -gt 0))
        {
            break
        }
         
        if($l.StartsWith("--------"))
        {
            $start = 1
            continue
        }
 
        if( $start -gt 0 )
        {
            $line = $l
            $obj = New-Object psobject
            $date = [datetime]$line.Substring(0,20)
            $obj | Add-Member -MemberType NoteProperty -Name Date -Value $date
            $line = $line.Substring(20)
 
 
            $itemtype = $line.Substring(0,5)
            $type = ""
            if($itemtype.Equals("....A"))
            {
                $type = "File"
            }
            else
            {
                $type = "Directory"
            }
            $obj | Add-Member -MemberType NoteProperty -Name "Type" -Value $type
             
            $line = $line.Substring(5)
             
            Write-Host $line
 
 
            $items = $line.Split() | Where-Object {$_.Length -gt 0}
 
            Write-Host $items
 
 
            $size = 0;
            $compsize = 0;
            $itemname = ""
 
            <#
            if( $items.Count -eq 3)
            {
                $size = $items[0]
                $compsize = $items[1]
                $itemname = $items[2]
            }
            else
            {
                $size = $items[0]
                $compsize = $null
                $itemname = $items[1]
            }
 
            if($size -ne $null)
            {
                $size = [int]::Parse($size) / 1MB
                $size = [Math]::Round($size,2)
            }
            if($compsize -ne $null)
            {
                $compsize = [int]::Parse($compsize) / 1MB
                $compsize = [Math]::Round($compsize,2)
            }
 
            Write-Host "Size: $size"
            Write-Host "CSize: $compsize"
 
            $obj | Add-Member -MemberType NoteProperty -Name "Size (MB)" -Value $size
            $obj | Add-Member -MemberType NoteProperty -Name "Compressed Size (MB)" -Value $compsize
            $obj | Add-Member -MemberType NoteProperty -Name "Name" -Value $itemname
 
            Write-Output $obj
        }
    }
    #>


# Write-OutPut $stdout

    if($p.ExitCode -ne 0)
    {
        Write-Error $stderr
    }
}
#endregion

#region Test-7ZFile
Function Test-7ZFile
{
    Param
    (
        [CmdletBinding()]

        [Parameter(Position = 0, Mandatory = $true)]
        [string]$Path,

        [switch]$Recurse,

        [int]$Threads
    )

    # Get the path to the 7Z.exe
    $7ZPath = Get-7ZPath

    # Test the 7Z files
    Test-7ZInstallation

    # Test if source path exists
    if(-Not (Test-Path $Path))
    {
        Write-Output "Could not find path $Path!"
        return
    }

    # Get the full path to the source
    $sourcePath = (Resolve-Path $Path).Path

    # Form the arguments
    $arguments = @()

    $arguments += "t"

    if($Threads)
    {
        $arguments += "-mmt=$Threads"
    }

    $arguments += '"' + $sourcePath + '"'

    if( $Recurse )
    {
        $arguments += "-r"
    }
    
    # Start 7z
    $pinfo = New-Object System.Diagnostics.ProcessStartInfo
    $pinfo.FileName = $7ZPath
    $pinfo.RedirectStandardError = $true
    $pinfo.RedirectStandardOutput = $true
    $pinfo.CreateNoWindow = $true
    $pinfo.UseShellExecute = $false
    $pinfo.Arguments = $arguments
    $p = New-Object System.Diagnostics.Process
    $p.StartInfo = $pinfo
    $p.Start() | Out-Null
    $p.WaitForExit()
    $stdout = $p.StandardOutput.ReadToEnd()
    $stderr = $p.StandardError.ReadToEnd()

    Write-Verbose $stdout

    if($p.ExitCode -ne 0)
    {
        Write-Error $stderr
    }
}
#endregion

#region Extract-7ZFile
Function Extract-7ZFile
{
    Param
    (
        [CmdletBinding()]

        [Parameter(Position = 0, Mandatory = $true)]
        [string]$Path,

        [Parameter(Position = 1, Mandatory = $false )] 
        [string]$Destination,

        [int]$Threads
    )

    # Get the path to the 7Z.exe
    $7ZPath = Get-7ZPath

    # Test the 7Z files
    Test-7ZInstallation

    # Test if source path exists
    if(-Not (Test-Path $Path))
    {
        Write-Output "Could not find path $Path!"
        return
    }

    # Get the full path to the source
    $sourcePath = (Resolve-Path $Path).Path

    # Get the full path to the destination directory
    $destinationpath = (Resolve-NonExistentPath $Destination).Fullname

    # Form the arguments
    $arguments = @()

    $arguments += "x"
    $arguments += "-y"

    if($Threads)
    {
        $arguments += "-mmt=$Threads"
    }

    $arguments += '"' + $sourcePath + '"'
    $arguments += '"' + "-o" + $destinationpath + '"'
    
    # Start 7z
    $pinfo = New-Object System.Diagnostics.ProcessStartInfo
    $pinfo.FileName = $7ZPath
    $pinfo.RedirectStandardError = $true
    $pinfo.RedirectStandardOutput = $true
    $pinfo.CreateNoWindow = $true
    $pinfo.UseShellExecute = $false
    $pinfo.Arguments = $arguments
    $p = New-Object System.Diagnostics.Process
    $p.StartInfo = $pinfo
    $p.Start() | Out-Null
    $p.WaitForExit()
    $stdout = $p.StandardOutput.ReadToEnd()
    $stderr = $p.StandardError.ReadToEnd()

    Write-Verbose $stdout

    if($p.ExitCode -ne 0)
    {
        Write-Error $stderr
    }
}
#endregion

#region Update-7ZFile
Function Update-7ZFile
{
    Param
    (
        [CmdletBinding()]

        [Parameter(Position = 0, Mandatory = $true)]
        [string]$Path,
        [string]$ItemPath,
        [Parameter(ParameterSetName=’add’)]
        [switch]$Remove,
        [Parameter(ParameterSetName=’remove’)]
        [switch]$Add,
        [int]$Threads
    )

    # Get the path to the 7Z.exe
    $7ZPath = Get-7ZPath

    # Test the 7Z files
    Test-7ZInstallation

    # Test if archive path exists
    if(-Not (Test-Path $Path))
    {
        Write-Output "Could not find path $Path!"
        return
    }

    # Get the full path to the archive
    $sourcePath = (Resolve-Path $Path).Path

    # In case we're adding items to the archive, check if the items exist
    if($Add)
    {
        if(-Not (Test-Path $ItemPath))
        {
            Write-Error "Could not find path $ItemPath!"
            return
        }
    }

    # Form the arguments
    $arguments = @()

    if($Remove)
    {
        $arguments += "d"

        if($Threads)
        {
            $arguments += "-mmt=$Threads"
        }

        $arguments += '"' + $sourcePath + '"'
        $arguments += '"' + $ItemPath + '"'
    }
    else
    {
        $arguments += "a"

        if($Threads)
        {
            $arguments += "-mmt=$Threads"
        }

        $arguments += '"' + $sourcePath + '"'

        $ItemPathFull = (Resolve-Path $ItemPath).Path
        $arguments += '"' + $ItemPathFull + '"'
    }
    
    # Start 7z
    $pinfo = New-Object System.Diagnostics.ProcessStartInfo
    $pinfo.FileName = $7ZPath
    $pinfo.RedirectStandardError = $true
    $pinfo.RedirectStandardOutput = $true
    $pinfo.CreateNoWindow = $true
    $pinfo.UseShellExecute = $false
    $pinfo.Arguments = $arguments
    $p = New-Object System.Diagnostics.Process
    $p.StartInfo = $pinfo
    $p.Start() | Out-Null
    $p.WaitForExit()
    $stdout = $p.StandardOutput.ReadToEnd()
    $stderr = $p.StandardError.ReadToEnd()

    Write-Verbose $stdout

    if($p.ExitCode -ne 0)
    {
        Write-Error $stderr
    }
}
#endregion

#region Get-7ZVersion
Function Get-7ZVersion
{
    Param
    (
    )

    # Get the path to the 7Z.exe
    $7ZPath = Get-7ZPath

    # Form the arguments
    $arguments = @("/?")

    # Start 7z
    $pinfo = New-Object System.Diagnostics.ProcessStartInfo
    $pinfo.FileName = $7ZPath
    $pinfo.RedirectStandardError = $true
    $pinfo.RedirectStandardOutput = $true
    $pinfo.CreateNoWindow = $true
    $pinfo.UseShellExecute = $false
    $pinfo.Arguments = $arguments
    $p = New-Object System.Diagnostics.Process
    $p.StartInfo = $pinfo
    $p.Start() | Out-Null
    $p.WaitForExit()
    $stdout = $p.StandardOutput.ReadToEnd()
    $stderr = $p.StandardError.ReadToEnd()

    # Return the output
    $stdout |
        Where-Object {$_.length -gt 0}
}
#endregion

#region Helper Functions
#region Get-7ZPath
Function Get-7ZPath
{
    # Get the system architecture since it affects the path
    if([System.Environment]::Is64BitOperatingSystem)
    {
        $arch = "x64"
    }
    else
    {
        $arch = "x86"
    }

    # Get the path to the 7Z.exe
    $7ZPath = (Get-Module -Name CPolydorou.7Z).Path
    $7ZPath = $7Zpath.Substring(0, $7ZPath.Length - 19) + "\7Zip\" + $arch + "\7za.exe"

    return $7ZPath
}
#endregion

#region Test-7ZInstallation
Function Test-7ZInstallation
{
    $7ZPath = Get-7ZPath

    # Test if 7Zip binaries exist
    if( -Not (Test-Path ($7ZPath)))
    {
        Write-Error "Could not locate 7za.exe!"
        return
    }
    if( -Not (Test-Path ($7ZPath.Replace(".exe",".dll"))))
    {
        Write-Error "Could not locate 7z.dll!"
        return
    }
    if( -Not (Test-Path ($7ZPath.Replace("7za.exe","7zxa.dll"))))
    {
        Write-Error "Could not locate 7zxa.dll!"
        return
    }
}
#endregion
#endregion