#!/usr/bin/env python3 # Copyright (C) 2020-2021, Dhinak G, Mykola Grymalyuk from __future__ import print_function import subprocess, sys, time, platform from Resources import build, ModelArray, Constants, SysPatch, utilities, CliMenu class OpenCoreLegacyPatcher(): def __init__(self): self.constants = Constants.Constants() self.current_model: str = None opencore_model: str = subprocess.run("nvram 4D1FDA02-38C7-4A6A-9CC6-4BCCA8B30102:oem-product".split(), stdout=subprocess.PIPE, stderr=subprocess.STDOUT).stdout.decode() if not opencore_model.startswith("nvram: Error getting variable"): opencore_model = [line.strip().split(":oem-product ", 1)[1] for line in opencore_model.split("\n") if line.strip().startswith("4D1FDA02-38C7-4A6A-9CC6-4BCCA8B30102:")][0] self.current_model = opencore_model else: self.current_model = subprocess.run("system_profiler SPHardwareDataType".split(), stdout=subprocess.PIPE, stderr=subprocess.STDOUT) self.current_model = [line.strip().split(": ", 1)[1] for line in self.current_model.stdout.decode().split("\n") if line.strip().startswith("Model Identifier")][0] self.constants.detected_os = int(platform.uname().release.partition(".")[0]) if self.current_model in ModelArray.NoAPFSsupport: self.constants.serial_settings = "Moderate" def build_opencore(self): build.BuildOpenCore(self.constants.custom_model or self.current_model, self.constants).build_opencore() def install_opencore(self): build.BuildOpenCore(self.constants.custom_model or self.current_model, self.constants).copy_efi() def change_model(self): utilities.cls() utilities.header(["Select Different Model"]) print(""" Tip: Run the following command on the target machine to find the model identifier: system_profiler SPHardwareDataType | grep 'Model Identifier' """) self.constants.custom_model = input("Please enter the model identifier of the target machine: ").strip() if self.constants.custom_model not in ModelArray.SupportedSMBIOS: print(f""" {self.constants.custom_model} is not a valid SMBIOS Identifier for macOS {self.constants.os_support}! """) print_models = input(f"Print list of valid options for macOS {self.constants.os_support}? (y/n)") if print_models in {"y", "Y", "yes", "Yes"}: print("\n".join(ModelArray.SupportedSMBIOS)) input("Press any key to continue...") if self.constants.custom_model in ModelArray.NoAPFSsupport: self.constants.serial_settings = "Moderate" def patcher_settings(self): response = None while not (response and response == -1): title = [ "Adjust Patcher Settings" ] menu = utilities.TUIMenu(title, "Please select an option: ", auto_number=True, top_level=True) options = [ # TODO: Enable setting OS target when more OSes become supported by the patcher #[f"Change OS version:\t\t\tCurrently macOS {self.constants.os_support}", self.change_os], [f"Enable Verbose Mode:\t\tCurrently {self.constants.verbose_debug}", CliMenu.MenuOptions(self.constants.custom_model or self.current_model, self.constants).change_verbose], [f"Enable OpenCore DEBUG:\t\tCurrently {self.constants.opencore_debug}", CliMenu.MenuOptions(self.constants.custom_model or self.current_model, self.constants).change_oc], [f"Enable Kext DEBUG:\t\t\tCurrently {self.constants.kext_debug}", CliMenu.MenuOptions(self.constants.custom_model or self.current_model, self.constants).change_kext], [f"Force iMac Metal Patch:\t\tCurrently {self.constants.imac_vendor}", CliMenu.MenuOptions(self.constants.custom_model or self.current_model, self.constants).change_metal], [f"Assume Upgraded Wifi Always:\tCurrently {self.constants.wifi_build}", CliMenu.MenuOptions(self.constants.custom_model or self.current_model, self.constants).change_wifi], [f"Set ShowPicker Mode:\t\tCurrently {self.constants.showpicker}", CliMenu.MenuOptions(self.constants.custom_model or self.current_model, self.constants).change_showpicker], [f"Set Vault Mode:\t\t\tCurrently {self.constants.vault}", CliMenu.MenuOptions(self.constants.custom_model or self.current_model, self.constants).change_vault], [f"Set SIP and SecureBootModel:\tSIP: {self.constants.sip_status} SBM: {self.constants.secure_status}", CliMenu.MenuOptions(self.constants.custom_model or self.current_model, self.constants).change_sip], [f"Set SMBIOS Mode:\t\t\tCurrently {self.constants.serial_settings}", CliMenu.MenuOptions(self.constants.custom_model or self.current_model, self.constants).change_serial], ] for option in options: menu.add_menu_option(option[0], function=option[1]) response = menu.start() def credits(self): utilities.TUIOnlyPrint(["Credits"], "Press [Enter] to go back.\n", ["""Many thanks to the following: - Acidanthera:\tOpenCore, kexts and other tools - Khronokernel:\tWriting and maintaining this patcher - DhinakG:\t\tWriting and maintaining this patcher - Syncretic:\t\tAAAMouSSE and telemetrap - Slice:\t\tVoodooHDA - cdf:\t\tNightShiftEnabler"""]).start() def PatchVolume(self): utilities.cls() utilities.header(["Patching System Volume"]) print("""Patches Root volume to fix misc issues such as: - Audio (AppleHDA) Patch for 2011 and older (Excluding MacPro4,1+) - Ethernet (AppleBCM5701Ethernet) Patch for certain 2009-2011 Macs Note: When the system volume is patched, you can no longer have Delta updates or have FileVault enabled. Please ensure you have all important user data backed up. Supported Options: 1. Patch System Volume 2. Unpatch System Volume B. Exit """) change_menu = input("Patch System Volume?: ") if change_menu == "1": SysPatch.PatchSysVolume(self.constants.custom_model or self.current_model, self.constants).start_patch() elif change_menu == "2": SysPatch.PatchSysVolume(self.constants.custom_model or self.current_model, self.constants).start_unpatch() else: print("Returning to main menu") def main_menu(self): response = None ModelArray.SupportedSMBIOS = ModelArray.SupportedSMBIOS11 while not (response and response == -1): title = [ f"OpenCore Legacy Patcher v{self.constants.patcher_version}", f"Selected Model: {self.constants.custom_model or self.current_model}", f"Target OS: macOS {self.constants.os_support}", ] if (self.constants.custom_model or self.current_model) not in ModelArray.SupportedSMBIOS: in_between = [ 'Your model is not supported by this patcher!', '', 'If you plan to create the USB for another machine, please select the "Change Model" option in the menu.' ] elif not self.constants.custom_model and self.current_model == "iMac7,1" and \ "SSE4.1" not in subprocess.run("sysctl machdep.cpu.features".split(), stdout=subprocess.PIPE, stderr=subprocess.STDOUT).stdout.decode(): in_between = [ 'Your model requires a CPU upgrade to a CPU supporting SSE4.1+ to be supported by this patcher!', '', f'If you plan to create the USB for another {self.current_model} with SSE4.1+, please select the "Change Model" option in the menu.' ] elif self.constants.custom_model == "iMac7,1": in_between = ["This model is supported", "However please ensure the CPU has been upgraded to support SSE4.1+" ] else: in_between = ["This model is supported"] menu = utilities.TUIMenu(title, "Please select an option: ", in_between=in_between, auto_number=True, top_level=True) options = ( [["Build OpenCore", self.build_opencore]] if ((self.constants.custom_model or self.current_model) in ModelArray.SupportedSMBIOS) else []) + [ ["Install OpenCore to USB/internal drive", self.install_opencore], ["Post-Install Volume Patch", self.PatchVolume], ["Change Model", self.change_model], ["Patcher Settings", self.patcher_settings], ["Credits", self.credits] ] for option in options: menu.add_menu_option(option[0], function=option[1]) response = menu.start() if getattr(sys, "frozen", False): subprocess.run("""osascript -e 'tell application "Terminal" to close first window' & exit""", shell=True) OpenCoreLegacyPatcher().main_menu()