Files
OpCore-Simplify/Scripts/hardware_customizer.py
2025-08-30 18:10:46 +07:00

305 lines
15 KiB
Python

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 ("BIOS", "GPU", "Sound", "Biometric", "Network", "Storage Controllers", "Bluetooth", "SD Controller"):
self.customized_hardware[device_type] = devices
continue
self.customized_hardware[device_type] = {}
if device_type == "BIOS":
self.customized_hardware[device_type] = devices.copy()
if devices.get("Firmware Type") != "UEFI":
print("\n*** BIOS Firmware Type is not UEFI")
print("")
print("Do you want to build the EFI for UEFI?")
print("If yes, please make sure to update your BIOS and enable UEFI Boot Mode in your BIOS settings.")
print("You can still proceed with Legacy if you prefer.")
print("")
answer = self.utils.request_input("Build EFI for UEFI? (Y/n): ").strip().lower() or "y"
if answer == "n":
self.customized_hardware[device_type]["Firmware Type"] = "Legacy"
else:
self.customized_hardware[device_type]["Firmware Type"] = "UEFI"
continue
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():
for device_name, device_props in device_dict.items():
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 _get_device_combinations(self, device_indices):
devices = sorted(list(device_indices))
n = len(devices)
all_combinations = []
if n == 0:
return []
for i in range(1, 1 << n):
current_combination = []
for j in range(n):
if (i >> j) & 1:
current_combination.append(devices[j])
if 1 <= len(current_combination) <= n:
all_combinations.append(current_combination)
all_combinations.sort(key=lambda combo: (len(combo), combo))
return all_combinations
def _handle_device_selection(self, device_type):
devices = self._get_compatible_devices(device_type)
device_groups = None
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":
_apu_index = None
_navi_22_indices = set()
_navi_indices = set()
_intel_gpu_indices = set()
_other_indices = set()
for index, (gpu_name, gpu_props) in enumerate(devices.items()):
gpu_manufacturer = gpu_props.get("Manufacturer")
gpu_codename = gpu_props.get("Codename")
gpu_type = gpu_props.get("Device Type")
if gpu_manufacturer == "AMD":
if gpu_type == "Integrated GPU":
_apu_index = index
continue
elif gpu_type == "Discrete GPU":
if gpu_codename.startswith("Navi"):
if gpu_codename == "Navi 22":
_navi_22_indices.add(index)
else:
_navi_indices.add(index)
continue
elif gpu_manufacturer == "Intel":
_intel_gpu_indices.add(index)
continue
_other_indices.add(index)
if _apu_index or _navi_22_indices:
print("Multiple active GPUs can cause kext conflicts in macOS.")
device_groups = []
if _apu_index:
device_groups.append({_apu_index} | _other_indices)
if _navi_22_indices:
device_groups.append(_navi_22_indices | _other_indices)
if _navi_indices or _intel_gpu_indices or _other_indices:
device_groups.append(_navi_indices | _intel_gpu_indices | _other_indices)
selected_devices = self._select_device(device_type, devices, device_groups)
if selected_devices:
for selected_device in selected_devices:
if not device_type in self.selected_devices:
self.selected_devices[device_type] = {}
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, device_groups=None):
print("")
if device_groups:
print("Please select a {} combination configuration:".format(device_type))
else:
print("Please select which {} device you want to use:".format(device_type))
print("")
if device_groups:
valid_combinations = []
for group in device_groups:
device_combinations = self._get_device_combinations(group)
for device_combination in device_combinations:
group_devices = []
group_compatibility = None
group_indices = set()
has_oclp_required = False
for index in device_combination:
device_name = list(devices.keys())[index]
device_props = devices[device_name]
group_devices.append(device_name)
group_indices.add(index)
compatibility = device_props.get("Compatibility")
if compatibility:
if group_compatibility is None:
group_compatibility = compatibility
else:
if self.utils.parse_darwin_version(compatibility[0]) < self.utils.parse_darwin_version(group_compatibility[0]):
group_compatibility = (compatibility[0], group_compatibility[1])
if self.utils.parse_darwin_version(compatibility[1]) > self.utils.parse_darwin_version(group_compatibility[1]):
group_compatibility = (group_compatibility[0], compatibility[1])
if device_props.get("OCLP Compatibility"):
has_oclp_required = True
if has_oclp_required and len(device_combination) > 1:
continue
if group_devices and (group_devices, group_indices, group_compatibility) not in valid_combinations:
valid_combinations.append((group_devices, group_indices, group_compatibility))
valid_combinations.sort(key=lambda x: (len(x[0]), x[2][0]))
for idx, (group_devices, _, group_compatibility) in enumerate(valid_combinations, start=1):
print("{}. {}".format(idx, " + ".join(group_devices)))
if group_compatibility:
print(" Compatibility: {}".format(self.compatibility_checker.show_macos_compatibility(group_compatibility)))
if len(group_devices) == 1:
device_props = devices[group_devices[0]]
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(group_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} combination (1-{len(valid_combinations)}): ")
try:
choice_num = int(choice)
if 1 <= choice_num <= len(valid_combinations):
selected_devices, _, _ = valid_combinations[choice_num - 1]
for device in devices:
if device not in selected_devices:
self._disable_device(device_type, device, devices[device])
return selected_devices
else:
print("Invalid option. Please try again.")
except ValueError:
print("Please enter a valid number.")
else:
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 ValueError:
print("Please enter a valid 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(hardware_category, device_name)] = device_props