# Description: Functions for decompressing archives or installers function Invoke-Extraction { param ( [string] $Path, [string[]] $Name, [psobject] $Manifest, [Alias('Arch', 'Architecture')] [string] $ProcessorArchitecture ) $uri = @(url $Manifest $ProcessorArchitecture) # 'extract_dir' and 'extract_to' are paired $extractDir = @(extract_dir $Manifest $ProcessorArchitecture) $extractTo = @(extract_to $Manifest $ProcessorArchitecture) $extracted = 0 for ($i = 0; $i -lt $Name.Length; $i++) { # work out extraction method, if applicable $extractFn = $null switch -regex ($Name[$i]) { '\.zip$' { if ((Test-HelperInstalled -Helper 7zip) -or ((get_config USE_EXTERNAL_7ZIP) -and (Test-CommandAvailable 7z))) { $extractFn = 'Expand-7zipArchive' } else { $extractFn = 'Expand-ZipArchive' } continue } '\.msi$' { $extractFn = 'Expand-MsiArchive' continue } '\.exe$' { if ($Manifest.innosetup) { $extractFn = 'Expand-InnoArchive' } continue } { Test-7zipRequirement -Uri $_ } { $extractFn = 'Expand-7zipArchive' continue } } if ($extractFn) { $fnArgs = @{ Path = Join-Path $Path $Name[$i] DestinationPath = Join-Path $Path $extractTo[$extracted] ExtractDir = $extractDir[$extracted] } Write-Host "Extracting $([char]0x1b)[36m$(url_remote_filename $uri[$i])$([char]0x1b)[0m... " -NoNewline & $extractFn @fnArgs -Removal Write-Host 'Done.' -ForegroundColor Green $extracted++ } } } function Expand-7zipArchive { [CmdletBinding()] param ( [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] [String] $Path, [Parameter(Position = 1)] [String] $DestinationPath = (Split-Path $Path), [String] $ExtractDir, [Parameter(ValueFromRemainingArguments = $true)] [String] $Switches, [ValidateSet('All', 'Skip', 'Rename')] [String] $Overwrite, [Switch] $Removal ) if ((get_config USE_EXTERNAL_7ZIP)) { try { $7zPath = (Get-Command '7z' -CommandType Application -ErrorAction Stop | Select-Object -First 1).Source } catch [System.Management.Automation.CommandNotFoundException] { abort "`nCannot find external 7-Zip (7z.exe) while 'use_external_7zip' is 'true'!`nRun 'scoop config use_external_7zip false' or install 7-Zip manually and try again." } } else { $7zPath = Get-HelperPath -Helper 7zip } $LogPath = "$(Split-Path $Path)\7zip.log" $DestinationPath = $DestinationPath.TrimEnd('\') $ArgList = @('x', $Path, "-o$DestinationPath", '-xr!*.nsis', '-y') $IsTar = ((strip_ext $Path) -match '\.tar$') -or ($Path -match '\.t[abgpx]z2?$') if (!$IsTar -and $ExtractDir) { $ArgList += "-ir!$ExtractDir\*" } if ($Switches) { $ArgList += (-split $Switches) } switch ($Overwrite) { 'All' { $ArgList += '-aoa' } 'Skip' { $ArgList += '-aos' } 'Rename' { $ArgList += '-aou' } } $Status = Invoke-ExternalCommand $7zPath $ArgList -LogPath $LogPath if (!$Status) { abort "Failed to extract files from $Path.`nLog file:`n $(friendly_path $LogPath)`n$(new_issue_msg $app $bucket 'decompress error')" } if ($IsTar) { # Check for tar $Status = Invoke-ExternalCommand $7zPath @('l', $Path) -LogPath $LogPath if ($Status) { # get inner tar file name $TarFile = (Select-String -Path $LogPath -Pattern '[^ ]*tar$').Matches.Value Expand-7zipArchive -Path "$DestinationPath\$TarFile" -DestinationPath $DestinationPath -ExtractDir $ExtractDir -Removal } else { abort "Failed to list files in $Path.`nNot a 7-Zip supported archive file." } } if (!$IsTar -and $ExtractDir) { movedir "$DestinationPath\$ExtractDir" $DestinationPath | Out-Null # Remove temporary directory if it is empty $ExtractDirTopPath = [string] "$DestinationPath\$($ExtractDir -replace '[\\/].*')" if ((Get-ChildItem -Path $ExtractDirTopPath -Force -ErrorAction Ignore).Count -eq 0) { Remove-Item -Path $ExtractDirTopPath -Recurse -Force -ErrorAction Ignore } } if (Test-Path $LogPath) { Remove-Item $LogPath -Force } if ($Removal) { if (($Path -replace '.*\.([^\.]*)$', '$1') -eq '001') { # Remove splitted 7-zip archive parts Get-ChildItem "$($Path -replace '\.[^\.]*$', '').???" | Remove-Item -Force } elseif (($Path -replace '.*\.part(\d+)\.rar$', '$1')[-1] -eq '1') { # Remove splitted RAR archive parts Get-ChildItem "$($Path -replace '\.part(\d+)\.rar$', '').part*.rar" | Remove-Item -Force } else { # Remove original archive file Remove-Item $Path -Force } } } function Expand-ZstdArchive { [CmdletBinding()] param ( [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] [String] $Path, [Parameter(Position = 1)] [String] $DestinationPath = (Split-Path $Path), [String] $ExtractDir, [Parameter(ValueFromRemainingArguments = $true)] [String] $Switches, [Switch] $Removal ) # TODO: Remove this function after 2024/12/31 Show-DeprecatedWarning $MyInvocation 'Expand-7zipArchive' Expand-7zipArchive -Path $Path -DestinationPath $DestinationPath -ExtractDir $ExtractDir -Switches $Switches -Removal:$Removal } function Expand-MsiArchive { [CmdletBinding()] param ( [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] [String] $Path, [Parameter(Position = 1)] [String] $DestinationPath = (Split-Path $Path), [String] $ExtractDir, [Parameter(ValueFromRemainingArguments = $true)] [String] $Switches, [Switch] $Removal ) $DestinationPath = $DestinationPath.TrimEnd('\') if ($ExtractDir) { $OriDestinationPath = $DestinationPath $DestinationPath = "$DestinationPath\_tmp" } if ((get_config USE_LESSMSI)) { $MsiPath = Get-HelperPath -Helper Lessmsi $ArgList = @('x', $Path, "$DestinationPath\") } else { $MsiPath = 'msiexec.exe' $ArgList = @('/a', $Path, '/qn', "TARGETDIR=$DestinationPath\SourceDir") } $LogPath = "$(Split-Path $Path)\msi.log" if ($Switches) { $ArgList += (-split $Switches) } $Status = Invoke-ExternalCommand $MsiPath $ArgList -LogPath $LogPath if (!$Status) { abort "Failed to extract files from $Path.`nLog file:`n $(friendly_path $LogPath)`n$(new_issue_msg $app $bucket 'decompress error')" } if ($ExtractDir -and (Test-Path "$DestinationPath\SourceDir")) { movedir "$DestinationPath\SourceDir\$ExtractDir" $OriDestinationPath | Out-Null Remove-Item $DestinationPath -Recurse -Force } elseif ($ExtractDir) { movedir "$DestinationPath\$ExtractDir" $OriDestinationPath | Out-Null Remove-Item $DestinationPath -Recurse -Force } elseif (Test-Path "$DestinationPath\SourceDir") { movedir "$DestinationPath\SourceDir" $DestinationPath | Out-Null } if (($DestinationPath -ne (Split-Path $Path)) -and (Test-Path "$DestinationPath\$(fname $Path)")) { Remove-Item "$DestinationPath\$(fname $Path)" -Force } if (Test-Path $LogPath) { Remove-Item $LogPath -Force } if ($Removal) { # Remove original archive file Remove-Item $Path -Force } } function Expand-InnoArchive { [CmdletBinding()] param ( [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] [String] $Path, [Parameter(Position = 1)] [String] $DestinationPath = (Split-Path $Path), [String] $ExtractDir, [Parameter(ValueFromRemainingArguments = $true)] [String] $Switches, [Switch] $Removal ) $LogPath = "$(Split-Path $Path)\innounp.log" $ArgList = @('-x', "-d$DestinationPath", $Path, '-y') switch -Regex ($ExtractDir) { '^[^{].*' { $ArgList += "-c{app}\$ExtractDir" } '^{.*' { $ArgList += "-c$ExtractDir" } Default { $ArgList += '-c{app}' } } if ($Switches) { $ArgList += (-split $Switches) } $Status = Invoke-ExternalCommand (Get-HelperPath -Helper Innounp) $ArgList -LogPath $LogPath if (!$Status) { abort "Failed to extract files from $Path.`nLog file:`n $(friendly_path $LogPath)`n$(new_issue_msg $app $bucket 'decompress error')" } if (Test-Path $LogPath) { Remove-Item $LogPath -Force } if ($Removal) { # Remove original archive file Remove-Item $Path -Force } } function Expand-ZipArchive { [CmdletBinding()] param ( [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] [String] $Path, [Parameter(Position = 1)] [String] $DestinationPath = (Split-Path $Path), [String] $ExtractDir, [Switch] $Removal ) if ($ExtractDir) { $OriDestinationPath = $DestinationPath $DestinationPath = "$DestinationPath\_tmp" } # Disable progress bar to gain performance $oldProgressPreference = $ProgressPreference $global:ProgressPreference = 'SilentlyContinue' # Compatible with Pscx v3 (https://github.com/Pscx/Pscx) ('Microsoft.PowerShell.Archive' is not needed for Pscx v4) Microsoft.PowerShell.Archive\Expand-Archive -Path $Path -DestinationPath $DestinationPath -Force $global:ProgressPreference = $oldProgressPreference if ($ExtractDir) { movedir "$DestinationPath\$ExtractDir" $OriDestinationPath | Out-Null Remove-Item $DestinationPath -Recurse -Force } if ($Removal) { # Remove original archive file Remove-Item $Path -Force } } function Expand-DarkArchive { [CmdletBinding()] param ( [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] [String] $Path, [Parameter(Position = 1)] [String] $DestinationPath = (Split-Path $Path), [Parameter(ValueFromRemainingArguments = $true)] [String] $Switches, [Switch] $Removal ) $LogPath = "$(Split-Path $Path)\dark.log" $DarkPath = Get-HelperPath -Helper Dark if ((Split-Path $DarkPath -Leaf) -eq 'wix.exe') { $ArgList = @('burn', 'extract', $Path, '-out', $DestinationPath, '-outba', "$DestinationPath\UX") } else { $ArgList = @('-nologo', '-x', $DestinationPath, $Path) } if ($Switches) { $ArgList += (-split $Switches) } $Status = Invoke-ExternalCommand $DarkPath $ArgList -LogPath $LogPath if (!$Status) { abort "Failed to extract files from $Path.`nLog file:`n $(friendly_path $LogPath)`n$(new_issue_msg $app $bucket 'decompress error')" } if (Test-Path "$DestinationPath\WixAttachedContainer") { Rename-Item "$DestinationPath\WixAttachedContainer" 'AttachedContainer' -ErrorAction Ignore } else { if (Test-Path "$DestinationPath\AttachedContainer\a0") { $Xml = [xml](Get-Content -Raw "$DestinationPath\UX\manifest.xml" -Encoding utf8) $Xml.BurnManifest.UX.Payload | ForEach-Object { Rename-Item "$DestinationPath\UX\$($_.SourcePath)" $_.FilePath -ErrorAction Ignore } $Xml.BurnManifest.Payload | ForEach-Object { Rename-Item "$DestinationPath\AttachedContainer\$($_.SourcePath)" $_.FilePath -ErrorAction Ignore } } } if (Test-Path $LogPath) { Remove-Item $LogPath -Force } if ($Removal) { # Remove original archive file Remove-Item $Path -Force } }