Skip to content

Instantly share code, notes, and snippets.

@caccialdo
Last active April 29, 2025 22:02
Show Gist options
  • Save caccialdo/d26e89e9eb7b697811c4207e856d8d45 to your computer and use it in GitHub Desktop.
Save caccialdo/d26e89e9eb7b697811c4207e856d8d45 to your computer and use it in GitHub Desktop.
Windows-Image-Splitter

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment