diff --git a/src/answer.sh b/src/answer.sh index 2c546930..84b7305e 100644 --- a/src/answer.sh +++ b/src/answer.sh @@ -275,13 +275,20 @@ generateEvalXML() { local id="$1" local detected_index="${2:-}" - local source="/run/assets/${id::-5}.xml" + local normal="${id::-5}" + local source="/run/assets/$normal.xml" local target="/run/assets/$id.xml" local index="$detected_index" tmp [[ "${id,,}" == *"-eval" ]] || return 1 removeGeneratedXML "$source" || return 1 + + if [ ! -s "$source" ]; then + source="/run/assets/${normal%%-*}.xml" + removeGeneratedXML "$source" || return 1 + fi + [ -s "$source" ] || return 1 if [ -n "$index" ] && [[ ! "$index" =~ ^[1-9][0-9]*$ ]]; then @@ -456,22 +463,6 @@ setXML() { fi done - # Known Server editions normally share one answer file. When the image - # contains only one uniquely detected Server edition and no explicit - # EDITION was requested, generate a keyless answer file using that index. - if [ -n "$index" ] && [ -z "${EDITION:-}" ] && - [[ "${DETECTED,,}" == win20* && "${DETECTED,,}" != *-* ]] && - [ -s "$target" ]; then - - file="/run/assets/$DETECTED-selected.xml" - - removeGeneratedXML "$file" || return 1 - generateFallbackXML "$DETECTED-selected" "$index" || return 1 - - XML="$file" - return 0 - fi - file="$1" if [[ "${DETECTED,,}" == *"-eval" ]]; then diff --git a/src/define.sh b/src/define.sh index 816eb4c3..7903d058 100644 --- a/src/define.sh +++ b/src/define.sh @@ -759,20 +759,6 @@ normalizeEditionID() { esac case "${id,,}" in - "winvista"* ) - case "$edition" in - "business" ) - edition="" ;; - "home-basic" | "home-premium" ) - edition="home" ;; - esac - ;; - "win7"* ) - case "$edition" in - "home-basic" | "home-premium" ) - edition="home" ;; - esac - ;; "win10"* | "win11"* ) case "$edition" in "iot-enterprise-ltsc" | \ @@ -866,19 +852,17 @@ normalizeServerEditionID() { edition=$(normalizeServerEdition "$1") || return 1 case "$edition" in - "" | "core" | \ - "standard" | "standard-core" | "standardcore" | \ - "serverstandard" | "serverstandardcore" | \ - "datacenter" | "datacenter-core" | "datacentercore" | \ - "serverdatacenter" | "serverdatacentercore" | \ - "enterprise" | "enterprise-core" | "enterprisecore" | \ - "serverenterprise" | "serverenterprisecore" | \ - "web" | "web-core" | "webcore" | \ - "serverweb" | "serverwebcore" | \ - "foundation" | "serverfoundation" | \ - "essentials" | "serveressentials" ) - edition="" - ;; + "" | "standard" | "serverstandard" ) edition="" ;; + "core" | "standard-core" | "standardcore" | "serverstandardcore" ) edition="standard-core" ;; + "datacenter" | "serverdatacenter" ) edition="datacenter" ;; + "datacenter-core" | "datacentercore" | "serverdatacentercore" ) edition="datacenter-core" ;; + "enterprise" | "serverenterprise" ) edition="enterprise" ;; + "enterprise-core" | "enterprisecore" | "serverenterprisecore" ) edition="enterprise-core" ;; + "web" | "serverweb" ) edition="web" ;; + "web-core" | "webcore" | "serverwebcore" ) edition="web-core" ;; + "foundation" | "serverfoundation" ) edition="foundation" ;; + "essentials" | "serveressentials" ) edition="essentials" ;; + * ) edition="unknown" ;; esac echo "$edition" diff --git a/src/image.sh b/src/image.sh index b439855c..19d3d5f9 100644 --- a/src/image.sh +++ b/src/image.sh @@ -166,17 +166,16 @@ detectVersion() { local xml="$1" local suggested="${2:-}" - local tag name id base match platform - local priority edition suffix i tried="" + local result_name="$3" + local index_name="$4" + local -n result="$result_name" + local -n result_index="$index_name" - local -a tags=( - "DISPLAYNAME" - "PRODUCTNAME" - "NAME" - ) - local -a versions=() local -a bases=() local -a groups=() + local -a versions=() + local -A image_indexes=() + local -a priorities=( "enterprise" "ultimate" @@ -188,6 +187,7 @@ detectVersion() { "starter" "hv" ) + local -a suffixes=( "-enterprise" "-ultimate" @@ -196,49 +196,114 @@ detectVersion() { "-ltsc" "-education" "-home" + "-home-premium" + "-home-basic" "-starter" "-hv" ) + local -a server_suffixes=( + "" + "-datacenter" + "-enterprise" + "-web" + "-foundation" + "-essentials" + "-standard-core" + "-datacenter-core" + "-enterprise-core" + "-web-core" + ) + + result="" + result_index="" + + local image image_index key + local display product platform + platform=$(getPlatform "$xml") - for tag in "${tags[@]}"; do - while IFS= read -r name; do - [[ "$name" == *"Operating System"* ]] && continue - [ -z "$name" ] && continue + while IFS='|' read -r image_index display product image; do - base=$(fromName "$name" "$platform") - id=$(getVersion "$name" "$platform") + [ -n "$image_index" ] || continue - if [ -z "$base" ] || [ -z "$id" ]; then - warn "Unknown ${tag,,}: '$name'" + local candidate candidate_id candidate_base found="" + + for candidate in "$display" "$product" "$image"; do + + [[ "$candidate" == *"Operating System"* ]] && continue + [ -z "$candidate" ] && continue + + candidate_base=$(fromName "$candidate" "$platform") + candidate_id=$(getVersion "$candidate" "$platform") + + if [ -z "$candidate_base" ] || [ -z "$candidate_id" ]; then continue fi - versions+=("$id") - bases+=("$base") - groups+=("$(getVersionPriority "$id" "$base")") - done < <( - sed -n \ - "/$tag/{s/.*<$tag>\(.*\)<\/$tag>.*/\1/;p}" \ - <<< "$xml" - ) - done + found="Y" + key="${candidate_id,,}" + [[ -v "image_indexes[$key]" ]] && continue + + image_indexes["$key"]="$image_index" + versions+=("$candidate_id") + bases+=("$candidate_base") + groups+=("$(getVersionPriority "$candidate_id" "$candidate_base")") + + done + + if [ -z "$found" ]; then + local name="${display:-${product:-$image}}" + [ -n "$name" ] && warn "Unknown image name: '$name'" + fi + + done < <( + awk ' + // { + display = $0 + sub(/^.*/, "", display) + sub(/<\/DISPLAYNAME>.*$/, "", display) + } + + image_index != "" && // { + product = $0 + sub(/^.*/, "", product) + sub(/<\/PRODUCTNAME>.*$/, "", product) + } + + image_index != "" && // { + name = $0 + sub(/^.*/, "", name) + sub(/<\/NAME>.*$/, "", name) + } + + /<\/IMAGE>/ { + print image_index "|" display "|" product "|" name + image_index = "" + } + ' <<< "$xml" + ) [ "${#versions[@]}" -eq 0 ] && return 0 + local base match prefer + if [ -n "$EDITION" ]; then + local edition tried="" + for base in "${bases[@]}"; do case "${base,,}" in - "win2003"* | "win2008"* | "win2012"* | "win2016"* | \ - "win2019"* | "win2022"* | "win2025"* ) + "win20"* ) edition=$(normalizeServerEditionID "$EDITION") - - # Known Server editions continue using the generic Server ID. - # updateXML() applies Standard, Datacenter or Core to that template. - [ -z "$edition" ] && continue ;; * ) edition=$(normalizeEditionID "$EDITION" "$base") @@ -246,130 +311,112 @@ detectVersion() { esac tried="Y" - local prefer="$base" + prefer="$base" [ -n "$edition" ] && prefer+="-$edition" if match=$(hasVersion "$prefer" "${versions[@]}"); then - echo "$match" + key="${match,,}" + result="$match" + result_index="${image_indexes[$key]}" + return 0 fi + done if [ -n "$tried" ]; then warn "edition '$EDITION' is not supported by this image, using automatic selection instead." fi + fi # For reused automatic media, prefer the edition selected by parseVersion() # when that edition is actually present in the image. An explicit EDITION # remains authoritative because it is handled above. if [ -n "$suggested" ]; then + if match=$(hasVersion "$suggested" "${versions[@]}"); then - echo "$match" + key="${match,,}" + result="$match" + result_index="${image_indexes[$key]}" + return 0 fi + fi + # Server media defaults to the normal Standard GUI edition. If Standard + # is absent, prefer another known GUI edition before any Core variant. + + local suffix + + for suffix in "${server_suffixes[@]}"; do + for base in "${bases[@]}"; do + + case "${base,,}" in + "win20"* ) + + prefer="$base$suffix" + + if match=$(hasVersion "$prefer" "${versions[@]}"); then + key="${match,,}" + result="$match" + result_index="${image_indexes[$key]}" + return 0 + fi + ;; + esac + + done + done + # Prefer the normal edition within each selection family. hasVersion() # still allows its Evaluation counterpart when the normal variant is absent. for suffix in "${suffixes[@]}"; do for base in "${bases[@]}"; do - local prefer="$base$suffix" + + prefer="$base$suffix" if match=$(hasVersion "$prefer" "${versions[@]}"); then - echo "$match" + key="${match,,}" + result="$match" + result_index="${image_indexes[$key]}" return 0 fi + done done # When the normal edition is absent, select another compatible member of # that family, such as N, Workstations, or a future dynamic variant. + local priority i + for priority in "${priorities[@]}"; do for (( i=0; i<${#versions[@]}; i++ )); do + [[ "${groups[$i]}" == "$priority" ]] || continue local actual="${versions[$i]}" if match=$(hasVersion "$actual" "${versions[@]}"); then - echo "$match" + key="${match,,}" + result="$match" + result_index="${image_indexes[$key]}" return 0 fi + done done # Future or unusual editions that do not belong to a known selection # family use the first recognized WIM image. - echo "${versions[0]}" + result="${versions[0]}" + key="${result,,}" + result_index="${image_indexes[$key]}" + return 0 } -getImageIndex() { - - local xml="$1" - local wanted="$2" - local platform tag index name id - - local -a matches=() - - [ -z "$wanted" ] && return 1 - - platform=$(getPlatform "$xml") - - for tag in DISPLAYNAME PRODUCTNAME NAME; do - - matches=() - - while IFS=$'\t' read -r index name; do - [ -n "$index" ] || continue - [[ "$name" == *"Operating System"* ]] && continue - [ -z "$name" ] && continue - - id=$(getVersion "$name" "$platform") - [[ "${id,,}" == "${wanted,,}" ]] || continue - - matches+=("$index") - done < <( - awk -v tag="$tag" ' - /" { - value = $0 - sub("^.*<" tag ">", "", value) - sub(".*$", "", value) - print image_index "\t" value - } - - /<\/IMAGE>/ { - image_index = "" - } - ' <<< "$xml" - ) - - case "${#matches[@]}" in - 0 ) - continue - ;; - 1 ) - echo "${matches[0]}" - return 0 - ;; - * ) - # The current field is ambiguous, so try the remaining WIM fields - # before concluding that no unique image index can be determined. - continue - ;; - esac - - done - - return 1 -} - detectLanguage() { local xml="$1" @@ -475,7 +522,7 @@ detectImage() { local dir="$1" local version="$2" - local desc language + local desc XML="" @@ -518,7 +565,7 @@ detectImage() { return 0 fi - local src wim info index suggested edition + local src src=$(find "$dir" -maxdepth 1 -type d -iname sources -print -quit) if [ ! -d "$src" ]; then @@ -526,6 +573,7 @@ detectImage() { return 1 fi + local wim wim=$(find "$src" -maxdepth 1 -type f \ \( -iname install.wim -or -iname install.esd \) -print -quit) @@ -534,6 +582,7 @@ detectImage() { return 1 fi + local info info=$(wimlib-imagex info -xml "$wim" | iconv -f UTF-16LE -t UTF-8) || { local rc=$? @@ -548,15 +597,18 @@ detectImage() { checkPlatform "$info" || exit 67 - suggested="" + local suggested="" if [ -z "$CUSTOM" ] && [ -n "${REUSED_ISO:-}" ]; then suggested="${SUGGEST:-}" fi - DETECTED=$(detectVersion "$info" "$suggested") + local index + detectVersion "$info" "$suggested" DETECTED index if [ -n "$EDITION" ]; then + local edition + case "${DETECTED,,}" in "win2003"* | "win2008"* | "win2012"* | "win2016"* | \ "win2019"* | "win2022"* | "win2025"* ) @@ -584,12 +636,12 @@ detectImage() { return 0 fi - index=$(getImageIndex "$info" "$DETECTED") || index="" desc=$(printEdition "$DETECTED" "$DETECTED" "Y") detectLanguage "$info" if [[ "${LANGUAGE,,}" != "en" && "${LANGUAGE,,}" != "en-"* ]]; then + local language language=$(getLanguage "$LANGUAGE" "desc") desc+=" ($language)" fi diff --git a/src/install.sh b/src/install.sh index de93c9fa..54bc73b3 100644 --- a/src/install.sh +++ b/src/install.sh @@ -734,7 +734,7 @@ setMachine() { BOOT_MODE="windows_legacy" [ -z "${SOUND:-}" ] && SOUND="usb-audio" - if [ -z "${CPU_MODEL,,}" ] || [[ "${CPU_MODEL,,}" == "host" ]]; then + if [ -z "${CPU_MODEL:-}" ] || [[ "${CPU_MODEL:-}" == "host" ]]; then # Workaround for boot loop on AMD EPYC processors if [[ "${CPU,,}" == *"amd epyc"* ]]; then CPU_MODEL="qemu32"