fix: Preserve WIM indices during edition detection (#1954)

This commit is contained in:
Kroese
2026-07-25 00:29:50 +02:00
committed by GitHub
parent fe36fe3ab8
commit 4b5eb2c882
4 changed files with 181 additions and 154 deletions
+161 -109
View File
@@ -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 '
/<IMAGE INDEX="/ {
image_index = $0
sub(/^.*<IMAGE INDEX="/, "", image_index)
sub(/".*$/, "", image_index)
display = product = name = ""
}
image_index != "" && /<DISPLAYNAME>/ {
display = $0
sub(/^.*<DISPLAYNAME>/, "", display)
sub(/<\/DISPLAYNAME>.*$/, "", display)
}
image_index != "" && /<PRODUCTNAME>/ {
product = $0
sub(/^.*<PRODUCTNAME>/, "", product)
sub(/<\/PRODUCTNAME>.*$/, "", product)
}
image_index != "" && /<NAME>/ {
name = $0
sub(/^.*<NAME>/, "", 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" '
/<IMAGE INDEX="/ {
image_index = $0
sub(/^.*<IMAGE INDEX="/, "", image_index)
sub(/".*$/, "", image_index)
}
image_index != "" && $0 ~ "<" tag ">" {
value = $0
sub("^.*<" tag ">", "", value)
sub("</" tag ">.*$", "", 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