Skip to content

Instantly share code, notes, and snippets.

@bunkbail
Last active February 1, 2026 20:25
Show Gist options
  • Select an option

  • Save bunkbail/380e460ae6a8ef8160c09d3b226b7627 to your computer and use it in GitHub Desktop.

Select an option

Save bunkbail/380e460ae6a8ef8160c09d3b226b7627 to your computer and use it in GitHub Desktop.
Convert Windows ISO images into DD-able Linux .img images (Arch Linux)
#!/bin/bash
# ==============================================================================
# Windows ISO to IMG Converter (Production Version)
# Converts Windows ISOs to DD-able images for Ventoy/Rufus-style writing.
# Features: UDF Support (>4GB files), Non-Root, Scriptable Partitioning
# Dependencies: 7z, genisoimage (cdrkit), mtools, sfdisk, dd, truncate
# ==============================================================================
set -e
# --- Configuration ---
ISO_PATH="$1"
if [ -z "$ISO_PATH" ]; then
echo "Usage: $0 <path_to_windows_iso>"
exit 1
fi
# Resolve absolute path
ISO_PATH=$(realpath "$ISO_PATH")
ISO_BASENAME=$(basename "$ISO_PATH")
OUTPUT_IMG="${ISO_BASENAME%.*}.img"
# --- Check Dependencies ---
# We require genisoimage for UDF support (xorriso lacks this on Arch)
if ! command -v genisoimage &> /dev/null; then
echo "Error: 'genisoimage' is required but not installed."
echo "Install it via: sudo pacman -S cdrkit"
exit 1
fi
for cmd in 7z mtools sfdisk dd truncate; do
if ! command -v $cmd &> /dev/null; then
echo "Error: Missing dependency '$cmd'"
exit 1
fi
done
# --- Setup Temp Directories ---
EXTRACT_DIR=$(mktemp -d)
BUILD_DIR=$(mktemp -d)
cleanup() {
rm -rf "$EXTRACT_DIR" "$BUILD_DIR"
}
trap cleanup EXIT
# --- Step 1: Extract ISO ---
echo "[1/5] Extracting ISO..."
SEVEN_Z="7z"
if ! command -v 7z &>/dev/null; then SEVEN_Z="7zz"; fi
$SEVEN_Z x -y -o"$EXTRACT_DIR" "$ISO_PATH" > /dev/null
# --- Step 2: Locate Boot Files ---
echo "[2/5] Locating sources..."
BOOT_WIM=$(find "$EXTRACT_DIR" -type f -ipath "*/sources/boot.wim" | head -n 1)
EFI_DIR=$(find "$EXTRACT_DIR" -type d -ipath "*/efi" | head -n 1)
BOOT_DIR=$(find "$EXTRACT_DIR" -type d -ipath "*/boot" -not -path "*/efi/*" | head -n 1)
if [ -z "$BOOT_WIM" ]; then echo "Error: boot.wim not found."; exit 1; fi
# --- Step 3: Build EFI Partition (FAT32) ---
echo "[3/5] Building EFI System Partition..."
SIZE_BYTES=$(du -sb "$BOOT_WIM" "$EFI_DIR" 2>/dev/null | awk '{sum+=$1} END {print sum}')
SIZE_MB=$(echo "($SIZE_BYTES / 1024 / 1024) + 64" | bc)
BOOT_IMG="$BUILD_DIR/esp.img"
truncate -s "${SIZE_MB}M" "$BOOT_IMG" > /dev/null
mformat -i "$BOOT_IMG" -F :: > /dev/null 2>&1
mcopy -i "$BOOT_IMG" -s "$EFI_DIR" ::/EFI > /dev/null 2>&1
if [ -n "$BOOT_DIR" ]; then
mcopy -i "$BOOT_IMG" -s "$BOOT_DIR" ::/BOOT > /dev/null 2>&1
fi
mmd -i "$BOOT_IMG" ::/sources > /dev/null 2>&1
mcopy -i "$BOOT_IMG" "$BOOT_WIM" ::/sources/boot.wim > /dev/null 2>&1
# --- Step 4: Build Data Partition (UDF) ---
echo "[4/5] Building Data Partition (UDF)..."
DATA_IMG="$BUILD_DIR/data.img"
# UDF is required for Windows >4GB install.wim files
genisoimage -o "$DATA_IMG" -iso-level 3 -J -r -UDF -V "INSTALL" "$EXTRACT_DIR" > /dev/null 2>&1
# --- Step 5: Assemble Disk Image ---
echo "[5/5] Assembling final image..."
SEC_BOOT=$(echo "($(wc -c < "$BOOT_IMG") + 511) / 512" | bc)
SEC_DATA=$(echo "($(wc -c < "$DATA_IMG") + 511) / 512" | bc)
P1_START=2048
P1_END=$(echo "$P1_START + $SEC_BOOT - 1" | bc)
P2_START=$(echo "$P1_END + 1" | bc)
P2_END=$(echo "$P2_START + $SEC_DATA - 1" | bc)
TOTAL_SEC=$(echo "$P2_END + 2048" | bc)
# Create image and partition table
dd if=/dev/zero of="$OUTPUT_IMG" bs=512 count=0 seek=$TOTAL_SEC status=none
{
echo "label: gpt"
echo "unit: sectors"
echo "start=$P1_START, size=$SEC_BOOT, type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B, name=ESP"
echo "start=$P2_START, size=$SEC_DATA, type=EBD0A0A2-B9E5-4433-87C0-68B6B72699C7, name=Windows"
} | sfdisk "$OUTPUT_IMG" > /dev/null 2>&1
# Inject partitions
dd if="$BOOT_IMG" of="$OUTPUT_IMG" bs=512 seek=$P1_START count=$SEC_BOOT conv=notrunc status=none
dd if="$DATA_IMG" of="$OUTPUT_IMG" bs=512 seek=$P2_START count=$SEC_DATA conv=notrunc status=none
# --- Done ---
echo "--------------------------------------------------"
echo "Success! Created: $OUTPUT_IMG"
echo "Size: $(du -h "$OUTPUT_IMG" | cut -f1)"
echo "Flash with: sudo dd if=\"$OUTPUT_IMG\" of=/dev/sdX bs=4M status=progress"
echo "--------------------------------------------------"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment