waypoit debuguing

This commit is contained in:
2026-09-25 16:52:30 -06:00
parent 251c9aa06c
commit 56b08f32da
+419 -432
View File
@@ -60,14 +60,13 @@ param(
) )
# --- ENVIRONMENT SANITY CHECK --- # --- ENVIRONMENT SANITY CHECK ---
# Ensure PowerShell 7+ is used since ForEach-Object -Parallel is required.
if ($PSVersionTable.PSVersion.Major -lt 7) { if ($PSVersionTable.PSVersion.Major -lt 7) {
Write-Error "checksum: This script requires PowerShell 7 or higher for parallel execution (-Parallel support). Current version is $($PSVersionTable.PSVersion)." Write-Error "checksum: This script requires PowerShell 7 or higher for parallel execution (-Parallel support). Current version is $($PSVersionTable.PSVersion)."
exit 1 exit 1
} }
begin { # --- HELPER FUNCTIONS ---
function Show-Help { function Show-Help {
@' @'
Princess Pi's Magical CHECKSUM! Princess Pi's Magical CHECKSUM!
@@ -143,17 +142,17 @@ Princess Pi LOVES YOU!~
https://linktr.ee/princesspi3 https://linktr.ee/princesspi3
'@ '@
}; }
function Write-ErrorWithHelp { function Write-ErrorWithHelp {
param([string]$ErrorMessage); param([string]$ErrorMessage);
Show-Help; Show-Help;
Write-Host ""; Write-Host "";
Write-Error $ErrorMessage; Write-Error $ErrorMessage;
}; }
function Show-PrincessPiArt { function Show-PrincessPiArt {
$art = @' $art = @'
=#= -- ##*+++++ *+ =#= -- ##*+++++ *+
-#=#- +# -*%%= ##++++++++++# ##*********# -#=#- +# -*%%= ##++++++++++# ##*********#
@@ -212,458 +211,446 @@ Princess Pi
https://linktr.ee/princesspi3 https://linktr.ee/princesspi3
'@ '@
Write-Host $art -ForegroundColor Blue; Write-Host $art -ForegroundColor Blue;
};
function Get-AlgorithmFromHashLength {
param([string]$HashString);
switch ($HashString.Trim().Length) {
32 { return 'MD5'; }
40 { return 'SHA1'; }
64 { return 'SHA256'; }
96 { return 'SHA384'; }
128 { return 'SHA512'; }
default {
Write-ErrorWithHelp "checksum: Could not auto-detect algorithm for hash length $($HashString.Length). Please specify -Algorithm.";
return $null;
}
};
};
} }
process { function Get-AlgorithmFromHashLength {
$helpInPath = $false; param([string]$HashString);
if ($FilePath) {
foreach ($p in ($FilePath)) {
if ($p -match '^(?i)[-/]{1,2}(h|help|\?)$') {
$helpInPath = $true;
break;
}
};
};
if ($Help.IsPresent -or $helpInPath) { switch ($HashString.Trim().Length) {
Show-Help; 32 { return 'MD5'; }
return; 40 { return 'SHA1'; }
}; 64 { return 'SHA256'; }
96 { return 'SHA384'; }
if ($PrincessPi.IsPresent) { 128 { return 'SHA512'; }
Show-PrincessPiArt; default {
return; Write-ErrorWithHelp "checksum: Could not auto-detect algorithm for hash length $($HashString.Length). Please specify -Algorithm.";
}; return $null;
if (-not [string]::IsNullOrWhiteSpace($Algorithm)) {
$Algorithm = $Algorithm.ToUpper();
};
# Determine if progress bar should display (-r, -c/-Check, or wildcard input '*')
$isWildcardGlobal = $false;
if ($FilePath) {
foreach ($p in $FilePath) {
if ($p -match '\*') { $isWildcardGlobal = $true; }
} }
} }
$showProgress = $Recursive.IsPresent -or $isWildcardGlobal -or ($PSCmdlet.ParameterSetName -eq 'Verify'); }
# --- ITEM GATHERING (Generate & Match Modes) --- # --- MAIN EXECUTION FLOW ---
if ($PSCmdlet.ParameterSetName -in @('Generate', 'Match')) { $helpInPath = $false;
if (-not $FilePath -or $FilePath.Count -eq 0) { if ($FilePath) {
Write-ErrorWithHelp "checksum: Missing file path parameter. Use -Help for usage."; foreach ($p in ($FilePath)) {
return; if ($p -match '^(?i)[-/]{1,2}(h|help|\?)$') {
}; $helpInPath = $true;
break;
}
}
}
$allItems = @(); if ($Help.IsPresent -or $helpInPath) {
$skipExtensions = @('.md5', '.sha1', '.sha256', '.sha384', '.sha512'); Show-Help;
return;
}
foreach ($pathEntry in ($FilePath)) { if ($PrincessPi.IsPresent) {
$isWildcard = ($pathEntry -match '\*'); Show-PrincessPiArt;
$resolvedItems = @(); return;
}
if ($Recursive) { if (-not [string]::IsNullOrWhiteSpace($Algorithm)) {
$resolvedItems = Get-ChildItem -Path ($pathEntry) -Recurse -File -ErrorAction 'SilentlyContinue'; $Algorithm = $Algorithm.ToUpper();
}
$isWildcardGlobal = $false;
if ($FilePath) {
foreach ($p in $FilePath) {
if ($p -match '\*') { $isWildcardGlobal = $true; }
}
}
$showProgress = $Recursive.IsPresent -or $isWildcardGlobal -or ($PSCmdlet.ParameterSetName -eq 'Verify');
# --- ITEM GATHERING (Generate & Match Modes) ---
if ($PSCmdlet.ParameterSetName -in @('Generate', 'Match')) {
if (-not $FilePath -or $FilePath.Count -eq 0) {
Write-ErrorWithHelp "checksum: Missing file path parameter. Use -Help for usage.";
return;
}
$allItems = @();
$skipExtensions = @('.md5', '.sha1', '.sha256', '.sha384', '.sha512');
foreach ($pathEntry in ($FilePath)) {
$isWildcard = ($pathEntry -match '\*');
$resolvedItems = @();
if ($Recursive) {
$resolvedItems = Get-ChildItem -Path ($pathEntry) -Recurse -File -ErrorAction 'SilentlyContinue';
} else {
if ($isWildcard -or ($pathEntry -match '\?')) {
$resolvedItems = Get-ChildItem -Path ($pathEntry) -File -ErrorAction 'SilentlyContinue';
} else { } else {
if ($isWildcard -or ($pathEntry -match '\?')) { $resolvedItems = Get-Item -Path ($pathEntry) -ErrorAction 'SilentlyContinue';
$resolvedItems = Get-ChildItem -Path ($pathEntry) -File -ErrorAction 'SilentlyContinue'; }
} else { }
$resolvedItems = Get-Item -Path ($pathEntry) -ErrorAction 'SilentlyContinue';
if (-not $resolvedItems) {
Write-ErrorWithHelp "checksum: $($pathEntry): No such file or directory";
continue;
}
$explicitFileFound = $false;
if ((-not $isWildcard) -and (Test-Path -Path ($pathEntry) -PathType 'Leaf')) {
$explicitFileFound = $true;
}
foreach ($item in ($resolvedItems)) {
if ($item.PSIsContainer) { continue; }
if (-not $explicitFileFound) {
if ($skipExtensions -contains $item.Extension.ToLower()) {
continue;
} }
}; }
$allItems += $item;
}
}
}
if (-not $resolvedItems) { # --- DIRECT MATCH MODE (-m / -Match) ---
Write-ErrorWithHelp "checksum: $($pathEntry): No such file or directory"; if ($PSCmdlet.ParameterSetName -eq 'Match') {
continue; if ([string]::IsNullOrWhiteSpace($Algorithm)) {
}; $Algorithm = Get-AlgorithmFromHashLength -HashString ($Match);
if (-not $Algorithm) { return; }
}
$explicitFileFound = $false; $expectedHash = $Match.Trim().ToLower();
if ((-not $isWildcard) -and (Test-Path -Path ($pathEntry) -PathType 'Leaf')) { $totalFiles = $allItems.Count;
$explicitFileFound = $true; $currentIndex = 0;
};
foreach ($item in ($resolvedItems)) { $allItems | ForEach-Object -Parallel {
if ($item.PSIsContainer) { continue; }; $item = $_
$algo = $using:Algorithm
$exp = $using:expectedHash
if (-not $explicitFileFound) { $actualHash = (Get-FileHash -Path ($item.FullName) -Algorithm ($algo)).Hash.ToLower();
if ($skipExtensions -contains $item.Extension.ToLower()) { $targetFile = Resolve-Path -Path ($item.FullName) -Relative -ErrorAction 'SilentlyContinue';
continue; if (-not $targetFile) { $targetFile = $item.Name; }
} if ($targetFile.StartsWith(".\") -or $targetFile.StartsWith("./")) { $targetFile = $targetFile.Substring(2); }
[PSCustomObject]@{
TargetFile = $targetFile
IsMatch = ($actualHash -eq $exp)
}
} -ThrottleLimit 16 | ForEach-Object {
$currentIndex++
if ($showProgress -and $totalFiles -gt 0) {
Write-Progress -Activity "Matching Checksums ($Algorithm)" -Status "Processing file $currentIndex of $totalFiles" -PercentComplete (($currentIndex / $totalFiles) * 100);
}
if ($_.IsMatch) {
Write-Host "$($_.TargetFile): " -NoNewline;
Write-Host "OK" -ForegroundColor Green;
} else {
Write-Host "$($_.TargetFile): " -NoNewline;
Write-Host "FAILED" -ForegroundColor Red;
}
}
if ($showProgress) {
Write-Progress -Activity "Matching Checksums ($Algorithm)" -Completed;
}
}
# --- VERIFICATION MODE (-c / -Check) ---
elseif ($PSCmdlet.ParameterSetName -eq 'Verify') {
$checkFiles = @();
if ($Recursive) {
$checkFileName = Split-Path -Path ($Check) -Leaf;
$checkFiles = Get-ChildItem -Path '.\' -Filter ($checkFileName) -Recurse -File -ErrorAction 'SilentlyContinue';
} else {
if (Test-Path -Path ($Check) -PathType 'Leaf') {
$checkFiles = @(Get-Item -Path ($Check));
}
}
if (-not $checkFiles -or $checkFiles.Count -eq 0) {
Write-ErrorWithHelp "checksum: $($Check): No such file or directory";
return;
}
foreach ($cf in ($checkFiles)) {
$lines = Get-Content -Path ($cf.FullName);
$baseDir = $cf.DirectoryName;
$fileAlgo = $Algorithm;
if ([string]::IsNullOrWhiteSpace($fileAlgo)) {
foreach ($line in ($lines)) {
if ([string]::IsNullOrWhiteSpace($line) -or $line.StartsWith("#")) { continue; }
if ($line -match '^([a-fA-F0-9]+)\s+') {
$fileAlgo = Get-AlgorithmFromHashLength -HashString ($Matches[1]);
break;
}
elseif ($line -match '^([a-fA-F0-9]{128}|[a-fA-F0-9]{96}|[a-fA-F0-9]{64}|[a-fA-F0-9]{40}|[a-fA-F0-9]{32})') {
$fileAlgo = Get-AlgorithmFromHashLength -HashString ($Matches[1]);
break;
}
}
if ([string]::IsNullOrWhiteSpace($fileAlgo)) { $fileAlgo = 'SHA256'; }
}
$verifyItems = @();
foreach ($line in ($lines)) {
if ([string]::IsNullOrWhiteSpace($line) -or $line.StartsWith("#")) { continue; }
$expectedHash = $null;
$targetFile = $null;
if ($line -match '^([a-fA-F0-9]+)\s+[* ]?(.*)$') {
$expectedHash = $Matches[1].ToLower();
$targetFile = $Matches[2].Trim().TrimStart('*');
}
elseif ($line -match '^([a-fA-F0-9]{128}|[a-fA-F0-9]{96}|[a-fA-F0-9]{64}|[a-fA-F0-9]{40}|[a-fA-F0-9]{32})[* ]?(.*)$') {
$expectedHash = $Matches[1].ToLower();
$targetFile = $Matches[2].Trim().TrimStart('*');
}
if ($expectedHash -and $targetFile) {
$targetFullPath = Join-Path -Path ($baseDir) -ChildPath ($targetFile);
$verifyItems += [PSCustomObject]@{
ExpectedHash = $expectedHash
TargetFile = $targetFile
TargetFullPath = $targetFullPath
BaseDir = $baseDir
}; };
$allItems += $item; }
}; }
};
};
# --- DIRECT MATCH MODE (-m / -Match) --- $totalFiles = $verifyItems.Count;
if ($PSCmdlet.ParameterSetName -eq 'Match') {
if ([string]::IsNullOrWhiteSpace($Algorithm)) {
$Algorithm = Get-AlgorithmFromHashLength -HashString ($Match);
if (-not $Algorithm) { return; };
};
$expectedHash = $Match.Trim().ToLower();
$totalFiles = $allItems.Count;
$currentIndex = 0; $currentIndex = 0;
# Stream parallel results and update progress bar in real time $verifyItems | ForEach-Object -Parallel {
$allItems | ForEach-Object -Parallel {
$item = $_ $item = $_
$algo = $using:Algorithm $algo = $using:fileAlgo
$exp = $using:expectedHash
$actualHash = (Get-FileHash -Path ($item.FullName) -Algorithm ($algo)).Hash.ToLower(); if (Test-Path -Path ($item.TargetFullPath) -PathType 'Leaf') {
$targetFile = Resolve-Path -Path ($item.FullName) -Relative -ErrorAction 'SilentlyContinue'; $actualHash = (Get-FileHash -Path ($item.TargetFullPath) -Algorithm ($algo)).Hash.ToLower();
if (-not $targetFile) { $targetFile = $item.Name; };
if ($targetFile.StartsWith(".\") -or $targetFile.StartsWith("./")) { $targetFile = $targetFile.Substring(2); };
[PSCustomObject]@{ $displayPath = Resolve-Path -Path ($item.TargetFullPath) -Relative -ErrorAction 'SilentlyContinue';
TargetFile = $targetFile if (-not $displayPath) { $displayPath = $item.TargetFile; }
IsMatch = ($actualHash -eq $exp) if ($displayPath.StartsWith(".\") -or $displayPath.StartsWith("./")) { $displayPath = $displayPath.Substring(2); }
[PSCustomObject]@{
DisplayPath = $displayPath
Status = if ($actualHash -eq $item.ExpectedHash) { "OK" } else { "FAILED" }
IsError = $false
}
} else {
$displayPath = $item.TargetFile;
if ($item.BaseDir -ne (Get-Location).Path) {
$displayPath = Join-Path -Path (Resolve-Path -Path ($item.BaseDir) -Relative -ErrorAction 'SilentlyContinue') -ChildPath ($item.TargetFile);
if ($displayPath.StartsWith(".\") -or $displayPath.StartsWith("./")) { $displayPath = $displayPath.Substring(2); }
}
[PSCustomObject]@{
DisplayPath = $displayPath
Status = "FAILED open or read"
IsError = $true
}
} }
} -ThrottleLimit 16 | ForEach-Object { } -ThrottleLimit 16 | ForEach-Object {
$currentIndex++ $currentIndex++
if ($showProgress -and $totalFiles -gt 0) { if ($totalFiles -gt 0) {
Write-Progress -Activity "Matching Checksums ($Algorithm)" -Status "Processing file $currentIndex of $totalFiles" -PercentComplete (($currentIndex / $totalFiles) * 100); Write-Progress -Activity "Verifying Checksums ($fileAlgo)" -Status "Processing file $currentIndex of $totalFiles" -PercentComplete (($currentIndex / $totalFiles) * 100);
} }
if ($_.IsMatch) { Write-Host "$($_.DisplayPath): " -NoNewline;
Write-Host "$($_.TargetFile): " -NoNewline; if ($_.IsError) {
Write-Host "$($_.Status)" -ForegroundColor Red;
} elseif ($_.Status -eq "OK") {
Write-Host "OK" -ForegroundColor Green; Write-Host "OK" -ForegroundColor Green;
} else { } else {
Write-Host "$($_.TargetFile): " -NoNewline;
Write-Host "FAILED" -ForegroundColor Red; Write-Host "FAILED" -ForegroundColor Red;
} }
};
if ($showProgress) {
Write-Progress -Activity "Matching Checksums ($Algorithm)" -Completed;
} }
} Write-Progress -Activity "Verifying Checksums ($fileAlgo)" -Completed;
# --- VERIFICATION MODE (-c / -Check) ---
elseif ($PSCmdlet.ParameterSetName -eq 'Verify') {
$checkFiles = @();
if ($Recursive) {
$checkFileName = Split-Path -Path ($Check) -Leaf;
$checkFiles = Get-ChildItem -Path '.\' -Filter ($checkFileName) -Recurse -File -ErrorAction 'SilentlyContinue';
} else {
if (Test-Path -Path ($Check) -PathType 'Leaf') {
$checkFiles = @(Get-Item -Path ($Check));
}
};
if (-not $checkFiles -or $checkFiles.Count -eq 0) {
Write-ErrorWithHelp "checksum: $($Check): No such file or directory";
return;
};
foreach ($cf in ($checkFiles)) {
$lines = Get-Content -Path ($cf.FullName);
$baseDir = $cf.DirectoryName;
$fileAlgo = $Algorithm;
if ([string]::IsNullOrWhiteSpace($fileAlgo)) {
foreach ($line in ($lines)) {
if ([string]::IsNullOrWhiteSpace($line) -or $line.StartsWith("#")) { continue; };
if ($line -match '^([a-fA-F0-9]+)\s+') {
$fileAlgo = Get-AlgorithmFromHashLength -HashString ($Matches[1]);
break;
}
elseif ($line -match '^([a-fA-F0-9]{128}|[a-fA-F0-9]{96}|[a-fA-F0-9]{64}|[a-fA-F0-9]{40}|[a-fA-F0-9]{32})') {
$fileAlgo = Get-AlgorithmFromHashLength -HashString ($Matches[1]);
break;
}
};
if ([string]::IsNullOrWhiteSpace($fileAlgo)) { $fileAlgo = 'SHA256'; };
};
# Parse verification items
$verifyItems = @();
foreach ($line in ($lines)) {
if ([string]::IsNullOrWhiteSpace($line) -or $line.StartsWith("#")) { continue; };
$expectedHash = $null;
$targetFile = $null;
if ($line -match '^([a-fA-F0-9]+)\s+[* ]?(.*)$') {
$expectedHash = $Matches[1].ToLower();
$targetFile = $Matches[2].Trim().TrimStart('*');
}
elseif ($line -match '^([a-fA-F0-9]{128}|[a-fA-F0-9]{96}|[a-fA-F0-9]{64}|[a-fA-F0-9]{40}|[a-fA-F0-9]{32})[* ]?(.*)$') {
$expectedHash = $Matches[1].ToLower();
$targetFile = $Matches[2].Trim().TrimStart('*');
};
if ($expectedHash -and $targetFile) {
$targetFullPath = Join-Path -Path ($baseDir) -ChildPath ($targetFile);
$verifyItems += [PSCustomObject]@{
ExpectedHash = $expectedHash
TargetFile = $targetFile
TargetFullPath = $targetFullPath
BaseDir = $baseDir
};
}
}
$totalFiles = $verifyItems.Count;
$currentIndex = 0;
# Run verification items in parallel and stream progress live
$verifyItems | ForEach-Object -Parallel {
$item = $_
$algo = $using:fileAlgo
if (Test-Path -Path ($item.TargetFullPath) -PathType 'Leaf') {
$actualHash = (Get-FileHash -Path ($item.TargetFullPath) -Algorithm ($algo)).Hash.ToLower();
$displayPath = Resolve-Path -Path ($item.TargetFullPath) -Relative -ErrorAction 'SilentlyContinue';
if (-not $displayPath) { $displayPath = $item.TargetFile; };
if ($displayPath.StartsWith(".\") -or $displayPath.StartsWith("./")) { $displayPath = $displayPath.Substring(2); };
[PSCustomObject]@{
DisplayPath = $displayPath
Status = if ($actualHash -eq $item.ExpectedHash) { "OK" } else { "FAILED" }
IsError = $false
}
} else {
$displayPath = $item.TargetFile;
if ($item.BaseDir -ne (Get-Location).Path) {
$displayPath = Join-Path -Path (Resolve-Path -Path ($item.BaseDir) -Relative -ErrorAction 'SilentlyContinue') -ChildPath ($item.TargetFile);
if ($displayPath.StartsWith(".\") -or $displayPath.StartsWith("./")) { $displayPath = $displayPath.Substring(2); };
};
[PSCustomObject]@{
DisplayPath = $displayPath
Status = "FAILED open or read"
IsError = $true
}
}
} -ThrottleLimit 16 | ForEach-Object {
$currentIndex++
if ($totalFiles -gt 0) {
Write-Progress -Activity "Verifying Checksums ($fileAlgo)" -Status "Processing file $currentIndex of $totalFiles" -PercentComplete (($currentIndex / $totalFiles) * 100);
}
Write-Host "$($_.DisplayPath): " -NoNewline;
if ($_.IsError) {
Write-Host "$($_.Status)" -ForegroundColor Red;
} elseif ($_.Status -eq "OK") {
Write-Host "OK" -ForegroundColor Green;
} else {
Write-Host "FAILED" -ForegroundColor Red;
}
};
Write-Progress -Activity "Verifying Checksums ($fileAlgo)" -Completed;
};
}
# --- GENERATION MODE ---
else {
if ([string]::IsNullOrWhiteSpace($Algorithm)) {
$Algorithm = 'SHA256';
};
$wantCustomOut = $PSBoundParameters.ContainsKey('OutFile');
$wantAutoOut = $Save.IsPresent;
if ($wantCustomOut -or $wantAutoOut) {
# Recursive wildcard auto-saves per directory
if ($Recursive -and $isWildcardGlobal) {
$groupedItems = $allItems | Group-Object DirectoryName;
foreach ($group in ($groupedItems)) {
$dirPath = $group.Name;
$filesInDir = $group.Group;
if ($wantCustomOut) {
$currentOutFile = Join-Path -Path ($dirPath) -ChildPath ($OutFile);
} else {
$currentOutFile = Join-Path -Path ($dirPath) -ChildPath "checksums.$($Algorithm.ToLower())";
};
$totalFiles = $filesInDir.Count;
$currentIndex = 0;
$linesToWrite = @();
$filesInDir | ForEach-Object -Parallel {
$item = $_
$algo = $using:Algorithm
$hash = (Get-FileHash -Path ($item.FullName) -Algorithm ($algo)).Hash.ToLower();
$relativePath = Resolve-Path -Path ($item.FullName) -Relative -ErrorAction 'SilentlyContinue';
if (-not $relativePath) { $relativePath = $item.Name; };
if ($relativePath.StartsWith(".\") -or $relativePath.StartsWith("./")) {
$relativePath = $relativePath.Substring(2);
};
[PSCustomObject]@{
Item = $item
Hash = $hash
RelativePath = $relativePath
OutputLine = "${hash} ${relativePath}"
ItemOutFile = "$($item.FullName).$($algo.ToLower())"
}
} -ThrottleLimit 16 | ForEach-Object {
$currentIndex++;
if ($showProgress -and $totalFiles -gt 0) {
Write-Progress -Activity "Computing Checksums ($Algorithm)" -Status "Directory: $(Split-Path $dirPath -Leaf) [$currentIndex/$totalFiles]" -PercentComplete (($currentIndex / $totalFiles) * 100);
}
$linesToWrite += $_.OutputLine;
Write-Output $_.OutputLine;
};
if ($showProgress) {
Write-Progress -Activity "Computing Checksums ($Algorithm)" -Completed;
}
Set-Content -Path ($currentOutFile) -Value $linesToWrite -Encoding utf8;
};
}
else {
# Custom file OR Auto file with a wildcard
if ($wantCustomOut -or ($wantAutoOut -and $isWildcardGlobal)) {
if ($wantCustomOut) {
$singleOutFile = $OutFile;
} else {
$singleOutFile = "checksums.$($Algorithm.ToLower())";
};
$totalFiles = $allItems.Count;
$currentIndex = 0;
$linesToWrite = @();
$allItems | ForEach-Object -Parallel {
$item = $_
$algo = $using:Algorithm
$hash = (Get-FileHash -Path ($item.FullName) -Algorithm ($algo)).Hash.ToLower();
$relativePath = Resolve-Path -Path ($item.FullName) -Relative -ErrorAction 'SilentlyContinue';
if (-not $relativePath) { $relativePath = $item.Name; };
if ($relativePath.StartsWith(".\") -or $relativePath.StartsWith("./")) {
$relativePath = $relativePath.Substring(2);
};
[PSCustomObject]@{
Item = $item
Hash = $hash
RelativePath = $relativePath
OutputLine = "${hash} ${relativePath}"
ItemOutFile = "$($item.FullName).$($algo.ToLower())"
}
} -ThrottleLimit 16 | ForEach-Object {
$currentIndex++;
if ($showProgress -and $totalFiles -gt 0) {
Write-Progress -Activity "Computing Checksums ($Algorithm)" -Status "Processing file $currentIndex of $totalFiles" -PercentComplete (($currentIndex / $totalFiles) * 100);
}
$linesToWrite += $_.OutputLine;
Write-Output $_.OutputLine;
};
if ($showProgress) {
Write-Progress -Activity "Computing Checksums ($Algorithm)" -Completed;
}
Set-Content -Path ($singleOutFile) -Value $linesToWrite -Encoding utf8;
}
# Auto file for explicit individual files (No wildcard)
else {
$totalFiles = $allItems.Count;
$currentIndex = 0;
$allItems | ForEach-Object -Parallel {
$item = $_
$algo = $using:Algorithm
$hash = (Get-FileHash -Path ($item.FullName) -Algorithm ($algo)).Hash.ToLower();
$relativePath = Resolve-Path -Path ($item.FullName) -Relative -ErrorAction 'SilentlyContinue';
if (-not $relativePath) { $relativePath = $item.Name; };
if ($relativePath.StartsWith(".\") -or $relativePath.StartsWith("./")) {
$relativePath = $relativePath.Substring(2);
};
[PSCustomObject]@{
Item = $item
Hash = $hash
RelativePath = $relativePath
OutputLine = "${hash} ${relativePath}"
ItemOutFile = "$($item.FullName).$($algo.ToLower())"
}
} -ThrottleLimit 16 | ForEach-Object {
$currentIndex++;
if ($showProgress -and $totalFiles -gt 0) {
Write-Progress -Activity "Computing Checksums ($Algorithm)" -Status "Processing file $currentIndex of $totalFiles" -PercentComplete (($currentIndex / $totalFiles) * 100);
}
Write-Output $_.OutputLine;
Set-Content -Path ($_.ItemOutFile) -Value $_.OutputLine -Encoding utf8;
};
if ($showProgress) {
Write-Progress -Activity "Computing Checksums ($Algorithm)" -Completed;
}
};
};
}
# Standard console output (No save flags passed)
else {
$totalFiles = $allItems.Count;
$currentIndex = 0;
$allItems | ForEach-Object -Parallel {
$item = $_
$algo = $using:Algorithm
$hash = (Get-FileHash -Path ($item.FullName) -Algorithm ($algo)).Hash.ToLower();
$relativePath = Resolve-Path -Path ($item.FullName) -Relative -ErrorAction 'SilentlyContinue';
if (-not $relativePath) { $relativePath = $item.Name; };
if ($relativePath.StartsWith(".\") -or $relativePath.StartsWith("./")) {
$relativePath = $relativePath.Substring(2);
};
[PSCustomObject]@{
Item = $item
Hash = $hash
RelativePath = $relativePath
OutputLine = "${hash} ${relativePath}"
ItemOutFile = "$($item.FullName).$($algo.ToLower())"
}
} -ThrottleLimit 16 | ForEach-Object {
$currentIndex++;
if ($showProgress -and $totalFiles -gt 0) {
Write-Progress -Activity "Computing Checksums ($Algorithm)" -Status "Processing file $currentIndex of $totalFiles" -PercentComplete (($currentIndex / $totalFiles) * 100);
}
Write-Output $_.OutputLine;
};
if ($showProgress) {
Write-Progress -Activity "Computing Checksums ($Algorithm)" -Completed;
}
};
} }
} }
# --- GENERATION MODE ---
else {
if ([string]::IsNullOrWhiteSpace($Algorithm)) {
$Algorithm = 'SHA256';
}
end {} $wantCustomOut = $PSBoundParameters.ContainsKey('OutFile');
$wantAutoOut = $Save.IsPresent;
if ($wantCustomOut -or $wantAutoOut) {
if ($Recursive -and $isWildcardGlobal) {
$groupedItems = $allItems | Group-Object DirectoryName;
foreach ($group in ($groupedItems)) {
$dirPath = $group.Name;
$filesInDir = $group.Group;
if ($wantCustomOut) {
$currentOutFile = Join-Path -Path ($dirPath) -ChildPath ($OutFile);
} else {
$currentOutFile = Join-Path -Path ($dirPath) -ChildPath "checksums.$($Algorithm.ToLower())";
}
$totalFiles = $filesInDir.Count;
$currentIndex = 0;
$linesToWrite = @();
$filesInDir | ForEach-Object -Parallel {
$item = $_
$algo = $using:Algorithm
$hash = (Get-FileHash -Path ($item.FullName) -Algorithm ($algo)).Hash.ToLower();
$relativePath = Resolve-Path -Path ($item.FullName) -Relative -ErrorAction 'SilentlyContinue';
if (-not $relativePath) { $relativePath = $item.Name; }
if ($relativePath.StartsWith(".\") -or $relativePath.StartsWith("./")) {
$relativePath = $relativePath.Substring(2);
}
[PSCustomObject]@{
Item = $item
Hash = $hash
RelativePath = $relativePath
OutputLine = "${hash} ${relativePath}"
ItemOutFile = "$($item.FullName).$($algo.ToLower())"
}
} -ThrottleLimit 16 | ForEach-Object {
$currentIndex++;
if ($showProgress -and $totalFiles -gt 0) {
Write-Progress -Activity "Computing Checksums ($Algorithm)" -Status "Directory: $(Split-Path $dirPath -Leaf) [$currentIndex/$totalFiles]" -PercentComplete (($currentIndex / $totalFiles) * 100);
}
$linesToWrite += $_.OutputLine;
Write-Output $_.OutputLine;
}
if ($showProgress) {
Write-Progress -Activity "Computing Checksums ($Algorithm)" -Completed;
}
Set-Content -Path ($currentOutFile) -Value $linesToWrite -Encoding utf8;
}
}
else {
if ($wantCustomOut -or ($wantAutoOut -and $isWildcardGlobal)) {
if ($wantCustomOut) {
$singleOutFile = $OutFile;
} else {
$singleOutFile = "checksums.$($Algorithm.ToLower())";
}
$totalFiles = $allItems.Count;
$currentIndex = 0;
$linesToWrite = @();
$allItems | ForEach-Object -Parallel {
$item = $_
$algo = $using:Algorithm
$hash = (Get-FileHash -Path ($item.FullName) -Algorithm ($algo)).Hash.ToLower();
$relativePath = Resolve-Path -Path ($item.FullName) -Relative -ErrorAction 'SilentlyContinue';
if (-not $relativePath) { $relativePath = $item.Name; }
if ($relativePath.StartsWith(".\") -or $relativePath.StartsWith("./")) {
$relativePath = $relativePath.Substring(2);
}
[PSCustomObject]@{
Item = $item
Hash = $hash
RelativePath = $relativePath
OutputLine = "${hash} ${relativePath}"
ItemOutFile = "$($item.FullName).$($algo.ToLower())"
}
} -ThrottleLimit 16 | ForEach-Object {
$currentIndex++;
if ($showProgress -and $totalFiles -gt 0) {
Write-Progress -Activity "Computing Checksums ($Algorithm)" -Status "Processing file $currentIndex of $totalFiles" -PercentComplete (($currentIndex / $totalFiles) * 100);
}
$linesToWrite += $_.OutputLine;
Write-Output $_.OutputLine;
}
if ($showProgress) {
Write-Progress -Activity "Computing Checksums ($Algorithm)" -Completed;
}
Set-Content -Path ($singleOutFile) -Value $linesToWrite -Encoding utf8;
}
else {
$totalFiles = $allItems.Count;
$currentIndex = 0;
$allItems | ForEach-Object -Parallel {
$item = $_
$algo = $using:Algorithm
$hash = (Get-FileHash -Path ($item.FullName) -Algorithm ($algo)).Hash.ToLower();
$relativePath = Resolve-Path -Path ($item.FullName) -Relative -ErrorAction 'SilentlyContinue';
if (-not $relativePath) { $relativePath = $item.Name; }
if ($relativePath.StartsWith(".\") -or $relativePath.StartsWith("./")) {
$relativePath = $relativePath.Substring(2);
}
[PSCustomObject]@{
Item = $item
Hash = $hash
RelativePath = $relativePath
OutputLine = "${hash} ${relativePath}"
ItemOutFile = "$($item.FullName).$($algo.ToLower())"
}
} -ThrottleLimit 16 | ForEach-Object {
$currentIndex++;
if ($showProgress -and $totalFiles -gt 0) {
Write-Progress -Activity "Computing Checksums ($Algorithm)" -Status "Processing file $currentIndex of $totalFiles" -PercentComplete (($currentIndex / $totalFiles) * 100);
}
Write-Output $_.OutputLine;
Set-Content -Path ($_.ItemOutFile) -Value $_.OutputLine -Encoding utf8;
}
if ($showProgress) {
Write-Progress -Activity "Computing Checksums ($Algorithm)" -Completed;
}
}
}
}
else {
$totalFiles = $allItems.Count;
$currentIndex = 0;
$allItems | ForEach-Object -Parallel {
$item = $_
$algo = $using:Algorithm
$hash = (Get-FileHash -Path ($item.FullName) -Algorithm ($algo)).Hash.ToLower();
$relativePath = Resolve-Path -Path ($item.FullName) -Relative -ErrorAction 'SilentlyContinue';
if (-not $relativePath) { $relativePath = $item.Name; }
if ($relativePath.StartsWith(".\") -or $relativePath.StartsWith("./")) {
$relativePath = $relativePath.Substring(2);
}
[PSCustomObject]@{
Item = $item
Hash = $hash
RelativePath = $relativePath
OutputLine = "${hash} ${relativePath}"
ItemOutFile = "$($item.FullName).$($algo.ToLower())"
}
} -ThrottleLimit 16 | ForEach-Object {
$currentIndex++;
if ($showProgress -and $totalFiles -gt 0) {
Write-Progress -Activity "Computing Checksums ($Algorithm)" -Status "Processing file $currentIndex of $totalFiles" -PercentComplete (($currentIndex / $totalFiles) * 100);
}
Write-Output $_.OutputLine;
}
if ($showProgress) {
Write-Progress -Activity "Computing Checksums ($Algorithm)" -Completed;
}
}
}