Advertisement
jargon

7z Ripper.ps1

Mar 11th, 2025 (edited)
286
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PowerShell 24.24 KB | Gaming | 0 0
  1. # "7Scan.ps1" - Copyright Tim Keal alias jargon 2025 03/14
  2. # This script scans for 7zip archives and lists file names matched by similarity threshold.
  3. # This script is for Powershell 7 ("pwsh.exe" not "Powershell.exe")
  4.  
  5. # Locate 7-Zip dynamically if not in default location
  6. $SevenZipPath = (Get-Command 7z.exe -ErrorAction SilentlyContinue).Source
  7. if (-not $SevenZipPath) {
  8.     $SevenZipPath = "C:\Program Files\7-Zip\7z.exe"
  9. }
  10.  
  11. $debugMode = $false
  12.  
  13. # Variable to control early exit
  14. $EarlyExit = 0
  15. $hits = @() # Initialize as an empty array instead of a hashtable
  16.  
  17. $scannedArchives = @()
  18. $scannedFiles = @()
  19.  
  20. function str_repeat {
  21.     param (
  22.         [string]$string,
  23.         [int]$count
  24.     )
  25.     return ($string * $count) -join ''
  26. }
  27.  
  28. function Convert-Type {
  29.     param (
  30.         [string]$typeString,
  31.         [Parameter(Mandatory=$true)]
  32.         [Object]$inputValue
  33.     )
  34.  
  35.     # Extract type name if it's enclosed in brackets (e.g., "[int]" -> "int")
  36.     if ($typeString -match "^\[(.+)\]$") {
  37.         $typeString = $matches[1]
  38.     }
  39.  
  40.     # Map common PowerShell type accelerators
  41.     $typeMap = @{
  42.         "int"       = [int]
  43.         "string"    = [string]
  44.         "bool"      = [bool]
  45.         "double"    = [double]
  46.         "datetime"  = [datetime]
  47.         "decimal"   = [decimal]
  48.         "guid"      = [guid]
  49.         "float"     = [float]
  50.         "char"      = [char]
  51.         "byte"      = [byte]
  52.         "sbyte"     = [sbyte]
  53.         "short"     = [int16]
  54.         "ushort"    = [uint16]
  55.         "uint"      = [uint32]
  56.         "long"      = [int64]
  57.         "ulong"     = [uint64]
  58.     }
  59.  
  60.     # Determine target type
  61.     $targetType = $typeMap[$typeString] ?? [System.Type]::GetType("System.$typeString")
  62.  
  63.     if (-not $targetType) {
  64.         throw "Invalid type string: $typeString"
  65.     }
  66.  
  67.     # Attempt conversion using PowerShell's built-in conversion
  68.     try {
  69.         return [System.Management.Automation.LanguagePrimitives]::ConvertTo($inputValue, $targetType)
  70.     } catch {
  71.         throw "Failed to convert value '$inputValue' to type '$typeString'. Error: $_"
  72.     }
  73. }
  74.  
  75. function barLog {
  76.     param (
  77.         [string]$line
  78.     )
  79.    
  80.     if ($line -cmatch "{{ ([a-z]+) bar }}") {
  81.         switch ($matches[1]) {
  82.             "single" { $line = str_repeat -string "-" -count 36 }
  83.             "double" { $line = str_repeat -string "=" -count 36 }
  84.         }
  85.     }
  86.    
  87.     return $line
  88. }
  89.  
  90. function writeLog {
  91.     param (
  92.         [string]$path = "",
  93.         [string]$line = "{{ double bar }}",
  94.         [string]$color = "Yellow",
  95.         [string]$cast = "",
  96.         [string]$append = 1
  97.     )
  98.    
  99.     if ($path -eq "") { return }
  100.     if ($line -eq "") { return }
  101.    
  102.     $line = barLog -line $line
  103.        
  104.     echoLog -line $line -color $color -cast $cast -path $path
  105.    
  106.     if ($cast -ne "") { $line = "$($cast): $($line)" }
  107.    
  108.     if ($append -eq 1) {
  109.         Add-Content -Path $path -Value "$($line)`r`n"
  110.     } else {
  111.         Set-Content -Path $path -Value "$($line)`r`n"
  112.     }
  113. }
  114.  
  115. function debugLog {
  116.     param (
  117.         [string]$line = "",
  118.         [string]$color = "Cyan",
  119.         [string]$cast = "Debug",
  120.         [string]$path = "",
  121.         [string]$append = 0
  122.     )
  123.        
  124.     if ($global:debugMode -eq $false) { return }
  125.    
  126.     $line = barLog -line $line
  127.  
  128.     $color = "Cyan"
  129.     $cast = "Debug"
  130.    
  131.     echoLog -line $line -color $color -cast $cast -path $path -append $append
  132. }
  133.  
  134. function echoLog {
  135.     param (
  136.         [string]$line = "",
  137.         [string]$color = "Yellow",
  138.         [string]$cast = "",
  139.         [string]$path = "",
  140.         [string]$append = 0
  141.        
  142.     )
  143.    
  144.     if ($line -eq "") { return }
  145.    
  146.     $line = barLog -line $line
  147.  
  148.     if ($color -eq "") { $color = "White" }
  149.    
  150.     if ($cast -ne "") { $line = "$($cast): $($line)" }
  151.    
  152.     Write-Host "$($line)" -ForegroundColor $color
  153.  
  154. }
  155.  
  156. # Function to safely load JSON files
  157. function Get-SafeJsonContent {
  158.     param (
  159.         [string]$FilePath
  160.     )
  161.     if (-not (Test-Path $FilePath -PathType Leaf)) {
  162.         echoLog -line "Missing JSON file - $FilePath" -color "Yellow" -cast "Warning"
  163.         return @()  # Return empty array
  164.     }
  165.     try {
  166.         return (Get-Content -Path $FilePath -Raw | ConvertFrom-Json)
  167.     } catch {
  168.         echoLog -line "Invalid JSON format in $($FilePath)" -color "Red" -cast "Error"
  169.         return @()
  170.     }
  171. }
  172.    
  173. $cleanPatterns = (Get-SafeJsonContent "Setup\Patterns.json")
  174.  
  175. # Validate 7-Zip installation
  176. if (-not (Test-Path $SevenZipPath -PathType Leaf)) {
  177.     echoLog -line "7-Zip not found. Please install it or adjust the path." -color "Red" -cast "Error"
  178.     Read-Host "Press Enter to exit"
  179.     exit 1
  180. }
  181.  
  182. echoLog -line "7-Zip found: $($SevenZipPath)" -color "Green" -cast ""
  183.  
  184. function EarlyExit {
  185.  
  186.     # Check if a key is pressed
  187.     if ([System.Console]::KeyAvailable) {
  188.         $key = [System.Console]::ReadKey($true)  # Read the key without displaying it  
  189.         if ($key.Key -eq "Escape") {
  190.             $global:EarlyExit = 1
  191.         }
  192.     }
  193.            
  194.     return $global:EarlyExit
  195. }
  196.  
  197. function archiveAbout {
  198.     param (
  199.         [object]$archiveNameOnly = ""
  200.     )
  201.        
  202.     if ($archiveNameOnly -match $global:config.timestampPattern) {
  203.         $timestamp = $matches[0]
  204.     } else {
  205.         $timestamp = "NO_TIMESTAMP"
  206.     }
  207.    
  208.     $pattern = "^(.*?)[\-\s]+" + [regex]::Escape($timestamp)
  209.  
  210.     if ($archiveNameOnly -match $pattern) {
  211.         $project = $matches[1]
  212.         $project = $project -replace "-", " "
  213.     } else {
  214.         $project = "UNKNOWN_PROJECT"
  215.     }
  216.    
  217.     $cleanedName = ($archiveNameOnly -replace $pattern, "" -replace "\s{2,}", " " -replace "^\s+|\s+$", "")
  218.    
  219.     $pattern = '.*\((.*?)\).*'
  220.     if ($cleanedName -cmatch $pattern) {
  221.         $cleanedName = $matches[1]
  222.     }
  223.    
  224.     foreach ($p in $cleanPatterns) {
  225.         $cleanedName = ($cleanedName -replace $p, " " -replace "\s{2,}", " " -replace "^\s+|\s+$", "")
  226.     }
  227.    
  228.     foreach ($p in $global:config.titleCasePattern) {
  229.         $cleanedName = ($cleanedName -replace $p, { Convert-UpperCaseSeries $_.Value })
  230.     }
  231.    
  232.     $cleanedName = $cleanedName.Trim()
  233.    
  234.     $archiveNameOnly = $archiveNameOnly -replace "\\", "/"
  235.    
  236.     if ($archiveNameOnly -match "^(.+)\/([^\/]+)$") {
  237.         $archivePathOnly = $matches[1]
  238.         $archiveBaseOnly = $matches[2]
  239.     } else {
  240.         # echoLog -line "Regex did not match. Check `$archiveNameOnly." -color "Red" -cast "Debug"
  241.         $archivePathOnly = ""
  242.         $archiveBaseOnly = ""
  243.     }
  244.    
  245.     # Return as an object
  246.     return [PSCustomObject]@{
  247.         archiveNameOnly = $archiveNameOnly
  248.         archivePathOnly = $archivePathOnly
  249.         archiveBaseOnly = $archiveBaseOnly
  250.         cleanedName = $cleanedName
  251.         project = $project
  252.         timestamp = $timestamp
  253.     }
  254. }
  255.  
  256. function fileNameOnly {
  257.     param (
  258.         [string]$line
  259.     )
  260.    
  261.     $fileNameOnly = ($line -replace '^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\s+\S+\s+\d+\s+', '')
  262.    
  263.     if ($fileNameOnly -match '^\s*$' -or $line -match 'D\.\.\.\.') { return $False }
  264.    
  265.     if ( $fileNameOnly -match "^(.*(\\|\/)){0,}\.($($global:config.searchBlacklist))((\\|\/).*){0,}$" ) { return $False }
  266.    
  267.     $fileNameOnly = $fileNameOnly -replace "\\", "/"
  268.    
  269.     $FileFullName = $fileNameOnly
  270.        
  271.     if ($fileNameOnly -match '([^\\\/]+)$') {
  272.         $fileNameOnly = $matches[1]
  273.     }
  274.    
  275.     if ($fileNameOnly -match '^.*\.([^\.]+)$') {
  276.         $fileExtOnly = $matches[1]
  277.     }
  278.  
  279.     if ($fileNameOnly -match '^(.*)\.[^\.]+$') {
  280.         $fileNameOnly = $matches[1]
  281.     }
  282.    
  283.     # Return as an object
  284.     return [PSCustomObject]@{
  285.         FileFullName = $FileFullName
  286.         FileNameOnly = $fileNameOnly
  287.         FileExtOnly  = $fileExtOnly
  288.     }
  289. }
  290.  
  291. function Convert-UpperCaseSeries {
  292.     param (
  293.         [string]$inputString
  294.     )
  295.  
  296.     if ($inputString.Length -gt 1) {
  297.         return $inputString.Substring(0,1) + $inputString.Substring(1).ToLower()
  298.     }
  299.    
  300.     return $inputString
  301. }
  302.  
  303. # Function to calculate Levenshtein Distance as a percentile
  304. function Get-LevenshteinSimilarity {
  305.     param (
  306.         [string]$source,
  307.         [string]$target,
  308.         [string]$flags = "ickd",
  309.         [double]$percentileTolerance = 0.0  # Default to 0 (no early exit)
  310.        
  311.         # case (i)nsensitive
  312.         # (c)ontains
  313.         # (k)eywords
  314.         # Levenshtein (d)istance
  315.     )
  316.    
  317.     # case (i)nsensitive
  318.     if($flags.Contains("i")) {
  319.         $source = $source.ToLower()
  320.         $target = $target.ToLower()
  321.     }
  322.    
  323.     $sourceLength = $source.Length
  324.     $targetLength = $target.Length
  325.    
  326.     if ($sourceLength -eq 0 -and $targetLength -eq 0) { return 100.000000 }
  327.     if ($sourceLength -ne 0 -and $targetLength -eq 0) { return 0.000000 }
  328.    
  329.     # (c)ontains
  330.     if($flags.Contains("c")) {
  331.         if ($target.Contains($source)) { return 100.000000 }
  332.     }
  333.  
  334.     # (k)eywords
  335.     if($flags.Contains("k")) {
  336.         # Split the source string into an array of keywords
  337.         $keywords = $source -split '\s+'
  338.    
  339.         if ($keywords.Count -gt 0) {
  340.             # Count the number of keywords that appear in the target string
  341.             $count = ($keywords | Where-Object { $target -match "\b$_\b" }).Count
  342.            
  343.             if ($count -gt 0) {
  344.                 return $count / $keywords.Count * 100.000000
  345.             }
  346.         }
  347.     }
  348.    
  349.     # Levenshtein (d)istance
  350.     if($flags.Contains("d")) {
  351.         $distanceMatrix = @()
  352.         for ($i = 0; $i -le $sourceLength; $i++) {
  353.             $distanceMatrix += ,(@(0) * ($targetLength + 1))  
  354.         }
  355.        
  356.         for ($i = 0; $i -le $sourceLength; $i++) { $distanceMatrix[$i][0] = $i }
  357.         for ($j = 0; $j -le $targetLength; $j++) { $distanceMatrix[0][$j] = $j }
  358.        
  359.         for ($i = 1; $i -le $sourceLength; $i++) {
  360.             for ($j = 1; $j -le $targetLength; $j++) {
  361.                 $cost = if ($source[$i - 1] -eq $target[$j - 1]) { 0 } else { 1 }
  362.                
  363.                 $delete = $distanceMatrix[$i - 1][$j] + 1
  364.                 $insert = $distanceMatrix[$i][$j - 1] + 1
  365.                 $substitute = $distanceMatrix[$i - 1][$j - 1] + $cost
  366.                
  367.                 $distanceMatrix[$i][$j] = [math]::Min([math]::Min($delete, $insert), $substitute)
  368.             }
  369.            
  370.             # Early exit condition:
  371.             $maxLength = [math]::Max($sourceLength, $targetLength)
  372.             $currentDistance = $distanceMatrix[$i][$targetLength]
  373.             $projectedSimilarity = ((1 - ($currentDistance / $maxLength)) * 100)
  374.            
  375.             if ($projectedSimilarity -lt $percentileTolerance) {
  376.                 return 0.000000
  377.             }
  378.         }
  379.        
  380.         $distance = $distanceMatrix[$sourceLength][$targetLength]
  381.         $maxLength = [math]::Max($sourceLength, $targetLength)
  382.         $similarity = ((1 - ($distance / $maxLength)) * 100)
  383.         return [math]::Round($similarity, 8)
  384.     }
  385.    
  386.     return 0.000000
  387. }
  388.  
  389. # Function to scan inside .7z files that meet archive similarity criteria
  390. function Scan-Archives {
  391.     param (
  392.         [string]$archiveExt = "7z"
  393.     )
  394.    
  395.     if ((EarlyExit) -eq 1)  { return }
  396.    
  397.     # $basePath = Resolve-Path -Path $global:config.searchPath
  398.     $basePath = $global:config.searchPath
  399.  
  400.     $basePath = $basePath -replace "\\", "/"
  401.  
  402.     if ($basePath -match "^(.+)[\\\/]([^\\\/]+)$") {
  403.         $archivePathOnly = $matches[1]
  404.         $archiveBaseOnly = $matches[2]
  405.     } else {
  406.         # echoLog -line "Regex did not match. Check `$archiveNameOnly." -color "Red" -cast "Debug"
  407.         $archivePathOnly = ""
  408.         $archiveBaseOnly = ""
  409.     }
  410.    
  411.     echoLog -line "$($archiveBaseOnly)" -color "Yellow" -cast "Scanning"
  412.    
  413.     try {
  414.        
  415.         $archives = Get-ChildItem -Path $basePath -Filter "*.$($archiveExt)" -Recurse -File | ForEach-Object {
  416.        
  417.             $archive = $_
  418.            
  419.             if ((EarlyExit) -eq 1) {
  420.                
  421.                 throw
  422.             }
  423.                
  424.             if ($archive.FullName -match ".*(\/|\\)\.($($global:config.searchBlacklist))(\/|\\).*") {
  425.                 echoLog -line "$($archive.FullName)" -color "Red" -cast "Blacklisted"
  426.                 # Use 'return' to effectively 'continue' to the next item
  427.                 return
  428.             }
  429.            
  430.             $archiveFullName = $archive.FullName
  431.             $archiveFullName -match "^(.+)[\\\/]([^\\\/]+)$"
  432.             $archivePathOnly = $matches[1]
  433.             $archiveBaseOnly = $matches[2]
  434.        
  435.             echoLog -line "$($archiveBaseOnly)" -color "Yellow" -cast "Crawling"   
  436.            
  437.             Scan-Archive -archive $archive.FullName
  438.         }
  439.     }
  440.     catch {
  441.         return
  442.     }
  443. }
  444.  
  445. function Scan-Archive {
  446.     param (
  447.         [string]$archive = ""
  448.     )
  449.    
  450.     $global:scannedArchives += $archive
  451.    
  452.     if ((EarlyExit) -eq 1)  { return }
  453.    
  454.     if ($archive -eq "") { return }
  455.    
  456.     $partsArchive = (archiveAbout -archive $archive)
  457.  
  458.     $archiveSimilarity = (Get-LevenshteinSimilarity -source $global:config.archiveTestString -target $partsArchive.cleanedName -flags $global:config.archiveFlags -percentileTolerance $global:config.archiveThreshold)
  459.        
  460.     $formattedArchiveSimilarity = $archiveSimilarity.ToString("000.00000000")
  461.        
  462.     $dumpName = "$($formattedArchiveSimilarity)% > $($partsArchive.archiveBaseOnly) > $($partsArchive.timestamp) > $($partsArchive.cleanedName)"
  463.        
  464.     if ($archiveSimilarity -lt $global:config.archiveThreshold) {
  465.         echoLog -line "Did not meet or exceed archival tolerance of $($global:config.archiveThreshold)% -- is $($archiveSimilarity)% for $($partsArchive.archiveBaseOnly) > $($partsArchive.timestamp) > $($partsArchive.cleanedName)" -color "Red" -cast "Skipping"
  466.     }
  467.  
  468.     echoLog -line "$($partsArchive.archiveBaseOnly)" -color "Yellow" -cast "Scanning"
  469.        
  470.     $output = (& $SevenZipPath l -ba "$($archive)" 2>$null)
  471.  
  472.     foreach ($line in $output) {
  473.        
  474.         if ((EarlyExit) -eq 1)  { break }
  475.        
  476.         $partsFile = ( fileNameOnly -line $line )
  477.        
  478.         if ($partsFile -eq $False) { continue }
  479.                
  480.         if ($partsFile.fileExtOnly -notmatch  "^(?:$($global:config.searchExtensions))$") {
  481.             # echoLog -line "$($partsFile.fileExtOnly) > $($partsFile.fileNameOnly)" -color "Red" -cast "Skipping"
  482.             continue
  483.         }
  484.        
  485.         # echoLog -line "$($partsFile.fileExtOnly) > $($partsFile.fileNameOnly)" -color "Blue" -cast "Checking"
  486.                
  487.         $similarity = (Get-LevenshteinSimilarity -source $global:config.searchTerm -target $partsFile.fileNameOnly -flags $global:config.searchFlags -percentileTolerance $global:config.searchThreshold)
  488.        
  489.         $formattedSimilarity = $similarity.ToString("000.00000000")
  490.        
  491.         $matchString = "$($formattedSimilarity)% > $($partsArchive.archiveBaseOnly) > $($partsFile.fileExtOnly) > $($partsFile.fileNameOnly)"
  492.        
  493.         $global:scannedFiles += $partsFile.FileFullName
  494.                        
  495.         if ($similarity -ge $global:config.searchThreshold) {          
  496.            
  497.             $global:hits += "$($formattedSimilarity)% > $($partsArchive.archiveNameOnly) > $($partsFile.FileFullName)"
  498.             # Ensure we are adding to an array
  499.  
  500.             echoLog -line "$($global:hits.Count) > $($matchString)" -color "Green" -cast "Found"
  501.  
  502.         }else{
  503.            
  504.             # echoLog -line "Did not meet or exceed filepath tolerance of $($global:config.searchThreshold)% -- is $($similarity)% for $($partsFile.archiveBaseOnly) > $($partsFile.fileExtOnly) > $($partsFile.fileNameOnly)" -color "Red" -cast "Failed"
  505.         }
  506.     }
  507. }
  508.  
  509. function Json-Results {
  510.     param (
  511.         [array]$results
  512.     )
  513.  
  514.     $arr = @()  # Initialize as an array
  515.  
  516.     foreach ($result in $results) {
  517.        
  518.         $parts = $result -split " > "
  519.  
  520.         # Ensure we have enough parts to avoid index errors
  521.         if ($parts.Count -ge 3) {
  522.            
  523.             $score = $parts[0] -replace "%", ""  # Remove % symbol
  524.  
  525.             $obj = @{
  526.                 score   = $score
  527.                 archive = $parts[1]
  528.                 file    = $parts[2]
  529.             }
  530.  
  531.             $arr += $obj  # Append object to array
  532.         }
  533.     }
  534.    
  535.     # Convert the array to a JSON string
  536.     $jsonString = $arr | ConvertTo-Json -Depth 1
  537.     return $jsonString
  538. }
  539.  
  540. function Save-Results {
  541.     param (
  542.         [array]$hits
  543.     )
  544.    
  545.     $path = "Results/$($global:config.configFile)"
  546.     if (-Not (Test-Path -Path $path)) {
  547.         New-Item -Path $path -ItemType Directory | Out-Null
  548.     }
  549.    
  550.     $ts = Get-Date -Format "$($global:config.timestampLayout)"
  551.    
  552.     $outputPrefix = "Results/$($global:config.configFile)/$($global:config.configFile) $($ts)"
  553.    
  554.     $outputFile = "$($outputPrefix).txt"
  555.     $jsonOutputFile = "$($outputPrefix).json"
  556.    
  557.     $json = (Json-Results -results $hits)
  558.     Set-Content -Path $jsonOutputFile -Value $json
  559.  
  560.     writeLog -path $outputFile -line "7Scan Archive Results" -color "White" -cast "" -append 0
  561.    
  562.     # Ensure the Results directory exists
  563.     if (-not (Test-Path -Path "Results")) {
  564.         New-Item -ItemType Directory -Path "Results" | Out-Null
  565.     }
  566.    
  567.     # Ensure $hits is an array before sorting
  568.     $sortedHits = $hits | Sort-Object {
  569.         $similarity = 0
  570.         if ($_ -match '^(\d+\.\d+)%') {
  571.             $similarity = [double]$matches[1]
  572.         }
  573.         $similarity
  574.     } -Descending
  575.    
  576.     $archivesCount = $global:scannedArchives.Count  # Ensure proper counting
  577.    
  578.     $filesCount = $global:scannedFiles.Count  # Ensure proper counting
  579.    
  580.     $hitCount = $sortedHits.Count  # Ensure proper counting
  581.  
  582.     echoLog -line "$($hitCount) results to $($outputFile)" -color "Green" -cast "Saving"
  583.  
  584.     # Write to output file
  585.    
  586.     writeLog -path $outputFile -line "7Scan Archive Results" -color "White" -cast "" -append 0
  587.     writeLog -path $outputFile -line "{{ single bar }}" -color "Green" -cast ""
  588.     writeLog -path $outputFile -line "$($filesCount) files in $($archivesCount) archives" -color "White" -cast "Scanned"
  589.     writeLog -path $outputFile -line "$($hits.Count)" -color "White" -cast "Hits"
  590.     writeLog -path $outputFile -line "{{ single bar }}" -color "Blue" -cast ""
  591.  
  592.     # Dynamically write config settings
  593.     foreach ($key in $global:config.Keys) {
  594.         writeLog -path $outputFile -line "$($key): $($global:config[$key])" -color "White" -cast ""
  595.     }
  596.  
  597.     writeLog -path $outputFile -line "{{ double bar }}" -color "Green" -cast ""
  598.  
  599.     writeLog -path $outputFile -line ($sortedHits -join "`r`n") -color "White" -cast ""
  600.  
  601.     writeLog -path $outputFile -line "{{ double bar }}" -color "Green" -cast ""
  602.  
  603.     writeLog -path $outputFile -line "$($hitCount) scanned results saved to $($outputFile)" -color "White" -cast ""
  604. }
  605.  
  606. function Convert-Type {
  607.     param (
  608.         [string]$typeString,
  609.         [string]$inputValue
  610.     )
  611.     switch ($typeString) {
  612.         "int" { return [int]$inputValue }
  613.         "bool" { return [bool]$inputValue }
  614.         "float" { return [float]$inputValue }
  615.         "string" { return [string]$inputValue }
  616.         default { return $inputValue }
  617.     }
  618. }
  619.  
  620. function Ini-Config {
  621.     param (
  622.         [string]$configFile = "Default"
  623.     )
  624.  
  625.     # Ensure the global config exists
  626.     if (-not $global:config) {
  627.         $global:config = @{}
  628.     }
  629.  
  630.     # Assign configuration properties
  631.     $global:config.configFile = $configFile
  632.     $global:config.configPath = "Config/$($global:config.configFile) Config.json"
  633.  
  634.     # Debugging: Print config path before proceeding
  635.     debugLog -line "Config path set to '$($global:config.configPath)'" -color "Cyan" -cast "Debug"
  636.  
  637.     # Check if the config file exists
  638.     if (-Not (Test-Path $global:config.configPath)) {
  639.         debugLog -line "Config file not found: $($global:config.configPath)" -color "Red" -cast "Error"
  640.         return $false
  641.     }
  642.  
  643.     return $true
  644. }
  645.  
  646. function Load-Config {
  647.     param (
  648.         [bool]$PromptUser = $true
  649.     )
  650.  
  651.     # Ensure the global config exists
  652.     if (-not $global:config) {
  653.         debugLog -line "Global config is not initialized!" -color "Red" -cast "Error"
  654.         return $false
  655.     }
  656.  
  657.     # Debugging: Print config before JSON loading
  658.     debugLog -line "Checking configPath at start of Load-Config: '$($global:config.configPath)'" -color "Yellow" -cast "Debug"
  659.  
  660.     # Ensure the config path is set
  661.     if (-not $global:config.configPath) {
  662.         debugLog -line "Config path is not set properly at Load-Config start!" -color "Red" -cast "Error"
  663.         return $false
  664.     }
  665.  
  666.     # Debugging: Print config just before reading JSON
  667.     debugLog -line "Attempting to load config from '$($global:config.configPath)'" -color "Yellow" -cast "Debug"
  668.  
  669.     # Check if the config file exists
  670.     if (-Not (Test-Path $global:config.configPath)) {
  671.         debugLog -line "Config file not found: $($global:config.configPath)" -color "Red" -cast "Error"
  672.         return $false
  673.     }
  674.  
  675.     try {
  676.         # Read JSON file
  677.         $rawJson = Get-Content -Path $global:config.configPath -Raw
  678.  
  679.         # Debugging: Print file contents
  680.         debugLog -line "Raw JSON Data: $($rawJson)" -color "Cyan" -cast "Debug"
  681.  
  682.         if (-not $rawJson) {
  683.             debugLog -line "Config file is empty or unreadable!" -color "Red" -cast "Error"
  684.             return $false
  685.         }
  686.  
  687.         $parsedJson = $rawJson | ConvertFrom-Json
  688.  
  689.         # Debugging: Print parsed JSON
  690.         debugLog -line "Parsed JSON: $(($parsedJson | ConvertTo-Json -Compress))" -color "Cyan" -cast "Debug"
  691.  
  692.         if (-not $parsedJson) {
  693.             debugLog -line "Error: Failed to parse JSON file!" -color "Red" -cast "Error"
  694.             return $false
  695.         }
  696.  
  697.         # Special case for configFile (allows reloading)
  698.         if ($parsedJson.PSObject.Properties.Name -contains "configFile") {
  699.             $entry = $parsedJson.configFile
  700.             if ($entry -is [array] -and $entry.Count -ge 2) {
  701.                 $defaultValue = $entry[1]
  702.  
  703.                 # Debugging: Print before modifying config path
  704.                 debugLog -line "Before modifying configPath: '$($global:config.configPath)'" -color "Cyan" -cast "Debug"
  705.  
  706.                 # User input for config file selection
  707.                 if ($PromptUser) {
  708.                     $userInput = Read-Host "Choose a config file (default: '$($defaultValue)')"
  709.                     $global:config.configFile = if ($userInput) { $userInput } else { $defaultValue }
  710.                 } else {
  711.                     $global:config.configFile = $defaultValue
  712.                 }
  713.  
  714.                 # ✅ Fix: Prevent overwriting configPath with an empty string
  715.                 if ($global:config.configFile -and $global:config.configFile -ne "") {
  716.                     $global:config.configPath = "Config/$($global:config.configFile) Config.json"
  717.                     debugLog -line "Updated configPath: '$($global:config.configPath)'" -color "Cyan" -cast "Debug"
  718.                 } else {
  719.                     debugLog -line "Warning: configFile is empty. Retaining previous configPath." -color "Yellow" -cast "Debug"
  720.                 }
  721.  
  722.                 # Revalidate config file existence
  723.                 if (-not (Test-Path $global:config.configPath)) {
  724.                     debugLog -line "Updated config file not found: $global:config.configPath" -color "Red" -cast "Error"
  725.                     return $false
  726.                 }
  727.             }
  728.         }
  729.  
  730.         # Debugging: Final config path after loading JSON
  731.         debugLog -line "Final config path in Load-Config: '$($global:config.configPath)'" -color "Green" -cast "Debug"
  732.  
  733.         return $true
  734.     }
  735.     catch {
  736.         debugLog -line "Error parsing config file: $_" -color "Red" -cast "Error"
  737.         return $false
  738.     }
  739. }
  740.  
  741. function Process-Config {
  742.     param (
  743.         [bool]$PromptUser = $true
  744.     )
  745.  
  746.     # Ensure the global config exists
  747.     if (-not $global:config) {
  748.         echoLog -line "Global config is not initialized!" -color "Red" -cast "Error"
  749.         return $false
  750.     }
  751.  
  752.     # Ensure the config path is set
  753.     if (-not $global:config.configPath) {
  754.         echoLog -line "Config path is not set properly at Load-Config start!" -color "Red" -cast "Error"
  755.         return $false
  756.     }
  757.  
  758.     debugLog -line "Attempting to load config from '$($global:config.configPath)'" -color "Yellow" -cast "Debug"
  759.  
  760.     # Check if the config file exists
  761.     if (-Not (Test-Path $global:config.configPath)) {
  762.         echoLog -line "Config file not found: $($global:config.configPath)" -color "Red" -cast "Error"
  763.         return $false
  764.     }
  765.  
  766.     try {
  767.         # Read JSON file
  768.         $rawJson = Get-Content -Path $global:config.configPath -Raw
  769.         if (-not $rawJson) {
  770.             echoLog -line "Config file is empty or unreadable!" -color "Red" -cast "Error"
  771.             return $false
  772.         }
  773.  
  774.         # Convert JSON to object
  775.         $parsedJson = $rawJson | ConvertFrom-Json
  776.         if (-not $parsedJson) {
  777.             echoLog -line "Error: Failed to parse JSON file!" -color "Red" -cast "Error"
  778.             return $false
  779.         }
  780.  
  781.         # Process configuration
  782.         foreach ($key in $parsedJson.PSObject.Properties.Name) {
  783.             $valueArray = $parsedJson.$key  # Expecting: ["type", "default_value"]
  784.  
  785.             # Validate the expected structure
  786.             if ($valueArray -isnot [System.Array] -or $valueArray.Count -ne 2) {
  787.                 echoLog -line "Skipping invalid config entry: $key" -color "Red" -cast "Error"
  788.                 continue
  789.             }
  790.  
  791.             $type = $valueArray[0]  # Expected type
  792.             $defaultValue = $valueArray[1]  # Default value
  793.  
  794.             # Skip `configFile` after reading it
  795.             if ($key -eq "configFile") {
  796.                 $global:config[$key] = $defaultValue
  797.                 continue
  798.             }
  799.  
  800.             # Prompt user if necessary
  801.             if ($PromptUser) {
  802.                 $userInput = Read-Host "Enter value for '$key' (default: '$defaultValue')"
  803.                 $finalValue = if ($userInput) { $userInput } else { $defaultValue }
  804.             } else {
  805.                 $finalValue = $defaultValue
  806.             }
  807.  
  808.             # Ensure type safety
  809.             switch ($type) {
  810.                 "double" {
  811.                     $global:config[$key] = [double]$finalValue
  812.                 }
  813.                 "int" {
  814.                     $global:config[$key] = [int]$finalValue
  815.                 }
  816.                 "string" {
  817.                     $global:config[$key] = [string]$finalValue
  818.                 }
  819.                 default {
  820.                     echoLog -line "Unknown type '$type' for '$key', storing as string." -color "Yellow" -cast "Warning"
  821.                     $global:config[$key] = [string]$finalValue
  822.                 }
  823.             }
  824.         }
  825.        
  826.         return $true
  827.     }
  828.     catch {
  829.         echoLog -line "Error parsing config file: $_" -color "Red" -cast "Error"
  830.         return $false
  831.     }
  832. }
  833.  
  834. $config = @{}
  835.  
  836. # Main Execution
  837. if (Ini-Config -configFile "Default") {
  838.    
  839.     debugLog -line "Confirming config path before Load-Config: '$($config.configPath)'" -color "Cyan" -cast "Debug"
  840.    
  841.     # Debugging: Print entire global config
  842.     debugLog -line "Global Config Dump BEFORE Load-Config: $(($config | ConvertTo-Json -Compress))" -color "Cyan" -cast "Debug"
  843.  
  844.     if (Load-Config -PromptUser $true) {
  845.         if (Process-Config -PromptUser $true) {
  846.             Scan-Archives
  847.         }
  848.     }
  849. }
  850. else {
  851.     debugLog -line "Ini-Config failed! Check why config path is not being set." -color "Red" -cast "Error"
  852. }
  853.  
  854. Save-Results -hits $hits
  855. Read-Host "Press Enter to exit"
  856.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement