diff --git a/src/batch.sh b/src/batch.sh index 22725c83..525d1c80 100644 --- a/src/batch.sh +++ b/src/batch.sh @@ -55,6 +55,10 @@ Win9xInstall() { validateWin95OSR2 "$target" "$desc" || return 1 fi + # Resolve the filenames used by patching and staging before either can create + # a second case variant that would collide on the FAT system volume. + prepareWin9xFileNames "$target" "$desc" "normalize" || return 1 + [ -z "$WIDTH" ] && WIDTH="1024" [ -z "$HEIGHT" ] && HEIGHT="768" @@ -258,6 +262,70 @@ Win9xInstall() { return 0 } +prepareWin9xFileNames() { + + local dir="$1" + local desc="$2" + local mode="${3:-check}" + + if ! python3 - "$dir" "$mode" <<'PY' +from pathlib import Path +import os +import re +import sys + +root = Path(sys.argv[1]) +mode = sys.argv[2] + +# FAT cannot store two entries whose names differ only in case. Check complete +# source trees, including OEM files, before copying anything onto the volume. +for directory, folders, files in os.walk(root): + seen = {} + for name in sorted(folders + files): + key = name.casefold() + if key in seen: + raise SystemExit( + f'Case-insensitive filename collision in {directory}: ' + f'{seen[key]!r} and {name!r}.' + ) + seen[key] = name + +if mode == 'check': + raise SystemExit(0) +if mode != 'normalize': + raise SystemExit(f'Unknown Windows 9x filename preparation mode: {mode}') + +# Match the exact names written by Patcher9x and the setup-source staging steps. +# Patcher9x writes WIN.COM/WIN.CNF in lowercase, while its VxD names are uppercase. +# Cabinet names stay intact because split cabinets can reference those names. +canonical_names = ( + 'VMM32.VXD', 'VMM.VXD', 'NTKERN.VXD', 'IOS.VXD', 'ESDI_506.PDR', + 'SCSIPORT.PDR', 'NDIS.VXD', 'NDIS.386', 'VCACHE.VXD', 'win.com', 'win.cnf', + 'SETUPPP.INF', 'MOUSE.DRV', 'SCANDISK.INI', 'WMP.INF', 'MPLAYER2.INF', + 'DOCKER.PWL', 'HIDE.EXE', 'WAIT.EXE', 'POST9X.BAT', 'POST9X.NEW', + 'POST9X.REG', 'WIN9XDMA.EXE', 'W9XSCAN.INI', 'PATCH9X.EXE', 'CWSDPMI.EXE', + 'PATCH9X.NEW', 'W9XAUTO.BAT', 'Shared.lnk', 'MEIO.SYS', 'MECOM.COM', + 'MEREGENV.EXE', 'MEBOOT.BAT', 'MEPOWER.EXE', 'MEFINAL.BAT', 'MSBATCH.INF', + 'VMDISP9X', 'vmdisp9x.inf', 'qemumini.drv', 'qemumini.vxd', + 'vmwsmini.drv', 'vmwsmini.vxd', 'vmhal9x.dll', 'vmhal486.dll', 'vmdisp9x.dll', +) +canonical = {name.casefold(): name for name in canonical_names} + +for entry in sorted(root.iterdir()): + name = canonical.get(entry.name.casefold(), entry.name) + if re.fullmatch(r'layout[0-9]*\.inf', entry.name, re.IGNORECASE): + name = entry.name.upper() + if name != entry.name: + entry.rename(root / name) +PY + then + error "Failed to prepare $desc filenames for the system image!" + return 1 + fi + + return 0 +} + validateWin95OSR2() { local dir="$1" @@ -3254,7 +3322,15 @@ createWin9xSystemImage() { for entry in "${entries[@]}"; do - if ! MTOOLSRC="$config" mcopy -Q -s "$entry" w:/; then + # Reject ambiguous case variants before an unattended copy. All intended + # replacements already share one source filename, so copy order cannot + # select an older unpatched payload over the staged replacement. + if ! prepareWin9xFileNames "$entry" "$desc"; then + rm -f -- "$tmp" + return 1 + fi + + if ! MTOOLSRC="$config" mcopy -Q -s -o "$entry" w:/; then rm -f -- "$tmp" error "Failed to copy $desc file: $entry" return 1 diff --git a/src/define.sh b/src/define.sh index 84e162fa..0c9c2e95 100644 --- a/src/define.sh +++ b/src/define.sh @@ -1024,6 +1024,8 @@ getLink1() { [[ "${lang,,}" != "en" && "${lang,,}" != "en-us" ]] && return 0 + return 0 # Disable + case "${id,,}" in "win11x64" | "win11x64-enterprise" ) size=6927149056 diff --git a/src/sif.sh b/src/sif.sh index 0f14af0f..a8c2ea8b 100644 --- a/src/sif.sh +++ b/src/sif.sh @@ -375,7 +375,7 @@ disableGenericDisplay() { [[ "$driver" == "2k3" && "${arch,,}" == "amd64" ]] || return 0 - local qbochs_inf="$drivers/qbochs/$driver/x64/qbochs.inf" + local qbochs_inf="$drivers/qbochs/$driver/$arch/qbochs.inf" local qbochs_id='PCI\VEN_1234&DEV_1111&SUBSYS_11001AF4' # Do not remove the generic VGA match unless the exact QBochs device is