diff --git a/write.ps1 b/write.ps1 new file mode 100644 index 0000000..dbf739d --- /dev/null +++ b/write.ps1 @@ -0,0 +1,336 @@ +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 "" \ No newline at end of file