Last active
February 1, 2026 20:25
-
-
Save bunkbail/380e460ae6a8ef8160c09d3b226b7627 to your computer and use it in GitHub Desktop.
Convert Windows ISO images into DD-able Linux .img images (Arch Linux)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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