Skip to content

Instantly share code, notes, and snippets.

@jetfir3
Last active October 28, 2025 12:06
Show Gist options
  • Save jetfir3/6b28fd279bbcadbae70980bd711a844f to your computer and use it in GitHub Desktop.
Save jetfir3/6b28fd279bbcadbae70980bd711a844f to your computer and use it in GitHub Desktop.

Revisions

  1. jetfir3 created this gist Jun 18, 2024.
    535 changes: 535 additions & 0 deletions download_fusion.sh
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,535 @@
    #!/usr/bin/env bash

    # Download VMware Fusion for macOS without a Broadcom account.
    #
    # This script allows you to download various versions of VMware Fusion
    # from Broadcom's Cloudflare CDN (versions 8.0.0 to 13.6.3)
    # or from the archive.org VMware Workstation archive (versions 8.x.x+).
    #
    # Options:
    # -k: Keep the downloaded file compressed (Cloudflare only; ignored for archive.org).
    # -s <source>: Specify source (archive.org or cloudflare).
    # -v <version>: Specify version (only supports 13.x.x+ universal builds).
    #
    # Usage: $0 [-k] [-s <archive.org|cloudflare>] [-v <version>]

    BASE_URL="https://softwareupdate-prod.broadcom.com/cds/vmw-desktop"
    CDN_MIRROR="softwareupdate-prod.broadcom.com:443:softwareupdate-prod.broadcom.com.cdn.cloudflare.net:443"
    ARCHIVE_BASE_URL="https://archive.org/download/vmwareworkstationarchive/Fusion"

    # Cloudflare CDN metadata
    read -r -d '' XML_DATA << 'EOF'
    <metalist>
    <metadata><productid>fusion-x86_64</productid><version>8.0.0</version><url>fusion/8.0.0/2985594/core/</url></metadata>
    <metadata><productid>fusion-x86_64</productid><version>8.0.0</version><url>fusion/8.0.1/3094680/core/</url></metadata>
    <metadata><productid>fusion-x86_64</productid><version>8.0.0</version><url>fusion/8.0.2/3164312/core/</url></metadata>
    <metadata><productid>fusion-x86_64</productid><version>8.0.0</version><url>fusion/8.1.0/3272237/core/</url></metadata>
    <metadata><productid>fusion-x86_64</productid><version>8.0.0</version><url>fusion/8.1.1/3771013/core/</url></metadata>
    <metadata><productid>fusion-x86_64</productid><version>8.0.0</version><url>fusion/8.5.0/4352717/core/</url></metadata>
    <metadata><productid>fusion-x86_64</productid><version>8.0.0</version><url>fusion/8.5.1/4543325/core/</url></metadata>
    <metadata><productid>fusion-x86_64</productid><version>8.0.0</version><url>fusion/8.5.10/7527438/core/</url></metadata>
    <metadata><productid>fusion-x86_64</productid><version>8.0.0</version><url>fusion/8.5.2/4635224/core/</url></metadata>
    <metadata><productid>fusion-x86_64</productid><version>8.0.0</version><url>fusion/8.5.3/4696910/core/</url></metadata>
    <metadata><productid>fusion-x86_64</productid><version>8.0.0</version><url>fusion/8.5.4/5115894/core/</url></metadata>
    <metadata><productid>fusion-x86_64</productid><version>8.0.0</version><url>fusion/8.5.5/5192483/core/</url></metadata>
    <metadata><productid>fusion-x86_64</productid><version>8.0.0</version><url>fusion/8.5.6/5234762/core/</url></metadata>
    <metadata><productid>fusion-x86_64</productid><version>8.0.0</version><url>fusion/8.5.7/5528452/core/</url></metadata>
    <metadata><productid>fusion-x86_64</productid><version>8.0.0</version><url>fusion/8.5.8/5824040/core/</url></metadata>
    <metadata><productid>fusion-x86_64</productid><version>8.0.0</version><url>fusion/8.5.9/7098239/core/</url></metadata>
    <metadata><productid>fusion-x86_64</productid><version>10.0.0</version><url>fusion/10.0.0/6665085/core/</url></metadata>
    <metadata><productid>fusion-x86_64</productid><version>10.0.0</version><url>fusion/10.0.1/6754183/core/</url></metadata>
    <metadata><productid>fusion-x86_64</productid><version>10.0.0</version><url>fusion/10.1.0/7370838/core/</url></metadata>
    <metadata><productid>fusion-x86_64</productid><version>10.0.0</version><url>fusion/10.1.1/7520154/core/</url></metadata>
    <metadata><productid>fusion-x86_64</productid><version>10.0.0</version><url>fusion/10.1.2/8502123/core/</url></metadata>
    <metadata><productid>fusion-x86_64</productid><version>10.0.0</version><url>fusion/10.1.3/9472307/core/</url></metadata>
    <metadata><productid>fusion-x86_64</productid><version>10.0.0</version><url>fusion/10.1.4/10700604/core/</url></metadata>
    <metadata><productid>fusion-x86_64</productid><version>10.0.0</version><url>fusion/10.1.5/10950653/core/</url></metadata>
    <metadata><productid>fusion-x86_64</productid><version>10.0.0</version><url>fusion/10.1.6/12989998/core/</url></metadata>
    <metadata><productid>fusion-x86_64</productid><version>11.0.0</version><url>fusion/11.0.0/10120384/core/</url></metadata>
    <metadata><productid>fusion-x86_64</productid><version>11.0.0</version><url>fusion/11.0.1/10738065/core/</url></metadata>
    <metadata><productid>fusion-x86_64</productid><version>11.0.0</version><url>fusion/11.0.2/10952296/core/</url></metadata>
    <metadata><productid>fusion-x86_64</productid><version>11.0.0</version><url>fusion/11.0.3/12992109/core/</url></metadata>
    <metadata><productid>fusion-x86_64</productid><version>11.0.0</version><url>fusion/11.1.0/13668589/core/</url></metadata>
    <metadata><productid>fusion-x86_64</productid><version>11.5.0</version><url>fusion/11.5.0/14634996/core/</url></metadata>
    <metadata><productid>fusion-x86_64</productid><version>11.5.0</version><url>fusion/11.5.1/15018442/core/</url></metadata>
    <metadata><productid>fusion-x86_64</productid><version>11.5.0</version><url>fusion/11.5.2/15794494/core/</url></metadata>
    <metadata><productid>fusion-x86_64</productid><version>11.5.0</version><url>fusion/11.5.3/15870345/core/</url></metadata>
    <metadata><productid>fusion-x86_64</productid><version>11.5.0</version><url>fusion/11.5.5/16269456/core/</url></metadata>
    <metadata><productid>fusion-x86_64</productid><version>11.5.0</version><url>fusion/11.5.6/16696540/core/</url></metadata>
    <metadata><productid>fusion-x86_64</productid><version>11.5.0</version><url>fusion/11.5.7/17130923/core/</url></metadata>
    <metadata><productid>fusion-x86_64</productid><version>12.0.0</version><url>fusion/12.1.0/17195230/core/</url></metadata>
    <metadata><productid>fusion-x86_64</productid><version>12.0.0</version><url>fusion/12.1.1/17801503/core/</url></metadata>
    <metadata><productid>fusion-x86_64</productid><version>12.0.0</version><url>fusion/12.1.2/17964953/core/</url></metadata>
    <metadata><productid>fusion-x86_64</productid><version>12.0.0</version><url>fusion/12.2.0/18760249/x86/core/</url></metadata>
    <metadata><productid>fusion-arm64</productid><version>12.0.0</version><url>fusion/12.2.0/18760249/arm64/core/</url></metadata>
    <metadata><productid>fusion-x86_64</productid><version>12.0.0</version><url>fusion/12.2.1/18811640/x86/core/</url></metadata>
    <metadata><productid>fusion-arm64</productid><version>12.0.0</version><url>fusion/12.2.1/18811640/arm64/core/</url></metadata>
    <metadata><productid>fusion-x86_64</productid><version>12.0.0</version><url>fusion/12.2.3/19436697/x86/core/</url></metadata>
    <metadata><productid>fusion-arm64</productid><version>12.0.0</version><url>fusion/12.2.3/19436697/arm64/core/</url></metadata>
    <metadata><productid>fusion-x86_64</productid><version>12.0.0</version><url>fusion/12.2.4/20071091/x86/core/</url></metadata>
    <metadata><productid>fusion-arm64</productid><version>12.0.0</version><url>fusion/12.2.4/20071091/arm64/core/</url></metadata>
    <metadata><productid>fusion-x86_64</productid><version>12.0.0</version><url>fusion/12.2.5/20904517/x86/core/</url></metadata>
    <metadata><productid>fusion-arm64</productid><version>12.0.0</version><url>fusion/12.2.5/20904517/arm64/core/</url></metadata>
    <metadata><productid>fusion-x86_64</productid><version>13.0.0</version><url>fusion/13.0.0/20802013/x86/core/</url></metadata>
    <metadata><productid>fusion-arm64</productid><version>13.0.0</version><url>fusion/13.0.0/20802013/arm64/core/</url></metadata>
    <metadata><productid>fusion-universal</productid><version>13.0.0</version><url>fusion/13.0.0/20802013/universal/core/</url></metadata>
    <metadata><productid>fusion-universal</productid><version>13.0.0</version><url>fusion/13.0.1/21139760/universal/core/</url></metadata>
    <metadata><productid>fusion-universal</productid><version>13.0.0</version><url>fusion/13.0.2/21581413/universal/core/</url></metadata>
    <metadata><productid>fusion-universal</productid><version>13.0.0</version><url>fusion/13.5.0/22583790/universal/core/</url></metadata>
    <metadata><productid>fusion-universal</productid><version>13.0.0</version><url>fusion/13.5.1/23298085/universal/core/</url></metadata>
    <metadata><productid>fusion-universal</productid><version>13.0.0</version><url>fusion/13.5.2/23775688/universal/core/</url></metadata>
    <metadata><productid>fusion-universal</productid><version>13.0.0</version><url>fusion/13.6.0/24238079/universal/core/</url></metadata>
    <metadata><productid>fusion-universal</productid><version>13.0.0</version><url>fusion/13.6.1/24319021/universal/core/</url></metadata>
    <metadata><productid>fusion-universal</productid><version>13.0.0</version><url>fusion/13.6.2/24409261/universal/core/</url></metadata>
    <metadata><productid>fusion-universal</productid><version>13.0.0</version><url>fusion/13.6.3/24585314/universal/core/</url></metadata>
    </metalist>
    EOF

    KEEP_COMPRESSED=false
    SOURCE=""
    VERSION=""

    usage() {
    echo "Usage: $0 [-k] [-s <archive.org|cloudflare>] [-v <version>]"
    echo " -k: Keep the downloaded file compressed (Cloudflare only; ignored for archive.org)"
    echo " -s: Specify source (archive.org or cloudflare)"
    echo " -v: Specify version (only supports 13.x.x+ universal builds)"
    exit 1
    }

    validate_version_format() {
    local version="$1"
    if [[ ! $version =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
    echo "Error: Version '$version' must be in the format x.y.z (e.g., 13.0.0)." >&2
    exit 1
    fi
    local major_version
    major_version=$(echo "$version" | cut -d'.' -f1)
    if [[ $major_version -lt 13 ]]; then
    echo "Error: Only universale builds for v13.0.0 and higher are allowed." >&2
    exit 1
    fi
    }

    while getopts "ks:v:" opt; do
    case $opt in
    k) KEEP_COMPRESSED=true ;;
    s) SOURCE="$OPTARG" ;;
    v) VERSION="$OPTARG"; validate_version_format "$VERSION" ;;
    *) usage ;;
    esac
    done

    requirements_check() {
    command -v curl >/dev/null 2>&1 || { echo "Error: curl is required but not installed." >&2; exit 1; }
    command -v grep >/dev/null 2>&1 || { echo "Error: grep is required but not installed." >&2; exit 1; }
    command -v awk >/dev/null 2>&1 || { echo "Error: awk is required but not installed." >&2; exit 1; }
    command -v sed >/dev/null 2>&1 || { echo "Error: sed is required but not installed." >&2; exit 1; }
    command -v tar >/dev/null 2>&1 || { echo "Error: tar is required but not installed." >&2; exit 1; }
    command -v sort >/dev/null 2>&1 || { echo "Error: sort is required but not installed." >&2; exit 1; }
    }

    get_product_ids() {
    echo "$XML_DATA" | sed -n 's/.*<productid>\(.*\)<\/productid>.*/\1/p' | sort -u
    }

    get_base_versions_for_product() {
    local product_id="$1"
    echo "$XML_DATA" | awk -v prodid="$product_id" '
    BEGIN { RS="</metadata>" }
    $0 ~ "<productid>" prodid "</productid>" {
    if (match($0, /<version>[^<]+<\/version>/)) {
    line = $0
    sub(/.*<version>/, "", line)
    sub(/<\/version>.*/, "", line)
    print line
    }
    }
    ' | $SORT_UNIQUE_CMD
    }

    get_full_versions_and_urls() {
    local product_id="$1"
    local base_version="$2"
    echo "$XML_DATA" | awk -v prodid="$product_id" -v basever="$base_version" '
    BEGIN { RS="</metadata>" }
    ($0 ~ "<productid>" prodid "</productid>") && ($0 ~ "<version>" basever "</version>") {
    if (match($0, /<url>[^<]+<\/url>/)) {
    line = $0
    sub(/.*<url>/, "", line)
    sub(/<\/url>.*/, "", line)
    url_path = line
    split(url_path, parts, "/")
    print parts[2], url_path
    }
    }
    ' | $SORT_CMD
    }

    get_cloudflare_universal_versions() {
    echo "$XML_DATA" | awk -v prodid="fusion-universal" '
    BEGIN { RS="</metadata>" }
    $0 ~ "<productid>" prodid "</productid>" {
    if (match($0, /<url>[^<]+<\/url>/)) {
    line = $0
    sub(/.*<url>/, "", line)
    sub(/<\/url>.*/, "", line)
    split(line, parts, "/")
    print parts[2]
    }
    }
    ' | $SORT_UNIQUE_CMD
    }

    get_cloudflare_version_url() {
    local version="$1"
    echo "$XML_DATA" | awk -v prodid="fusion-universal" -v ver="$version" '
    BEGIN { RS="</metadata>" }
    ($0 ~ "<productid>" prodid "</productid>") && ($0 ~ ver "/") {
    if (match($0, /<url>[^<]+<\/url>/)) {
    line = $0
    sub(/.*<url>/, "", line)
    sub(/<\/url>.*/, "", line)
    url_path = line
    split(url_path, parts, "/")
    print parts[2], url_path
    }
    }
    ' | head -n 1
    }

    get_archive_major_versions() {
    local html
    html=$(curl -s -f -L --max-time 30 "$ARCHIVE_BASE_URL/" || { echo "Error: Failed to fetch archive.org page." >&2; exit 1; })
    echo "$html" | grep -o '<a href="\([0-9][0-9]*\.x/\)"' | sed 's/<a href="\([^"]*\)".*/\1/' | sed 's|/$||' | $SORT_UNIQUE_CMD
    }

    get_archive_full_versions() {
    local major_version="$1"
    local html
    html=$(curl -s -f -L --max-time 30 "$ARCHIVE_BASE_URL/$major_version/" || { echo "Error: Failed to fetch archive.org page for $major_version." >&2; exit 1; })
    echo "$html" | grep -o '<a href="VMware-Fusion-[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*[^"]*\.dmg"' | sed 's/<a href="\([^"]*\)".*/\1/' | while read -r filename; do
    if [[ $filename =~ VMware-Fusion-([0-9]+\.[0-9]+\.[0-9]+) ]]; then
    echo "${BASH_REMATCH[1]} $filename"
    fi
    done | $SORT_CMD
    }

    validate_cloudflare_version() {
    local version="$1"
    local valid_versions
    valid_versions=$(get_cloudflare_universal_versions)
    if ! echo "$valid_versions" | grep -Fx "$version" >/dev/null; then
    echo "Error: Version '$version' is not available for universal builds on Cloudflare. Available versions:" >&2
    echo "$valid_versions" >&2
    exit 1
    fi
    }

    validate_archive_version() {
    local version="$1"
    local major_version
    major_version=$(echo "$version" | cut -d'.' -f1)
    if [[ $major_version -lt 13 ]]; then
    echo "Error: Version '$version' is not supported for archive.org (only 13.0.0+ allowed)." >&2
    exit 1
    fi
    local full_versions
    full_versions=$(get_archive_full_versions "13.x" | awk '{print $1}')
    if ! echo "$full_versions" | grep -Fx "$version" >/dev/null; then
    echo "Error: Version '$version' is not available on archive.org. Available versions:" >&2
    echo "$full_versions" >&2
    exit 1
    fi
    }

    check_url() {
    local url=$1
    curl -s --head -L --connect-to "$CDN_MIRROR" --max-time 30 "$url" | head -n 1 | grep "200" >/dev/null
    }

    download_archive_file() {
    local major_version="$1"
    local filename="$2"
    local version="$3"
    local download_url="$ARCHIVE_BASE_URL/$major_version/$filename"
    local download_file="$DOWNLOAD_DIR/$filename"
    local max_retries=3
    local retry_delay=5
    local attempt=1

    echo "Attempting to download from URL: $download_url"
    while [ $attempt -le $max_retries ]; do
    echo "Attempt $attempt of $max_retries..."
    if [ $attempt -gt 1 ] && [ -f "$download_file" ]; then
    echo "Resuming partial download..."
    fi
    if curl -k -q --progress-bar -f -L -C - --max-time 300 -o "$download_file" "$download_url"; then
    echo -e "\nDownload successful."
    echo "Downloaded file location: $download_file"
    return 0
    else
    echo "Attempt $attempt failed." >&2
    if [ $attempt -lt $max_retries ]; then
    echo "Retrying in $retry_delay seconds..."
    sleep $retry_delay
    fi
    fi
    ((attempt++))
    done
    echo "Error: Download failed after $max_retries attempts." >&2
    exit 1
    }

    requirements_check

    if sort --version 2>/dev/null | grep -q "GNU coreutils"; then
    SORT_CMD="sort -V"
    SORT_UNIQUE_CMD="sort -uV"
    else
    SORT_CMD="sort -t. -k1,1n -k2,2n -k3,3n -k4,4n"
    SORT_UNIQUE_CMD="sort -t. -k1,1n -k2,2n -k3,3n -k4,4n -u"
    fi

    IS_MACOS=false
    if [[ $(uname | tr '[:upper:]' '[:lower:]') == "darwin"* ]]; then
    IS_MACOS=true
    else
    KEEP_COMPRESSED=true
    echo "Warning: Not running on macOS. Downloaded file will be kept compressed." >&2
    fi

    if [[ -n "$SOURCE" ]]; then
    case "$SOURCE" in
    archive.org|cloudflare) ;;
    *) echo "Error: Invalid source '$SOURCE'. Must be 'archive.org' or 'cloudflare'." >&2; usage ;;
    esac
    else
    echo "Select Download Source:"
    echo "1. Archive.org"
    echo " - v8.x - 12.x ; x86_64 only"
    echo " - v13.x - 13.6.4 and up ; universal only"
    echo " - Potentially slower downloads"
    echo "2. Cloudflare CDN"
    echo " - v8.0.0 - 13.6.3 ONLY ; includes all architectures"
    echo " - Faster downloads from official CDN"
    echo " - Core installers only ; no bundled VMware Tools"
    read -p "Enter the number of your choice (default is 1): " choice
    case "$choice" in
    2) SOURCE="cloudflare" ;;
    ""|1)
    SOURCE="archive.org"
    if $KEEP_COMPRESSED; then
    echo "Warning: -k is ignored for archive.org downloads, as files are already uncompressed .dmg files." >&2
    fi
    ;;
    *) echo "Error: Invalid choice. Please enter 1 or 2." >&2; exit 1 ;;
    esac
    fi

    if [[ -n "$VERSION" ]]; then
    validate_version_format "$VERSION"
    if [[ "$SOURCE" == "cloudflare" ]]; then
    validate_cloudflare_version "$VERSION"
    else
    validate_archive_version "$VERSION"
    fi
    fi

    if [[ "$SOURCE" == "archive.org" ]]; then
    if [[ -n "$VERSION" ]]; then
    selected_major_version="13.x"
    selected_version="$VERSION"
    selected_filename=$(get_archive_full_versions "13.x" | grep "^$VERSION " | awk '{print $2}')
    if [[ -z "$selected_filename" ]]; then
    echo "Error: Could not find filename for version $VERSION on archive.org." >&2
    exit 1
    fi
    echo "Selected VMware Fusion $selected_version from archive.org"
    else
    echo "Fetching available base versions from archive.org..."
    major_versions=()
    while IFS= read -r line; do
    major_versions+=("$line")
    done < <(get_archive_major_versions)

    [[ ${#major_versions[@]} -eq 0 ]] && { echo "Error: No base versions found on archive.org." >&2; exit 1; }

    PS3="Select a major version: "
    select selected_major_version in "${major_versions[@]}"; do
    [[ -n "$selected_major_version" ]] && break || echo "Invalid selection. Please try again."
    done

    echo "Fetching available full versions for $selected_major_version..."
    full_versions=()
    filenames=()
    while IFS=' ' read -r version filename; do
    full_versions+=("$version")
    filenames+=("$filename")
    done < <(get_archive_full_versions "$selected_major_version")

    [[ ${#full_versions[@]} -eq 0 ]] && { echo "Error: No full versions found for $selected_major_version." >&2; exit 1; }

    PS3="Select a full version to download: "
    select selected_version in "${full_versions[@]}"; do
    if [[ -n "$selected_version" ]]; then
    selected_filename="${filenames[$REPLY-1]}"
    break
    else
    echo "Invalid selection. Please try again."
    fi
    done

    echo "Selected VMware Fusion $selected_version from archive.org"
    fi

    DOWNLOAD_DIR="${HOME}/Downloads"
    mkdir -p "${DOWNLOAD_DIR}"
    download_archive_file "$selected_major_version" "$selected_filename" "$selected_version"

    if $IS_MACOS; then
    xattr -d com.apple.quarantine "${DOWNLOAD_DIR}/${selected_filename}" &>/dev/null
    LICENSE_FILES=$(ls -1 "/Library/Preferences/VMware Fusion/license-fusion"* 2>/dev/null)

    if [[ -n "${LICENSE_FILES}" ]]; then
    echo -e "\nNotice: Existing license file(s) found." >&2
    echo "Deletion may be required if converting to the 'Free for Personal Use' model." >&2
    echo "To remove them, run the following command(s):" >&2
    for LICENSE_FILE in ${LICENSE_FILES}; do
    echo "sudo rm \"${LICENSE_FILE}\"" >&2
    done
    fi
    fi

    echo -e "\nFinished."
    echo "VMware Fusion .dmg location: ${DOWNLOAD_DIR}/${selected_filename}"
    else
    if [[ -n "$VERSION" ]]; then
    selected_product="fusion-universal"
    read -r selected_version url_path <<< "$(get_cloudflare_version_url "$VERSION")"
    if [[ -z "$selected_version" || -z "$url_path" ]]; then
    echo "Error: Could not find URL path for version $VERSION on Cloudflare." >&2
    exit 1
    fi
    echo "Selected VMware Fusion $selected_version (universal) from Cloudflare CDN"
    VERSION="$selected_version"
    URL_PATH="$url_path"
    BUILD=$(echo "$URL_PATH" | cut -d'/' -f3)
    else
    echo "Fetching available architectures..."
    products=()
    while IFS= read -r line; do
    products+=("$line")
    done < <(get_product_ids)

    [[ ${#products[@]} -eq 0 ]] && { echo "No product architectures found in XML data. Exiting." >&2; exit 1; }

    PS3="Select an architecture: "
    select selected_product in "${products[@]}"; do
    [[ -n "$selected_product" ]] && break || echo "Invalid selection. Please try again."
    done

    echo "Fetching available base versions for $selected_product..."
    base_versions=()
    while IFS= read -r line; do
    base_versions+=("$line")
    done < <(get_base_versions_for_product "$selected_product")

    [[ ${#base_versions[@]} -eq 0 ]] && { echo "No base versions found for $selected_product. Exiting." >&2; exit 1; }

    PS3="Select a base version: "
    select selected_base_version in "${base_versions[@]}"; do
    [[ -n "$selected_base_version" ]] && break || echo "Invalid selection. Please try again."
    done

    echo "Fetching available full versions under $selected_base_version..."
    version_url_pairs=()
    while IFS= read -r line; do
    version_url_pairs+=("$line")
    done < <(get_full_versions_and_urls "$selected_product" "$selected_base_version")

    [[ ${#version_url_pairs[@]} -eq 0 ]] && { echo "No full versions found for the selected criteria. Exiting." >&2; exit 1; }

    display_versions=()
    for item in "${version_url_pairs[@]}"; do
    display_versions+=("$(echo "$item" | awk '{print $1}')")
    done

    PS3="Select a full version to download: "
    select full_version_selected in "${display_versions[@]}"; do
    if [[ -n "$full_version_selected" ]]; then
    selected_pair="${version_url_pairs[$REPLY-1]}"
    read -r VERSION URL_PATH <<< "$selected_pair"
    break
    else
    echo "Invalid selection. Please try again."
    fi
    done

    BUILD=$(echo "$URL_PATH" | cut -d'/' -f3)
    echo "Selected VMware Fusion $VERSION (Build $BUILD) from Cloudflare CDN"
    fi

    [[ -z "$VERSION" || -z "$BUILD" || -z "$URL_PATH" ]] && { echo "Error: Could not determine version, build, or URL path. Exiting." >&2; exit 1; }

    DOWNLOAD_URL="${BASE_URL}/${URL_PATH}com.vmware.fusion.zip.tar"
    DOWNLOAD_DIR="${HOME}/Downloads"
    DOWNLOAD_FILE="${DOWNLOAD_DIR}/com.vmware.fusion-${VERSION}-${BUILD}.zip.tar"
    UNZIP_DIR="${DOWNLOAD_DIR}/com.vmware.fusion-${VERSION}-${BUILD}"
    APP_PATH="${UNZIP_DIR}/VMware Fusion.app"

    echo "Downloading VMware Fusion v${VERSION} (${BUILD})..."
    mkdir -p "${DOWNLOAD_DIR}"

    check_url "$DOWNLOAD_URL" || { echo "Error: Installer file not found at $DOWNLOAD_URL. Please check the XML data or network connection." >&2; exit 1; }

    curl -k -q --progress-bar -f -L --max-time 300 -o "${DOWNLOAD_FILE}" --connect-to "$CDN_MIRROR" "${DOWNLOAD_URL}" || {
    echo "Error: Download failed for ${DOWNLOAD_URL}." >&2
    exit 1
    }

    if $KEEP_COMPRESSED; then
    echo -e "\nFinished. Downloaded file location: ${DOWNLOAD_FILE}"
    exit 0
    fi

    echo "Extracting tar..."
    mkdir -p "${UNZIP_DIR}"
    tar -xf "${DOWNLOAD_FILE}" -C "${UNZIP_DIR}" || {
    echo "Error: Extraction of tar file failed. The file may be corrupt." >&2
    echo "Downloaded file location: ${DOWNLOAD_FILE}" >&2
    rm -rf "${UNZIP_DIR}"
    exit 1
    }

    echo "Extracting zip..."
    unzip -q "${UNZIP_DIR}/com.vmware.fusion.zip" "payload/VMware Fusion.app/*" -d "${UNZIP_DIR}" || {
    echo "Error: Extraction of zip file failed. The file may be corrupt." >&2
    echo "Downloaded file location: ${DOWNLOAD_FILE}" >&2
    rm -rf "${UNZIP_DIR}"
    exit 1
    }

    echo "Cleaning up..."
    mv "${UNZIP_DIR}/payload/VMware Fusion.app" "${APP_PATH}"
    if $IS_MACOS; then
    xattr -dr com.apple.quarantine "${APP_PATH}" &>/dev/null
    fi
    rm -rf "${DOWNLOAD_FILE}" "${UNZIP_DIR}/com.vmware.fusion.zip" "${UNZIP_DIR}/descriptor.xml" "${UNZIP_DIR}/payload" 2>/dev/null

    if $IS_MACOS; then
    LICENSE_FILES=$(ls -1 "/Library/Preferences/VMware Fusion/license-fusion"* 2>/dev/null)

    if [[ -n "${LICENSE_FILES}" ]]; then
    echo -e "\nNotice: Existing license file(s) found." >&2
    echo "Deletion may be required if converting to the 'Free for Personal Use' model." >&2
    echo "To remove them, run the following command(s):" >&2
    for LICENSE_FILE in ${LICENSE_FILES}; do
    echo "sudo rm \"${LICENSE_FILE}\"" >&2
    done
    fi
    fi

    echo -e "\nFinished. You can now move the app to your desired location."
    echo "VMware Fusion.app location: ${APP_PATH}"
    fi