From 623bcbfbb8caa42c6a2c18e275707a95452ead59 Mon Sep 17 00:00:00 2001 From: Hoang Hong Quan Date: Thu, 20 Mar 2025 20:15:01 +0700 Subject: [PATCH] Add compatibility information and improve multi hardware management --- OpCore-Simplify.py | 91 ++++++++------ Scripts/compatibility_checker.py | 205 ++++++++++++++++--------------- Scripts/config_prodigy.py | 11 +- Scripts/hardware_customizer.py | 176 ++++++++++++++++++++++++++ 4 files changed, 342 insertions(+), 141 deletions(-) create mode 100644 Scripts/hardware_customizer.py diff --git a/OpCore-Simplify.py b/OpCore-Simplify.py index a0f8bbb..dbc9c67 100644 --- a/OpCore-Simplify.py +++ b/OpCore-Simplify.py @@ -3,6 +3,7 @@ from Scripts import acpi_guru from Scripts import compatibility_checker from Scripts import config_prodigy from Scripts import gathering_files +from Scripts import hardware_customizer from Scripts import kext_maestro from Scripts import run from Scripts import smbios @@ -18,10 +19,11 @@ import time class OCPE: def __init__(self): self.u = utils.Utils("OpCore Simplify") - self.o = gathering_files.gatheringFiles() self.ac = acpi_guru.ACPIGuru() self.c = compatibility_checker.CompatibilityChecker() self.co = config_prodigy.ConfigProdigy() + self.o = gathering_files.gatheringFiles() + self.h = hardware_customizer.HardwareCustomizer() self.k = kext_maestro.KextMaestro() self.s = smbios.SMBIOS() self.r = run.Run() @@ -45,11 +47,10 @@ class OCPE: self.u.head("Select hardware report") print("") if os.name == "nt": - print("\033[1;36m", end="") - print("Note:") + print("\033[93mNote:\033[0m") print("- Ensure you are using the latest version of Hardware Sniffer before generating the hardware report.") print("- Hardware Sniffer will not collect information related to Resizable BAR option of GPU (disabled by default) and monitor connections in Windows PE.") - print("\033[0m", end="") + print("") if self.hardware_sniffer: print("") print("E. Export hardware report (Recommended)") @@ -92,28 +93,50 @@ class OCPE: return path, data - def select_macos_version(self, native_macos_version, ocl_patched_macos_version): + def select_macos_version(self, hardware_report, native_macos_version, ocl_patched_macos_version): + suggested_macos_version = native_macos_version[1] version_pattern = re.compile(r'^(\d+)(?:\.(\d+)(?:\.(\d+))?)?$') + for device_type in ("GPU", "Network", "Bluetooth", "SD Controller"): + if device_type in hardware_report: + for device_name, device_props in hardware_report[device_type].items(): + if device_props.get("Compatibility", (None, None)) != (None, None): + if device_type == "GPU" and device_props.get("Device Type") == "Integrated GPU": + device_id = device_props.get("Device ID", ""*8)[5:] + + if device_props.get("Manufacturer") == "AMD" or device_id.startswith(("59", "87C0")): + suggested_macos_version = "22.99.99" + elif device_id.startswith(("09", "19")): + suggested_macos_version = "21.99.99" + + if self.u.parse_darwin_version(suggested_macos_version) > self.u.parse_darwin_version(device_props.get("Compatibility")[0]): + suggested_macos_version = device_props.get("Compatibility")[0] + while True: self.u.head("Select macOS Version") + if native_macos_version[1][:2] != suggested_macos_version[:2]: + print("") + print("\033[1;36mSuggested macOS version:\033[0m") + print("- For better compatibility and stability, we suggest you to use only {} or older.".format(os_data.get_macos_name_by_darwin(suggested_macos_version))) + print("") + print("Available macOS versions:") print("") if ocl_patched_macos_version: - print("* Native macOS versions: ") + print(" * Native macOS versions: ") for darwin_version in range(int(native_macos_version[0][:2]), min(int(native_macos_version[-1][:2]), (int(ocl_patched_macos_version[-1][:2]) - 1) if ocl_patched_macos_version else 99) + 1): print("{}{}. {}".format(" "*3 if ocl_patched_macos_version else "", darwin_version, os_data.get_macos_name_by_darwin(str(darwin_version)))) if ocl_patched_macos_version: - print("* Requires OpenCore Legacy Patcher: ") + print(" * Requires OpenCore Legacy Patcher: ") for darwin_version in range(int(ocl_patched_macos_version[-1][:2]), int(ocl_patched_macos_version[0][:2]) + 1): print(" {}. {}".format(darwin_version, os_data.get_macos_name_by_darwin(str(darwin_version)))) print("") - print("Please enter the macOS version you want to select:") + print("\033[93mNote:\033[0m") print("- To select a major version, enter the number (e.g., 19).") print("- To specify a full version, use the Darwin version format (e.g., 22.4.6).") print("") print("Q. Quit") print("") - option = self.u.request_input("Select macOS version: ") + option = self.u.request_input("Please enter the macOS version you want to use (default: {}): ".format(os_data.get_macos_name_by_darwin(suggested_macos_version))) or suggested_macos_version if option.lower() == "q": self.u.exit_program() @@ -125,7 +148,7 @@ class OCPE: (ocl_patched_macos_version and self.u.parse_darwin_version(ocl_patched_macos_version[-1]) <= self.u.parse_darwin_version(target_version) <= self.u.parse_darwin_version(ocl_patched_macos_version[0])): return target_version - def build_opencore_efi(self, hardware_report, unsupported_devices, smbios_model, macos_version, needs_oclp): + def build_opencore_efi(self, hardware_report, disabled_devices, smbios_model, macos_version, needs_oclp): self.u.head("Building OpenCore EFI") print("") print("1. Copy EFI base to results folder...", end=" ") @@ -149,7 +172,7 @@ class OCPE: config_data["ACPI"]["Patch"] = [] if self.ac.ensure_dsdt(): self.ac.hardware_report = hardware_report - self.ac.unsupported_devices = unsupported_devices + self.ac.disabled_devices = disabled_devices self.ac.acpi_directory = os.path.join(self.result_dir, "EFI", "OC", "ACPI") self.ac.smbios_model = smbios_model self.ac.lpc_bus_device = self.ac.get_lpc_name() @@ -179,7 +202,7 @@ class OCPE: config_data["Kernel"]["Add"] = self.k.load_kexts(hardware_report, macos_version, kexts_directory) print("Done") print("4. Generate config.plist...", end=" ") - self.co.genarate(hardware_report, unsupported_devices, smbios_model, macos_version, needs_oclp, self.k.kexts, config_data) + self.co.genarate(hardware_report, disabled_devices, smbios_model, macos_version, needs_oclp, self.k.kexts, config_data) self.u.write_file(config_file, config_data) print("Done") print("5. Clean up unused drivers, resources, and tools...", end=" ") @@ -272,7 +295,7 @@ class OCPE: def main(self): hardware_report_path = None native_macos_version = None - unsupported_devices = None + disabled_devices = None macos_version = None ocl_patched_macos_version = None needs_oclp = False @@ -287,11 +310,10 @@ class OCPE: print("* Hardware Compatibility:") if native_macos_version: print(" - Native macOS Version: {}".format(self.c.show_macos_compatibility((native_macos_version[-1], native_macos_version[0])))) - if unsupported_devices: - print(" - Unsupported devices:") - for index, device_name in enumerate(unsupported_devices, start=1): - device_props = unsupported_devices.get(device_name) - print("{}{}. {}{}".format(" "*6, index, device_name, "" if not device_props.get("Audio Endpoints") else " ({})".format(", ".join(device_props.get("Audio Endpoints"))))) + if disabled_devices: + print(" - Disabled Devices:") + for index, device_name in enumerate(disabled_devices, start=1): + print("{}{}. {}".format(" "*6, index, device_name)) print("* EFI Options:") print(" - macOS Version: {}{}{}".format("Unknown" if not macos_version else os_data.get_macos_name_by_darwin(macos_version), "" if not macos_version else " ({})".format(macos_version), ". \033[1;36mRequires OpenCore Legacy Patcher\033[0m" if needs_oclp else "")) print(" - SMBIOS: {}".format("Unknown" if not smbios_model else smbios_model)) @@ -316,40 +338,39 @@ class OCPE: if option == 1: hardware_report_path, hardware_report = self.select_hardware_report() - native_macos_version, hardware_report, unsupported_devices, needs_oclp, ocl_patched_macos_version = self.c.check_compatibility(hardware_report) - macos_version = native_macos_version[-1] - if int(macos_version[:2]) == os_data.macos_versions[-1].darwin_version and os_data.macos_versions[-1].release_status == "beta": - macos_version = str(int(macos_version[:2]) - 1) + macos_version[2:] - smbios_model = self.s.select_smbios_model(hardware_report, macos_version) + hardware_report, native_macos_version, ocl_patched_macos_version = self.c.check_compatibility(hardware_report) + macos_version = self.select_macos_version(hardware_report, native_macos_version, ocl_patched_macos_version) + customized_hardware, disabled_devices, needs_oclp = self.h.hardware_customization(hardware_report, macos_version) + smbios_model = self.s.select_smbios_model(customized_hardware, macos_version) if not self.ac.ensure_dsdt(): self.ac.select_acpi_tables() - self.ac.select_acpi_patches(hardware_report, unsupported_devices) - self.k.select_required_kexts(hardware_report, macos_version, needs_oclp, self.ac.patches) - self.s.smbios_specific_options(hardware_report, smbios_model, macos_version, self.ac.patches, self.k) + self.ac.select_acpi_patches(customized_hardware, disabled_devices) + self.k.select_required_kexts(customized_hardware, macos_version, needs_oclp, self.ac.patches) + self.s.smbios_specific_options(customized_hardware, smbios_model, macos_version, self.ac.patches, self.k) elif option < 7: try: - hardware_report + customized_hardware except: self.u.request_input("\nPlease select a hardware report to proceed") continue if option == 2: macos_version = self.select_macos_version(native_macos_version, ocl_patched_macos_version) - hardware_report, unsupported_devices, needs_oclp = self.c.get_unsupported_devices(macos_version) - smbios_model = self.s.select_smbios_model(hardware_report, macos_version) - self.k.select_required_kexts(hardware_report, macos_version, needs_oclp, self.ac.patches) - self.s.smbios_specific_options(hardware_report, smbios_model, macos_version, self.ac.patches, self.k) + customized_hardware, disabled_devices, needs_oclp = self.h.hardware_customization(macos_version) + smbios_model = self.s.select_smbios_model(customized_hardware, macos_version) + self.k.select_required_kexts(customized_hardware, macos_version, needs_oclp, self.ac.patches) + self.s.smbios_specific_options(customized_hardware, smbios_model, macos_version, self.ac.patches, self.k) elif option == 3: self.ac.customize_patch_selection() elif option == 4: self.k.kext_configuration_menu(macos_version) elif option == 5: - smbios_model = self.s.customize_smbios_model(hardware_report, smbios_model, macos_version) - self.s.smbios_specific_options(hardware_report, smbios_model, macos_version, self.ac.patches, self.k) + smbios_model = self.s.customize_smbios_model(customized_hardware, smbios_model, macos_version) + self.s.smbios_specific_options(customized_hardware, smbios_model, macos_version, self.ac.patches, self.k) elif option == 6: self.gathering_files(macos_version) - self.build_opencore_efi(hardware_report, unsupported_devices, smbios_model, macos_version, needs_oclp) - self.results(hardware_report, smbios_model, self.k.kexts) + self.build_opencore_efi(customized_hardware, disabled_devices, smbios_model, macos_version, needs_oclp) + self.results(customized_hardware, smbios_model, self.k.kexts) if __name__ == '__main__': update_flag = updater.Updater().run_update() diff --git a/Scripts/compatibility_checker.py b/Scripts/compatibility_checker.py index b40f693..41f85e8 100644 --- a/Scripts/compatibility_checker.py +++ b/Scripts/compatibility_checker.py @@ -1,4 +1,3 @@ -from Scripts.datasets import chipset_data from Scripts.datasets import gpu_data from Scripts.datasets import os_data from Scripts.datasets import pci_data @@ -22,16 +21,16 @@ class CompatibilityChecker: max_version = self.utils.parse_darwin_version(os_data.get_latest_darwin_version())[0] min_version = self.utils.parse_darwin_version(os_data.get_lowest_darwin_version())[0] - if min_version < min_compatibility and max_compatibility < max_version: - return "\033[1;32m{} - {}\033[0m".format( - os_data.get_macos_name_by_darwin(device_compatibility[0]), - os_data.get_macos_name_by_darwin(device_compatibility[-1]) - ) - if max_compatibility == min_version: return "\033[1;36mMaximum support up to {}\033[0m".format( os_data.get_macos_name_by_darwin(device_compatibility[-1]) ) + + if min_version < min_compatibility or max_compatibility < max_version: + return "\033[1;32m{} to {}\033[0m".format( + os_data.get_macos_name_by_darwin(device_compatibility[-1]), + os_data.get_macos_name_by_darwin(device_compatibility[0]) + ) return "\033[1;36mUp to {}\033[0m".format( os_data.get_macos_name_by_darwin(device_compatibility[0]) @@ -53,19 +52,31 @@ class CompatibilityChecker: max_version = "21.99.99" self.hardware_report["CPU"]["Compatibility"] = (max_version, min_version) + print("{}- {}: {}".format(" "*3, self.hardware_report.get("CPU").get("Processor Name"), self.show_macos_compatibility(self.hardware_report["CPU"].get("Compatibility")))) if max_version == min_version and max_version == None: - self.utils.request_input("\n\nThe CPU is not compatible with macOS!") + print("") + print("Missing required SSE4.x instruction set.") + print("Your CPU is not supported by macOS versions newer than Sierra (10.12).") + print("") + self.utils.request_input() self.utils.exit_program() self.max_native_macos_version = max_version self.min_native_macos_version = min_version def check_gpu_compatibility(self): - gpu_compatibility = [] + if not self.hardware_report.get("GPU"): + print("") + print("No GPU found!") + print("Please make sure to export the hardware report with the GPU information") + print("and try again.") + print("") + self.utils.request_input() + self.utils.exit_program() - for gpu_name, gpu_props in self.hardware_report.get("GPU").items(): + for gpu_name, gpu_props in self.hardware_report["GPU"].items(): gpu_manufacturer = gpu_props.get("Manufacturer") gpu_codename = gpu_props.get("Codename") device_id = gpu_props.get("Device ID")[5:] @@ -84,14 +95,14 @@ class CompatibilityChecker: max_version = "20.99.99" elif device_id.startswith(("04", "0A", "0C", "0D", "0B", "16")): max_version = "21.99.99" - elif device_id.startswith(("09", "19", "59", "59", "3E", "87", "9B")) and not device_id in ("3E90", "3E93", "3E99", "3E9C", "3EA1", "3EA4", "9B21", "9BA0", "9BA2", "9BA4", "9BA5", "9BA8", "9BAA", "9BAB", "9BAC"): + elif device_id.startswith(("09", "19", "59", "3E", "87", "9B")) and not device_id in ("3E90", "3E93", "3E99", "3E9C", "3EA1", "3EA4", "9B21", "9BA0", "9BA2", "9BA4", "9BA5", "9BA8", "9BAA", "9BAB", "9BAC"): pass elif device_id.startswith("8A"): min_version = "19.4.0" else: max_version = min_version = None - if self.is_low_end_intel_cpu(self.hardware_report.get("CPU").get("Processor Name")) or self.hardware_report.get("Motherboard").get("Chipset") in chipset_data.IntelChipsets[127:141]: + if self.is_low_end_intel_cpu(self.hardware_report.get("CPU").get("Processor Name")): max_version = min_version = None elif "AMD" in gpu_manufacturer: if "Navi 2" in gpu_codename: @@ -144,15 +155,11 @@ class CompatibilityChecker: if max_version != ocl_patched_max_version: gpu_props["OCLP Compatibility"] = (ocl_patched_max_version, ocl_patched_min_version if self.utils.parse_darwin_version(ocl_patched_min_version) > self.utils.parse_darwin_version("{}.{}.{}".format(int(max_version[:2]) + 1, 0, 0)) else "{}.{}.{}".format(int(max_version[:2]) + 1, 0, 0)) - print("{}- {}: {}{}".format( - " "*3, - gpu_name, - self.show_macos_compatibility(gpu_props.get("Compatibility")), - " \033[1;36m(requires monitor)\033[0m" - if max_version and \ - not "Intel" in gpu_manufacturer and \ - not any(monitor_info.get("Connected GPU", gpu_name) == gpu_name for monitor_name, monitor_info in self.hardware_report.get("Monitor", {}).items()) else "" - )) + print("{}- {}: {}".format(" "*3, gpu_name, self.show_macos_compatibility(gpu_props.get("Compatibility")))) + + if "OCLP Compatibility" in gpu_props: + print("{}- OCLP Compatibility: {}".format(" "*6, self.show_macos_compatibility(gpu_props.get("OCLP Compatibility")))) + connected_monitors = [] for monitor_name, monitor_info in self.hardware_report.get("Monitor", {}).items(): if monitor_info.get("Connected GPU") == gpu_name: @@ -182,50 +189,46 @@ class CompatibilityChecker: self.ocl_patched_macos_version = (gpu_props.get("OCLP Compatibility")[0], self.ocl_patched_macos_version[-1] if self.ocl_patched_macos_version and self.utils.parse_darwin_version(self.ocl_patched_macos_version[-1]) < self.utils.parse_darwin_version(gpu_props.get("OCLP Compatibility")[-1]) else gpu_props.get("OCLP Compatibility")[-1]) if max_supported_gpu_version == min_supported_gpu_version and max_supported_gpu_version == None: - self.utils.request_input("\n\nNo compatible GPU card for macOS was found!") + print("") + print("No compatible GPU for macOS was found!") + if self.hardware_report.get("Motherboard").get("Platform") == "Desktop": + print("Consider purchasing a compatible GPU for your system.") + print("") + self.utils.request_input() self.utils.exit_program() self.max_native_macos_version = max_supported_gpu_version if self.utils.parse_darwin_version(max_supported_gpu_version) < self.utils.parse_darwin_version(self.max_native_macos_version) else self.max_native_macos_version self.min_native_macos_version = min_supported_gpu_version if self.utils.parse_darwin_version(min_supported_gpu_version) > self.utils.parse_darwin_version(self.min_native_macos_version) else self.min_native_macos_version - return gpu_compatibility - def check_sound_compatibility(self): - sound_info = {} - for audio_device, audio_props in self.hardware_report.get("Sound", {}).items(): codec_id = audio_props.get("Device ID") + + max_version = min_version = None + if "USB" in audio_props.get("Bus Type") or \ codec_id.startswith("1002") or \ codec_id.startswith("8086") and not codec_id in pci_data.IntelSSTIDs or \ codec_id in codec_layouts.data: - audio_props["Compatibility"] = (os_data.get_latest_darwin_version(), os_data.get_lowest_darwin_version()) - if codec_id in codec_layouts.data: - sound_info = {**{audio_device: audio_props}, **sound_info} - else: - sound_info[audio_device] = audio_props - else: - audio_props["Compatibility"] = (None, None) - sound_info[audio_device] = audio_props + max_version, min_version = os_data.get_latest_darwin_version(), os_data.get_lowest_darwin_version() + + audio_props["Compatibility"] = (max_version, min_version) + print("{}- {}: {}".format(" "*3, audio_device, self.show_macos_compatibility(audio_props.get("Compatibility")))) + audio_endpoints = audio_props.get("Audio Endpoints") if audio_endpoints: print("{}- Audio Endpoint{}: {}".format(" "*6, "s" if len(audio_endpoints) > 1 else "", ", ".join(audio_endpoints))) - - self.hardware_report["Sound"] = sound_info def check_biometric_compatibility(self): + print(" \033[93mNote:\033[0m Biometric authentication in macOS requires Apple T2 Chip,") + print(" which is not available for Hackintosh systems.") + print("") for biometric_device, biometric_props in self.hardware_report.get("Biometric", {}).items(): biometric_props["Compatibility"] = (None, None) print("{}- {}: {}".format(" "*3, biometric_device, self.show_macos_compatibility(biometric_props.get("Compatibility")))) def check_network_compatibility(self): - primary_wifi_device = next((device_props.get("Device ID") for device_name, device_props in self.hardware_report.get("Network", {}).items() if device_props.get("Device ID") in pci_data.BroadcomWiFiIDs), None) - if not primary_wifi_device: - primary_wifi_device = next((device_props.get("Device ID") for device_name, device_props in self.hardware_report.get("Network", {}).items() if device_props.get("Device ID") in pci_data.IntelWiFiIDs), None) - if not primary_wifi_device: - primary_wifi_device = next((device_props.get("Device ID") for device_name, device_props in self.hardware_report.get("Network", {}).items() if device_props.get("Device ID") in pci_data.AtherosWiFiIDs), None) - for device_name, device_props in self.hardware_report.get("Network", {}).items(): bus_type = device_props.get("Bus Type") device_id = device_props.get("Device ID") @@ -253,12 +256,10 @@ class CompatibilityChecker: min_version = "21.0.0" if device_id in pci_data.WirelessCardIDs: - if device_id == primary_wifi_device: - if not device_id in pci_data.IntelWiFiIDs and not device_id in pci_data.AtherosWiFiIDs[8:]: - device_props["OCLP Compatibility"] = (ocl_patched_max_version, ocl_patched_min_version) - self.ocl_patched_macos_version = (ocl_patched_max_version, self.ocl_patched_macos_version[-1] if self.ocl_patched_macos_version and self.utils.parse_darwin_version(self.ocl_patched_macos_version[-1]) < self.utils.parse_darwin_version(device_props.get("OCLP Compatibility")[-1]) else device_props.get("OCLP Compatibility")[-1]) - device_props["Compatibility"] = (max_version, min_version) - primary_wifi_device = None + if not device_id in pci_data.IntelWiFiIDs and not device_id in pci_data.AtherosWiFiIDs[8:]: + device_props["OCLP Compatibility"] = (ocl_patched_max_version, ocl_patched_min_version) + self.ocl_patched_macos_version = (ocl_patched_max_version, self.ocl_patched_macos_version[-1] if self.ocl_patched_macos_version and self.utils.parse_darwin_version(self.ocl_patched_macos_version[-1]) < self.utils.parse_darwin_version(device_props.get("OCLP Compatibility")[-1]) else device_props.get("OCLP Compatibility")[-1]) + device_props["Compatibility"] = (max_version, min_version) elif device_id in pci_data.EthernetIDs + pci_data.WirelessUSBIDs: device_props["Compatibility"] = (max_version, min_version) @@ -267,9 +268,31 @@ class CompatibilityChecker: print("{}- {}: {}".format(" "*3, device_name, self.show_macos_compatibility(device_props.get("Compatibility")))) + if device_id in pci_data.WirelessCardIDs: + if device_id in pci_data.BroadcomWiFiIDs: + print("{}- Continuity Support: \033[1;32mFull\033[0m (AirDrop, Handoff, Universal Clipboard, Instant Hotspot,...)".format(" "*6)) + elif device_id in pci_data.IntelWiFiIDs: + print("{}- Continuity Support: \033[1;33mPartial\033[0m (Handoff and Universal Clipboard with AirportItlwm)".format(" "*6)) + print("{}\033[93mNote:\033[0m AirDrop, Universal Clipboard, Instant Hotspot,... not available".format(" "*6)) + elif device_id in pci_data.AtherosWiFiIDs: + print("{}- Continuity Support: \033[1;31mLimited\033[0m (No Continuity features available)".format(" "*6)) + print("{}\033[93mNote:\033[0m Atheros cards are not recommended for macOS".format(" "*6)) + + if "OCLP Compatibility" in device_props: + print("{}- OCLP Compatibility: {}".format(" "*6, self.show_macos_compatibility(device_props.get("OCLP Compatibility")))) + def check_storage_compatibility(self): - for controller_name, controller_props in self.hardware_report.get("Storage Controllers", {}).items(): - if "PCI" not in controller_props.get("Bus Type"): + if not self.hardware_report.get("Storage Controllers"): + print("") + print("No storage controller found!") + print("Please make sure to export the hardware report with the storage controller information") + print("and try again.") + print("") + self.utils.request_input() + self.utils.exit_program() + + for controller_name, controller_props in self.hardware_report["Storage Controllers"].items(): + if controller_props.get("Bus Type") != "PCI": continue device_id = controller_props.get("Device ID") @@ -279,18 +302,27 @@ class CompatibilityChecker: min_version = os_data.get_lowest_darwin_version() if device_id in pci_data.IntelVMDIDs: - self.utils.request_input("\n\nDisable Intel RST VMD in the BIOS before exporting the hardware report and try again with the new report") + print("") + print("Intel VMD controllers are not supported in macOS.") + print("Please disable Intel VMD in the BIOS settings and try again with new hardware report.") + print("") + self.utils.request_input() self.utils.exit_program() - controller_props["Compatibility"] = (max_version, min_version) - if next((device for device in pci_data.UnsupportedNVMeSSDIDs if device_id == device[0] and subsystem_id in device[1]), None): - controller_props["Compatibility"] = (None, None) + max_version = min_version = None + + controller_props["Compatibility"] = (max_version, min_version) print("{}- {}: {}".format(" "*3, controller_name, self.show_macos_compatibility(controller_props.get("Compatibility")))) - if all(controller_props.get("Compatibility") == (None, None) for controller_name, controller_props in self.hardware_report.get("Storage Controllers", {}).items()): - self.utils.request_input("\n\nNo compatible storage for macOS was found!") + if all(controller_props.get("Compatibility") == (None, None) for controller_name, controller_props in self.hardware_report["Storage Controllers"].items()): + print("") + print("No compatible storage controller for macOS was found!") + print("Consider purchasing a compatible SSD NVMe for your system.") + print("Western Digital NVMe SSDs are generally recommended for good macOS compatibility.") + print("") + self.utils.request_input() self.utils.exit_program() def check_bluetooth_compatibility(self): @@ -308,59 +340,34 @@ class CompatibilityChecker: max_version = min_version = None bluetooth_props["Compatibility"] = (max_version, min_version) + print("{}- {}: {}".format(" "*3, bluetooth_name, self.show_macos_compatibility(bluetooth_props.get("Compatibility")))) def check_sd_controller_compatibility(self): for controller_name, controller_props in self.hardware_report.get("SD Controller", {}).items(): - if controller_props.get("Device ID") not in pci_data.RealtekCardReaderIDs: - controller_props["Compatibility"] = (None, None) + device_id = controller_props.get("Device ID") + + max_version = os_data.get_latest_darwin_version() + min_version = os_data.get_lowest_darwin_version() + + if device_id in pci_data.RealtekCardReaderIDs: + if device_id in pci_data.RealtekCardReaderIDs[:5]: + max_version = "23.99.99" else: - controller_props["Compatibility"] = (os_data.get_latest_darwin_version() if controller_props.get("Device ID") in pci_data.RealtekCardReaderIDs[5:] else "23.99.99", os_data.get_lowest_darwin_version()) + max_version = min_version = None + + controller_props["Compatibility"] = (max_version, min_version) + print("{}- {}: {}".format(" "*3, controller_name, self.show_macos_compatibility(controller_props.get("Compatibility")))) - def get_unsupported_devices(self, macos_verison): - new_hardware_report = {} - unsupported_device = {} - needs_oclp = False - - for device_type, devices in self.hardware_report.items(): - if device_type in ("Motherboard", "BIOS", "CPU", "USB Controllers", "Input", "System Devices"): - new_hardware_report[device_type] = devices - continue - - new_hardware_report[device_type] = {} - - for device_name in devices: - device_props = devices[device_name].copy() - if device_props.get("OCLP Compatibility") and self.utils.parse_darwin_version(device_props.get("OCLP Compatibility")[0]) >= self.utils.parse_darwin_version(macos_verison) >= self.utils.parse_darwin_version(device_props.get("OCLP Compatibility")[-1]): - new_hardware_report[device_type][device_name] = device_props - needs_oclp = True - continue - - device_compatibility = device_props.get("Compatibility") - - if device_compatibility: - if device_compatibility[0] is None or not self.utils.parse_darwin_version(device_compatibility[0]) >= self.utils.parse_darwin_version(macos_verison) >= self.utils.parse_darwin_version(device_compatibility[-1]): - unsupported_device["{}: {}{}".format(device_props.get("Device Type") or device_type, device_name, "" if not device_props.get("Audio Endpoints") else " ({})".format(", ".join(device_props.get("Audio Endpoints"))))] = device_props - else: - new_hardware_report[device_type][device_name] = device_props - else: - new_hardware_report[device_type][device_name] = device_props - - if new_hardware_report[device_type].get(device_name) and new_hardware_report[device_type][device_name].get("OCLP Compatibility"): - del new_hardware_report[device_type][device_name]["OCLP Compatibility"] - - if not new_hardware_report[device_type]: - del new_hardware_report[device_type] - - return new_hardware_report, unsupported_device, needs_oclp - def check_compatibility(self, hardware_report): self.hardware_report = hardware_report self.ocl_patched_macos_version = None self.utils.head("Compatibility Checker") - print() + print("") + print("Checking compatibility with macOS for the following devices:") + print("") steps = [ ('CPU', self.check_cpu_compatibility), @@ -378,10 +385,10 @@ class CompatibilityChecker: if self.hardware_report.get(device_type): index += 1 print("{}. {}:".format(index, device_type)) - time.sleep(0.5) + time.sleep(0.25) function() print("") self.utils.request_input() - return (self.min_native_macos_version, self.max_native_macos_version), *self.get_unsupported_devices(self.max_native_macos_version), self.ocl_patched_macos_version \ No newline at end of file + return hardware_report, (self.min_native_macos_version, self.max_native_macos_version), self.ocl_patched_macos_version \ No newline at end of file diff --git a/Scripts/config_prodigy.py b/Scripts/config_prodigy.py index 1da7b4a..e9ea911 100644 --- a/Scripts/config_prodigy.py +++ b/Scripts/config_prodigy.py @@ -304,7 +304,7 @@ class ConfigProdigy: except: continue - def deviceproperties(self, hardware_report, unsupported_devices, macos_version, kexts): + def deviceproperties(self, hardware_report, disabled_devices, macos_version, kexts): deviceproperties_add = {} def add_device_property(pci_path, properties): @@ -325,7 +325,6 @@ class ConfigProdigy: if device_id in pci_data.IntelWiFiIDs and network_props.get("PCI Path"): add_device_property(network_props.get("PCI Path"), {"IOName": "pci14e4,43a0"}) elif kext.name == "WhateverGreen": - discrete_gpu = None for gpu_name, gpu_info in hardware_report.get("GPU", {}).items(): if gpu_info.get("Device Type") == "Integrated GPU": if "Intel" in gpu_info.get("Manufacturer"): @@ -348,8 +347,6 @@ class ConfigProdigy: elif "Ivy Bridge" in gpu_info.get("Codename") and device_id in "8086-1C3A": add_device_property(device_info.get("PCI Path", "PciRoot(0x0)/Pci(0x16,0x0)"), {"device-id": "3A1E0000"}) elif gpu_info.get("Device Type") == "Discrete GPU": - discrete_gpu = gpu_info - if not gpu_info.get("Device ID") in pci_data.SpoofGPUIDs: continue @@ -393,7 +390,7 @@ class ConfigProdigy: if not device_props.get("ACPI Path"): add_device_property(device_props.get("PCI Path"), {"built-in": "01"}) - for device_name, device_props in unsupported_devices.items(): + for device_name, device_props in disabled_devices.items(): if "GPU" in device_name and not device_props.get("Disabled", False): add_device_property(device_props.get("PCI Path"), {"disable-gpu": True}) @@ -601,7 +598,7 @@ class ConfigProdigy: return uefi_drivers - def genarate(self, hardware_report, unsupported_devices, smbios_model, macos_version, needs_oclp, kexts, config): + def genarate(self, hardware_report, disabled_devices, smbios_model, macos_version, needs_oclp, kexts, config): del config["#WARNING - 1"] del config["#WARNING - 2"] del config["#WARNING - 3"] @@ -626,7 +623,7 @@ class ConfigProdigy: config["Booter"]["Quirks"]["SetupVirtualMap"] = hardware_report.get("BIOS").get("Firmware Type") == "UEFI" and not hardware_report.get("Motherboard").get("Chipset") in chipset_data.AMDChipsets[11:17] + chipset_data.IntelChipsets[90:100] config["Booter"]["Quirks"]["SyncRuntimePermissions"] = "AMD" in hardware_report.get("CPU").get("Manufacturer") or hardware_report.get("Motherboard").get("Chipset") in chipset_data.IntelChipsets[90:100] + chipset_data.IntelChipsets[104:] - config["DeviceProperties"]["Add"] = self.deviceproperties(hardware_report, unsupported_devices, macos_version, kexts) + config["DeviceProperties"]["Add"] = self.deviceproperties(hardware_report, disabled_devices, macos_version, kexts) config["Kernel"]["Block"] = self.block_kext_bundle(kexts) spoof_cpuid = self.spoof_cpuid( diff --git a/Scripts/hardware_customizer.py b/Scripts/hardware_customizer.py new file mode 100644 index 0000000..c0de282 --- /dev/null +++ b/Scripts/hardware_customizer.py @@ -0,0 +1,176 @@ +from Scripts.datasets import os_data +from Scripts.datasets import pci_data +from Scripts import compatibility_checker +from Scripts import utils + +class HardwareCustomizer: + def __init__(self): + self.compatibility_checker = compatibility_checker.CompatibilityChecker() + self.utils = utils.Utils() + + def hardware_customization(self, hardware_report, macos_version): + self.hardware_report = hardware_report + self.macos_version = macos_version + self.customized_hardware = {} + self.disabled_devices = {} + self.selected_devices = {} + needs_oclp = False + + self.utils.head("Hardware Customization") + + for device_type, devices in self.hardware_report.items(): + if not device_type in ("GPU", "Sound", "Biometric", "Network", "Storage Controllers", "Bluetooth", "SD Controller"): + self.customized_hardware[device_type] = devices + continue + + self.customized_hardware[device_type] = {} + + for device_name in devices: + device_props = devices[device_name].copy() + if device_props.get("OCLP Compatibility") and self.utils.parse_darwin_version(device_props.get("OCLP Compatibility")[0]) >= self.utils.parse_darwin_version(macos_version) >= self.utils.parse_darwin_version(device_props.get("OCLP Compatibility")[-1]): + self.customized_hardware[device_type][device_name] = device_props + needs_oclp = True + continue + + device_compatibility = device_props.get("Compatibility", (os_data.get_latest_darwin_version(), os_data.get_lowest_darwin_version())) + + try: + if self.utils.parse_darwin_version(device_compatibility[0]) >= self.utils.parse_darwin_version(macos_version) >= self.utils.parse_darwin_version(device_compatibility[-1]): + self.customized_hardware[device_type][device_name] = device_props + except: + self.disabled_devices["{}: {}{}".format(device_props["Device Type"] if not "Unknown" in device_props.get("Device Type", "Unknown") else device_type, device_name, "" if not device_props.get("Audio Endpoints") else " ({})".format(", ".join(device_props.get("Audio Endpoints"))))] = device_props + + if self.customized_hardware[device_type].get(device_name) and self.customized_hardware[device_type][device_name].get("OCLP Compatibility"): + del self.customized_hardware[device_type][device_name]["OCLP Compatibility"] + + if not self.customized_hardware[device_type]: + del self.customized_hardware[device_type] + else: + if device_type in ("GPU", "Network", "Bluetooth"): + self._handle_device_selection(device_type if device_type != "Network" else "WiFi") + + if self.selected_devices: + self.utils.head("Device Selection Summary") + print("") + print("Selected devices:") + print("") + print("Type Device Device ID") + print("------------------------------------------------------------------") + for device_type, device_dict in self.selected_devices.items(): + device_name = list(device_dict.keys())[0] + device_props = list(device_dict.values())[0] + device_id = device_props.get("Device ID", "Unknown") + + print("{:<13} {:<42} {}".format(device_type, device_name[:38], device_id)) + print("") + print("All other devices of the same type have been disabled.") + print("") + self.utils.request_input("Press Enter to continue...") + + return self.customized_hardware, self.disabled_devices, needs_oclp + + def _handle_device_selection(self, device_type): + devices = self._get_compatible_devices(device_type) + if len(devices) > 1: + print("\n*** Multiple {} Devices Detected".format(device_type)) + if device_type == "WiFi" or device_type == "Bluetooth": + print(f"macOS works best with only one {device_type} device enabled.") + elif device_type == "GPU": + _has_multiple_compatible_devices = False + + for gpu_name, gpu_props in devices.items(): + gpu_manufacturer = gpu_props.get("Manufacturer") + gpu_codename = gpu_props.get("Codename") + + if gpu_manufacturer == "AMD": + if gpu_props.get("Device Type") == "Integrated GPU": + _has_multiple_compatible_devices = True + elif gpu_props.get("Device Type") == "Discrete GPU" and gpu_codename == "Navi 22": + _has_multiple_compatible_devices = True + + if _has_multiple_compatible_devices: + print("Multiple active GPUs can cause kext conflicts in macOS.") + print("It's recommended to use only one GPU at a time.") + else: + return + + selected_device = self._select_device(device_type, devices) + if selected_device: + self.selected_devices[device_type] = { + selected_device: devices[selected_device] + } + + def _get_compatible_devices(self, device_type): + compatible_devices = {} + + if device_type == "WiFi": + hardware_category = "Network" + else: + hardware_category = device_type + + for device_name, device_props in self.customized_hardware.get(hardware_category, {}).items(): + if device_type == "WiFi": + device_id = device_props.get("Device ID") + + if device_id not in pci_data.WirelessCardIDs: + continue + + compatible_devices[device_name] = device_props + + return compatible_devices + + def _select_device(self, device_type, devices): + print("") + print("Please select which {} device you want to use:".format(device_type)) + print("") + + for index, device_name in enumerate(devices, start=1): + device_props = devices[device_name] + compatibility = device_props.get("Compatibility") + + print("{}. {}".format(index, device_name)) + print(" Device ID: {}".format(device_props.get("Device ID", "Unknown"))) + print(" Compatibility: {}".format(self.compatibility_checker.show_macos_compatibility(compatibility))) + + if device_props.get("OCLP Compatibility"): + oclp_compatibility = device_props.get("OCLP Compatibility") + + if self.utils.parse_darwin_version(oclp_compatibility[0]) > self.utils.parse_darwin_version(compatibility[0]): + print(" OCLP Compatibility: {}".format(self.compatibility_checker.show_macos_compatibility((oclp_compatibility[0], os_data.get_lowest_darwin_version())))) + print() + + while True: + choice = self.utils.request_input(f"Select a {device_type} device (1-{len(devices)}): ") + + try: + choice_num = int(choice) + if 1 <= choice_num <= len(devices): + selected_device = list(devices)[choice_num - 1] + + for device in devices: + if device != selected_device: + self._disable_device(device_type, device, devices[device]) + + return selected_device + else: + print("Invalid option. Please try again.") + except: + print("Please enter a number.") + + def _disable_device(self, device_type, device_name, device_props): + if device_type == "WiFi": + device_id = device_props.get("Device ID") + if not device_id or device_id not in pci_data.WirelessCardIDs: + return + + hardware_category = "Network" + else: + hardware_category = device_type + + if (hardware_category in self.customized_hardware and device_name in self.customized_hardware[hardware_category]): + del self.customized_hardware[hardware_category][device_name] + + if not self.customized_hardware[hardware_category]: + del self.customized_hardware[hardware_category] + + self.disabled_devices["{}: {}".format(device_type, device_name)] = device_props \ No newline at end of file