#!/usr/bin/env bash


# Here the quick 'n dirty guide to adding a new OS to quickget
#
#  1. Add the new OS, all lowercase, to os_support()
#  2. Add a "pretty name" display name to pretty_name()
#  3. Create a releases_newos() generator that outputs the currently supported release versions
#  4. Add the new OS to make_vm_config()
#  5. Create a get_newos() function that does something like this:
#     function get_newos() {
#         local HASH=""
#         local ISO=""
#         local URL=""
#
#         validate_release "releases_newos"
#         ISO="newos-${RELEASE}-amd64.iso"
#         URL="https://www.newos.org/download/${ISO}"
#         web_get "${URL}" "${VM_PATH}"
#         web_get "${URL}/SHA256SUMS" "${VM_PATH}"
#         HASH=$(cat "${VM_PATH}/SHA256SUMS" | cut -d' ' -f1)
#         check_hash "${ISO}" "${HASH}"
#         make_vm_config "${ISO}"
#     }
#  6. Add new OS to the argument parser at the bottom of the script

function cleanup() {
  if [ -n "$(jobs -p)" ]; then
    kill "$(jobs -p)"
  fi
}

function pretty_name() {
  local SIMPLE_NAME=""
  local PRETTY_NAME=""
  SIMPLE_NAME="${1}"
  case ${SIMPLE_NAME} in
    alma)               PRETTY_NAME="Alma Linux";;
    android)            PRETTY_NAME="Android x86";;
    archlinux)          PRETTY_NAME="Arch Linux";;
    elementary)         PRETTY_NAME="elementary OS";;
    freebsd)            PRETTY_NAME="FreeBSD";;
    garuda)             PRETTY_NAME="Garuda Linux";;
    kdeneon)            PRETTY_NAME="KDE Neon";;
    linuxmint-cinnamon) PRETTY_NAME="Linux Mint Cinnamon";;
    linuxmint-mate)     PRETTY_NAME="Linux Mint MATE";;
    linuxmint-xfce)     PRETTY_NAME="Linux Mint XFCE";;
    nixos-gnome)        PRETTY_NAME="NixOS Gnome";;
    nixos-plasma5)      PRETTY_NAME="NixOS KDE";;
    nixos-minimal)      PRETTY_NAME="NixOS Minimal";;
    macos)              PRETTY_NAME="macOS";;
    openbsd)            PRETTY_NAME="OpenBSD";;
    opensuse)           PRETTY_NAME="openSUSE";;
    oraclelinux)        PRETTY_NAME="Oracle Linux";;
    popos)              PRETTY_NAME="Pop!_OS";;
    regolith)           PRETTY_NAME="Regolith Linux";;
    rockylinux)         PRETTY_NAME="Rocky Linux";;
    ubuntu-budgie)      PRETTY_NAME="Ubuntu Budgie";;
    ubuntu-kylin)       PRETTY_NAME="Ubuntu Kylin";;
    ubuntu-mate)        PRETTY_NAME="Ubuntu MATE";;
    ubuntu-studio)      PRETTY_NAME="Ubuntu Studio";;
    zorin)              PRETTY_NAME="Zorin OS";;
    *)                  PRETTY_NAME="${SIMPLE_NAME^}";;
  esac
  echo "${PRETTY_NAME}"
}

function validate_release() {
  local DISPLAY_NAME=""
  local RELEASE_GENERATOR="${1}"
  local RELEASES=""

  DISPLAY_NAME="$(pretty_name "${OS}")"
  RELEASES=$(${RELEASE_GENERATOR})
  if [[ "${RELEASES}" != *"${RELEASE}"* ]]; then
      echo "ERROR! ${DISPLAY_NAME} ${RELEASE} is not a supported release."
      echo "${RELEASES}"
      exit 1
  fi
}

function list_json() {
  # Reference: https://stackoverflow.com/a/67359273
  list_csv | jq -R 'split(",") as $h|reduce inputs as $in ([]; . += [$in|split(",")|. as $a|reduce range(0,length) as $i ({};.[$h[$i]]=$a[$i])])'
  exit 0
}

function list_csv() {
  local DISPLAY_NAME
  local DOWNLOADER
  local FUNC
  local OPTION
  local OS
  local PNG
  local RELEASE
  local SVG
  local ZS=""
  local DL=""

# check if we have a zsync installed somewhere
  ZS="$(which zsync)"
  if  [ -x "${ZS}" ]; then
    DL="zsync"
  else
    DL="wget"
  fi
  echo "Display Name,OS,Release,Option,Downloader,PNG,SVG"
  for OS in $(os_support); do
    DISPLAY_NAME="$(pretty_name "${OS}")"
    if [[ "${OS}" == *"ubuntu"* ]]; then
      FUNC="ubuntu"
    elif [[ "${OS}" == *"linuxmint"* ]]; then
      FUNC="linuxmint"
    elif [[ "${OS}" == *"nixos"* ]]; then
      FUNC="nixos"
    else
      FUNC="${OS}"
    fi
    PNG="https://quickemu-project.github.io/quickemu-icons/png/${FUNC}/${FUNC}-quickemu-white-pinkbg.png"
    SVG="https://quickemu-project.github.io/quickemu-icons/svg/${FUNC}/${FUNC}-quickemu-white-pinkbg.svg"


    for RELEASE in $("releases_${FUNC}"); do
      if [ "${OS}" == "macos" ]; then
        DOWNLOADER="macrecovery"
      elif [ "${OS}" == "ubuntu" ] && [ "${RELEASE}" == "canary" ]; then
        DOWNLOADER="${DL}"
      elif [[ "${OS}" == *"ubuntu"* ]] && [ "${RELEASE}" == "devel" ]; then
        DOWNLOADER="${DL}"
      elif [ "${OS}" == "garuda" ]; then
        DOWNLOADER="${DL}"
      elif [[ "${OS}" == *"kdeneon"* ]]; then
        DOWNLOADER="${DL}"
      else
        DOWNLOADER="wget"
      fi

      if [ "${OS}" == "windows" ]; then
        for OPTION in "${LANGS[@]}"; do
          echo "${DISPLAY_NAME},${OS},${RELEASE},${OPTION},${DOWNLOADER},${PNG},${SVG}"
        done
      elif [ "${OS}" == "popos" ]; then
        for OPTION in intel nvidia; do
          echo "${DISPLAY_NAME},${OS},${RELEASE},${OPTION},${DOWNLOADER},${PNG},${SVG}"
        done
      elif [ "${OS}" == "debian" ]; then
        for OPTION in standard nonfree; do
         echo "${DISPLAY_NAME},${OS},${RELEASE},${OPTION},${DOWNLOADER},${PNG},${SVG}"
        done
      elif [ "${OS}" == "alma" ]; then
        for OPTION in minimal dvd; do
          echo "${DISPLAY_NAME},${OS},${RELEASE},${OPTION},${DOWNLOADER},${PNG},${SVG}"
        done
      else
        echo "${DISPLAY_NAME},${OS},${RELEASE},,${DOWNLOADER},${PNG},${SVG}"
      fi
    done
  done
  exit 0
}

function os_support() {
    echo alma \
    android \
    archlinux \
    debian \
    elementary \
    freebsd \
    fedora \
    garuda \
    kali \
    kdeneon \
    kubuntu \
    linuxmint-cinnamon \
    linuxmint-mate \
    linuxmint-xfce \
    nixos-gnome \
    nixos-plasma5 \
    nixos-minimal \
    lubuntu \
    macos \
    openbsd \
    opensuse \
    oraclelinux \
    popos \
    regolith \
    rockylinux \
    solus \
    ubuntu \
    ubuntu-budgie \
    ubuntu-kylin \
    ubuntu-mate \
    ubuntu-studio \
    windows \
    xubuntu \
    zorin
}

function releases_alma() {
    # consider flavours for boot and dvd as well as
    echo 8.4 \
    8.5
}

function releases_android() {
  echo 9.0 \
  8.1 \
  7.1 \
  6.0 \
  5.1 \
  4.4 \
  4.0 \
  2.2
}

function releases_archlinux() {
    echo latest
}

# later refactor these DE variants like languages and avoid the arch ?
# all these are available with a "nonfree" option too
function releases_debian() {
    echo 11.1.0-amd64-cinnamon \
    11.1.0-amd64-gnome \
    11.1.0-amd64-kde \
    11.1.0-amd64-lxde \
    11.1.0-amd64-lxqt \
    11.1.0-amd64-mate \
    11.1.0-amd64-standard \
    11.1.0-amd64-xfce
}

function releases_elementary() {
    echo 6.0
}

function releases_freebsd(){
    echo 12.2 \
    13.0
}

function releases_fedora(){
    echo 33 \
    34 \
    35
}

function releases_garuda() {
    echo bspwm \
    dr460nized \
    dr460nized-blackarch \
    dr460nized-gaming \
    gnome \
    i3 \
    kde-barebones \
    lxqt-kwin \
    qtile \
    sway \
    wayfire \
    xfce \
    mate \
    cinnamon
}

function releases_kali() {
    echo latest \
    weekly
}

function releases_kdeneon() {
    echo user \
    testing \
    unstable \
    developer
}

function releases_linuxmint(){
    echo 20.2
}

function releases_nixos(){
    echo 21.05
}

function releases_openbsd(){
    echo 7.0
}

function releases_opensuse(){
    echo 15.0 \
    15.1 \
    15.2 \
    15.3 \
    microos \
    tumbleweed
}

function releases_oraclelinux() {
    echo 8.5 \
    8.4 \
    8.3 \
    8.2 \
    7.9 \
    7.8 \
    7.7
}

function releases_macos() {
    echo high-sierra \
    mojave \
    catalina \
    big-sur \
    monterey
}

function releases_popos() {
    echo 20.04 \
    21.04
}

function releases_regolith() {
    echo 1.6.0_hirsute \
    1.6.0_focal \
    2.0.0_impish \
    2.0.0_hirsute
}


function releases_rockylinux() {
    echo 8.5 \
    8.4 \
    8.3 \
    8.2 \
    8.1 \
    8.0
}

function releases_solus() {
    echo 4.3-budgie \
    4.3-gnome \
    4.3-mate \
    4.3-plasma
}

function releases_ubuntu() {
    echo bionic \
    focal \
    hirsute \
    impish \
    devel \
    canary
}

function languages_windows() {
    LANGS=(Arabic
    "Brazilian Portuguese"
    Bulgarian
    "Chinese (Simplified)"
    "Chinese (Traditional)"
    Croatian
    Czech
    Danish
    Dutch
    English
    "English International"
    Estonian
    Finnish
    French
    "French Canadian"
    German
    Greek
    Hebrew
    Hungarian
    Italian
    Japanese
    Korean
    Latvian
    Lithuanian
    Norwegian
    Polish
    Portuguese
    Romanian
    Russian
    "Serbian Latin"
    Slovak
    Slovenian
    Spanish
    "Spanish (Mexico)"
    Swedish
    Thai
    Turkish
    Ukrainian)
}


function releases_windows() {
    echo 8 \
    10 \
    11
}

function releases_zorin() {
    echo 16core64 \
    15lite64 \
    15lite32 \
    15education64 \
    15edulite64 \
    15edulite32
}

function check_hash() {
    local iso=""
    local hash=""
    local hash_algo=""
    iso="${VM_PATH}/${1}"
    hash="${2}"

    # Guess the hash algorithm by the hash length
    case ${#hash} in
      32) hash_algo=md5sum;;
      40) hash_algo=sha1sum;;
      64) hash_algo=sha256sum;;
      128) hash_algo=sha512sum;;
       *) echo "WARNING! Can't guess hash algorithm, not checking ${iso} hash."
          return;;
    esac

    echo -n "Checking ${iso} with ${hash_algo}... "
    if ! echo "${hash} ${iso}" | ${hash_algo} --check --status; then
        echo "ERROR!"
        echo "${iso} doesn't match ${hash}. Try running 'quickget' again."
        exit 1
    else
        echo "Good!"
    fi
}

function web_get() {
    local DIR="${2}"
    local FILE=""
    local URL="${1}"

    if [ -n "${3}" ]; then
        FILE="${3}"
    else
        FILE="${URL##*/}"
    fi

    if ! mkdir -p "${DIR}" 2>/dev/null; then
      echo "ERROR! Unable to create directory ${DIR}"
      exit 1
    fi

    if ! wget --quiet --continue --show-progress --progress=bar:force:noscroll "${URL}" -O "${DIR}/${FILE}"; then
        echo "ERROR! Failed to download ${URL}. Try running 'quickget' again."
        exit 1
    fi
}

function zsync_get() {
    local DIR="${2}"
    local FILE=""
    local OUT=""
    local URL="${1}"
    FILE="${URL##*/}"
    local ZS=""

# check if we have a zsync installed somewhere
  ZS="$(which zsync)"
    if  [ -x "${ZS}" ]; then
      if [ -n "${3}" ]; then
          OUT="${3}"
      else
          OUT="${FILE}"
      fi

      if ! mkdir -p "${DIR}" 2>/dev/null; then
        echo "ERROR! Unable to create directory ${DIR}"
        exit 1
      fi

      if ! zsync "${URL}.zsync" -i "${DIR}/${OUT}" -o "${DIR}/${OUT}" 2>/dev/null; then
          echo "ERROR! Failed to download ${URL}.zsync"
          exit 1
      fi

      if [ -e "${DIR}/${OUT}.zs-old" ]; then
          rm "${DIR}/${OUT}.zs-old"
      fi
    else
      echo "INFO: zsync not found, falling back to wget"
      web_get "${ISO}"
    fi
}

function start_vm_info() {
    echo
    echo "To start your ${OS} ${RELEASE} virtual machine run:"
    echo "    quickemu --vm ${OS}-${RELEASE}.conf"
    echo
}

function make_vm_config() {
    local IMAGE_FILE=""
    local ISO_FILE=""
    local IMAGE_TYPE=""
    local GUEST=""
    local SEC_BOOT=""

    IMAGE_FILE="${1}"
    ISO_FILE="${2}"
    if [ "${OS}" == "alma" ]; then
        GUEST="linux"
        IMAGE_TYPE="iso"
    elif [ "${OS}" == "android" ]; then
        GUEST="linux"
        IMAGE_TYPE="iso"
    elif [ "${OS}" == "archlinux" ]; then
        GUEST="linux"
        IMAGE_TYPE="iso"
    elif [ "${OS}" == "debian" ]; then
        GUEST="linux"
        IMAGE_TYPE="iso"
    elif [ "${OS}" == "elementary" ]; then
        GUEST="linux"
        IMAGE_TYPE="iso"
    elif [ "${OS}" == "freebsd" ]; then
        GUEST="freebsd"
        IMAGE_TYPE="iso"
    elif [ "${OS}" == "fedora" ]; then
        GUEST="linux"
        IMAGE_TYPE="iso"
    elif [ "${OS}" == "kali" ]; then
        GUEST="linux"
        IMAGE_TYPE="iso"
    elif [ "${OS}" == "garuda" ]; then
        GUEST="linux"
        IMAGE_TYPE="iso"
    elif [ "${OS}" == "kdeneon" ]; then
        GUEST="linux"
        IMAGE_TYPE="iso"
    elif [[ "${OS}" == *"linuxmint"* ]]; then
        GUEST="linux"
        IMAGE_TYPE="iso"
    elif [[ "${OS}" == *"nixos"* ]]; then
        GUEST="linux"
        IMAGE_TYPE="iso"
    elif [ "${OS}" == "openbsd" ]; then
        GUEST="openbsd"
        IMAGE_TYPE="iso"
    elif [ "${OS}" == "opensuse" ]; then
        GUEST="linux"
        IMAGE_TYPE="iso"
    elif [ "${OS}" == "oraclelinux" ]; then
        GUEST="linux"
        IMAGE_TYPE="iso"
    elif [ "${OS}" == "popos" ]; then
        GUEST="linux"
        IMAGE_TYPE="iso"
    elif [ "${OS}" == "regolith" ]; then
        GUEST="linux"
        IMAGE_TYPE="iso"
    elif [ "${OS}" == "rockylinux" ]; then
        GUEST="linux"
        IMAGE_TYPE="iso"
    elif [ "${OS}" == "solus" ]; then
        GUEST="linux"
        IMAGE_TYPE="iso"
    elif [[ "${OS}" == *"ubuntu"* ]]; then
        GUEST="linux"
        IMAGE_TYPE="iso"
    elif [ "${OS}" == "macos" ]; then
        GUEST="macos"
        IMAGE_TYPE="img"
    elif [ "${OS}" == "windows" ]; then
        GUEST="windows"
        IMAGE_TYPE="iso"
    elif [ "${OS}" == "zorin" ]; then
        GUEST="linux"
        IMAGE_TYPE="iso"
    fi


    if [ -n "${ISOTYPE}" ]; then
        RELEASE=${RELEASE}-${ISOTYPE}
    fi

    if [ ! -e "${OS}-${RELEASE}.conf" ]; then
        echo "Making VM configuration for ${OS}-${RELEASE}..."
        cat << EOF > "${OS}-${RELEASE}.conf"
guest_os="${GUEST}"
disk_img="${VM_PATH}/disk.qcow2"
${IMAGE_TYPE}="${VM_PATH}/${IMAGE_FILE}"
EOF
        if [ -n "${ISO_FILE}" ]; then
            echo "fixed_iso=\"${VM_PATH}/${ISO_FILE}\"" >> "${OS}-${RELEASE}.conf"
        fi
        if [ "${OS}" == "alma"  ] && [ ${ISOTYPE} == "dvd" ]; then
            echo "disk_size=\"32G\"" >> "${OS}-${RELEASE}.conf"
        fi
        if [ "${OS}" == "openbsd" ]; then
            echo "boot=\"legacy\"" >> "${OS}-${RELEASE}.conf"
        fi

        if [ "${OS}" == "macos" ]; then
            echo "macos_release=\"${RELEASE}\"" >> "${OS}-${RELEASE}.conf"
        fi

        if [ "${OS}" == "garuda"  ]; then
            echo "disk_size=\"32G\"" >> "${OS}-${RELEASE}.conf"
        fi

        if [ "${OS}" == "oraclelinux"  ]; then
            echo "disk_size=\"20G\"" >> "${OS}-${RELEASE}.conf"
        fi

        if [ "${OS}" == "zorin"  ]; then
            case  ${RELEASE} in
              15education64|15edulite64|15edulite32)
                echo "disk_size=\"32G\"" >> "${OS}-${RELEASE}.conf";;
            esac
        fi

        # Enable TPM for Windows 11
        if [ "${OS}" == "windows" ] && [ "${RELEASE}" -ge 11 ]; then
            echo "tpm=\"on\"" >> "${OS}-${RELEASE}.conf"
            # Only force SecureBoot on for non-Debian/Ubuntu distros.
            if [ -e "/usr/share/OVMF/OVMF_CODE_4M.fd" ] && [ -e "/usr/share/OVMF/OVMF_VARS_4M.fd" ]; then
              SEC_BOOT="off"
            else
              SEC_BOOT="on"
            fi
            echo "secureboot=\"${SEC_BOOT}\"" >> "${OS}-${RELEASE}.conf"
        fi
    fi
    start_vm_info
}

function get_android() {
  local HASH=""
  local ISO=""
  local URL=""

  validate_release "releases_android"
  fosshubVersionInfo=$(wget -O - -q "https://www.fosshub.com/Android-x86-old.html" | grep "var settings =")
  version="android-x86-${RELEASE}"
  releaseJson=$(echo "${fosshubVersionInfo:16}" | jq --arg ver "${version}" 'first(.pool.f[] | select((.n | startswith($ver)) and (.n | endswith(".iso"))))')

  HASH=$(echo "${releaseJson}" | jq -r .hash.sha256)
  ISO=$(echo "${releaseJson}" | jq -r .n)

  baseurl="https://mirrors.gigenet.com/OSDN/android-x86/"

  releaseFolders=$(wget -q -O - ${baseurl} | grep -o -E '[0-9]{5}' | uniq)
  for item in $releaseFolders; do
    file=$(wget -O - -q "${baseurl}${item}" | grep "${ISO}")
    if [[ $file != "" ]]; then
      URL="${baseurl}${item}/${ISO}"
      break
    fi
  done
  web_get "${URL}" "${VM_PATH}"
  check_hash "${ISO}" "${HASH}"
  make_vm_config "${ISO}"
}

function get_alma() {
    local HASH=""
    local ISO=""
    local URL=""
    local VERSION=""
    #local isotype=""

    validate_release "releases_alma"

    ISOTYPE="minimal" # boot is a step too far for now - needs setting install source to mirror tree ... nope
    if [ -n "${1}" ]; then
      ISOTYPE="${1}"
    fi


    # The mirror url returns 10 or so local mirrors with some kind or RR rotation/load balancing
    # We'll just grab the first

    URL=$(wget -qq -O- "https://mirrors.almalinux.org/isos/x86_64/${RELEASE}.html" | awk -F"<li>|</li>" '{for(i=2;i<=NF;i+=2) {print $i}}' RS=""  |grep href|cut -d\" -f2|head -1)

    #VM_PATH="${VM_PATH}"-${ISOTYPE}
    ISO=AlmaLinux-${RELEASE}-x86_64-${ISOTYPE}.iso
    HASH="$(wget -q -O- "${URL}/CHECKSUM" | grep \(${ISO} | cut -d\   -f4)"
    web_get "${URL}/${ISO}" "${VM_PATH}"
    check_hash "${ISO}" "${HASH}"
    make_vm_config "${ISO}"
}
function get_archlinux() {
    local HASH=""
    local ISO=""
    local URL=""
    local VERSION=""

    validate_release "releases_archlinux"
    VERSION=$(wget -q -O- 'https://archlinux.org/releng/releases/json/' | jq '.latest_version' | cut -d "\"" -f 2)
    URL="https://mirror.rackspace.com/archlinux/iso/${VERSION}"
    ISO="archlinux-${VERSION}-x86_64.iso"
    HASH=$(wget -q -O- 'https://archlinux.org/releng/releases/json/' | jq '.releases[0].sha1_sum' | cut -d "\"" -f 2)
    web_get "${URL}/${ISO}" "${VM_PATH}"
    check_hash "${ISO}" "${HASH}"
    make_vm_config "${ISO}"
}


function get_debian() {
    local HASH=""
    local ISO=""
    local URL=""
    local HASHLINE=""
    local FREEDOM=""


    validate_release "releases_debian"

    if [ "${1}" == "nonfree" ]; then
      RELEASE="${RELEASE}+nonfree"
    fi
    case $RELEASE in
      *+nonfree)  URL="http://cdimage.debian.org/cdimage/unofficial/non-free/cd-including-firmware/current-live/amd64/iso-hybrid" ;;
      *)          URL="https://cdimage.debian.org/debian-cd/current-live/amd64/iso-hybrid";;
    esac


    HASHLINE=$(wget -q -O- ${URL}/SHA512SUMS |grep ${RELEASE}.iso)
    ISO="$(echo ${HASHLINE} | awk '{print $NF}' )"
    HASH=$(echo ${HASHLINE} | cut -d\  -f1)

    web_get "${URL}/${ISO}" "${VM_PATH}"
    check_hash "${ISO}" "${HASH}"
    make_vm_config "${ISO}"
}

function get_elementary() {
    local ISO=""
    local URL=""
    local B66tim=""


    validate_release "releases_elementary"
    B66tim=$(date +%s | base64 )
    ISO="elementaryos-${RELEASE}-stable.20211005.iso"
    # TODO:  derive region from geoIP
    URL="https://ams3.dl.elementary.io/download/${B66tim}=/${ISO}"
    web_get "${URL}" "${VM_PATH}"
    make_vm_config "${ISO}"
}

function get_freebsd() {
    local HASH=""
    local ISO=""
    local URL=""

    validate_release "releases_freebsd"
    ISO="FreeBSD-${RELEASE}-RELEASE-amd64-dvd1.iso"
    HASH=$(wget -q -O- "https://download.freebsd.org/ftp/releases/amd64/amd64/ISO-IMAGES/${RELEASE}/CHECKSUM.SHA512-FreeBSD-${RELEASE}-RELEASE-amd64" | grep '('"${ISO}"')' | cut -d' ' -f4)
    URL="https://download.freebsd.org/ftp/releases/amd64/amd64/ISO-IMAGES/${RELEASE}/${ISO}"
    web_get "${URL}" "${VM_PATH}"
    check_hash "${ISO}" "${HASH}"
    make_vm_config "${ISO}"
}

function get_fedora() {
    local FEDORA_RELEASE=""
    local FEDORA_VERSIONS=""
    local HASH=""
    local ISO=""
    local URL=""
    local VERSION_NUM=""

    validate_release "releases_fedora"

    FEDORA_VERSIONS=$(wget -q -O- "https://getfedora.org/releases.json" | jq '.[] | select((.variant=="Workstation" or .variant=="Spins") and .arch=="x86_64")')
    if [[ "${RELEASE}" == *"beta"* ]]; then
        VERSION_NUM=${RELEASE%"_beta"}
        FEDORA_RELEASE=$(echo "${FEDORA_VERSIONS}" | jq -c '. | select(.version | contains("Beta"))' | jq '. | select(.variant=="Workstation")')
        ISO="Fedora-Workstation-Live-x86_64-${VERSION_NUM}_Beta-1.2.iso"
    else
        FEDORA_RELEASE=$(echo "${FEDORA_VERSIONS}" | jq '. | select(.variant=="Workstation" and .version=="'${RELEASE}'")')
        ISO="Fedora-Workstation-Live-x86_64-${RELEASE}-1.2.iso"
    fi

    URL=$(echo "${FEDORA_RELEASE}" | jq -r '.link')
    HASH=$(echo "${FEDORA_RELEASE}" | jq -r '.sha256')
    web_get "${URL}" "${VM_PATH}"
    check_hash "${ISO}" "${HASH}"
    make_vm_config "${ISO}"
}

function get_kali() {
    local HASH=""
    local ISO=""
    local URL=""
    local SUBDIR=""
    validate_release "releases_kali"

    if [[ "${RELEASE}" == "latest" ]]; then
        SUBDIR="current"
    else
        SUBDIR="kali-weekly"
    fi

    ISO=$(wget -q -O- "https://cdimage.kali.org/${SUBDIR}/?C=M;O=D" |grep -o ">kali-linux-.*-installer-amd64.iso"|head -n 1|cut -c 2-)
    HASH=$(wget -q -O- "https://cdimage.kali.org/${SUBDIR}/SHA256SUMS" | grep "${ISO}" | cut -d' ' -f1)
    URL="https://cdimage.kali.org/${SUBDIR}/${ISO}"
    web_get "${URL}" "${VM_PATH}"
    check_hash "${ISO}" "${HASH}"
    make_vm_config "${ISO}"
}

function get_kdeneon() {
    local ISO=""
    local URL=""

    validate_release "releases_kdeneon"
    ISO="neon-${RELEASE}-current.iso"
    # Get the URL of the mirror closest to the user's location
    URL=$(wget -q -O- "https://files.kde.org/neon/images/${RELEASE}/current/neon-${RELEASE}-current.iso.zsync.mirrorlist" | \
          grep "neon-${RELEASE}-current.iso.zsync" | grep '>1.<' | cut -d\" -f 6 | sed 's/https/http/g' | xargs dirname)
    zsync_get "${URL}/${ISO}" "${VM_PATH}"
    check_hash "${ISO}" "$(wget -q -O- "https://files.kde.org/neon/images/${RELEASE}/current/neon-${RELEASE}-current.sha256sum" | cut -d' ' -f1)"
    make_vm_config "${ISO}"
}

function get_linuxmint() {
    local FLAVOR=""
    local HASH=""
    local ISO=""
    local URL=""

    validate_release "releases_linuxmint"
    FLAVOR=$(echo "${OS}" | cut -d'-' -f2)
    ISO="linuxmint-${RELEASE}-${FLAVOR}-64bit.iso"
    HASH=$(wget -q -O- "https://mirror.bytemark.co.uk/linuxmint/stable/${RELEASE}/sha256sum.txt" | grep "${ISO}" | cut -d' ' -f1)
    URL="https://mirror.bytemark.co.uk/linuxmint/stable/${RELEASE}/${ISO}"
    web_get "${URL}" "${VM_PATH}"
    check_hash "${ISO}" "${HASH}"
    make_vm_config "${ISO}"
}
function get_nixos() {
    local FLAVOR=""
    local HASH=""
    local ISO=""
    local URL=""

    validate_release "releases_nixos"
    FLAVOR=$(echo "${OS}" | cut -d'-' -f2)
    ISO="latest-nixos-${FLAVOR}-x86_64-linux.iso"
    URL="https://channels.nixos.org/nixos-${RELEASE}/${ISO}"
    HASH=$(wget -q -O- "https://channels.nixos.org/nixos-${RELEASE}/${ISO}.sha256" |  cut -d' ' -f1)
    web_get "${URL}" "${VM_PATH}"
    check_hash "${ISO}" "${HASH}"
    make_vm_config "${ISO}"
}
function get_openbsd() {
    local HASH=""
    local ISO=""
    local URL=""

    validate_release "releases_openbsd"
    ISO="install${RELEASE//\./}.iso"
    URL="https://cdn.openbsd.org/pub/OpenBSD/${RELEASE}/amd64/${ISO}"
    HASH=$(wget -q -O- "https://cdn.openbsd.org/pub/OpenBSD/${RELEASE}/amd64/SHA256" | grep "${ISO}" | cut -d' ' -f4)
    web_get "${URL}" "${VM_PATH}"
    check_hash "${ISO}" "${HASH}"
    make_vm_config "${ISO}"
}

function get_zorin() {
    local ISO=""
    local URL=""

    validate_release "releases_zorin"
    # their redirector returns an href so we need to get that and parse out the iso
    URL=$(curl -s "https://zrn.co/${RELEASE}" |cut -d\" -f2)
    ISO=$(echo "${URL}"| awk -F\/ ' {print $NF}')
    web_get "${URL}" "${VM_PATH}"
    make_vm_config "${ISO}"
}

function get_rocky() {
    local HASH=""
    local ISO=""
    local URL=""

    local arch="x86_64"
    local baseurl="https://download.rockylinux.org/pub/rocky/${RELEASE}/isos/${arch}"

    validate_release "releases_rockylinux"
    ISO="Rocky-${RELEASE}-${arch}-${ISOTYPE}.iso"
    URL="${baseurl}/${ISO}"
    HASH=$(wget -q -O- "${baseurl}/CHECKSUM" | grep "SHA256 (${ISO})" | grep -E -i -w -o '[0-9a-z]{64}')

    web_get "${URL}" "${VM_PATH}"
    check_hash "${ISO}" "${HASH}"
    make_vm_config "${ISO}"
}

function get_solus() {
    local RELNUM=""
    local RELTYPE=""
    local HASH=""
    local ISO=""
    local URL=""

    validate_release "releases_solus"
    RELNUM=$(echo "${RELEASE}" | cut -d'-' -f1)
    RELTYPE=$(echo "${RELEASE}" | cut -d'-' -f2)
    case ${RELTYPE} in
      mate|gnome)
        RELTYPE=${RELTYPE^^};;
      *)
        RELTYPE=${RELTYPE^};;
    esac

    ISO="Solus-${RELNUM}-${RELTYPE}.iso"
    URL="https://mirrors.rit.edu/solus/images/${RELNUM}/${ISO}"
    HASH=$(wget -q -O- "${URL}.sha256sum" |  cut -d' ' -f1)
    web_get "${URL}" "${VM_PATH}"
    check_hash "${ISO}" "${HASH}"
    make_vm_config "${ISO}"
}

function get_opensuse() {
    local HASH=""
    local ISO=""
    local URL=""

    validate_release "releases_opensuse"
    if [ "${RELEASE}" == "tumbleweed" ]; then
        ISO="openSUSE-Tumbleweed-DVD-x86_64-Current.iso"
        URL="https://download.opensuse.org/tumbleweed/iso/${ISO}"
        HASH=$(wget -q -O- "${URL}.sha256" | cut -d' ' -f1)
    elif [ "${RELEASE}" == "microos" ]; then
        ISO="openSUSE-MicroOS-DVD-x86_64-Current.iso"
        URL="https://download.opensuse.org/tumbleweed/iso/${ISO}"
        HASH=$(wget -q -O- "${URL}.sha256" | cut -d' ' -f1)
    else
        ISO="openSUSE-Leap-${RELEASE}-DVD-x86_64.iso"
        URL="https://download.opensuse.org/distribution/leap/${RELEASE}/iso/${ISO}"
        HASH=$(wget -q -O- "${URL}.sha256" | cut -d' ' -f1)
    fi
    web_get "${URL}" "${VM_PATH}"
    check_hash "${ISO}" "${HASH}"
    make_vm_config "${ISO}"
}

function get_oraclelinux() {
    local HASH=""
    local ISO=""
    local URL=""

    local arch="x86_64"

    validate_release "releases_oraclelinux"

    local majorver=${RELEASE::1}
    local minorver=${RELEASE:2:1}

    local baseurl="https://yum.oracle.com/ISOS/OracleLinux/OL${majorver}/u${minorver}/${arch}/"
    local hashurl="https://linux.oracle.com/security/gpg/checksum/OracleLinux-R${majorver}-U${minorver}-Server-x86_64.checksum"

    if [ "${majorver}" == "8" ]; then
        ISO="OracleLinux-R${majorver}-U${minorver}-${arch}-dvd.iso"
    else
        ISO="OracleLinux-R${majorver}-U${minorver}-Server-${arch}-dvd.iso"
    fi

    URL="${baseurl}/${ISO}"
    HASH=$(wget -q -O- "${hashurl}" | grep "${ISO}" | cut -d' ' -f1)

    web_get "${URL}" "${VM_PATH}"
    check_hash "${ISO}" "${HASH}"
    make_vm_config "${ISO}"
}

function get_macos() {
    local BOARD_ID=""
    local CWD=""
    local MACRECOVERY=""
    local MLB=""

    case ${RELEASE} in
        high-sierra)
            BOARD_ID="Mac-7BA5B2D9E42DDD94"
            MLB="00000000000J80300";;
        mojave)
            BOARD_ID="Mac-7BA5B2DFE22DDD8C"
            MLB="00000000000KXPG00";;
        catalina)
            BOARD_ID="Mac-CFF7D910A743CAAF"
            MLB="00000000000PHCD00";;
        big-sur)
            BOARD_ID="Mac-35C1E88140C3E6CF"
            MLB="00000000000000000";;
        monterey)
            BOARD_ID="Mac-06F11F11946D27C5"
            MLB="00000000000000000";;
        *) echo "ERROR! Unknown release: ${RELEASE}"
           releases_macos
           exit 1;;
    esac

    # Use a bundled macrecovery if possible
    CWD="$(dirname "${0}")"
    if [ -x "${CWD}/macrecovery" ]; then
        MACRECOVERY="${CWD}/macrecovery"
    elif [ -x /usr/bin/macrecovery ]; then
        MACRECOVERY="/usr/bin/macrecovery"
    else
        web_get "https://raw.githubusercontent.com/wimpysworld/quickemu/master/macrecovery" "${HOME}/.quickemu"
        MACRECOVERY="python3 ${HOME}/.quickemu/macrecovery"
    fi

    if [ -z "${MACRECOVERY}" ]; then
        echo "ERROR! Can not find a usable macrecovery."
        exit 1
    fi

    # Get firmware
    web_get "https://github.com/kholia/OSX-KVM/raw/master/OpenCore/OpenCore.qcow2" "${VM_PATH}"
    web_get "https://github.com/kholia/OSX-KVM/raw/master/OVMF_CODE.fd" "${VM_PATH}"
    if [ ! -e "${VM_PATH}/OVMF_VARS-1024x768.fd" ]; then
        web_get "https://github.com/kholia/OSX-KVM/raw/master/OVMF_VARS-1024x768.fd" "${VM_PATH}"
    fi

    if [ ! -e "${VM_PATH}/RecoveryImage.chunklist" ]; then
        echo "Downloading ${RELEASE}..."
        ${MACRECOVERY} \
            --board-id "${BOARD_ID}" \
            --mlb "${MLB}" \
            --basename RecoveryImage \
            --outdir "${VM_PATH}" \
            download
    fi

    if [ -e "${VM_PATH}/RecoveryImage.dmg" ] && [ ! -e "${VM_PATH}/RecoveryImage.img" ]; then
        echo "Converting RecoveryImage..."
        qemu-img convert "${VM_PATH}/RecoveryImage.dmg" -O raw "${VM_PATH}/RecoveryImage.img"
    fi

    make_vm_config RecoveryImage.img
}

function get_popos() {
    local DRIVER="intel"
    local HASH=""
    local ISO=""
    local URL=""

    if [ -n "${1}" ]; then
      DRIVER="${1}"
    fi

    validate_release "releases_popos"

    URL=$(wget -q -O- "https://api.pop-os.org/builds/${RELEASE}/${DRIVER}" | jq ".url")
    URL="${URL//\"/}"
    ISO=$(echo "${URL}" | sed -e "s/.*\/\([^\/]*\)$/\1/")
    HASH=$(wget -q -O- "https://api.pop-os.org/builds/${RELEASE}/${DRIVER}" | jq ".sha_sum")
    HASH="${HASH//\"/}"
    web_get "${URL}" "${VM_PATH}"
    check_hash "${ISO}" "${HASH}"
    make_vm_config "${ISO}"
}


function get_regolith() {
    local HASH=""
    local ISO=""
    local URL=""
    local GHDL="https://github.com/regolith-linux/regolith-ubuntu-iso-builder/releases/download/"

    validate_release "releases_regolith"

    URL="${GHDL}"
    case ${RELEASE} in
      1.6.0_focal)
        URL="${URL}release-release-focal-focal_standard-1.6.0"
        HASH=$(wget -q -O- "${URL}/SHA256SUMS" | cut -d' ' -f1);;
      1.6.0_hirsute)
        URL="${URL}release-release-hirsute-hirsute_standard-1.6.0"
        HASH=$(wget -q -O- "${URL}/SHA256SUMS" | cut -d' ' -f1);;
      2.0.0_impish)
        URL="${URL}regolith-linux-2.0-impish-latest";;
      2.0.0_hirsute)
        URL="${URL}regolith-linux-2.0-hirsute-latest";;
    esac
    ISO="Regolith_${RELEASE}.iso"
    web_get "${URL}/${ISO}" "${VM_PATH}"
    if [ -n "${HASH}" ]; then
      check_hash "${ISO}" "${HASH}"
    fi
    make_vm_config "${ISO}"
}


function get_ubuntu() {
    local DEVEL="daily-live"
    local ISO=""
    local HASH=""
    local PROJECT=""
    local URL=""

    case ${OS} in
      kubuntu|lubuntu|ubuntu|ubuntu-budgie|ubuntu-mate|xubuntu)
        PROJECT="${OS}";;
      ubuntu-kylin)
        PROJECT="ubuntukylin";;
      ubuntu-studio)
        PROJECT="ubuntustudio"
        DEVEL="dvd";;
      *) echo "ERROR! ${OS} is not a recognised Ubuntu flavour."
         exit 1;;
    esac

    if [ "${RELEASE}" == "canary" ] && [ "${OS}" != "ubuntu" ]; then
      echo "ERROR! Canary is currently only available for Ubuntu."
      exit 1
    else
      validate_release "releases_ubuntu"
    fi

    if [ "${RELEASE}" == "canary" ]; then
        DEVEL="daily-canary"
        URL="http://cdimage.ubuntu.com/${PROJECT}/${DEVEL}/current"
    elif [ "${RELEASE}" == "devel" ]; then
        URL="http://cdimage.ubuntu.com/${PROJECT}/${DEVEL}/current"
    elif [ "${PROJECT}" == "ubuntu" ]; then
        URL="http://releases.ubuntu.com/${RELEASE}"
    else
        URL="http://cdimage.ubuntu.com/${PROJECT}/releases/${RELEASE}/release"
    fi

    HASH=$(wget -q -O- "${URL}/SHA256SUMS" | cut -d' ' -f1)
    ISO=$(wget -q -O- "${URL}/SHA256SUMS" | grep 'desktop\|dvd' | grep amd64 | cut -d' ' -f2 | sed 's|*||g')
    if [ "${RELEASE}" == "canary" ] || [ "${RELEASE}" == "devel" ]; then
        zsync_get "${URL}/${ISO}" "${VM_PATH}" "${OS}-${RELEASE}.iso"
        make_vm_config "${OS}-${RELEASE}.iso"
    else
        web_get "${URL}/${ISO}" "${VM_PATH}"
        check_hash "${ISO}" "${HASH}"
        make_vm_config "${ISO}"
    fi
}
function get_garuda() {
    local HASH=""
    local ISO=""
    local URL=""
    local REL_TYPE=""
    local LATEST_URL=""
    local HASH_URL=""
    local GLDL="http://mirrors.fossho.st/garuda/iso"

    validate_release "releases_garuda"

    # Part of the path is different for a couple of community releases vs the supported garuda ones
    case ${RELEASE} in
      mate|cinnamon)
        REL_TYPE="community";;
      *)
        REL_TYPE="garuda";;
    esac

    # need to follow daily releases and use contents of SHA sums file
    # to derive the filename and date
    LATEST_URL="${GLDL}/latest/${REL_TYPE}/${RELEASE}/latest.iso.sha256"
    HASH_URL="$(wget -q -O- ${LATEST_URL})"
    ISO="$(echo ${HASH_URL} | awk '{print $NF}' )"
    HASH=$(echo "${HASH_URL}" | cut -d\  -f1)
    LDATE=$(echo "${ISO}" | awk -F'-' '{print $NF}' |cut -d'.' -f1) #
    URL="${GLDL}/${REL_TYPE}/${RELEASE}/${LDATE}"

    #web_get "${URL}/${ISO}" "${VM_PATH}"
    zsync_get "${URL}/${ISO}" "${VM_PATH}" "${OS}-${RELEASE}.iso"
    #if [ -n "${HASH}" ]; then
    #  check_hash "${ISO}" "${HASH}"
    #fi
    make_vm_config "${OS}-${RELEASE}.iso"
}

function unattended_windows() {
    cat << 'EOF' > "${1}"
<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend"
  xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <!--
       For documentation on components:
       https://docs.microsoft.com/en-us/windows-hardware/customize/desktop/unattend/
  -->

  <settings pass="generalize">
    <component name="Microsoft-Windows-PnPSysprep"
      processorArchitecture="amd64"
      publicKeyToken="31bf3856ad364e35"
      language="neutral"
      versionScope="nonSxS">
      <PersistAllDeviceInstalls>true</PersistAllDeviceInstalls>
    </component>
  </settings>

  <settings pass="specialize">
    <component name="Microsoft-Windows-Security-SPP-UX"
      processorArchitecture="amd64"
      publicKeyToken="31bf3856ad364e35"
      language="neutral"
      versionScope="nonSxS"
      xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <SkipAutoActivation>false</SkipAutoActivation>
    </component>
    <component name="Microsoft-Windows-Shell-Setup"
    <component name="Microsoft-Windows-SQMApi"
      processorArchitecture="amd64"
      publicKeyToken="31bf3856ad364e35"
      language="neutral"
      versionScope="nonSxS"
      xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <CEIPEnabled>0</CEIPEnabled>
    </component>
  </settings>

  <settings pass="windowsPE">
    <component name="Microsoft-Windows-Setup"
      processorArchitecture="amd64"
      publicKeyToken="31bf3856ad364e35"
      language="neutral"
      versionScope="nonSxS"
      xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <DiskConfiguration>
        <Disk wcm:action="add">
          <DiskID>0</DiskID>
          <WillWipeDisk>true</WillWipeDisk>
          <CreatePartitions>
            <!-- Windows RE Tools partition -->
            <CreatePartition wcm:action="add">
              <Order>1</Order>
              <Type>Primary</Type>
              <Size>256</Size>
            </CreatePartition>
            <!-- System partition (ESP) -->
            <CreatePartition wcm:action="add">
              <Order>2</Order>
              <Type>EFI</Type>
              <Size>128</Size>
            </CreatePartition>
            <!-- Microsoft reserved partition (MSR) -->
            <CreatePartition wcm:action="add">
              <Order>3</Order>
              <Type>MSR</Type>
              <Size>128</Size>
            </CreatePartition>
            <!-- Windows partition -->
            <CreatePartition wcm:action="add">
              <Order>4</Order>
              <Type>Primary</Type>
              <Extend>true</Extend>
            </CreatePartition>
          </CreatePartitions>
          <ModifyPartitions>
            <!-- Windows RE Tools partition -->
            <ModifyPartition wcm:action="add">
              <Order>1</Order>
              <PartitionID>1</PartitionID>
              <Label>WINRE</Label>
              <Format>NTFS</Format>
              <TypeID>DE94BBA4-06D1-4D40-A16A-BFD50179D6AC</TypeID>
            </ModifyPartition>
            <!-- System partition (ESP) -->
            <ModifyPartition wcm:action="add">
              <Order>2</Order>
              <PartitionID>2</PartitionID>
              <Label>System</Label>
              <Format>FAT32</Format>
            </ModifyPartition>
            <!-- MSR partition does not need to be modified -->
            <ModifyPartition wcm:action="add">
              <Order>3</Order>
              <PartitionID>3</PartitionID>
            </ModifyPartition>
            <!-- Windows partition -->
              <ModifyPartition wcm:action="add">
              <Order>4</Order>
              <PartitionID>4</PartitionID>
              <Label>Windows</Label>
              <Letter>C</Letter>
              <Format>NTFS</Format>
            </ModifyPartition>
          </ModifyPartitions>
        </Disk>
      </DiskConfiguration>
      <ImageInstall>
        <OSImage>
          <InstallTo>
            <DiskID>0</DiskID>
            <PartitionID>4</PartitionID>
          </InstallTo>
          <InstallToAvailablePartition>false</InstallToAvailablePartition>
        </OSImage>
      </ImageInstall>
      <UserData>
        <AcceptEula>true</AcceptEula>
        <ProductKey>
          <key>VK7JG-NPHTM-C97JM-9MPGT-3V66T</key>
          <WillShowUI>Never</WillShowUI>
        </ProductKey>
      </UserData>
      <DynamicUpdate>
        <Enable>true</Enable>
        <WillShowUI>Never</WillShowUI>
      </DynamicUpdate>
    </component>

    <component name="Microsoft-Windows-PnpCustomizationsWinPE"
      publicKeyToken="31bf3856ad364e35"
      language="neutral"
      versionScope="nonSxS"
      processorArchitecture="amd64">

      <!--
           This makes the VirtIO drivers available to Windows, assuming that
           the VirtIO driver disk
           (https://github.com/virtio-win/virtio-win-pkg-scripts/blob/master/README.md)
           is available as drive E:
      -->
      <DriverPaths>
        <PathAndCredentials wcm:action="add" wcm:keyValue="1">
          <Path>E:\qemufwcfg\w10\amd64</Path>
        </PathAndCredentials>
        <PathAndCredentials wcm:action="add" wcm:keyValue="2">
          <Path>E:\vioinput\w10\amd64</Path>
        </PathAndCredentials>
        <PathAndCredentials wcm:action="add" wcm:keyValue="3">
          <Path>E:\vioscsi\w10\amd64</Path>
        </PathAndCredentials>
        <PathAndCredentials wcm:action="add" wcm:keyValue="4">
          <Path>E:\viostor\w10\amd64</Path>
        </PathAndCredentials>
        <PathAndCredentials wcm:action="add" wcm:keyValue="5">
          <Path>E:\vioserial\w10\amd64</Path>
        </PathAndCredentials>
        <PathAndCredentials wcm:action="add" wcm:keyValue="6">
          <Path>E:\qxldod\w10\amd64</Path>
        </PathAndCredentials>
        <PathAndCredentials wcm:action="add" wcm:keyValue="7">
          <Path>E:\amd64\w10</Path>
        </PathAndCredentials>
        <PathAndCredentials wcm:action="add" wcm:keyValue="8">
          <Path>E:\viogpudo\w10\amd64</Path>
        </PathAndCredentials>
        <PathAndCredentials wcm:action="add" wcm:keyValue="9">
          <Path>E:\viorng\w10\amd64</Path>
        </PathAndCredentials>
        <PathAndCredentials wcm:action="add" wcm:keyValue="10">
          <Path>E:\NetKVM\w10\amd64</Path>
        </PathAndCredentials>
        <PathAndCredentials wcm:action="add" wcm:keyValue="11">
          <Path>E:\viofs\w10\amd64</Path>
        </PathAndCredentials>
        <PathAndCredentials wcm:action="add" wcm:keyValue="12">
          <Path>E:\Balloon\w10\amd64</Path>
        </PathAndCredentials>
      </DriverPaths>
    </component>
  </settings>

  <settings pass="specialize">
    <component name="Microsoft-Windows-Shell-Setup"
      processorArchitecture="amd64"
      publicKeyToken="31bf3856ad364e35"
      language="neutral"
      versionScope="nonSxS"
      xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <ComputerName>*</ComputerName>
      <OEMInformation>
        <Manufacturer>Wimpys World</Manufacturer>
        <Model>Quickemu</Model>
        <SupportHours>24/7</SupportHours>
        <SupportPhone></SupportPhone>
        <SupportProvider>Wimpys World</SupportProvider>
        <SupportURL>https://github.com/wimpysworld/quickemu/issues</SupportURL>
      </OEMInformation>
      <OEMName>Wimpys World</OEMName>
    </component>
  </settings>

  <settings pass="oobeSystem">
    <component name="Microsoft-Windows-Shell-Setup"
      processorArchitecture="amd64"
      publicKeyToken="31bf3856ad364e35"
      language="neutral"
      versionScope="nonSxS"
      xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <OOBE>
        <HideEULAPage>true</HideEULAPage>
        <HideLocalAccountScreen>false</HideLocalAccountScreen>
        <HideOEMRegistrationScreen>true</HideOEMRegistrationScreen>
        <HideOnlineAccountScreens>false</HideOnlineAccountScreens>
        <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>
        <ProtectYourPC>3</ProtectYourPC>
        <SkipUserOOBE>false</SkipUserOOBE>
        <SkipMachineOOBE>false</SkipMachineOOBE>
        <VMModeOptimizations>
          <SkipWinREInitialization>true</SkipWinREInitialization>
        </VMModeOptimizations>
      </OOBE>
      <FirstLogonCommands>
        <SynchronousCommand wcm:action="add">
          <CommandLine>msiexec /i E:\guest-agent\qemu-ga-x86_64.msi /quiet /passive /qn</CommandLine>
          <Description>Install Virtio Guest Agent</Description>
          <Order>1</Order>
        </SynchronousCommand>
        <SynchronousCommand wcm:action="add">
          <CommandLine>msiexec /i F:\spice-webdavd-x64-latest.msi /quiet /passive /qn</CommandLine>
          <Description>Install spice-webdavd file sharing agent</Description>
          <Order>2</Order>
        </SynchronousCommand>
        <SynchronousCommand wcm:action="add">
          <CommandLine>msiexec /i F:\UsbDk_1.0.22_x64.msi /quiet /passive /qn</CommandLine>
          <Description>Install usbdk USB sharing agent</Description>
          <Order>3</Order>
        </SynchronousCommand>
        <SynchronousCommand wcm:action="add">
          <CommandLine>msiexec /i F:\spice-vdagent-x64-0.10.0.msi /quiet /passive /qn</CommandLine>
          <Description>Install spice-vdagent SPICE agent</Description>
          <Order>4</Order>
        </SynchronousCommand>
        <SynchronousCommand wcm:action="add">
          <CommandLine>Cmd /c POWERCFG -H OFF</CommandLine>
          <Description>Disable Hibernation</Description>
          <Order>5</Order>
        </SynchronousCommand>
      </FirstLogonCommands>
    </component>
  </settings>
</unattend>
EOF
}


function dbg_windows() {
  local DEBUG=0
  if [ ${DEBUG} -eq 1 ]; then
    echo "${1}"
  fi
}

# Adapted from https://gist.github.com/hongkongkiwi/15a5bf16437315df256c118c163607cb
function get_windows() {
    local ARCH="x64"
    local INDEX=0
    local LANG_CODE="en"
    local LATEST_WINDOWS_VERSION=""
    local WINDOWS_NAME=""
    local VERSION_ID=""
    local EDITION_ID=""
    local LANGUAGE_ID=""
    local FILE_NAME=""
    local ARCH_ID=""
    local DOWNLOAD_INFO=""
    local DOWNLOAD_ID=""
    local DOWNLOAD_URL=""

    validate_release "releases_windows"

    # Ignore the most recent Windows 10 release for now.
    if [ "${RELEASE}" -eq 10 ]; then
      INDEX=0
    fi

    if [ "${RELEASE}" -eq 11 ]; then
      INDEX=0
    fi

    echo "Getting Windows ${RELEASE} URL..."
    WINDOWS_VERSIONS=$(wget -q -O- "https://tb.rg-adguard.net/php/get_version.php?type_id=1" | jq '.versions | sort_by(-(.version_id | tonumber))')
    dbg_windows "${WINDOWS_VERSIONS}"
    LATEST_WINDOWS_VERSION=$(echo "${WINDOWS_VERSIONS}" | jq -c 'map(select(.name | contains("Windows '${RELEASE}'")))['${INDEX}']')
    dbg_windows "${LATEST_WINDOWS_VERSION}"

    WINDOWS_NAME=$(echo "${LATEST_WINDOWS_VERSION}" | jq -r .name)
    dbg_windows "${WINDOWS_NAME}"
    VERSION_ID=$(echo "${LATEST_WINDOWS_VERSION}" | jq -r .version_id)
    dbg_windows "${VERSION_ID}"

    case ${RELEASE} in
        8) EDITION_ID=$(wget -q -O- "https://tb.rg-adguard.net/php/get_edition.php?version_id=${VERSION_ID}&lang=name_${LANG_CODE}" | jq -r '.editions[] | select(.name_'${LANG_CODE}'=="Windows 8.1 Pro + Core").edition_id');;
        10|11) EDITION_ID=$(wget -q -O- "https://tb.rg-adguard.net/php/get_edition.php?version_id=${VERSION_ID}&lang=name_${LANG_CODE}" | jq -r '.editions[] | select(.name_'${LANG_CODE}'=="Windows '${RELEASE}'").edition_id');;
    esac
    dbg_windows "${EDITION_ID}"

    LANGUAGE_ID=$(wget -q -O- "https://tb.rg-adguard.net/php/get_language.php?edition_id=${EDITION_ID}&lang=name_${LANG_CODE}" | jq -r '.languages[] | select(.name_'${LANG_CODE}'=="'"${LANG_NAME}"'").language_id')
    dbg_windows "${LANGUAGE_ID}"
    ARCH_INFO=$(wget -q -O- "https://tb.rg-adguard.net/php/get_arch.php?language_id=${LANGUAGE_ID}")
    dbg_windows "${ARCH_INFO}"
    FILE_NAME=$(echo "${ARCH_INFO}" | jq -r '.archs[] | select(.name | contains("'${ARCH}'")).name')
    dbg_windows "${FILE_NAME}"
    ARCH_ID=$(echo "${ARCH_INFO}" | jq -r '.archs[] | select(.name | contains("'${ARCH}'")).arch_id')
    dbg_windows "${ARCH_ID}"
    DOWNLOAD_INFO=$(wget -q -O- "https://tb.rg-adguard.net/dl.php?fileName=${ARCH_ID}&lang=en")
    dbg_windows "${DOWNLOAD_INFO}"
    DOWNLOAD_SHA1=$(echo "${DOWNLOAD_INFO}" | sed -e 's/<[^>]*>//g' | grep -o -P '(?<=SHA1: ).*(?= expire)' | sed 's/Link//')
    dbg_windows "${DOWNLOAD_SHA1}"
    DOWNLOAD_ID=$(echo "${DOWNLOAD_INFO}" | grep -oP '(?<=https:\/\/tb\.rg-adguard\.net/dl\.php\?go=)[0-9a-z]+')
    dbg_windows "${DOWNLOAD_ID}"
    DOWNLOAD_URL="https://tb.rg-adguard.net/dl.php?go=${DOWNLOAD_ID}"
    dbg_windows "${DOWNLOAD_URL}"

    echo "Downloading ${WINDOWS_NAME}..."
    web_get "${DOWNLOAD_URL}" "${VM_PATH}" "${FILE_NAME}"

    # Windows 10 doesn't include a SHA1, so only check the integrity if the SHA1 is available.
    if [ -n "${DOWNLOAD_SHA1}" ]; then
      check_hash "${FILE_NAME}" "${DOWNLOAD_SHA1}"
    fi

    web_get "https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/stable-virtio/virtio-win.iso" "${VM_PATH}"

    rm -f "${VM_PATH}/unattended.iso"
    case ${RELEASE} in
        10|11)
            echo "Making unattended.iso"
            mkdir -p "${VM_PATH}/unattended" 2>/dev/null
            web_get https://www.spice-space.org/download/windows/spice-webdavd/spice-webdavd-x64-latest.msi "${VM_PATH}/unattended"
            web_get https://www.spice-space.org/download/windows/vdagent/vdagent-win-0.10.0/spice-vdagent-x64-0.10.0.msi "${VM_PATH}/unattended"
            web_get https://www.spice-space.org/download/windows/usbdk/UsbDk_1.0.22_x64.msi "${VM_PATH}/unattended"
            unattended_windows "${VM_PATH}/unattended/autounattend.xml"
            mkisofs -quiet -l -o "${VM_PATH}/unattended.iso" "${VM_PATH}/unattended/"
            ;;
    esac
    make_vm_config "${FILE_NAME}" "virtio-win.iso"
}

trap cleanup EXIT

if ((BASH_VERSINFO[0] < 4))
then
  echo "Sorry, you need bash 4.0 or newer to run this script."
  exit 1
fi

LANGS=()
languages_windows

if [ -n "${1}" ]; then
    OS="${1,,}"
    if [ "${OS}" == "list" ] || [ "${OS}" == "list_csv" ]; then
        list_csv
    elif [ "${OS}" == "list_json" ]; then
        list_json
    elif [ "${OS}" == "--version" ] || [ "${OS}" == "-version" ] || [ "${OS}" == "version" ]; then
      whereIam=$(dirname "${BASH_SOURCE[0]}")
      quickemu_version=$( "${whereIam}"/quickemu --version)
      echo "Quickemu Version: ${quickemu_version}"
      exit 0
    fi
else
    echo "ERROR! You must specify an operating system:"
    os_support
    exit 1
fi

if [ -n "${2}" ]; then
    RELEASE="${2,,}"
    VM_PATH="${OS}-${RELEASE}"

    if [ "${OS}" == "alma" ]; then
     if [ -n "${3}" ]; then
            ISOTYPE="${3,,}"
            ISOTYPES=(minimal dvd ) # boot) # a step too far
            if [[ ! ${ISOTYPES[*]} =~ ${ISOTYPE} ]]; then
                echo "iso ${ISOTYPE} is not supported:"
                for ISOTYPE in "${ISOTYPES[@]}"; do
                  echo "${ISOTYPE}"
                done
                exit 1
            fi
        else
            ISOTYPE="minimal"
        fi
        VM_PATH="${OS}-${RELEASE}-${ISOTYPE}"
        get_alma "${ISOTYPE}"
    elif [ "${OS}" == "android" ]; then
        get_android
    elif [ "${OS}" == "archlinux" ]; then
        get_archlinux
    elif [ "${OS}" == "debian" ]; then
     if [ -n "${3}" ]; then
            FREEDOM="${3}"
            FREEDOMS=(standard nonfree)
            if [[ ! ${FREEDOMS[*]} =~ ${FREEDOM} ]]; then
                echo "ERROR! ${FREEDOM} is not a supported freedom:"
                for DRIVER in "${FREEDOMS[@]}"; do
                  echo "${FREEDOM}"
                done
                exit 1
            fi
        else
            FREEDOM="standard"
        fi
        VM_PATH="${OS}-${RELEASE}-${FREEDOM}"
        get_debian  "${FREEDOM}"
    elif [ "${OS}" == "elementary" ]; then
        get_elementary
    elif [ "${OS}" == "macos" ]; then
        get_macos
    elif [ "${OS}" == "freebsd" ]; then
        get_freebsd
    elif [ "${OS}" == "fedora" ]; then
        get_fedora
    elif [ "${OS}" == "garuda" ]; then
        get_garuda
    elif [ "${OS}" == "kali" ]; then
        get_kali
    elif [ "${OS}" == "kdeneon" ]; then
        get_kdeneon
    elif [[ "${OS}" == *"linuxmint-"* ]]; then
        get_linuxmint
    elif [[ "${OS}" == *"nixos-"* ]]; then
        get_nixos
    elif [ "${OS}" == "openbsd" ]; then
        get_openbsd
    elif [ "${OS}" == "opensuse" ]; then
        get_opensuse
    elif [ "${OS}" == "oraclelinux" ]; then
        get_oraclelinux
  elif [ "${OS}" == "popos" ]; then
        if [ -n "${3}" ]; then
            DRIVER="${3}"
            DRIVERS=(intel nvidia)
            if [[ ! ${DRIVERS[*]} =~ ${DRIVER} ]]; then
                echo "ERROR! ${DRIVER} is not a supported driver:"
                for DRIVER in "${DRIVERS[@]}"; do
                  echo "${DRIVER}"
                done
                exit 1
            fi
        else
            DRIVER="intel"
        fi
        VM_PATH="${OS}-${RELEASE}-${DRIVER}"
        get_popos "${DRIVER}"
    elif  [ "${OS}" == "regolith" ]; then
        get_regolith
    elif [ "${OS}" == "rockylinux" ]; then
        if [ -n "${3}" ]; then
            ISOTYPE="${3}"
            ISOTYPES=(minimal dvd1 boot)
            if [[ ! ${ISOTYPES[*]} =~ ${ISOTYPE} ]]; then
                echo "iso ${ISOTYPE} is not supported:"
                for ISOTYPE in "${ISOTYPES[@]}"; do
                  echo "${ISOTYPE}"
                done
                exit 1
            fi
        else
            ISOTYPE="dvd1"
        fi
        VM_PATH="${OS}-${RELEASE}-${ISOTYPE}"
        get_rocky "${ISOTYPE}"
    elif [ "${OS}" == "solus" ]; then
        get_solus
    elif [[ "${OS}" == *"ubuntu"* ]]; then
        get_ubuntu
    elif [ "${OS}" == "windows" ]; then
        if [ -n "${3}" ]; then
            LANG_NAME="${3}"
            if [[ ! ${LANGS[*]} =~ ${LANG_NAME} ]]; then
                echo "ERROR! ${LANG_NAME} is not a supported language:"
                for LANG in "${LANGS[@]}"; do
                  echo "${LANG}"
                done
                exit 1
            fi
        else
            LANG_NAME="English International"
        fi
        get_windows "${LANG_NAME}"
    elif [ "${OS}" == "zorin" ]; then
        get_zorin
    else
        echo "ERROR! ${OS} is unknown:"
        os_support
        exit 1
    fi
else
    echo -n "ERROR! You must specify a release: "
    if [ "${OS}" == "alma" ]; then
        releases_alma
    elif [ "${OS}" == "android" ]; then
        releases_android
    elif [ "${OS}" == "archlinux" ]; then
        releases_archlinux
    elif [ "${OS}" == "debian" ]; then
        releases_debian
    elif [ "${OS}" == "elementary" ]; then
        releases_elementary
    elif [ "${OS}" == "freebsd" ]; then
        releases_freebsd
    elif [ "${OS}" == "fedora" ]; then
        releases_fedora
    elif [ "${OS}" == "garuda" ]; then
        releases_garuda
    elif [ "${OS}" == "kali" ]; then
        releases_kali
    elif [[ "${OS}" == *"linuxmint-"* ]]; then
        releases_linuxmint
    elif [[ "${OS}" == *"nixos-"* ]]; then
        releases_nixos
    elif [ "${OS}" == "opensuse" ]; then
        releases_opensuse
    elif [ "${OS}" == "oraclelinux" ]; then
        releases_oraclelinux
    elif [ "${OS}" == "openbsd" ]; then
        releases_openbsd
    elif [ "${OS}" == "macos" ]; then
        releases_macos
    elif [ "${OS}" == "popos" ]; then
        releases_popos
    elif [ "${OS}" == "regolith" ]; then
        releases_regolith
    elif [ "${OS}" == "rockylinux" ]; then
        releases_rockylinux
    elif [ "${OS}" == "solus" ]; then
        releases_solus
    elif [[ "${OS}" == *"ubuntu"* ]]; then
        releases_ubuntu
    elif [ "${OS}" == "windows" ]; then
        releases_windows
    elif [ "${OS}" == "zorin" ]; then
        releases_zorin
    else
        echo "${OS} is unknown"
        os_support
    fi
    exit 1
fi