mirror of
https://github.com/dortania/OpenCore-Legacy-Patcher.git
synced 2026-04-14 12:48:18 +10:00
Merge branch 'main' into colorsync-downgrade-toggle
This commit is contained in:
@@ -12,8 +12,8 @@ from data import os_data
|
||||
class Constants:
|
||||
def __init__(self) -> None:
|
||||
# Patcher Versioning
|
||||
self.patcher_version: str = "0.6.5" # OpenCore-Legacy-Patcher
|
||||
self.patcher_support_pkg_version: str = "0.9.6" # PatcherSupportPkg
|
||||
self.patcher_version: str = "0.6.6" # OpenCore-Legacy-Patcher
|
||||
self.patcher_support_pkg_version: str = "0.9.7" # PatcherSupportPkg
|
||||
self.copyright_date: str = "Copyright © 2020-2023 Dortania"
|
||||
|
||||
# URLs
|
||||
@@ -239,6 +239,10 @@ class Constants:
|
||||
def auto_patch_launch_agent_path(self):
|
||||
return self.payload_path / Path("com.dortania.opencore-legacy-patcher.auto-patch.plist")
|
||||
|
||||
@property
|
||||
def rsr_monitor_launch_daemon_path(self):
|
||||
return self.payload_path / Path("com.dortania.opencore-legacy-patcher.rsr-monitor.plist")
|
||||
|
||||
# ACPI
|
||||
@property
|
||||
def pci_ssdt_path(self):
|
||||
|
||||
@@ -1782,7 +1782,7 @@ class wx_python_gui:
|
||||
if model in ["MacPro3,1", "MacPro4,1", "MacPro5,1"]:
|
||||
has_legacy_usb = True
|
||||
issues_list = "- Lack of Keyboard/Mouse in macOS installer without a USB hub\n"
|
||||
elif model in smbios_data.smbios_dictionary[model]:
|
||||
elif model in smbios_data.smbios_dictionary:
|
||||
if "CPU Generation" in smbios_data.smbios_dictionary[model]:
|
||||
if smbios_data.smbios_dictionary[model]["CPU Generation"] <= cpu_data.cpu_data.penryn:
|
||||
has_legacy_usb = True
|
||||
@@ -2060,7 +2060,17 @@ class wx_python_gui:
|
||||
logging.info("Installer(s) found:")
|
||||
for app in available_installers:
|
||||
logging.info(f"- {available_installers[app]['Short Name']}: {available_installers[app]['Version']} ({available_installers[app]['Build']})")
|
||||
self.install_selection = wx.Button(self.frame, label=f"{available_installers[app]['Short Name']}: {available_installers[app]['Version']} ({available_installers[app]['Build']})", size=(320, 30))
|
||||
|
||||
app_str = f"{available_installers[app]['Short Name']}"
|
||||
unsupported: bool = available_installers[app]['Minimum Host OS'] > self.constants.detected_os
|
||||
|
||||
if unsupported:
|
||||
min_str = os_data.os_conversion.convert_kernel_to_marketing_name(available_installers[app]['Minimum Host OS'])
|
||||
app_str += f" (Requires {min_str})"
|
||||
else:
|
||||
app_str += f": {available_installers[app]['Version']} ({available_installers[app]['Build']})"
|
||||
|
||||
self.install_selection = wx.Button(self.frame, label=app_str, size=(320, 30))
|
||||
i = i + 25
|
||||
self.install_selection.SetPosition(
|
||||
wx.Point(
|
||||
@@ -2070,6 +2080,9 @@ class wx_python_gui:
|
||||
)
|
||||
self.install_selection.Bind(wx.EVT_BUTTON, lambda event, temp=app: self.format_usb_menu(available_installers[temp]['Short Name'], available_installers[temp]['Path']))
|
||||
self.install_selection.Centre(wx.HORIZONTAL)
|
||||
|
||||
if unsupported:
|
||||
self.install_selection.Disable()
|
||||
else:
|
||||
logging.info("No installers found")
|
||||
# Label: No Installers Found
|
||||
@@ -2099,6 +2112,7 @@ class wx_python_gui:
|
||||
def format_usb_menu(self, installer_name, installer_path):
|
||||
self.frame.DestroyChildren()
|
||||
logging.info(installer_path)
|
||||
self.frame.SetSize(370, -1)
|
||||
|
||||
# Header
|
||||
self.header = wx.StaticText(self.frame, label="Format USB")
|
||||
@@ -2171,7 +2185,7 @@ class wx_python_gui:
|
||||
|
||||
def format_usb_progress(self, disk, installer_name, installer_path):
|
||||
self.frame.DestroyChildren()
|
||||
self.frame.SetSize(500, -1)
|
||||
self.frame.SetSize(520, -1)
|
||||
# Header
|
||||
self.header = wx.StaticText(self.frame, label=f"Creating Installer: {installer_name}")
|
||||
self.header.SetFont(wx.Font(19, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False, ".AppleSystemUIFont"))
|
||||
|
||||
@@ -13,10 +13,24 @@ from resources import network_handler, utilities
|
||||
|
||||
APPLICATION_SEARCH_PATH: str = "/Applications"
|
||||
SFR_SOFTWARE_UPDATE_PATH: str = "SFR/com_apple_MobileAsset_SFRSoftwareUpdate/com_apple_MobileAsset_SFRSoftwareUpdate.xml"
|
||||
|
||||
CATALOG_URL_BASE: str = "https://swscan.apple.com/content/catalogs/others/index"
|
||||
CATALOG_URL_EXTENSION: str = "13-12-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog"
|
||||
CATALOG_URL_VERSION: str = "13"
|
||||
CATALOG_URL_BASE: str = "https://swscan.apple.com/content/catalogs/others/index"
|
||||
CATALOG_URL_EXTENSION: str = ".merged-1.sucatalog"
|
||||
CATALOG_URL_VARIANTS: list = [
|
||||
"13",
|
||||
"12",
|
||||
"10.16",
|
||||
"10.15",
|
||||
"10.14",
|
||||
"10.13",
|
||||
"10.12",
|
||||
"10.11",
|
||||
"10.10",
|
||||
"10.9",
|
||||
"mountainlion",
|
||||
"lion",
|
||||
"snowleopard",
|
||||
"leopard",
|
||||
]
|
||||
|
||||
tmp_dir = tempfile.TemporaryDirectory()
|
||||
|
||||
@@ -222,15 +236,15 @@ class RemoteInstallerCatalog:
|
||||
Parses Apple's Software Update catalog and finds all macOS installers.
|
||||
"""
|
||||
|
||||
def __init__(self, seed_override: SeedType = SeedType.PublicRelease) -> None:
|
||||
def __init__(self, seed_override: SeedType = SeedType.PublicRelease, os_override: int = os_data.os_data.ventura) -> None:
|
||||
|
||||
self.catalog_url: str = self._construct_catalog_url(seed_override)
|
||||
self.catalog_url: str = self._construct_catalog_url(seed_override, os_override)
|
||||
|
||||
self.available_apps: dict = self._parse_catalog()
|
||||
self.available_apps_latest: dict = self._list_newest_installers_only()
|
||||
|
||||
|
||||
def _construct_catalog_url(self, seed_type: SeedType) -> str:
|
||||
def _construct_catalog_url(self, seed_type: SeedType, os_kernel: int) -> str:
|
||||
"""
|
||||
Constructs the catalog URL based on the seed type
|
||||
|
||||
@@ -241,17 +255,30 @@ class RemoteInstallerCatalog:
|
||||
str: The catalog URL
|
||||
"""
|
||||
|
||||
url: str = CATALOG_URL_BASE
|
||||
|
||||
url: str = ""
|
||||
os_version: str = os_data.os_conversion.kernel_to_os(os_kernel)
|
||||
os_version = "10.16" if os_version == "11" else os_version
|
||||
if os_version not in CATALOG_URL_VARIANTS:
|
||||
logging.error(f"OS version {os_version} is not supported, defaulting to latest")
|
||||
os_version = CATALOG_URL_VARIANTS[0]
|
||||
|
||||
url += f"-{os_version}"
|
||||
if seed_type == SeedType.DeveloperSeed:
|
||||
url = f"{CATALOG_URL_BASE}-{CATALOG_URL_VERSION}seed-{CATALOG_URL_EXTENSION}"
|
||||
url += f"seed"
|
||||
elif seed_type == SeedType.PublicSeed:
|
||||
url = f"{CATALOG_URL_BASE}-{CATALOG_URL_VERSION}beta-{CATALOG_URL_EXTENSION}"
|
||||
url += f"beta"
|
||||
elif seed_type == SeedType.CustomerSeed:
|
||||
url = f"{CATALOG_URL_BASE}-{CATALOG_URL_VERSION}customerseed-{CATALOG_URL_EXTENSION}"
|
||||
else:
|
||||
url = f"{CATALOG_URL_BASE}-{CATALOG_URL_EXTENSION}"
|
||||
url += f"customerseed"
|
||||
|
||||
did_find_variant: bool = False
|
||||
for variant in CATALOG_URL_VARIANTS:
|
||||
if variant in url:
|
||||
did_find_variant = True
|
||||
if did_find_variant:
|
||||
url += f"-{variant}"
|
||||
|
||||
url += f"{CATALOG_URL_EXTENSION}"
|
||||
|
||||
return url
|
||||
|
||||
@@ -356,13 +383,10 @@ class RemoteInstallerCatalog:
|
||||
continue
|
||||
if "IntegrityDataURL" not in ia_package:
|
||||
continue
|
||||
if "Size" not in ia_package:
|
||||
size = 0
|
||||
|
||||
download_link = ia_package["URL"]
|
||||
integrity = ia_package["IntegrityDataURL"]
|
||||
size = ia_package["Size"]
|
||||
|
||||
size = ia_package["Size"] if ia_package["Size"] else 0
|
||||
|
||||
if any([version, build, download_link, size, integrity]) is None:
|
||||
continue
|
||||
@@ -518,28 +542,34 @@ class LocalInstallerCatalog:
|
||||
if "CFBundleDisplayName" not in application_info_plist:
|
||||
continue
|
||||
|
||||
app_version = application_info_plist["DTPlatformVersion"]
|
||||
clean_name = application_info_plist["CFBundleDisplayName"]
|
||||
app_version: str = application_info_plist["DTPlatformVersion"]
|
||||
clean_name: str = application_info_plist["CFBundleDisplayName"]
|
||||
app_sdk: str = application_info_plist["DTSDKBuild"] if "DTSDKBuild" in application_info_plist else "Unknown"
|
||||
min_required: str = application_info_plist["LSMinimumSystemVersion"] if "LSMinimumSystemVersion" in application_info_plist else "Unknown"
|
||||
|
||||
if "DTSDKBuild" in application_info_plist:
|
||||
app_sdk = application_info_plist["DTSDKBuild"]
|
||||
else:
|
||||
app_sdk = "Unknown"
|
||||
kernel: int = 0
|
||||
try:
|
||||
kernel = int(app_sdk[:2])
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
min_required = os_data.os_conversion.os_to_kernel(min_required) if min_required != "Unknown" else 0
|
||||
|
||||
if min_required == os_data.os_data.sierra and kernel == os_data.os_data.ventura:
|
||||
# Ventura's installer requires El Capitan minimum
|
||||
# Ref: https://github.com/dortania/OpenCore-Legacy-Patcher/discussions/1038
|
||||
min_required = os_data.os_data.el_capitan
|
||||
|
||||
# app_version can sometimes report GM instead of the actual version
|
||||
# This is a workaround to get the actual version
|
||||
if app_version.startswith("GM"):
|
||||
try:
|
||||
app_version = int(app_sdk[:2])
|
||||
if app_version < 20:
|
||||
app_version = f"10.{app_version - 4}"
|
||||
else:
|
||||
app_version = f"{app_version - 9}.0"
|
||||
except ValueError:
|
||||
if kernel == 0:
|
||||
app_version = "Unknown"
|
||||
else:
|
||||
app_version = os_data.os_conversion.kernel_to_os(kernel)
|
||||
|
||||
# Check if App Version is High Sierra or newer
|
||||
if os_data.os_conversion.os_to_kernel(app_version) < os_data.os_data.high_sierra:
|
||||
if kernel < os_data.os_data.high_sierra:
|
||||
continue
|
||||
|
||||
results = self._parse_sharedsupport_version(Path(APPLICATION_SEARCH_PATH) / Path(application)/ Path("Contents/SharedSupport/SharedSupport.dmg"))
|
||||
@@ -554,6 +584,7 @@ class LocalInstallerCatalog:
|
||||
"Version": app_version,
|
||||
"Build": app_sdk,
|
||||
"Path": application,
|
||||
"Minimum Host OS": min_required,
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -312,9 +312,71 @@ class AutomaticSysPatch:
|
||||
utilities.process_status(utilities.elevated(["chmod", "644", "/Library/LaunchAgents/com.dortania.opencore-legacy-patcher.auto-patch.plist"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
|
||||
utilities.process_status(utilities.elevated(["chown", "root:wheel", "/Library/LaunchAgents/com.dortania.opencore-legacy-patcher.auto-patch.plist"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
|
||||
|
||||
# Copy over our launch daemon
|
||||
if self._create_rsr_monitor_daemon() is True:
|
||||
logging.info("- Copying rsr-monitor.plist Launch Daemon to /Library/LaunchDaemons/")
|
||||
if Path("/Library/LaunchDaemons/com.dortania.opencore-legacy-patcher.rsr-monitor.plist").exists():
|
||||
logging.info("- Deleting existing rsr-monitor.plist")
|
||||
utilities.process_status(utilities.elevated(["rm", "/Library/LaunchDaemons/com.dortania.opencore-legacy-patcher.rsr-monitor.plist"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
|
||||
if not Path("/Library/LaunchDaemons/").exists():
|
||||
logging.info("- Creating /Library/LaunchDaemons/")
|
||||
utilities.process_status(utilities.elevated(["mkdir", "-p", "/Library/LaunchDaemons/"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
|
||||
utilities.process_status(utilities.elevated(["cp", self.constants.rsr_monitor_launch_daemon_path, "/Library/LaunchDaemons/"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
|
||||
|
||||
# Set the permissions on the com.dortania.opencore-legacy-patcher.rsr-monitor.plist
|
||||
logging.info("- Setting permissions on rsr-monitor.plist")
|
||||
utilities.process_status(utilities.elevated(["chmod", "644", "/Library/LaunchDaemons/com.dortania.opencore-legacy-patcher.rsr-monitor.plist"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
|
||||
utilities.process_status(utilities.elevated(["chown", "root:wheel", "/Library/LaunchDaemons/com.dortania.opencore-legacy-patcher.rsr-monitor.plist"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
|
||||
|
||||
# Making app alias
|
||||
# Simply an easy way for users to notice the app
|
||||
# If there's already an alias or exiting app, skip
|
||||
if not Path("/Applications/OpenCore-Patcher.app").exists():
|
||||
logging.info("- Making app alias")
|
||||
utilities.process_status(utilities.elevated(["ln", "-s", "/Library/Application Support/Dortania/OpenCore-Patcher.app", "/Applications/OpenCore-Patcher.app"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
|
||||
utilities.process_status(utilities.elevated(["ln", "-s", "/Library/Application Support/Dortania/OpenCore-Patcher.app", "/Applications/OpenCore-Patcher.app"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
|
||||
|
||||
|
||||
def _create_rsr_monitor_daemon(self) -> bool:
|
||||
# Get kext list in /Library/Extensions that have the 'GPUCompanionBundles' property
|
||||
# This is used to determine if we need to run the RSRMonitor
|
||||
logging.info("- Checking if RSRMonitor is needed")
|
||||
|
||||
cryptex_path = f"/System/Volumes/Preboot/{utilities.get_preboot_uuid()}/cryptex1/current/OS.dmg"
|
||||
if not Path(cryptex_path).exists():
|
||||
logging.info("- No OS.dmg, skipping RSRMonitor")
|
||||
return False
|
||||
|
||||
kexts = []
|
||||
for kext in Path("/Library/Extensions").glob("*.kext"):
|
||||
if not Path(f"{kext}/Contents/Info.plist").exists():
|
||||
continue
|
||||
kext_plist = plistlib.load(open(f"{kext}/Contents/Info.plist", "rb"))
|
||||
if "GPUCompanionBundles" not in kext_plist:
|
||||
continue
|
||||
logging.info(f" - Found kext with GPUCompanionBundles: {kext.name}")
|
||||
kexts.append(kext.name)
|
||||
|
||||
# If we have no kexts, we don't need to run the RSRMonitor
|
||||
if not kexts:
|
||||
logging.info("- No kexts found with GPUCompanionBundles, skipping RSRMonitor")
|
||||
return False
|
||||
|
||||
# Load the RSRMonitor plist
|
||||
rsr_monitor_plist = plistlib.load(open(self.constants.rsr_monitor_launch_daemon_path, "rb"))
|
||||
|
||||
arguments = ["rm", "-Rfv"]
|
||||
arguments += [f"/Library/Extensions/{kext}" for kext in kexts]
|
||||
|
||||
# Add the arguments to the RSRMonitor plist
|
||||
rsr_monitor_plist["ProgramArguments"] = arguments
|
||||
|
||||
# Next add monitoring for '/System/Volumes/Preboot/{UUID}/cryptex1/OS.dmg'
|
||||
logging.info(f" - Adding monitor: {cryptex_path}")
|
||||
rsr_monitor_plist["WatchPaths"] = [
|
||||
cryptex_path,
|
||||
]
|
||||
|
||||
# Write the RSRMonitor plist
|
||||
plistlib.dump(rsr_monitor_plist, Path(self.constants.rsr_monitor_launch_daemon_path).open("wb"))
|
||||
|
||||
return True
|
||||
|
||||
@@ -451,6 +451,15 @@ def monitor_disk_output(disk):
|
||||
return output
|
||||
|
||||
|
||||
def get_preboot_uuid() -> str:
|
||||
"""
|
||||
Get the UUID of the Preboot volume
|
||||
"""
|
||||
args = ["ioreg", "-a", "-n", "chosen", "-p", "IODeviceTree", "-r"]
|
||||
output = plistlib.loads(subprocess.run(args, stdout=subprocess.PIPE).stdout)
|
||||
return output[0]["apfs-preboot-uuid"].strip(b"\0").decode()
|
||||
|
||||
|
||||
def block_os_updaters():
|
||||
# Disables any processes that would be likely to mess with
|
||||
# the root volume while we're working with it.
|
||||
|
||||
Reference in New Issue
Block a user