Browse Source

wizard: reorder hardware and boot pages

Now that we support preconfigured machines, we can put the hardware
page before the boot page.
osy 2 weeks ago
parent
commit
cbdb86ed3e

+ 114 - 26
Platform/Shared/VMWizardHardwareView.swift

@@ -20,20 +20,28 @@ import Virtualization
 #endif
 #endif
 
 
 struct VMWizardHardwareView: View {
 struct VMWizardHardwareView: View {
-    private enum ClassicMacSystem: CaseIterable, Identifiable {
+    private enum SupportedMachine: CaseIterable, Identifiable {
         case quadra800
         case quadra800
         //case powerMacG3Beige
         //case powerMacG3Beige
         case powerMacG4
         case powerMacG4
         //case powerMacG5
         //case powerMacG5
+        case i440FX
+        case q35
+        case arm64Virt
+        case riscv64Virt
 
 
         var id: Self { self }
         var id: Self { self }
 
 
         var title: LocalizedStringKey {
         var title: LocalizedStringKey {
             switch self {
             switch self {
-            case .quadra800: "Macintosh Quadra 800 (M68K)"
-            //case .powerMacG3Beige: "Power Macintosh G3 (Beige)"
-            case .powerMacG4: "Power Macintosh G4 (PPC)"
-            //case .powerMacG5: "Power Macintosh G5 (PPC64)"
+            case .quadra800: "Macintosh Quadra 800 (1993, M68K)"
+            //case .powerMacG3Beige: "Power Macintosh G3 (1997, Beige)"
+            case .powerMacG4: "Power Macintosh G4 (1999, PPC)"
+            //case .powerMacG5: "Power Macintosh G5 (2003, PPC64)"
+            case .i440FX: "Intel i440FX based PC (1996, x86_64)"
+            case .q35: "Intel ICH9 based PC (2009, x86_64)"
+            case .arm64Virt: "ARM64 virtual machine (2014, ARM64)"
+            case .riscv64Virt: "RISC-V64 virtual machine (2018, RISC-V64)"
             }
             }
         }
         }
 
 
@@ -43,6 +51,10 @@ struct VMWizardHardwareView: View {
             //case .powerMacG3Beige: return .ppc
             //case .powerMacG3Beige: return .ppc
             case .powerMacG4: return .ppc
             case .powerMacG4: return .ppc
             //case .powerMacG5: return .ppc64
             //case .powerMacG5: return .ppc64
+            case .i440FX: return .x86_64
+            case .q35: return .x86_64
+            case .arm64Virt: return .aarch64
+            case .riscv64Virt: return .riscv64
             }
             }
         }
         }
 
 
@@ -52,6 +64,10 @@ struct VMWizardHardwareView: View {
             //case .powerMacG3Beige: return QEMUTarget_ppc.g3beige
             //case .powerMacG3Beige: return QEMUTarget_ppc.g3beige
             case .powerMacG4: return QEMUTarget_ppc.mac99
             case .powerMacG4: return QEMUTarget_ppc.mac99
             //case .powerMacG5: return QEMUTarget_ppc.mac99
             //case .powerMacG5: return QEMUTarget_ppc.mac99
+            case .i440FX: return QEMUTarget_x86_64.pc
+            case .q35: return QEMUTarget_x86_64.q35
+            case .arm64Virt: return QEMUTarget_aarch64.virt
+            case .riscv64Virt: return QEMUTarget_riscv64.virt
             }
             }
         }
         }
 
 
@@ -61,6 +77,7 @@ struct VMWizardHardwareView: View {
             //case .powerMacG3Beige: return 32
             //case .powerMacG3Beige: return 32
             case .powerMacG4: return 64
             case .powerMacG4: return 64
             //case .powerMacG5: return 64
             //case .powerMacG5: return 64
+            default: return 0
             }
             }
         }
         }
 
 
@@ -70,6 +87,7 @@ struct VMWizardHardwareView: View {
             //case .powerMacG3Beige: return 2047
             //case .powerMacG3Beige: return 2047
             case .powerMacG4: return 2048
             case .powerMacG4: return 2048
             //case .powerMacG5: return 2048
             //case .powerMacG5: return 2048
+            default: return 0
             }
             }
         }
         }
 
 
@@ -79,11 +97,62 @@ struct VMWizardHardwareView: View {
             //case .powerMacG3Beige: return 512
             //case .powerMacG3Beige: return 512
             case .powerMacG4: return 512
             case .powerMacG4: return 512
             //case .powerMacG5: return 512
             //case .powerMacG5: return 512
+            #if os(macOS)
+            default: return 4096
+            #else
+            default: return 512
+            #endif
+            }
+        }
+
+        var defaultStorageGiB: Int {
+            switch self {
+            case .quadra800, .powerMacG4: return 2
+            #if os(macOS)
+            default: return 64
+            #else
+            default: return 2
+            #endif
+            }
+        }
+
+        var maxSupportedCores: Int {
+            switch self {
+            case .quadra800, .powerMacG4: return 1
+            default: return 0
+            }
+        }
+
+        var isLegacyHardware: Bool {
+            switch self {
+            case .quadra800, .powerMacG4, .i440FX: return true
+            default: return false
+            }
+        }
+
+        func isSupported(running os: VMWizardOS) -> Bool {
+            switch os {
+            case .Other: return true
+            case .macOS: return [.arm64Virt].contains(self)
+            case .Linux: return true
+            case .Windows: return [.i440FX, .q35, .arm64Virt].contains(self)
+            case .ClassicMacOS: return [.quadra800, .powerMacG4].contains(self)
+            }
+        }
+
+        static func `default`(for os: VMWizardOS) -> Self {
+            switch os {
+            case .Other: return .q35
+            case .macOS: return .arm64Virt
+            case .Linux: return .q35
+            case .Windows: return .q35
+            case .ClassicMacOS: return .powerMacG4
             }
             }
         }
         }
     }
     }
     @ObservedObject var wizardState: VMWizardState
     @ObservedObject var wizardState: VMWizardState
-    @State private var classicMacSystem: ClassicMacSystem = .powerMacG4
+    @State private var isExpertMode: Bool = false
+    @State private var selectedMachine: SupportedMachine?
 
 
     var minCores: Int {
     var minCores: Int {
         #if canImport(Virtualization)
         #if canImport(Virtualization)
@@ -119,7 +188,11 @@ struct VMWizardHardwareView: View {
     
     
     var body: some View {
     var body: some View {
         VMWizardContent("Hardware") {
         VMWizardContent("Hardware") {
-            if !wizardState.useVirtualization && wizardState.operatingSystem != .ClassicMacOS {
+            if !wizardState.useVirtualization {
+                Toggle("Expert Mode", isOn: $isExpertMode)
+                    .help("List all supported hardware. May require manual configuration to boot.")
+            }
+            if !wizardState.useVirtualization && isExpertMode {
                 Section {
                 Section {
                     VMConfigConstantPicker(selection: $wizardState.systemArchitecture)
                     VMConfigConstantPicker(selection: $wizardState.systemArchitecture)
                         .onChange(of: wizardState.systemArchitecture) { newValue in
                         .onChange(of: wizardState.systemArchitecture) { newValue in
@@ -135,27 +208,32 @@ struct VMWizardHardwareView: View {
                     Text("System")
                     Text("System")
                 }
                 }
 
 
-            } else if wizardState.operatingSystem == .ClassicMacOS {
-                Picker("Machine", selection: $classicMacSystem) {
-                    ForEach(ClassicMacSystem.allCases) { system in
+            } else if !isExpertMode {
+                Picker("Machine", selection: $selectedMachine) {
+                    ForEach(SupportedMachine.allCases.filter({ $0.isSupported(running: wizardState.operatingSystem )})) { system in
                         Text(system.title).tag(system)
                         Text(system.title).tag(system)
                     }
                     }
                 }.pickerStyle(.inline)
                 }.pickerStyle(.inline)
-                .onChange(of: classicMacSystem) { newValue in
+                .onChange(of: selectedMachine) { newValue in
+                    guard let newValue = newValue else {
+                        return
+                    }
                     wizardState.systemArchitecture = newValue.architecture
                     wizardState.systemArchitecture = newValue.architecture
                     wizardState.systemTarget = newValue.target
                     wizardState.systemTarget = newValue.target
                     wizardState.systemMemoryMib = newValue.defaultRam
                     wizardState.systemMemoryMib = newValue.defaultRam
-                    wizardState.systemCpuCount = 1
-                    wizardState.storageSizeGib = 2
+                    wizardState.systemCpuCount = newValue.maxSupportedCores
+                    wizardState.storageSizeGib = newValue.defaultStorageGiB
+                    wizardState.legacyHardware = newValue.isLegacyHardware
                 }
                 }
             }
             }
             Section {
             Section {
                 RAMSlider(systemMemory: $wizardState.systemMemoryMib) { _ in
                 RAMSlider(systemMemory: $wizardState.systemMemoryMib) { _ in
-                    let validMax = wizardState.operatingSystem == .ClassicMacOS ? classicMacSystem.maxRam : maxMemoryMib
+                    let selectedMax = selectedMachine?.maxRam ?? 0
+                    let validMax = selectedMax > 0 ? selectedMax : maxMemoryMib
                     if wizardState.systemMemoryMib > validMax {
                     if wizardState.systemMemoryMib > validMax {
                         wizardState.systemMemoryMib = validMax
                         wizardState.systemMemoryMib = validMax
                     }
                     }
-                    let validMin = wizardState.operatingSystem == .ClassicMacOS ? classicMacSystem.minRam : 0
+                    let validMin = selectedMachine?.minRam ?? 0
                     if wizardState.systemMemoryMib < validMin {
                     if wizardState.systemMemoryMib < validMin {
                         wizardState.systemMemoryMib = validMin
                         wizardState.systemMemoryMib = validMin
                     }
                     }
@@ -164,7 +242,7 @@ struct VMWizardHardwareView: View {
                 Text("Memory")
                 Text("Memory")
             }
             }
 
 
-            if wizardState.operatingSystem != .ClassicMacOS {
+            if isExpertMode || selectedMachine?.maxSupportedCores == 0 {
                 Section {
                 Section {
                     HStack {
                     HStack {
                         Stepper(value: $wizardState.systemCpuCount, in: minCores...maxCores) {
                         Stepper(value: $wizardState.systemCpuCount, in: minCores...maxCores) {
@@ -202,10 +280,21 @@ struct VMWizardHardwareView: View {
                         .disabled(!wizardState.isDisplayEnabled)
                         .disabled(!wizardState.isDisplayEnabled)
                 }
                 }
             }
             }
+
+            if !wizardState.useVirtualization && isExpertMode {
+                Section {
+                    Toggle("Legacy Hardware", isOn: $wizardState.legacyHardware)
+                        .help("If checked, emulated devices with higher compatibility will be instantiated at the cost of performance.")
+                } header: {
+                    Text("Options")
+                }
+            }
         }
         }
         .textFieldStyle(.roundedBorder)
         .textFieldStyle(.roundedBorder)
         .onAppear {
         .onAppear {
             if wizardState.useVirtualization {
             if wizardState.useVirtualization {
+                isExpertMode = true
+                selectedMachine = nil
                 #if arch(arm64)
                 #if arch(arm64)
                 wizardState.systemArchitecture = .aarch64
                 wizardState.systemArchitecture = .aarch64
                 #elseif arch(x86_64)
                 #elseif arch(x86_64)
@@ -214,16 +303,15 @@ struct VMWizardHardwareView: View {
                 #error("Unsupported architecture.")
                 #error("Unsupported architecture.")
                 #endif
                 #endif
                 wizardState.systemTarget = wizardState.systemArchitecture.targetType.default
                 wizardState.systemTarget = wizardState.systemArchitecture.targetType.default
-            }
-            if wizardState.legacyHardware && wizardState.systemArchitecture == .x86_64 {
-                wizardState.systemTarget = QEMUTarget_x86_64.pc
-            }
-            if wizardState.operatingSystem == .ClassicMacOS {
-                wizardState.systemArchitecture = classicMacSystem.architecture
-                wizardState.systemTarget = classicMacSystem.target
-                wizardState.systemMemoryMib = classicMacSystem.defaultRam
-                wizardState.systemCpuCount = 1
-                wizardState.storageSizeGib = 2
+                wizardState.legacyHardware = false
+            } else if selectedMachine == nil {
+                selectedMachine = SupportedMachine.default(for: wizardState.operatingSystem)
+                wizardState.systemArchitecture = selectedMachine!.architecture
+                wizardState.systemTarget = selectedMachine!.target
+                wizardState.systemMemoryMib = selectedMachine!.defaultRam
+                wizardState.systemCpuCount = selectedMachine!.maxSupportedCores
+                wizardState.storageSizeGib = selectedMachine!.defaultStorageGiB
+                wizardState.legacyHardware = selectedMachine!.isLegacyHardware
             }
             }
         }
         }
     }
     }

+ 3 - 0
Platform/Shared/VMWizardOSClassicMacView.swift

@@ -104,6 +104,9 @@ struct VMWizardOSClassicMacView: View {
                 }
                 }
             }
             }
         }
         }
+        .onAppear {
+            wizardState.bootDevice = .cd
+        }
     }
     }
 }
 }
 
 

+ 5 - 0
Platform/Shared/VMWizardOSLinuxView.swift

@@ -53,6 +53,11 @@ struct VMWizardOSLinuxView: View {
                     Text("Import existing drive").tag(VMBootDevice.drive)
                     Text("Import existing drive").tag(VMBootDevice.drive)
                 }
                 }
             }.pickerStyle(.inline)
             }.pickerStyle(.inline)
+            .onAppear {
+                if ![.kernel, .cd, .drive].contains(wizardState.bootDevice) {
+                    wizardState.bootDevice = .cd
+                }
+            }
             if wizardState.bootDevice != .kernel {
             if wizardState.bootDevice != .kernel {
                 if wizardState.useAppleVirtualization {
                 if wizardState.useAppleVirtualization {
                     Link(destination: URL(string: "https://docs.getutm.app/guides/debian/")!) {
                     Link(destination: URL(string: "https://docs.getutm.app/guides/debian/")!) {

+ 3 - 1
Platform/Shared/VMWizardOSMacView.swift

@@ -48,6 +48,9 @@ struct VMWizardOSMacView: View {
         }
         }
         .fileImporter(isPresented: $isFileImporterPresented, allowedContentTypes: [.ipsw], onCompletion: processIpsw)
         .fileImporter(isPresented: $isFileImporterPresented, allowedContentTypes: [.ipsw], onCompletion: processIpsw)
         .onDrop(of: [.fileURL], delegate: self)
         .onDrop(of: [.fileURL], delegate: self)
+        .onAppear {
+            wizardState.bootDevice = .none
+        }
     }
     }
     
     
     private func processIpsw(_ result: Result<URL, Error>) {
     private func processIpsw(_ result: Result<URL, Error>) {
@@ -68,7 +71,6 @@ struct VMWizardOSMacView: View {
                 wizardState.macPlatform = UTMAppleConfigurationMacPlatform(newHardware: model)
                 wizardState.macPlatform = UTMAppleConfigurationMacPlatform(newHardware: model)
                 wizardState.macRecoveryIpswURL = url
                 wizardState.macRecoveryIpswURL = url
                 wizardState.macPlatformVersion = image.buildVersion.integerPrefix()
                 wizardState.macPlatformVersion = image.buildVersion.integerPrefix()
-                wizardState.bootDevice = .none
                 wizardState.bootImageURL = nil
                 wizardState.bootImageURL = nil
                 wizardState.next()
                 wizardState.next()
             }
             }

+ 19 - 10
Platform/Shared/VMWizardOSOtherView.swift

@@ -19,18 +19,24 @@ import SwiftUI
 struct VMWizardOSOtherView: View {
 struct VMWizardOSOtherView: View {
     @ObservedObject var wizardState: VMWizardState
     @ObservedObject var wizardState: VMWizardState
     @State private var isFileImporterPresented: Bool = false
     @State private var isFileImporterPresented: Bool = false
-    
+
+    private var supportsUefi: Bool {
+        UTMQemuConfigurationQEMU.uefiImagePrefix(forArchitecture: wizardState.systemArchitecture) != nil
+    }
+
     var body: some View {
     var body: some View {
         VMWizardContent("Other") {
         VMWizardContent("Other") {
             Picker("Boot Device", selection: $wizardState.bootDevice) {
             Picker("Boot Device", selection: $wizardState.bootDevice) {
                 Text("None").tag(VMBootDevice.none)
                 Text("None").tag(VMBootDevice.none)
                 Text("CD/DVD Image").tag(VMBootDevice.cd)
                 Text("CD/DVD Image").tag(VMBootDevice.cd)
-                Text("Floppy Image").tag(VMBootDevice.floppy)
+                if wizardState.legacyHardware {
+                    Text("Floppy Image").tag(VMBootDevice.floppy)
+                }
                 Text("Drive Image").tag(VMBootDevice.drive)
                 Text("Drive Image").tag(VMBootDevice.drive)
             }.pickerStyle(.inline)
             }.pickerStyle(.inline)
-            .onChange(of: wizardState.bootDevice) { bootDevice in
-                if bootDevice == .floppy {
-                    wizardState.legacyHardware = true
+            .onAppear {
+                if !wizardState.legacyHardware && wizardState.bootDevice == .floppy {
+                    wizardState.bootDevice = .none
                 }
                 }
             }
             }
             if wizardState.bootDevice != .none {
             if wizardState.bootDevice != .none {
@@ -50,11 +56,14 @@ struct VMWizardOSOtherView: View {
                     }
                     }
                 }
                 }
             }
             }
-            Section {
-                Toggle("Legacy Hardware", isOn: $wizardState.legacyHardware)
-                    .help("If checked, emulated devices with higher compatibility will be instantiated at the cost of performance.")
-            } header: {
-                Text("Options")
+            DetailedSection("Options") {
+                Toggle("UEFI Boot", isOn: $wizardState.systemBootUefi)
+                    .disabled(!supportsUefi)
+                    .onAppear {
+                        if !supportsUefi {
+                            wizardState.systemBootUefi = false
+                        }
+                    }
             }
             }
         }
         }
         .fileImporter(isPresented: $isFileImporterPresented, allowedContentTypes: [.data], onCompletion: processImage)
         .fileImporter(isPresented: $isFileImporterPresented, allowedContentTypes: [.data], onCompletion: processImage)

+ 5 - 5
Platform/Shared/VMWizardOSView.swift

@@ -27,7 +27,6 @@ struct VMWizardOSView: View {
                         wizardState.operatingSystem = .macOS
                         wizardState.operatingSystem = .macOS
                         wizardState.useAppleVirtualization = true
                         wizardState.useAppleVirtualization = true
                         wizardState.isGuestToolsInstallRequested = false
                         wizardState.isGuestToolsInstallRequested = false
-                        wizardState.legacyHardware = false
                         wizardState.next()
                         wizardState.next()
                     } label: {
                     } label: {
                         OperatingSystem(imageName: "mac", name: "macOS 12+")
                         OperatingSystem(imageName: "mac", name: "macOS 12+")
@@ -39,7 +38,6 @@ struct VMWizardOSView: View {
                         wizardState.operatingSystem = .ClassicMacOS
                         wizardState.operatingSystem = .ClassicMacOS
                         wizardState.useAppleVirtualization = false
                         wizardState.useAppleVirtualization = false
                         wizardState.isGuestToolsInstallRequested = false
                         wizardState.isGuestToolsInstallRequested = false
-                        wizardState.legacyHardware = true
                         wizardState.next()
                         wizardState.next()
                     } label: {
                     } label: {
                         OperatingSystem(imageName: "macos", name: "Classic Mac OS")
                         OperatingSystem(imageName: "macos", name: "Classic Mac OS")
@@ -49,15 +47,17 @@ struct VMWizardOSView: View {
                     wizardState.operatingSystem = .Windows
                     wizardState.operatingSystem = .Windows
                     wizardState.useAppleVirtualization = false
                     wizardState.useAppleVirtualization = false
                     wizardState.isGuestToolsInstallRequested = true
                     wizardState.isGuestToolsInstallRequested = true
-                    wizardState.legacyHardware = false
                     wizardState.next()
                     wizardState.next()
                 } label: {
                 } label: {
-                    OperatingSystem(imageName: "windows", name: "Windows")
+                    if wizardState.useVirtualization {
+                        OperatingSystem(imageName: "windows-11", name: "Windows")
+                    } else {
+                        OperatingSystem(imageName: "windows-9x", name: "Windows")
+                    }
                 }
                 }
                 Button {
                 Button {
                     wizardState.operatingSystem = .Linux
                     wizardState.operatingSystem = .Linux
                     wizardState.isGuestToolsInstallRequested = false
                     wizardState.isGuestToolsInstallRequested = false
-                    wizardState.legacyHardware = false
                     wizardState.next()
                     wizardState.next()
                 } label: {
                 } label: {
                     OperatingSystem(imageName: "linux", name: "Linux")
                     OperatingSystem(imageName: "linux", name: "Linux")

+ 15 - 3
Platform/Shared/VMWizardOSWindowsView.swift

@@ -22,6 +22,7 @@ struct VMWizardOSWindowsView: View {
     
     
     var body: some View {
     var body: some View {
         VMWizardContent("Windows") {
         VMWizardContent("Windows") {
+            #if !WITH_QEMU_TCI
             Section {
             Section {
                 Toggle("Install Windows 10 or higher", isOn: $wizardState.isWindows10OrHigher)
                 Toggle("Install Windows 10 or higher", isOn: $wizardState.isWindows10OrHigher)
                     .onChange(of: wizardState.isWindows10OrHigher) { newValue in
                     .onChange(of: wizardState.isWindows10OrHigher) { newValue in
@@ -30,11 +31,13 @@ struct VMWizardOSWindowsView: View {
                             wizardState.systemBootTpm = true
                             wizardState.systemBootTpm = true
                             wizardState.isGuestToolsInstallRequested = true
                             wizardState.isGuestToolsInstallRequested = true
                         } else {
                         } else {
+                            wizardState.systemBootUefi = false
                             wizardState.systemBootTpm = false
                             wizardState.systemBootTpm = false
                             wizardState.isGuestToolsInstallRequested = false
                             wizardState.isGuestToolsInstallRequested = false
                         }
                         }
                     }
                     }
-                
+                    .disabled(wizardState.legacyHardware)
+
                 if wizardState.isWindows10OrHigher {
                 if wizardState.isWindows10OrHigher {
                     #if os(macOS)
                     #if os(macOS)
                     Button {
                     Button {
@@ -59,7 +62,8 @@ struct VMWizardOSWindowsView: View {
             } header: {
             } header: {
                 Text("Image File Type")
                 Text("Image File Type")
             }
             }
-            
+            #endif
+
             Section {
             Section {
                 FileBrowseField(url: $wizardState.bootImageURL, isFileImporterPresented: $isFileImporterPresented, hasClearButton: false)
                 FileBrowseField(url: $wizardState.bootImageURL, isFileImporterPresented: $isFileImporterPresented, hasClearButton: false)
                 
                 
@@ -92,6 +96,15 @@ struct VMWizardOSWindowsView: View {
             }
             }
         }
         }
         .fileImporter(isPresented: $isFileImporterPresented, allowedContentTypes: [.data], onCompletion: processImage)
         .fileImporter(isPresented: $isFileImporterPresented, allowedContentTypes: [.data], onCompletion: processImage)
+        .onAppear {
+            wizardState.bootDevice = .cd
+            #if WITH_QEMU_TCI
+            wizardState.legacyHardware = true
+            #endif
+            if wizardState.legacyHardware {
+                wizardState.isWindows10OrHigher = false
+            }
+        }
     }
     }
     
     
     private func processImage(_ result: Result<URL, Error>) {
     private func processImage(_ result: Result<URL, Error>) {
@@ -99,7 +112,6 @@ struct VMWizardOSWindowsView: View {
             let url = try result.get()
             let url = try result.get()
             await MainActor.run {
             await MainActor.run {
                 wizardState.bootImageURL = url
                 wizardState.bootImageURL = url
-                wizardState.bootDevice = .cd
             }
             }
         }
         }
     }
     }

+ 0 - 1
Platform/Shared/VMWizardStartViewTCI.swift

@@ -24,7 +24,6 @@ struct VMWizardStartViewTCI: View {
             Section {
             Section {
                 Button {
                 Button {
                     wizardState.useVirtualization = false
                     wizardState.useVirtualization = false
-                    wizardState.operatingSystem = .Other
                     wizardState.next()
                     wizardState.next()
                 } label: {
                 } label: {
                     HStack {
                     HStack {

+ 30 - 54
Platform/Shared/VMWizardState.swift

@@ -224,12 +224,14 @@ struct AlertMessage: Identifiable {
         var nextPage = currentPage
         var nextPage = currentPage
         switch currentPage {
         switch currentPage {
         case .start:
         case .start:
-            #if WITH_QEMU_TCI
-            nextPage = .otherBoot
-            #else
             nextPage = .operatingSystem
             nextPage = .operatingSystem
-            #endif
         case .operatingSystem:
         case .operatingSystem:
+            nextPage = .hardware
+        case .hardware:
+            guard systemMemoryMib > 0 else {
+                alertMessage = AlertMessage(NSLocalizedString("Invalid RAM size specified.", comment: "VMWizardState"))
+                return
+            }
             switch operatingSystem {
             switch operatingSystem {
             case .Other:
             case .Other:
                 nextPage = .otherBoot
                 nextPage = .otherBoot
@@ -240,47 +242,34 @@ struct AlertMessage: Identifiable {
             case .Windows:
             case .Windows:
                 nextPage = .windowsBoot
                 nextPage = .windowsBoot
             case .ClassicMacOS:
             case .ClassicMacOS:
-                nextPage = .hardware
+                nextPage = .classicMacOSBoot
             }
             }
-        case .otherBoot:
-            guard bootDevice == .none || bootImageURL != nil else {
+        case .otherBoot, .macOSBoot, .linuxBoot, .windowsBoot, .classicMacOSBoot:
+            guard [.kernel, .none].contains(bootDevice) || bootImageURL != nil else {
                 alertMessage = AlertMessage(NSLocalizedString("Please select a boot image.", comment: "VMWizardState"))
                 alertMessage = AlertMessage(NSLocalizedString("Please select a boot image.", comment: "VMWizardState"))
                 return
                 return
             }
             }
-            nextPage = .hardware
-        case .macOSBoot:
-            #if os(macOS) && arch(arm64)
-            if #available(macOS 12, *) {
-                if macPlatform == nil || macRecoveryIpswURL == nil {
-                    fetchLatestPlatform()
+            if currentPage == .macOSBoot {
+                #if os(macOS) && arch(arm64)
+                if #available(macOS 12, *) {
+                    if macPlatform == nil || macRecoveryIpswURL == nil {
+                        fetchLatestPlatform()
+                    }
                 }
                 }
-                nextPage = .hardware
+                #endif
             }
             }
-            #endif
-        case .linuxBoot:
-            if bootDevice == .kernel {
-                guard linuxKernelURL != nil else {
+            if currentPage == .linuxBoot {
+                guard bootDevice != .kernel || linuxKernelURL != nil else {
                     alertMessage = AlertMessage(NSLocalizedString("Please select a kernel file.", comment: "VMWizardState"))
                     alertMessage = AlertMessage(NSLocalizedString("Please select a kernel file.", comment: "VMWizardState"))
                     return
                     return
                 }
                 }
-            } else {
-                guard bootImageURL != nil else {
-                    alertMessage = AlertMessage(NSLocalizedString("Please select a boot image.", comment: "VMWizardState"))
+            }
+            if currentPage == .classicMacOSBoot {
+                guard systemTarget.rawValue != QEMUTarget_m68k.q800.rawValue || quadra800RomUrl != nil else {
+                    alertMessage = AlertMessage(NSLocalizedString("Please select a ROM file.", comment: "VMWizardState"))
                     return
                     return
                 }
                 }
             }
             }
-            nextPage = .hardware
-        case .windowsBoot:
-            guard bootImageURL != nil else {
-                alertMessage = AlertMessage(NSLocalizedString("Please select a boot image.", comment: "VMWizardState"))
-                return
-            }
-            nextPage = .hardware
-        case .hardware:
-            guard systemMemoryMib > 0 else {
-                alertMessage = AlertMessage(NSLocalizedString("Invalid RAM size specified.", comment: "VMWizardState"))
-                return
-            }
             if bootDevice == .drive {
             if bootDevice == .drive {
                 nextPage = .sharing
                 nextPage = .sharing
             } else {
             } else {
@@ -295,19 +284,6 @@ struct AlertMessage: Identifiable {
                     }
                     }
                 }
                 }
             }
             }
-            if operatingSystem == .ClassicMacOS {
-                nextPage = .classicMacOSBoot
-            }
-        case .classicMacOSBoot:
-            guard bootImageURL != nil else {
-                alertMessage = AlertMessage(NSLocalizedString("Please select a boot image.", comment: "VMWizardState"))
-                return
-            }
-            guard systemTarget.rawValue != QEMUTarget_m68k.q800.rawValue || quadra800RomUrl != nil else {
-                alertMessage = AlertMessage(NSLocalizedString("Please select a ROM file.", comment: "VMWizardState"))
-                return
-            }
-            nextPage = .drives
         case .drives:
         case .drives:
             guard storageSizeGib > 0 else {
             guard storageSizeGib > 0 else {
                 alertMessage = AlertMessage(NSLocalizedString("Invalid drive size specified.", comment: "VMWizardState"))
                 alertMessage = AlertMessage(NSLocalizedString("Invalid drive size specified.", comment: "VMWizardState"))
@@ -476,10 +452,13 @@ struct AlertMessage: Identifiable {
             // change default sharing to virtfs if linux
             // change default sharing to virtfs if linux
             config.sharing.directoryShareMode = .virtfs
             config.sharing.directoryShareMode = .virtfs
         }
         }
-        if operatingSystem == .Windows {
-            // only change UEFI settings for Windows
+        if operatingSystem == .Windows || operatingSystem == .Other {
+            // only change UEFI settings for Windows or Other
             config.qemu.hasUefiBoot = systemBootUefi
             config.qemu.hasUefiBoot = systemBootUefi
-            config.qemu.hasTPMDevice = systemBootTpm
+            config.qemu.hasTPMDevice = operatingSystem == .Windows && systemBootTpm
+        } else if legacyHardware {
+            config.qemu.hasUefiBoot = false
+            config.qemu.hasTPMDevice = false
         }
         }
         if operatingSystem == .Linux && config.displays.first != nil {
         if operatingSystem == .Linux && config.displays.first != nil {
             // change default display to virtio-gpu if supported
             // change default display to virtio-gpu if supported
@@ -596,11 +575,8 @@ struct AlertMessage: Identifiable {
                 config.drives.append(toolsDiskDrive)
                 config.drives.append(toolsDiskDrive)
             }
             }
         }
         }
-        if legacyHardware {
-            config.qemu.hasUefiBoot = false
-            if systemArchitecture.hasUsbSupport && systemTarget.hasUsbSupport {
-                config.input.usbBusSupport = .usb2_0
-            }
+        if legacyHardware && systemArchitecture.hasUsbSupport && systemTarget.hasUsbSupport {
+            config.input.usbBusSupport = .usb2_0
         }
         }
         return config
         return config
     }
     }