336 lines
10 KiB
PowerShell
336 lines
10 KiB
PowerShell
param(
|
|
[string]$usbLetter,
|
|
[string]$srcPath = ".\src",
|
|
[string]$distPath = ".\dist\main.exe"
|
|
)
|
|
|
|
function Write-Header {
|
|
param([string]$title)
|
|
$width = 80
|
|
$padding = [math]::Floor(($width - $title.Length - 2) / 2)
|
|
Write-Host ""
|
|
Write-Host ("=" * $width) -ForegroundColor Cyan
|
|
Write-Host ("=" + (" " * $padding) + $title + (" " * $padding) + "=") -ForegroundColor Cyan
|
|
Write-Host ("=" * $width) -ForegroundColor Cyan
|
|
Write-Host ""
|
|
}
|
|
|
|
function Write-Step {
|
|
param([string]$message)
|
|
Write-Host "[*] " -NoNewline -ForegroundColor Green
|
|
Write-Host $message -ForegroundColor White
|
|
}
|
|
|
|
function Write-ErrorMsg {
|
|
param([string]$message)
|
|
Write-Host "[X] " -NoNewline -ForegroundColor Red
|
|
Write-Host $message -ForegroundColor Red
|
|
}
|
|
|
|
function Write-Success {
|
|
param([string]$message)
|
|
Write-Host "[+] " -NoNewline -ForegroundColor Green
|
|
Write-Host $message -ForegroundColor Green
|
|
}
|
|
|
|
function Write-Warning {
|
|
param([string]$message)
|
|
Write-Host "[!] " -NoNewline -ForegroundColor Yellow
|
|
Write-Host $message -ForegroundColor Yellow
|
|
}
|
|
|
|
function Write-ProgressBar {
|
|
param(
|
|
[int]$current,
|
|
[int]$total,
|
|
[string]$activity
|
|
)
|
|
$percent = [math]::Min(100, [math]::Floor(($current / $total) * 100))
|
|
$barWidth = 50
|
|
$completed = [math]::Floor(($percent / 100) * $barWidth)
|
|
$remaining = $barWidth - $completed
|
|
|
|
$completedBar = "#" * $completed
|
|
$remainingBar = "-" * $remaining
|
|
$bar = "[" + $completedBar + $remainingBar + "]"
|
|
|
|
Write-Host ("`r$activity $bar $percent% ($current/$total)") -NoNewline -ForegroundColor Cyan
|
|
|
|
if ($current -eq $total) {
|
|
Write-Host ""
|
|
}
|
|
}
|
|
|
|
function Test-Prerequisites {
|
|
$errors = @()
|
|
|
|
# Check if running as admin (for autorun.inf)
|
|
$isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
|
|
if (-not $isAdmin) {
|
|
Write-Warning "Not running as administrator. Autorun.inf may not work properly."
|
|
}
|
|
|
|
# Validate USB letter format
|
|
if ($usbLetter -notmatch '^[A-Z]:?$') {
|
|
$errors += "Invalid USB letter format. Use format like 'E:' or 'E'"
|
|
}
|
|
|
|
# Normalize USB letter
|
|
$script:usbLetter = $usbLetter.TrimEnd(':') + ':'
|
|
|
|
# Check if paths exist
|
|
if (-not (Test-Path $srcPath)) {
|
|
$errors += "Source path not found: $srcPath"
|
|
}
|
|
|
|
if (-not (Test-Path $distPath)) {
|
|
$errors += "Distribution executable not found: $distPath"
|
|
}
|
|
|
|
# Check USB drive
|
|
$usbRoot = "$($script:usbLetter)\"
|
|
if (-not (Test-Path $usbRoot)) {
|
|
$errors += "USB drive not found: $usbRoot"
|
|
} else {
|
|
# Check if USB has enough space
|
|
try {
|
|
$drive = Get-PSDrive -Name $script:usbLetter.TrimEnd(':') -ErrorAction Stop
|
|
$freeSpaceMB = [math]::Round($drive.Free / 1MB, 2)
|
|
|
|
# Estimate required space
|
|
$srcSize = (Get-ChildItem -Path $srcPath -Recurse -ErrorAction SilentlyContinue |
|
|
Where-Object { $_.FullName -notmatch "__pycache__" } |
|
|
Measure-Object -Property Length -Sum).Sum
|
|
$distSize = (Get-Item $distPath).Length
|
|
$requiredMB = [math]::Round(($srcSize + $distSize) / 1MB * 1.1, 2)
|
|
|
|
if ($freeSpaceMB -lt $requiredMB) {
|
|
$errors += "Insufficient space on USB. Required: ~$requiredMB MB, Available: $freeSpaceMB MB"
|
|
}
|
|
} catch {
|
|
Write-Warning "Could not check USB free space: $_"
|
|
}
|
|
}
|
|
|
|
return $errors
|
|
}
|
|
|
|
Clear-Host
|
|
Write-Header "wxIRC USB Writer"
|
|
|
|
# Show usage if no USB letter provided
|
|
if (-not $usbLetter) {
|
|
Write-Host "Usage: .\write.ps1 -usbLetter E:" -ForegroundColor Yellow
|
|
Write-Host ""
|
|
Write-Host "Parameters:" -ForegroundColor Cyan
|
|
Write-Host " -usbLetter : Target USB drive letter (required)" -ForegroundColor White
|
|
Write-Host " -srcPath : Source directory (default: .\src)" -ForegroundColor White
|
|
Write-Host " -distPath : Executable path (default: .\dist\main.exe)" -ForegroundColor White
|
|
Write-Host ""
|
|
exit 1
|
|
}
|
|
|
|
Write-Step "Validating prerequisites..."
|
|
$validationErrors = Test-Prerequisites
|
|
|
|
if ($validationErrors.Count -gt 0) {
|
|
Write-Host ""
|
|
foreach ($err in $validationErrors) {
|
|
Write-ErrorMsg $err
|
|
}
|
|
Write-Host ""
|
|
exit 1
|
|
}
|
|
|
|
Write-Success "All prerequisites validated"
|
|
|
|
# Convert to absolute paths
|
|
try {
|
|
$srcPath = (Resolve-Path $srcPath -ErrorAction Stop).Path
|
|
$distPath = (Resolve-Path $distPath -ErrorAction Stop).Path
|
|
} catch {
|
|
Write-ErrorMsg "Failed to resolve paths: $_"
|
|
exit 1
|
|
}
|
|
|
|
$usbRoot = "$usbLetter\"
|
|
|
|
Write-Host ""
|
|
Write-Host "Configuration:" -ForegroundColor Cyan
|
|
Write-Host " Source : $srcPath" -ForegroundColor White
|
|
Write-Host " Executable : $distPath" -ForegroundColor White
|
|
Write-Host " USB Target : $usbRoot" -ForegroundColor White
|
|
Write-Host ""
|
|
|
|
# Confirm before proceeding
|
|
Write-Host "This will overwrite existing files on the USB drive." -ForegroundColor Yellow
|
|
$response = Read-Host "Continue? (Y/N)"
|
|
if ($response -ne 'Y' -and $response -ne 'y') {
|
|
Write-Host "Operation cancelled." -ForegroundColor Yellow
|
|
exit 0
|
|
}
|
|
|
|
Write-Host ""
|
|
$startTime = Get-Date
|
|
|
|
Write-Step "Preparing source directory..."
|
|
$srcDest = Join-Path $usbRoot "src"
|
|
|
|
if (Test-Path $srcDest) {
|
|
try {
|
|
Remove-Item -Recurse -Force $srcDest -ErrorAction Stop
|
|
} catch {
|
|
Write-ErrorMsg "Failed to remove existing src directory: $_"
|
|
exit 1
|
|
}
|
|
}
|
|
|
|
try {
|
|
New-Item -ItemType Directory -Path $srcDest -ErrorAction Stop | Out-Null
|
|
} catch {
|
|
Write-ErrorMsg "Failed to create src directory: $_"
|
|
exit 1
|
|
}
|
|
|
|
Write-Step "Scanning source files..."
|
|
$allFiles = @(Get-ChildItem -Path $srcPath -Recurse -ErrorAction SilentlyContinue | Where-Object {
|
|
$_.FullName -notmatch "__pycache__"
|
|
})
|
|
|
|
$totalFiles = $allFiles.Count
|
|
$currentFile = 0
|
|
|
|
Write-Step "Copying $totalFiles files..."
|
|
|
|
foreach ($item in $allFiles) {
|
|
$currentFile++
|
|
|
|
try {
|
|
$relative = $item.FullName.Substring($srcPath.Length)
|
|
$target = Join-Path $srcDest $relative
|
|
|
|
if ($item.PSIsContainer) {
|
|
if (-not (Test-Path $target)) {
|
|
New-Item -ItemType Directory -Path $target -ErrorAction Stop | Out-Null
|
|
}
|
|
} else {
|
|
$targetDir = Split-Path -Parent $target
|
|
if (-not (Test-Path $targetDir)) {
|
|
New-Item -ItemType Directory -Path $targetDir -ErrorAction Stop | Out-Null
|
|
}
|
|
Copy-Item $item.FullName -Destination $target -Force -ErrorAction Stop
|
|
}
|
|
|
|
Write-ProgressBar -current $currentFile -total $totalFiles -activity "Copying files"
|
|
} catch {
|
|
Write-Host ""
|
|
Write-ErrorMsg "Failed to copy $($item.FullName): $_"
|
|
}
|
|
}
|
|
|
|
Write-Success "Source files copied successfully"
|
|
|
|
Write-Step "Copying executable..."
|
|
try {
|
|
$exeDest = Join-Path $usbRoot "wxIRC.exe"
|
|
$exeSize = (Get-Item $distPath).Length
|
|
$exeSizeMB = [math]::Round($exeSize / 1MB, 2)
|
|
|
|
Write-Host " Size: $exeSizeMB MB" -ForegroundColor Gray
|
|
|
|
# Copy with progress simulation for large files
|
|
if ($exeSize -gt 5MB) {
|
|
$buffer = 1MB
|
|
$read = 0
|
|
$totalChunks = [math]::Ceiling($exeSize / $buffer)
|
|
$currentChunk = 0
|
|
|
|
$sourceStream = [System.IO.File]::OpenRead($distPath)
|
|
$destStream = [System.IO.File]::Create($exeDest)
|
|
$bufferArray = New-Object byte[] $buffer
|
|
|
|
try {
|
|
while (($read = $sourceStream.Read($bufferArray, 0, $buffer)) -gt 0) {
|
|
$destStream.Write($bufferArray, 0, $read)
|
|
$currentChunk++
|
|
Write-ProgressBar -current $currentChunk -total $totalChunks -activity "Copying executable"
|
|
}
|
|
} finally {
|
|
$sourceStream.Close()
|
|
$destStream.Close()
|
|
}
|
|
} else {
|
|
# For smaller files, just copy directly
|
|
Copy-Item $distPath -Destination $exeDest -Force -ErrorAction Stop
|
|
Write-ProgressBar -current 1 -total 1 -activity "Copying executable"
|
|
}
|
|
|
|
Write-Success "Executable copied as wxIRC.exe"
|
|
} catch {
|
|
Write-ErrorMsg "Failed to copy executable: $_"
|
|
exit 1
|
|
}
|
|
|
|
Write-Step "Creating autorun.inf..."
|
|
try {
|
|
$autorun = Join-Path $usbRoot "autorun.inf"
|
|
@(
|
|
"[AutoRun]"
|
|
"open=wxIRC.exe"
|
|
"label=wxIRC"
|
|
"icon=src\icon.ico"
|
|
) | Set-Content -Path $autorun -Encoding ASCII -ErrorAction Stop
|
|
|
|
# Try to set hidden attribute (may fail without admin)
|
|
try {
|
|
$autorunItem = Get-Item $autorun -ErrorAction Stop
|
|
$autorunItem.Attributes = $autorunItem.Attributes -bor [System.IO.FileAttributes]::Hidden
|
|
} catch {
|
|
# Silently continue if can't set hidden attribute
|
|
}
|
|
|
|
Write-Success "Autorun.inf created"
|
|
} catch {
|
|
Write-ErrorMsg "Failed to create autorun.inf: $_"
|
|
}
|
|
|
|
Write-Step "Generating write info..."
|
|
$endTime = Get-Date
|
|
$duration = ($endTime - $startTime).TotalSeconds
|
|
|
|
try {
|
|
$sizeMB = (Get-ChildItem -Recurse -Path $usbRoot -ErrorAction SilentlyContinue |
|
|
Measure-Object Length -Sum).Sum / 1MB
|
|
$speed = if ($duration -gt 0) { [math]::Round(($sizeMB / $duration), 2) } else { 0 }
|
|
|
|
$infoFile = Join-Path $usbRoot "writeinfo.txt"
|
|
@(
|
|
"wxIRC USB Write Report"
|
|
"=" * 50
|
|
"Completed : $endTime"
|
|
"Total Size : $([math]::Round($sizeMB, 2)) MB"
|
|
"Duration : $([math]::Round($duration, 2)) seconds"
|
|
"Write Speed : $speed MB/s"
|
|
"Files Copied : $totalFiles"
|
|
""
|
|
"USB Drive : $usbRoot"
|
|
) | Set-Content -Path $infoFile -ErrorAction Stop
|
|
|
|
Write-Success "Write info saved to writeinfo.txt"
|
|
} catch {
|
|
Write-ErrorMsg "Failed to create writeinfo.txt: $_"
|
|
}
|
|
|
|
Write-Host ""
|
|
Write-Host ("=" * 80) -ForegroundColor Green
|
|
Write-Host " USB WRITE COMPLETED SUCCESSFULLY!" -ForegroundColor Green
|
|
Write-Host ("=" * 80) -ForegroundColor Green
|
|
Write-Host ""
|
|
Write-Host "Summary:" -ForegroundColor Cyan
|
|
Write-Host " Files Copied : $totalFiles" -ForegroundColor White
|
|
Write-Host " Total Size : $([math]::Round($sizeMB, 2)) MB" -ForegroundColor White
|
|
Write-Host " Duration : $([math]::Round($duration, 2)) seconds" -ForegroundColor White
|
|
Write-Host " Write Speed : $speed MB/s" -ForegroundColor White
|
|
Write-Host ""
|
|
Write-Host "The USB drive is ready to use!" -ForegroundColor Green
|
|
Write-Host "" |