$Version = '2026.06.23.1' $DownloadRoot = 'https://download.odineye.io' $ArchiveName = 'OdinScan.zip' $ExecutableName = 'OdinScan.exe' $DownloadUrl = "$DownloadRoot/v$Version/$ArchiveName" $InstallDirectory = Join-Path $env:TEMP "OdinScan-$Version" $ArchivePath = Join-Path $InstallDirectory $ArchiveName $ExecutablePath = Join-Path $InstallDirectory $ExecutableName $ErrorActionPreference = 'Stop' $ProgressPreference = 'SilentlyContinue' $script:OdinBannerLines = @( ' ########' ' ############' ' ####################' ' ########## ##########' ' ########## ##########' ' ########## ##########' ' ######## ########' ' ###### #### ######' ' ###### ###### ######' ' ###### ########## ######' ' ###### #### ########## ######' ' ###### ######## ########## ######' ' ###### #################### ######' ' ###### #################### ######' ' ###### #################### ######' ' ###### #################### ######' ' ###### ################ ######' ' ###### ######## ######' ' ++######## ##########' ' ############ ############' ' ########## ##########' ' ########## ##########' ' ++################++' ' ############' ' ####' ) $script:OdinArtTop = $null $script:OdinArtLeft = 0 $script:OdinArtWidth = ($script:OdinBannerLines | ForEach-Object { $_.Length } | Measure-Object -Maximum).Maximum $script:OdinConsoleWidth = 120 $script:OdinConsoleHeight = 30 function Get-OdinArtCenterColumn { $centers = @() foreach ($line in $script:OdinBannerLines) { $trimmed = $line.TrimEnd() if ($trimmed.Length -eq 0) { continue } $first = 0 while ($first -lt $trimmed.Length -and $trimmed[$first] -eq ' ') { $first++ } $last = $trimmed.Length - 1 while ($last -ge $first -and $trimmed[$last] -eq ' ') { $last-- } if ($last -ge $first) { $centers += (($first + $last) / 2.0) } } if ($centers.Count -eq 0) { return ($script:OdinArtWidth / 2.0) } return (($centers | Measure-Object -Average).Average) } function Get-OdinCenteredArtLine { param( [string]$Text ) $left = [Math]::Round((Get-OdinArtCenterColumn) - ($Text.Length / 2.0)) if ($left -lt 0) { $left = 0 } return (' ' * $left) + $Text } $script:OdinArtCenterColumn = Get-OdinArtCenterColumn $script:OdinVersionLabel = " OdinEye - v$Version" $script:OdinVersionLeft = 0 $script:OdinVersionLine = Get-OdinCenteredArtLine -Text $script:OdinVersionLabel $script:OdinVersionRow = $null $script:OdinInteractiveConsole = $null $script:OdinPreviousCursorVisible = $null $script:OdinWavePosition = -10.0 $script:OdinWaveDirection = 1 $script:OdinWaveLastFrame = [DateTime]::MinValue function Test-OdinInteractiveConsole { try { $null = [Console]::CursorTop $null = [Console]::BufferWidth return $true } catch { return $false } } function Initialize-OdinConsole { if ($null -ne $script:OdinInteractiveConsole) { return } $script:OdinInteractiveConsole = Test-OdinInteractiveConsole } function Get-OdinConsoleSize { Initialize-OdinConsole $width = 120 $height = 30 if ($script:OdinInteractiveConsole) { try { $width = [Console]::WindowWidth $height = [Console]::WindowHeight if ($width -lt 1) { $width = [Console]::BufferWidth } if ($height -lt 1) { $height = [Console]::BufferHeight } } catch { } } return @{ Width = $width Height = $height } } function Set-OdinBannerLayout { $size = Get-OdinConsoleSize $script:OdinConsoleWidth = $size.Width $script:OdinConsoleHeight = $size.Height $screenCenter = $script:OdinConsoleWidth / 2.0 $blockHeight = $script:OdinBannerLines.Count + 2 $script:OdinArtTop = [Math]::Max(0, [Math]::Floor(($script:OdinConsoleHeight - $blockHeight) / 2)) $script:OdinArtLeft = [Math]::Max(0, [Math]::Round($screenCenter - $script:OdinArtCenterColumn)) $script:OdinVersionRow = $script:OdinArtTop + $script:OdinBannerLines.Count + 1 $script:OdinVersionLeft = $script:OdinArtLeft + [Math]::Max(0, [Math]::Round($script:OdinArtCenterColumn - ($script:OdinVersionLabel.Length / 2.0))) } function Set-OdinCursorHidden { param( [bool]$Hidden ) Initialize-OdinConsole if (-not $script:OdinInteractiveConsole) { return } try { if ($null -eq $script:OdinPreviousCursorVisible) { $script:OdinPreviousCursorVisible = [Console]::CursorVisible } [Console]::CursorVisible = -not $Hidden } catch { } } function Restore-OdinCursor { if ($null -eq $script:OdinPreviousCursorVisible) { return } try { [Console]::CursorVisible = $script:OdinPreviousCursorVisible } catch { } } function Get-OdinBannerLineColor { param( [int]$LineIndex, [double]$WaveCenter ) $distance = [Math]::Abs($LineIndex - $WaveCenter) if ($distance -lt 0.55) { return [ConsoleColor]::White } return [ConsoleColor]::DarkBlue } function Get-OdinAnimatedLineCount { return $script:OdinBannerLines.Count + 1 } function Write-OdinColoredText { param( [string]$Text, [ConsoleColor]$Color ) $previousColor = [Console]::ForegroundColor try { [Console]::ForegroundColor = $Color [Console]::Out.Write($Text) } finally { [Console]::ForegroundColor = $previousColor } } function Write-OdinBannerFrame { param( [double]$WaveCenter = -10 ) Initialize-OdinConsole if (-not $script:OdinInteractiveConsole -or $null -eq $script:OdinArtTop) { return } try { for ($i = 0; $i -lt $script:OdinBannerLines.Count; $i++) { $line = $script:OdinBannerLines[$i] $color = Get-OdinBannerLineColor -LineIndex $i -WaveCenter $WaveCenter $row = $script:OdinArtTop + $i [Console]::SetCursorPosition($script:OdinArtLeft, $row) Write-OdinColoredText -Text $line -Color $color } if ($null -ne $script:OdinVersionRow) { [Console]::SetCursorPosition($script:OdinVersionLeft, $script:OdinVersionRow) $versionColor = Get-OdinBannerLineColor -LineIndex $script:OdinBannerLines.Count -WaveCenter $WaveCenter Write-OdinColoredText -Text $script:OdinVersionLabel -Color $versionColor } } catch { } } function Clear-OdinScreen { Initialize-OdinConsole try { if ($script:OdinInteractiveConsole) { [Console]::Clear() } else { Clear-Host } } catch { Clear-Host } } function Show-OdinBanner { Initialize-OdinConsole Clear-OdinScreen if ($script:OdinInteractiveConsole) { Set-OdinBannerLayout Write-OdinBannerFrame return } foreach ($line in $script:OdinBannerLines) { Write-Host $line -ForegroundColor DarkBlue } Write-Host '' Write-Host $script:OdinVersionLine -ForegroundColor DarkBlue } function Invoke-OdinBannerAnimation { param( [scriptblock]$Continue ) Initialize-OdinConsole if (-not $script:OdinInteractiveConsole -or $null -eq $script:OdinArtTop) { while (& $Continue) { Start-Sleep -Milliseconds 50 } return } $lineCount = Get-OdinAnimatedLineCount while (& $Continue) { $now = [DateTime]::UtcNow if (($now - $script:OdinWaveLastFrame).TotalMilliseconds -ge 55) { Write-OdinBannerFrame -WaveCenter $script:OdinWavePosition $script:OdinWavePosition += (0.42 * $script:OdinWaveDirection) if ($script:OdinWavePosition -ge ($lineCount - 1)) { $script:OdinWavePosition = $lineCount - 1 $script:OdinWaveDirection = -1 } elseif ($script:OdinWavePosition -le 0) { $script:OdinWavePosition = 0 $script:OdinWaveDirection = 1 } $script:OdinWaveLastFrame = $now } Start-Sleep -Milliseconds 12 } } function Invoke-OdinLoaderUntilComplete { $script:OdinDownloadJob = Start-OdinDownloadJob $script:OdinExtractJob = $null $script:OdinProcess = $null $script:OdinExitCode = 0 Invoke-OdinBannerAnimation { if ($null -ne $script:OdinDownloadJob) { if ($script:OdinDownloadJob.State -eq 'Running') { return $true } Receive-Job -Job $script:OdinDownloadJob -ErrorAction Stop | Out-Null Remove-Job -Job $script:OdinDownloadJob -Force -ErrorAction SilentlyContinue $script:OdinDownloadJob = $null $script:OdinExtractJob = Start-OdinExtractJob return $true } if ($null -ne $script:OdinExtractJob) { if ($script:OdinExtractJob.State -eq 'Running') { return $true } Receive-Job -Job $script:OdinExtractJob -ErrorAction Stop | Out-Null Remove-Job -Job $script:OdinExtractJob -Force -ErrorAction SilentlyContinue $script:OdinExtractJob = $null if (-not (Test-Path $ExecutablePath)) { throw "Executable not found: $ExecutablePath" } $startParams = @{ FilePath = $ExecutablePath PassThru = $true WindowStyle = 'Hidden' } if ($args.Count -gt 0) { $startParams['ArgumentList'] = $args } $script:OdinProcess = Start-Process @startParams return $true } if ($null -ne $script:OdinProcess) { if (-not $script:OdinProcess.HasExited) { return $true } $script:OdinExitCode = $script:OdinProcess.ExitCode $script:OdinProcess = $null return $false } return $false } return $script:OdinExitCode } function Start-OdinDownloadJob { Start-Job -ScriptBlock { param($Uri, $OutFile) $ErrorActionPreference = 'Stop' $ProgressPreference = 'SilentlyContinue' Invoke-WebRequest -Uri $Uri -OutFile $OutFile -UseBasicParsing } -ArgumentList $DownloadUrl, $ArchivePath } function Start-OdinExtractJob { Start-Job -ScriptBlock { param($ArchivePath, $DestinationPath) $ErrorActionPreference = 'Stop' $ProgressPreference = 'SilentlyContinue' Expand-Archive -Path $ArchivePath -DestinationPath $DestinationPath -Force } -ArgumentList $ArchivePath, $InstallDirectory } if (-not (Test-Path $InstallDirectory)) { New-Item -ItemType Directory -Path $InstallDirectory -Force | Out-Null } Show-OdinBanner $exitCode = 0 try { Set-OdinCursorHidden -Hidden $true $exitCode = Invoke-OdinLoaderUntilComplete Write-OdinBannerFrame -WaveCenter $script:OdinWavePosition } finally { Restore-OdinCursor } exit $exitCode