Run via:
irm "https://gist.githubusercontent.com/caccialdo/d26e89e9eb7b697811c4207e856d8d45/raw/win-iso-splitter.ps1" | iex
# Start logging the session to a file | |
$logFile = "$env:UserProfile\Desktop\winutil_log.txt" | |
Start-Transcript -Path $logFile -Append -Force | |
# Load WPF and Windows Forms assemblies | |
Add-Type -AssemblyName PresentationFramework | |
Add-Type -AssemblyName System.Windows.Forms | |
# Nord Dark Mode Polar Night Colors | |
$backgroundColor = "#2E3440" # Polar Night | |
$textColor = "#D8DEE9" # Snow Storm | |
$buttonColor = "#4C566A" # Polar Night Lighter | |
$textBoxBackgroundColor = "#4C566A" # Darker background for text box | |
$textBoxTextColor = "#ECEFF4" # Lighter text color inside the textbox | |
$completedColor = "#A3BE8C" # Frost Green (completed step color) | |
# Function to create the GUI with the Nord theme | |
function Show-GUI { | |
[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework') | |
# Create Window | |
$window = New-Object System.Windows.Window | |
$window.Title = "Windows Image Splitter" | |
$window.Width = 400 | |
$window.Height = 450 | |
$window.Background = (New-Object Windows.Media.SolidColorBrush ([System.Windows.Media.ColorConverter]::ConvertFromString($backgroundColor))) | |
$window.Foreground = (New-Object Windows.Media.SolidColorBrush ([System.Windows.Media.ColorConverter]::ConvertFromString($textColor))) | |
$window.WindowStartupLocation = "CenterScreen" | |
# Create StackPanel for layout | |
$panel = New-Object System.Windows.Controls.StackPanel | |
$panel.Margin = "10" | |
$window.Content = $panel | |
# Label for the image file path | |
$label = New-Object System.Windows.Controls.Label | |
$label.Content = "Select Windows ISO or install.wim:" | |
$label.FontSize = 14 | |
$label.Margin = "0,0,0,10" | |
$label.Foreground = (New-Object Windows.Media.SolidColorBrush ([System.Windows.Media.ColorConverter]::ConvertFromString($textColor))) | |
$panel.Children.Add($label) | |
# Textbox to show selected file | |
$textbox = New-Object System.Windows.Controls.TextBox | |
$textbox.Width = 350 | |
$textbox.Height = 25 | |
$textbox.Background = (New-Object Windows.Media.SolidColorBrush ([System.Windows.Media.ColorConverter]::ConvertFromString($textBoxBackgroundColor))) # Dark background | |
$textbox.Foreground = (New-Object Windows.Media.SolidColorBrush ([System.Windows.Media.ColorConverter]::ConvertFromString($textBoxTextColor))) # Light text | |
$panel.Children.Add($textbox) | |
# Button to browse file | |
$buttonBrowse = New-Object System.Windows.Controls.Button | |
$buttonBrowse.Content = "Browse" | |
$buttonBrowse.Width = 100 | |
$buttonBrowse.Background = (New-Object Windows.Media.SolidColorBrush ([System.Windows.Media.ColorConverter]::ConvertFromString($buttonColor))) | |
$buttonBrowse.Foreground = (New-Object Windows.Media.SolidColorBrush ([System.Windows.Media.ColorConverter]::ConvertFromString($textColor))) | |
$buttonBrowse.Margin = "0,10,0,10" | |
$panel.Children.Add($buttonBrowse) | |
# To-do list for tasks | |
$toDoList = New-Object System.Windows.Controls.ListBox | |
$toDoList.Width = 350 | |
$toDoList.Height = 150 | |
$toDoList.Background = (New-Object Windows.Media.SolidColorBrush ([System.Windows.Media.ColorConverter]::ConvertFromString($backgroundColor))) | |
$toDoList.Foreground = (New-Object Windows.Media.SolidColorBrush ([System.Windows.Media.ColorConverter]::ConvertFromString($textColor))) | |
$panel.Children.Add($toDoList) | |
# Add tasks to the to-do list | |
$tasks = @("Extract ISO", "Find install.wim", "Split install.wim", "Recreate ISO with install.swm", "Clean up unnecessary files") | |
foreach ($task in $tasks) { | |
$listItem = New-Object System.Windows.Controls.ListBoxItem | |
$listItem.Content = $task | |
$listItem.Foreground = (New-Object Windows.Media.SolidColorBrush ([System.Windows.Media.ColorConverter]::ConvertFromString($textColor))) | |
$toDoList.Items.Add($listItem) | |
} | |
# Button to start the process | |
$buttonStart = New-Object System.Windows.Controls.Button | |
$buttonStart.Content = "Start" | |
$buttonStart.Width = 100 | |
$buttonStart.Background = (New-Object Windows.Media.SolidColorBrush ([System.Windows.Media.ColorConverter]::ConvertFromString($buttonColor))) | |
$buttonStart.Foreground = (New-Object Windows.Media.SolidColorBrush ([System.Windows.Media.ColorConverter]::ConvertFromString($textColor))) | |
$panel.Children.Add($buttonStart) | |
# Button to auto-install the latest ADK | |
$buttonInstallADK = New-Object System.Windows.Controls.Button | |
$buttonInstallADK.Content = "Auto-Install the latest ADK" | |
$buttonInstallADK.Width = 200 | |
$buttonInstallADK.Background = (New-Object Windows.Media.SolidColorBrush ([System.Windows.Media.ColorConverter]::ConvertFromString($buttonColor))) | |
$buttonInstallADK.Foreground = (New-Object Windows.Media.SolidColorBrush ([System.Windows.Media.ColorConverter]::ConvertFromString($textColor))) | |
$buttonInstallADK.Margin = "0,20,0,0" | |
$panel.Children.Add($buttonInstallADK) | |
# Button action for file picker | |
$buttonBrowse.Add_Click({ | |
Add-Type -AssemblyName System.Windows.Forms # Load Forms assembly before using the dialog | |
$dialog = New-Object System.Windows.Forms.OpenFileDialog | |
$dialog.Filter = "ISO files (*.iso)|*.iso|WIM files (*.wim)|*.wim" | |
if ($dialog.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK) { | |
$textbox.Text = $dialog.FileName | |
} | |
}) | |
# Button action for starting the process | |
$buttonStart.Add_Click({ | |
$filePath = $textbox.Text | |
if (-not (Test-Path $filePath)) { | |
[System.Windows.MessageBox]::Show("Please select a valid file.") | |
return | |
} | |
# Step 1: Check necessary tools (DISM, oscdimg, etc.) | |
if (-not (Get-Command dism -ErrorAction SilentlyContinue)) { | |
[System.Windows.MessageBox]::Show("DISM tool not found. You can download it from Microsoft.") | |
return | |
} | |
# Step 2: Perform actions based on file type (ISO or WIM) | |
if ($filePath -like "*.iso") { | |
Update-ToDo $toDoList 0 | |
ExtractAndProcessISO $filePath | |
} elseif ($filePath -like "*.wim") { | |
Update-ToDo $toDoList 2 # Directly go to splitting the WIM file | |
SplitWIM $filePath | |
} | |
}) | |
# Button action for installing the latest ADK | |
$buttonInstallADK.Add_Click({ | |
$confirmInstall = [System.Windows.MessageBox]::Show("This will download and install the latest Windows ADK. Continue?", "Confirm ADK Installation", [System.Windows.MessageBoxButton]::YesNo) | |
if ($confirmInstall -eq [System.Windows.MessageBoxResult]::Yes) { | |
Install-LatestADK | |
} | |
}) | |
$window.ShowDialog() | |
} | |
# Function to update the to-do list and strike through completed tasks | |
function Update-ToDo { | |
param ( | |
[System.Windows.Controls.ListBox]$listBox, | |
[int]$index | |
) | |
$item = $listBox.Items[$index] | |
$item.Content = "[X] $($item.Content)" | |
$item.Foreground = (New-Object Windows.Media.SolidColorBrush ([System.Windows.Media.ColorConverter]::ConvertFromString($completedColor))) | |
} | |
# Function to split the install.wim and recreate ISO | |
function ExtractAndProcessISO { | |
param ($isoFilePath) | |
# Step 1: Extract ISO | |
Write-Host "Extracting the ISO..." | |
$outputFolder = "$env:UserProfile\Desktop\ExtractedISO" | |
New-Item -ItemType Directory -Path $outputFolder -Force | Out-Null | |
$mountFolder = "$outputFolder\mount" | |
$mountResult = Mount-DiskImage -ImagePath $isoFilePath -PassThru | Get-Volume | |
$driveLetter = $mountResult.DriveLetter | |
# Copy the contents of the mounted ISO to the output folder | |
robocopy "$driveLetter`:\" "$outputFolder" /E /R:2 /W:5 | |
Dismount-DiskImage -ImagePath $isoFilePath | |
Update-ToDo $toDoList 0 | |
# Step 2: Find install.wim and split it | |
$installWim = "$outputFolder\sources\install.wim" | |
if (-not (Test-Path $installWim)) { | |
Write-Host "install.wim not found. Exiting." | |
return | |
} | |
Write-Host "Splitting the install.wim..." | |
$swmOutput = "$outputFolder\sources\install.swm" | |
try { | |
# Run the DISM command and capture the output | |
$dismOutput = dism /Split-Image /ImageFile:$installWim /SWMFile:$swmOutput /FileSize:4000 2>&1 | |
Write-Host $dismOutput | |
# Check if the SWM files were created | |
if (-not (Test-Path "$outputFolder\sources\install.swm")) { | |
Write-Host "Error: No install.swm files were created. Aborting deletion of install.wim." | |
return | |
} | |
Write-Host "Successfully split the install.wim file." | |
Update-ToDo $toDoList 1 | |
Update-ToDo $toDoList 2 | |
} catch { | |
Write-Host "Error splitting the install.wim file: $_" | |
return | |
} | |
# Step 3: Delete original install.wim after confirming SWM creation | |
Remove-Item -Force $installWim | |
Write-Host "Deleted install.wim." | |
# Step 4: Recreate the ISO with install.swm | |
$inputFileName = [System.IO.Path]::GetFileNameWithoutExtension($isoFilePath) | |
$newIsoFolder = "$env:UserProfile\Desktop\ModifiedISO" | |
$newIso = "$newIsoFolder\$inputFileName`_FAT32.iso" | |
$oscdimgPath = FindOscdimg | |
New-Item -ItemType Directory -Path $newIsoFolder -Force | Out-Null | |
Start-Process -FilePath $oscdimgPath -ArgumentList "-m -o -u2 -udfver102 $outputFolder $newIso" -Wait | |
Update-ToDo $toDoList 3 | |
# Step 5: Clean up unnecessary files | |
Write-Host "Cleaning up unnecessary files..." | |
Remove-Item -Recurse -Force $outputFolder | |
Update-ToDo $toDoList 4 | |
[System.Windows.MessageBox]::Show("Process complete. Your new ISO is at $newIso.") | |
} | |
# Function to split a provided WIM file | |
function SplitWIM { | |
param ($wimFilePath) | |
$outputFolder = "$env:UserProfile\Desktop\ModifiedISO" | |
New-Item -ItemType Directory -Path $outputFolder -Force | Out-Null | |
$inputFileName = [System.IO.Path]::GetFileNameWithoutExtension($wimFilePath) | |
Dism /Split-Image /ImageFile:$wimFilePath /SWMFile:"$outputFolder\$inputFileName.swm" /FileSize:4000 | |
[System.Windows.MessageBox]::Show("Process complete. The split WIM files are in $outputFolder.") | |
Update-ToDo $toDoList 2 | |
} | |
# Function to find oscdimg.exe based on system architecture | |
function FindOscdimg { | |
Write-Host "Searching for oscdimg.exe..." | |
$oscdimgPaths = @( | |
"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\x86\Oscdimg\oscdimg.exe", | |
"C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\x64\Oscdimg\oscdimg.exe", | |
"C:\Program Files\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\Oscdimg\oscdimg.exe" | |
) | |
foreach ($path in $oscdimgPaths) { | |
if (Test-Path $path) { | |
Write-Host "Found oscdimg.exe at: $path" | |
return $path | |
} | |
} | |
Write-Host "oscdimg.exe not found. Please install the Windows ADK." | |
[System.Windows.MessageBox]::Show("oscdimg.exe not found in the standard paths. Please install the Windows ADK.") | |
return $null | |
} | |
# Function to auto-install the latest Windows ADK | |
function Install-LatestADK { | |
Write-Host "Starting the download of the latest Windows ADK..." | |
# URL of the official Microsoft ADK page | |
$adkUrl = "https://learn.microsoft.com/en-us/windows-hardware/get-started/adk-install" | |
$webContent = Invoke-WebRequest -Uri $adkUrl | |
# Extract the download URLs using regular expressions | |
$adkPattern = 'https:\/\/go\.microsoft\.com\/fwlink\/\?linkid=\d+' | |
$matches = [regex]::Matches($webContent.Content, $adkPattern) | |
if ($matches.Count -lt 2) { | |
Write-Host "Could not find ADK download links. Check the page manually." | |
return | |
} | |
# Extract the ADK installer URLs | |
$adkDownloadUrl = $matches[0].Value | |
$peAddonDownloadUrl = $matches[1].Value | |
# Output folder for the installers | |
$outputFolder = "$env:UserProfile\Downloads\ADK" | |
if (-not (Test-Path $outputFolder)) { | |
New-Item -ItemType Directory -Path $outputFolder | Out-Null | |
} | |
# Define file names for the installers | |
$adkInstallerPath = Join-Path $outputFolder "adksetup.exe" | |
$peAddonInstallerPath = Join-Path $outputFolder "adkwinpesetup.exe" | |
# Download ADK | |
Write-Host "Downloading Windows ADK installer..." | |
Invoke-WebRequest -Uri $adkDownloadUrl -OutFile $adkInstallerPath | |
# Download PE Add-on | |
Write-Host "Downloading Windows PE Add-on installer..." | |
Invoke-WebRequest -Uri $peAddonDownloadUrl -OutFile $peAddonInstallerPath | |
# Install ADK silently | |
Write-Host "Installing Windows ADK..." | |
Start-Process -FilePath $adkInstallerPath -ArgumentList "/quiet /norestart" -Wait | |
# Install PE Add-on silently | |
Write-Host "Installing Windows PE Add-on..." | |
Start-Process -FilePath $peAddonInstallerPath -ArgumentList "/quiet /norestart" -Wait | |
[System.Windows.MessageBox]::Show("Windows ADK and PE Add-on installation completed successfully.") | |
} | |
# Start the GUI | |
Show-GUI | |
# Stop logging at the end of the script | |
Stop-Transcript |