mirror of
https://github.com/outbackdingo/OpCore-Simplify.git
synced 2026-01-27 10:19:49 +00:00
Add GUI Support for OpCore Simplify (#512)
* Refactor OpCore-Simplify to GUI version * New ConfigEditor * Add requirement checks and installation in launchers * Add GitHub Actions workflow to generate manifest.json * Set compression level for asset * Skip .git and __pycache__ folders * Refactor update process to include integrity checker * Add SMBIOS model selection * Update README.md * Update to main branch
This commit is contained in:
@@ -1,482 +1,318 @@
|
||||
from Scripts.datasets import os_data
|
||||
from Scripts.datasets import chipset_data
|
||||
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 report_validator
|
||||
from Scripts import run
|
||||
from Scripts import smbios
|
||||
from Scripts import utils
|
||||
import updater
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
import shutil
|
||||
import platform
|
||||
import traceback
|
||||
import time
|
||||
|
||||
class OCPE:
|
||||
def __init__(self):
|
||||
self.u = utils.Utils("OpCore Simplify")
|
||||
self.u.clean_temporary_dir()
|
||||
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.v = report_validator.ReportValidator()
|
||||
self.r = run.Run()
|
||||
self.result_dir = self.u.get_temporary_dir()
|
||||
from PyQt6.QtCore import Qt, pyqtSignal
|
||||
from PyQt6.QtGui import QFont
|
||||
from PyQt6.QtWidgets import QApplication
|
||||
from qfluentwidgets import FluentWindow, NavigationItemPosition, FluentIcon, InfoBar, InfoBarPosition
|
||||
|
||||
def select_hardware_report(self):
|
||||
self.ac.dsdt = self.ac.acpi.acpi_tables = None
|
||||
from Scripts.datasets import os_data
|
||||
from Scripts.state import HardwareReportState, macOSVersionState, SMBIOSState, BuildState
|
||||
from Scripts.pages import HomePage, SelectHardwareReportPage, CompatibilityPage, ConfigurationPage, BuildPage, SettingsPage
|
||||
from Scripts.backend import Backend
|
||||
from Scripts import ui_utils
|
||||
from Scripts.custom_dialogs import set_default_gui_handler
|
||||
import updater
|
||||
|
||||
while True:
|
||||
self.u.head("Select hardware report")
|
||||
print("")
|
||||
if os.name == "nt":
|
||||
print("\033[1;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("")
|
||||
print("E. Export hardware report (Recommended)")
|
||||
print("")
|
||||
print("Q. Quit")
|
||||
print("")
|
||||
WINDOW_MIN_SIZE = (1000, 700)
|
||||
WINDOW_DEFAULT_SIZE = (1200, 800)
|
||||
|
||||
|
||||
class OCS(FluentWindow):
|
||||
open_result_folder_signal = pyqtSignal(str)
|
||||
|
||||
PLATFORM_FONTS = {
|
||||
"Windows": "Segoe UI",
|
||||
"Darwin": "SF Pro Display",
|
||||
"Linux": "Ubuntu"
|
||||
}
|
||||
|
||||
def __init__(self, backend):
|
||||
super().__init__()
|
||||
self.backend = backend
|
||||
self.settings = self.backend.settings
|
||||
self.ui_utils = ui_utils.UIUtils()
|
||||
|
||||
user_input = self.u.request_input("Drag and drop your hardware report here (.JSON){}: ".format(" or type \"E\" to export" if os.name == "nt" else ""))
|
||||
if user_input.lower() == "q":
|
||||
self.u.exit_program()
|
||||
if user_input.lower() == "e":
|
||||
hardware_sniffer = self.o.gather_hardware_sniffer()
|
||||
self._init_state()
|
||||
self._setup_window()
|
||||
self._connect_signals()
|
||||
self._setup_backend_handlers()
|
||||
self.init_navigation()
|
||||
|
||||
if not hardware_sniffer:
|
||||
continue
|
||||
def _init_state(self):
|
||||
self.hardware_state = HardwareReportState()
|
||||
self.macos_state = macOSVersionState()
|
||||
self.smbios_state = SMBIOSState()
|
||||
self.build_state = BuildState()
|
||||
|
||||
self.build_btn = None
|
||||
self.progress_bar = None
|
||||
self.progress_label = None
|
||||
self.build_log = None
|
||||
self.open_result_btn = None
|
||||
|
||||
report_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "SysReport")
|
||||
def _setup_window(self):
|
||||
self.setWindowTitle("OpCore Simplify")
|
||||
self.setMinimumSize(*WINDOW_MIN_SIZE)
|
||||
|
||||
self._restore_window_geometry()
|
||||
|
||||
self.u.head("Exporting Hardware Report")
|
||||
print("")
|
||||
print("Exporting hardware report to {}...".format(report_dir))
|
||||
|
||||
output = self.r.run({
|
||||
"args":[hardware_sniffer, "-e", "-o", report_dir]
|
||||
})
|
||||
|
||||
if output[-1] != 0:
|
||||
error_code = output[-1]
|
||||
if error_code == 3:
|
||||
error_message = "Error collecting hardware."
|
||||
elif error_code == 4:
|
||||
error_message = "Error generating hardware report."
|
||||
elif error_code == 5:
|
||||
error_message = "Error dumping ACPI tables."
|
||||
else:
|
||||
error_message = "Unknown error."
|
||||
|
||||
print("")
|
||||
print("Could not export the hardware report. {}".format(error_message))
|
||||
print("Please try again or using Hardware Sniffer manually.")
|
||||
print("")
|
||||
self.u.request_input()
|
||||
continue
|
||||
else:
|
||||
report_path = os.path.join(report_dir, "Report.json")
|
||||
acpitables_dir = os.path.join(report_dir, "ACPI")
|
||||
|
||||
report_data = self.u.read_file(report_path)
|
||||
self.ac.read_acpi_tables(acpitables_dir)
|
||||
|
||||
return report_path, report_data
|
||||
|
||||
path = self.u.normalize_path(user_input)
|
||||
font = QFont()
|
||||
system = platform.system()
|
||||
font_family = self.PLATFORM_FONTS.get(system, "Ubuntu")
|
||||
font.setFamily(font_family)
|
||||
font.setStyleHint(QFont.StyleHint.SansSerif)
|
||||
self.setFont(font)
|
||||
|
||||
def _restore_window_geometry(self):
|
||||
saved_geometry = self.settings.get("window_geometry")
|
||||
|
||||
if saved_geometry and isinstance(saved_geometry, dict):
|
||||
x = saved_geometry.get("x")
|
||||
y = saved_geometry.get("y")
|
||||
width = saved_geometry.get("width", WINDOW_DEFAULT_SIZE[0])
|
||||
height = saved_geometry.get("height", WINDOW_DEFAULT_SIZE[1])
|
||||
|
||||
is_valid, errors, warnings, data = self.v.validate_report(path)
|
||||
if x is not None and y is not None:
|
||||
screen = QApplication.primaryScreen()
|
||||
if screen:
|
||||
screen_geometry = screen.availableGeometry()
|
||||
if (screen_geometry.left() <= x <= screen_geometry.right() and
|
||||
screen_geometry.top() <= y <= screen_geometry.bottom()):
|
||||
self.setGeometry(x, y, width, height)
|
||||
return
|
||||
|
||||
self._center_window()
|
||||
|
||||
def _center_window(self):
|
||||
screen = QApplication.primaryScreen()
|
||||
if screen:
|
||||
screen_geometry = screen.availableGeometry()
|
||||
window_width = WINDOW_DEFAULT_SIZE[0]
|
||||
window_height = WINDOW_DEFAULT_SIZE[1]
|
||||
|
||||
self.v.show_validation_report(path, is_valid, errors, warnings)
|
||||
if not is_valid or errors:
|
||||
print("")
|
||||
print("\033[32mSuggestion:\033[0m Please re-export the hardware report and try again.")
|
||||
print("")
|
||||
self.u.request_input("Press Enter to go back...")
|
||||
else:
|
||||
return path, data
|
||||
x = screen_geometry.left() + (screen_geometry.width() - window_width) // 2
|
||||
y = screen_geometry.top() + (screen_geometry.height() - window_height) // 2
|
||||
|
||||
def show_oclp_warning(self):
|
||||
while True:
|
||||
self.u.head("OpenCore Legacy Patcher Warning")
|
||||
print("")
|
||||
print("1. OpenCore Legacy Patcher is the only solution to enable dropped GPU and Broadcom WiFi")
|
||||
print(" support in newer macOS versions, as well as to bring back AppleHDA for macOS Tahoe 26.")
|
||||
print("")
|
||||
print("2. OpenCore Legacy Patcher disables macOS security features including SIP and AMFI, which may")
|
||||
print(" lead to issues such as requiring full installers for updates, application crashes, and")
|
||||
print(" system instability.")
|
||||
print("")
|
||||
print("3. OpenCore Legacy Patcher is not officially supported for Hackintosh community.")
|
||||
print("")
|
||||
print("\033[1;91mImportant:\033[0m")
|
||||
print("Please consider these risks carefully before proceeding.")
|
||||
print("")
|
||||
print("\033[1;96mSupport for macOS Tahoe 26:\033[0m")
|
||||
print("To patch macOS Tahoe 26, you must download OpenCore-Patcher 3.0.0 or newer from")
|
||||
print("my repository: \033[4mlzhoang2801/OpenCore-Legacy-Patcher\033[0m on GitHub.")
|
||||
print("Older or official Dortania releases are NOT supported for Tahoe 26.")
|
||||
print("")
|
||||
option = self.u.request_input("Do you want to continue with OpenCore Legacy Patcher? (yes/No): ").strip().lower()
|
||||
if option == "yes":
|
||||
return True
|
||||
elif option == "no":
|
||||
self.setGeometry(x, y, window_width, window_height)
|
||||
else:
|
||||
self.resize(*WINDOW_DEFAULT_SIZE)
|
||||
|
||||
def _save_window_geometry(self):
|
||||
geometry = self.geometry()
|
||||
window_geometry = {
|
||||
"x": geometry.x(),
|
||||
"y": geometry.y(),
|
||||
"width": geometry.width(),
|
||||
"height": geometry.height()
|
||||
}
|
||||
self.settings.set("window_geometry", window_geometry)
|
||||
|
||||
def closeEvent(self, event):
|
||||
self._save_window_geometry()
|
||||
super().closeEvent(event)
|
||||
|
||||
def _connect_signals(self):
|
||||
self.backend.log_message_signal.connect(
|
||||
lambda message, level, to_build_log: (
|
||||
[
|
||||
self.build_log.append(line)
|
||||
for line in (message.splitlines() or [""])
|
||||
]
|
||||
if to_build_log and getattr(self, "build_log", None) else None
|
||||
)
|
||||
)
|
||||
self.backend.update_status_signal.connect(self.update_status)
|
||||
|
||||
self.open_result_folder_signal.connect(self._handle_open_result_folder)
|
||||
|
||||
def _setup_backend_handlers(self):
|
||||
self.backend.u.gui_handler = self
|
||||
set_default_gui_handler(self)
|
||||
|
||||
def init_navigation(self):
|
||||
self.homePage = HomePage(self, ui_utils_instance=self.ui_utils)
|
||||
self.SelectHardwareReportPage = SelectHardwareReportPage(self, ui_utils_instance=self.ui_utils)
|
||||
self.compatibilityPage = CompatibilityPage(self, ui_utils_instance=self.ui_utils)
|
||||
self.configurationPage = ConfigurationPage(self, ui_utils_instance=self.ui_utils)
|
||||
self.buildPage = BuildPage(self, ui_utils_instance=self.ui_utils)
|
||||
self.settingsPage = SettingsPage(self)
|
||||
|
||||
self.addSubInterface(
|
||||
self.homePage,
|
||||
FluentIcon.HOME,
|
||||
"Home",
|
||||
NavigationItemPosition.TOP
|
||||
)
|
||||
self.addSubInterface(
|
||||
self.SelectHardwareReportPage,
|
||||
FluentIcon.FOLDER_ADD,
|
||||
"1. Select Hardware Report",
|
||||
NavigationItemPosition.TOP
|
||||
)
|
||||
self.addSubInterface(
|
||||
self.compatibilityPage,
|
||||
FluentIcon.CHECKBOX,
|
||||
"2. Check Compatibility",
|
||||
NavigationItemPosition.TOP
|
||||
)
|
||||
self.addSubInterface(
|
||||
self.configurationPage,
|
||||
FluentIcon.EDIT,
|
||||
"3. Configure OpenCore EFI",
|
||||
NavigationItemPosition.TOP
|
||||
)
|
||||
self.addSubInterface(
|
||||
self.buildPage,
|
||||
FluentIcon.DEVELOPER_TOOLS,
|
||||
"4. Build & Review",
|
||||
NavigationItemPosition.TOP
|
||||
)
|
||||
|
||||
self.navigationInterface.addSeparator()
|
||||
self.addSubInterface(
|
||||
self.settingsPage,
|
||||
FluentIcon.SETTING,
|
||||
"Settings",
|
||||
NavigationItemPosition.BOTTOM
|
||||
)
|
||||
|
||||
def _handle_open_result_folder(self, folder_path):
|
||||
self.backend.u.open_folder(folder_path)
|
||||
|
||||
def update_status(self, message, status_type="INFO"):
|
||||
if status_type == "success":
|
||||
InfoBar.success(
|
||||
title="Success",
|
||||
content=message,
|
||||
orient=Qt.Orientation.Horizontal,
|
||||
isClosable=True,
|
||||
position=InfoBarPosition.TOP_RIGHT,
|
||||
duration=3000,
|
||||
parent=self
|
||||
)
|
||||
elif status_type == "ERROR":
|
||||
InfoBar.error(
|
||||
title="ERROR",
|
||||
content=message,
|
||||
orient=Qt.Orientation.Horizontal,
|
||||
isClosable=True,
|
||||
position=InfoBarPosition.TOP_RIGHT,
|
||||
duration=5000,
|
||||
parent=self
|
||||
)
|
||||
elif status_type == "WARNING":
|
||||
InfoBar.warning(
|
||||
title="WARNING",
|
||||
content=message,
|
||||
orient=Qt.Orientation.Horizontal,
|
||||
isClosable=True,
|
||||
position=InfoBarPosition.TOP_RIGHT,
|
||||
duration=4000,
|
||||
parent=self
|
||||
)
|
||||
else:
|
||||
InfoBar.info(
|
||||
title="INFO",
|
||||
content=message,
|
||||
orient=Qt.Orientation.Horizontal,
|
||||
isClosable=True,
|
||||
position=InfoBarPosition.TOP_RIGHT,
|
||||
duration=3000,
|
||||
parent=self
|
||||
)
|
||||
|
||||
def validate_prerequisites(self, require_hardware_report=True, require_dsdt=True, require_darwin_version=True, check_compatibility_error=True, require_customized_hardware=True, show_status=True):
|
||||
if require_hardware_report:
|
||||
if not self.hardware_state.hardware_report:
|
||||
if show_status:
|
||||
self.update_status("Please select hardware report first", "WARNING")
|
||||
return False
|
||||
|
||||
if require_dsdt:
|
||||
if not self.backend.ac._ensure_dsdt():
|
||||
if show_status:
|
||||
self.update_status("Please load ACPI tables first", "WARNING")
|
||||
return False
|
||||
|
||||
if check_compatibility_error:
|
||||
if self.hardware_state.compatibility_error:
|
||||
if show_status:
|
||||
self.update_status("Incompatible hardware detected, please select different hardware report and try again", "WARNING")
|
||||
return False
|
||||
|
||||
if require_darwin_version:
|
||||
if not self.macos_state.darwin_version:
|
||||
if show_status:
|
||||
self.update_status("Please select target macOS version first", "WARNING")
|
||||
return False
|
||||
|
||||
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:
|
||||
if "Beta" in os_data.get_macos_name_by_darwin(suggested_macos_version):
|
||||
suggested_macos_version = "{}{}".format(int(suggested_macos_version[:2]) - 1, suggested_macos_version[2:])
|
||||
else:
|
||||
break
|
||||
|
||||
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("")
|
||||
|
||||
oclp_min = int(ocl_patched_macos_version[-1][:2]) if ocl_patched_macos_version else 99
|
||||
oclp_max = int(ocl_patched_macos_version[0][:2]) if ocl_patched_macos_version else 0
|
||||
min_version = min(int(native_macos_version[0][:2]), oclp_min)
|
||||
max_version = max(int(native_macos_version[-1][:2]), oclp_max)
|
||||
|
||||
for darwin_version in range(min_version, max_version + 1):
|
||||
name = os_data.get_macos_name_by_darwin(str(darwin_version))
|
||||
label = " (\033[1;93mRequires OpenCore Legacy Patcher\033[0m)" if oclp_min <= darwin_version <= oclp_max else ""
|
||||
print(" {}. {}{}".format(darwin_version, name, label))
|
||||
|
||||
print("")
|
||||
print("\033[1;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("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()
|
||||
|
||||
match = version_pattern.match(option)
|
||||
if match:
|
||||
target_version = "{}.{}.{}".format(match.group(1), match.group(2) if match.group(2) else 99, match.group(3) if match.group(3) else 99)
|
||||
|
||||
if 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
|
||||
elif self.u.parse_darwin_version(native_macos_version[0]) <= self.u.parse_darwin_version(target_version) <= self.u.parse_darwin_version(native_macos_version[-1]):
|
||||
return target_version
|
||||
|
||||
def build_opencore_efi(self, hardware_report, disabled_devices, smbios_model, macos_version, needs_oclp):
|
||||
steps = [
|
||||
"Copying EFI base to results folder",
|
||||
"Applying ACPI patches",
|
||||
"Copying kexts and snapshotting to config.plist",
|
||||
"Generating config.plist",
|
||||
"Cleaning up unused drivers, resources, and tools"
|
||||
]
|
||||
if require_customized_hardware:
|
||||
if not self.hardware_state.customized_hardware:
|
||||
if show_status:
|
||||
self.update_status("Please reload hardware report and select target macOS version to continue", "WARNING")
|
||||
return False
|
||||
|
||||
title = "Building OpenCore EFI"
|
||||
return True
|
||||
|
||||
self.u.progress_bar(title, steps, 0)
|
||||
self.u.create_folder(self.result_dir, remove_content=True)
|
||||
def apply_macos_version(self, version):
|
||||
self.macos_state.darwin_version = version
|
||||
self.macos_state.selected_version_name = os_data.get_macos_name_by_darwin(version)
|
||||
|
||||
if not os.path.exists(self.k.ock_files_dir):
|
||||
raise Exception("Directory '{}' does not exist.".format(self.k.ock_files_dir))
|
||||
self.hardware_state.customized_hardware, self.hardware_state.disabled_devices, self.macos_state.needs_oclp = self.backend.h.hardware_customization(self.hardware_state.hardware_report, version)
|
||||
|
||||
self.smbios_state.model_name = self.backend.s.select_smbios_model(self.hardware_state.customized_hardware, version)
|
||||
|
||||
source_efi_dir = os.path.join(self.k.ock_files_dir, "OpenCorePkg")
|
||||
shutil.copytree(source_efi_dir, self.result_dir, dirs_exist_ok=True)
|
||||
|
||||
config_file = os.path.join(self.result_dir, "EFI", "OC", "config.plist")
|
||||
config_data = self.u.read_file(config_file)
|
||||
self.backend.ac.select_acpi_patches(self.hardware_state.customized_hardware, self.hardware_state.disabled_devices)
|
||||
|
||||
if not config_data:
|
||||
raise Exception("Error: The file {} does not exist.".format(config_file))
|
||||
self.macos_state.needs_oclp, audio_layout_id, audio_controller_properties = self.backend.k.select_required_kexts(self.hardware_state.customized_hardware, version, self.macos_state.needs_oclp, self.backend.ac.patches)
|
||||
|
||||
self.u.progress_bar(title, steps, 1)
|
||||
config_data["ACPI"]["Add"] = []
|
||||
config_data["ACPI"]["Delete"] = []
|
||||
config_data["ACPI"]["Patch"] = []
|
||||
if self.ac.ensure_dsdt():
|
||||
self.ac.hardware_report = hardware_report
|
||||
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()
|
||||
if audio_layout_id is not None:
|
||||
self.hardware_state.audio_layout_id = audio_layout_id
|
||||
self.hardware_state.audio_controller_properties = audio_controller_properties
|
||||
|
||||
for patch in self.ac.patches:
|
||||
if patch.checked:
|
||||
if patch.name == "BATP":
|
||||
patch.checked = getattr(self.ac, patch.function_name)()
|
||||
self.k.kexts[kext_maestro.kext_data.kext_index_by_name.get("ECEnabler")].checked = patch.checked
|
||||
continue
|
||||
self.backend.s.smbios_specific_options(self.hardware_state.customized_hardware, self.smbios_state.model_name, version, self.backend.ac.patches, self.backend.k)
|
||||
|
||||
acpi_load = getattr(self.ac, patch.function_name)()
|
||||
self.configurationPage.update_display()
|
||||
|
||||
if not isinstance(acpi_load, dict):
|
||||
continue
|
||||
def setup_exception_hook(self):
|
||||
def handle_exception(exc_type, exc_value, exc_traceback):
|
||||
if issubclass(exc_type, KeyboardInterrupt):
|
||||
sys.__excepthook__(exc_type, exc_value, exc_traceback)
|
||||
return
|
||||
|
||||
config_data["ACPI"]["Add"].extend(acpi_load.get("Add", []))
|
||||
config_data["ACPI"]["Delete"].extend(acpi_load.get("Delete", []))
|
||||
config_data["ACPI"]["Patch"].extend(acpi_load.get("Patch", []))
|
||||
|
||||
config_data["ACPI"]["Patch"].extend(self.ac.dsdt_patches)
|
||||
config_data["ACPI"]["Patch"] = self.ac.apply_acpi_patches(config_data["ACPI"]["Patch"])
|
||||
|
||||
self.u.progress_bar(title, steps, 2)
|
||||
kexts_directory = os.path.join(self.result_dir, "EFI", "OC", "Kexts")
|
||||
self.k.install_kexts_to_efi(macos_version, kexts_directory)
|
||||
config_data["Kernel"]["Add"] = self.k.load_kexts(hardware_report, macos_version, kexts_directory)
|
||||
|
||||
self.u.progress_bar(title, steps, 3)
|
||||
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)
|
||||
|
||||
self.u.progress_bar(title, steps, 4)
|
||||
files_to_remove = []
|
||||
|
||||
drivers_directory = os.path.join(self.result_dir, "EFI", "OC", "Drivers")
|
||||
driver_list = self.u.find_matching_paths(drivers_directory, extension_filter=".efi")
|
||||
driver_loaded = [kext.get("Path") for kext in config_data.get("UEFI").get("Drivers")]
|
||||
for driver_path, type in driver_list:
|
||||
if not driver_path in driver_loaded:
|
||||
files_to_remove.append(os.path.join(drivers_directory, driver_path))
|
||||
|
||||
resources_audio_dir = os.path.join(self.result_dir, "EFI", "OC", "Resources", "Audio")
|
||||
if os.path.exists(resources_audio_dir):
|
||||
files_to_remove.append(resources_audio_dir)
|
||||
|
||||
picker_variant = config_data.get("Misc", {}).get("Boot", {}).get("PickerVariant")
|
||||
if picker_variant in (None, "Auto"):
|
||||
picker_variant = "Acidanthera/GoldenGate"
|
||||
if os.name == "nt":
|
||||
picker_variant = picker_variant.replace("/", "\\")
|
||||
|
||||
resources_image_dir = os.path.join(self.result_dir, "EFI", "OC", "Resources", "Image")
|
||||
available_picker_variants = self.u.find_matching_paths(resources_image_dir, type_filter="dir")
|
||||
|
||||
for variant_name, variant_type in available_picker_variants:
|
||||
variant_path = os.path.join(resources_image_dir, variant_name)
|
||||
if ".icns" in ", ".join(os.listdir(variant_path)):
|
||||
if picker_variant not in variant_name:
|
||||
files_to_remove.append(variant_path)
|
||||
|
||||
tools_directory = os.path.join(self.result_dir, "EFI", "OC", "Tools")
|
||||
tool_list = self.u.find_matching_paths(tools_directory, extension_filter=".efi")
|
||||
tool_loaded = [tool.get("Path") for tool in config_data.get("Misc").get("Tools")]
|
||||
for tool_path, type in tool_list:
|
||||
if not tool_path in tool_loaded:
|
||||
files_to_remove.append(os.path.join(tools_directory, tool_path))
|
||||
|
||||
if "manifest.json" in os.listdir(self.result_dir):
|
||||
files_to_remove.append(os.path.join(self.result_dir, "manifest.json"))
|
||||
|
||||
for file_path in files_to_remove:
|
||||
error_details = "".join(traceback.format_exception(exc_type, exc_value, exc_traceback))
|
||||
error_message = "Uncaught exception detected:\n{}".format(error_details)
|
||||
|
||||
self.backend.u.log_message(error_message, level="ERROR")
|
||||
|
||||
try:
|
||||
if os.path.isdir(file_path):
|
||||
shutil.rmtree(file_path)
|
||||
else:
|
||||
os.remove(file_path)
|
||||
except Exception as e:
|
||||
print("Failed to remove file: {}".format(e))
|
||||
|
||||
self.u.progress_bar(title, steps, len(steps), done=True)
|
||||
|
||||
print("OpenCore EFI build complete.")
|
||||
time.sleep(2)
|
||||
|
||||
def check_bios_requirements(self, org_hardware_report, hardware_report):
|
||||
requirements = []
|
||||
|
||||
org_firmware_type = org_hardware_report.get("BIOS", {}).get("Firmware Type", "Unknown")
|
||||
firmware_type = hardware_report.get("BIOS", {}).get("Firmware Type", "Unknown")
|
||||
if org_firmware_type == "Legacy" and firmware_type == "UEFI":
|
||||
requirements.append("Enable UEFI mode (disable Legacy/CSM (Compatibility Support Module))")
|
||||
sys.__stderr__.write("\n[CRITICAL ERROR] {}\n".format(error_message))
|
||||
except:
|
||||
pass
|
||||
|
||||
secure_boot = hardware_report.get("BIOS", {}).get("Secure Boot", "Unknown")
|
||||
if secure_boot != "Disabled":
|
||||
requirements.append("Disable Secure Boot")
|
||||
|
||||
if hardware_report.get("Motherboard", {}).get("Platform") == "Desktop" and hardware_report.get("Motherboard", {}).get("Chipset") in chipset_data.IntelChipsets[112:]:
|
||||
resizable_bar_enabled = any(gpu_props.get("Resizable BAR", "Disabled") == "Enabled" for gpu_props in hardware_report.get("GPU", {}).values())
|
||||
if not resizable_bar_enabled:
|
||||
requirements.append("Enable Above 4G Decoding")
|
||||
requirements.append("Disable Resizable BAR/Smart Access Memory")
|
||||
|
||||
return requirements
|
||||
sys.excepthook = handle_exception
|
||||
|
||||
def before_using_efi(self, org_hardware_report, hardware_report):
|
||||
while True:
|
||||
self.u.head("Before Using EFI")
|
||||
print("")
|
||||
print("\033[93mPlease complete the following steps:\033[0m")
|
||||
print("")
|
||||
|
||||
bios_requirements = self.check_bios_requirements(org_hardware_report, hardware_report)
|
||||
if bios_requirements:
|
||||
print("* BIOS/UEFI Settings Required:")
|
||||
for requirement in bios_requirements:
|
||||
print(" - {}".format(requirement))
|
||||
print("")
|
||||
|
||||
print("* USB Mapping:")
|
||||
print(" - Use USBToolBox tool to map USB ports.")
|
||||
print(" - Add created UTBMap.kext into the {} folder.".format("EFI\\OC\\Kexts" if os.name == "nt" else "EFI/OC/Kexts"))
|
||||
print(" - Remove UTBDefault.kext in the {} folder.".format("EFI\\OC\\Kexts" if os.name == "nt" else "EFI/OC/Kexts"))
|
||||
print(" - Edit config.plist:")
|
||||
print(" - Use ProperTree to open your config.plist.")
|
||||
print(" - Run OC Snapshot by pressing Command/Ctrl + R.")
|
||||
print(" - If you have more than 15 ports on a single controller, enable the XhciPortLimit patch.")
|
||||
print(" - Save the file when finished.")
|
||||
print("")
|
||||
print("Type \"AGREE\" to open the built EFI for you\n")
|
||||
response = self.u.request_input("")
|
||||
if response.lower() == "agree":
|
||||
self.u.open_folder(self.result_dir)
|
||||
break
|
||||
else:
|
||||
print("\033[91mInvalid input. Please try again.\033[0m")
|
||||
|
||||
def main(self):
|
||||
hardware_report_path = None
|
||||
native_macos_version = None
|
||||
disabled_devices = None
|
||||
macos_version = None
|
||||
ocl_patched_macos_version = None
|
||||
needs_oclp = False
|
||||
smbios_model = None
|
||||
|
||||
while True:
|
||||
self.u.head()
|
||||
print("")
|
||||
print(" Hardware Report: {}".format(hardware_report_path or 'Not selected'))
|
||||
if hardware_report_path:
|
||||
print("")
|
||||
print(" macOS Version: {}".format(os_data.get_macos_name_by_darwin(macos_version) if macos_version else 'Not selected') + (' (' + macos_version + ')' if macos_version else '') + ('. \033[1;93mRequires OpenCore Legacy Patcher\033[0m' if needs_oclp else ''))
|
||||
print(" SMBIOS: {}".format(smbios_model or 'Not selected'))
|
||||
if disabled_devices:
|
||||
print(" Disabled Devices:")
|
||||
for device, _ in disabled_devices.items():
|
||||
print(" - {}".format(device))
|
||||
print("")
|
||||
|
||||
print("1. Select Hardware Report")
|
||||
print("2. Select macOS Version")
|
||||
print("3. Customize ACPI Patch")
|
||||
print("4. Customize Kexts")
|
||||
print("5. Customize SMBIOS Model")
|
||||
print("6. Build OpenCore EFI")
|
||||
print("")
|
||||
print("Q. Quit")
|
||||
print("")
|
||||
|
||||
option = self.u.request_input("Select an option: ")
|
||||
if option.lower() == "q":
|
||||
self.u.exit_program()
|
||||
|
||||
if option == "1":
|
||||
hardware_report_path, hardware_report = self.select_hardware_report()
|
||||
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(customized_hardware, disabled_devices)
|
||||
needs_oclp = 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)
|
||||
|
||||
if not hardware_report_path:
|
||||
self.u.head()
|
||||
print("\n\n")
|
||||
print("\033[1;93mPlease select a hardware report first.\033[0m")
|
||||
print("\n\n")
|
||||
self.u.request_input("Press Enter to go back...")
|
||||
continue
|
||||
|
||||
if option == "2":
|
||||
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)
|
||||
needs_oclp = 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(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":
|
||||
if needs_oclp and not self.show_oclp_warning():
|
||||
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)
|
||||
needs_oclp = 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)
|
||||
continue
|
||||
|
||||
try:
|
||||
self.o.gather_bootloader_kexts(self.k.kexts, macos_version)
|
||||
except Exception as e:
|
||||
print("\033[91mError: {}\033[0m".format(e))
|
||||
print("")
|
||||
self.u.request_input("Press Enter to continue...")
|
||||
continue
|
||||
|
||||
self.build_opencore_efi(customized_hardware, disabled_devices, smbios_model, macos_version, needs_oclp)
|
||||
self.before_using_efi(hardware_report, customized_hardware)
|
||||
|
||||
self.u.head("Result")
|
||||
print("")
|
||||
print("Your OpenCore EFI for {} has been built at:".format(customized_hardware.get("Motherboard").get("Name")))
|
||||
print("\t{}".format(self.result_dir))
|
||||
print("")
|
||||
self.u.request_input("Press Enter to main menu...")
|
||||
|
||||
if __name__ == '__main__':
|
||||
update_flag = updater.Updater().run_update()
|
||||
if update_flag:
|
||||
os.execv(sys.executable, ['python3'] + sys.argv)
|
||||
|
||||
o = OCPE()
|
||||
while True:
|
||||
try:
|
||||
o.main()
|
||||
except Exception as e:
|
||||
o.u.head("An Error Occurred")
|
||||
print("")
|
||||
print(traceback.format_exc())
|
||||
o.u.request_input()
|
||||
if __name__ == "__main__":
|
||||
backend = Backend()
|
||||
|
||||
app = QApplication(sys.argv)
|
||||
set_default_gui_handler(app)
|
||||
|
||||
window = OCS(backend)
|
||||
window.setup_exception_hook()
|
||||
window.show()
|
||||
|
||||
if backend.settings.get_auto_update_check():
|
||||
updater.Updater(
|
||||
utils_instance=backend.u,
|
||||
github_instance=backend.github,
|
||||
resource_fetcher_instance=backend.resource_fetcher,
|
||||
run_instance=backend.r,
|
||||
integrity_checker_instance=backend.integrity_checker
|
||||
).run_update()
|
||||
|
||||
sys.exit(app.exec())
|
||||
Reference in New Issue
Block a user