From 4929715830697364c1dd927b8129fb7fed2b21ea Mon Sep 17 00:00:00 2001 From: Mykola Grymalyuk Date: Tue, 2 Nov 2021 18:32:10 -0600 Subject: [PATCH 1/7] macOS Installer Creation: initial commit --- OpenCore-Patcher.command | 66 ++++++++++++--- resources/build.py | 2 +- resources/cli_menu.py | 93 ++++++++++++++++++++- resources/install.py | 36 ++++---- resources/installer.py | 174 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 337 insertions(+), 34 deletions(-) create mode 100644 resources/installer.py diff --git a/OpenCore-Patcher.command b/OpenCore-Patcher.command index fe892f4e5..d5f3758d3 100755 --- a/OpenCore-Patcher.command +++ b/OpenCore-Patcher.command @@ -17,7 +17,7 @@ class OpenCoreLegacyPatcher: self.constants = constants.Constants() self.generate_base_data() if utilities.check_cli_args() is None: - self.main_menu() + self.main_menu(True) def generate_base_data(self): self.constants.detected_os = os_probe.detect_kernel_major() @@ -37,8 +37,34 @@ class OpenCoreLegacyPatcher: arguments.arguments().parse_arguments(self.constants) else: print("- No arguments present, loading TUI") + + def first_setup(self): + # Order of operations: + # 1. Build OpenCore + # 2. Download macOS + # 3. Select USB drive + # 4. Format USB drive + # 5. Install OpenCore to ESP + # 6. flash macOS + # 7. Prompt user to reboot + build.BuildOpenCore(self.constants.custom_model or self.constants.computer.real_model, self.constants).build_opencore() + cli_menu.MenuOptions(self.constants.custom_model or self.computer.real_model, self.constants).download_macOS() + utilities.cls() - def main_menu(self): + sys.exit(0) + + + def post_install(self): + # Order of operations: + # 1. Build OpenCore + # 2. Prompt drive to install OC to + # 3. Install OpenCore to ESP + # 4. Determine whether root patching needed + # 5. Prompt user to reboot + print() + + + def main_menu(self, walkthrough): response = None while not (response and response == -1): title = [ @@ -67,17 +93,31 @@ class OpenCoreLegacyPatcher: menu = utilities.TUIMenu(title, "Please select an option: ", in_between=in_between, auto_number=True, top_level=True) - options = ( - [["Build OpenCore", build.BuildOpenCore(self.constants.custom_model or self.constants.computer.real_model, self.constants).build_opencore]] - if ((self.constants.custom_model or self.computer.real_model) in model_array.SupportedSMBIOS) or self.constants.allow_oc_everywhere is True - else [] - ) + [ - ["Install OpenCore to USB/internal drive", install.tui_disk_installation(self.constants).copy_efi], - ["Post-Install Volume Patch", cli_menu.MenuOptions(self.constants.custom_model or self.computer.real_model, self.constants).PatchVolume], - ["Change Model", cli_menu.MenuOptions(self.constants.custom_model or self.computer.real_model, self.constants).change_model], - ["Patcher Settings", cli_menu.MenuOptions(self.constants.custom_model or self.computer.real_model, self.constants).patcher_settings], - ["Credits", cli_menu.MenuOptions(self.constants.custom_model or self.computer.real_model, self.constants).credits], - ] + if walkthrough is True: + options = ( + [["First Time Setup", self.first_setup]] + if ((self.constants.custom_model or self.computer.real_model) in model_array.SupportedSMBIOS) or self.constants.allow_oc_everywhere is True + else [] + ) + [ + #["Post-Installation setup", cli_menu.MenuOptions(self.constants.custom_model or self.computer.real_model, self.constants).closing_message], + ["Change Model", cli_menu.MenuOptions(self.constants.custom_model or self.computer.real_model, self.constants).change_model], + ["Patcher Settings", cli_menu.MenuOptions(self.constants.custom_model or self.computer.real_model, self.constants).patcher_settings], + ["Credits", cli_menu.MenuOptions(self.constants.custom_model or self.computer.real_model, self.constants).credits], + ["Legacy Menu", lambda: self.main_menu(False)], + ] + else: + options = ( + [["Build OpenCore", build.BuildOpenCore(self.constants.custom_model or self.constants.computer.real_model, self.constants).build_opencore]] + if ((self.constants.custom_model or self.computer.real_model) in model_array.SupportedSMBIOS) or self.constants.allow_oc_everywhere is True + else [] + ) + [ + ["Install OpenCore to USB/internal drive", install.tui_disk_installation(self.constants).copy_efi], + ["Post-Install Volume Patch", cli_menu.MenuOptions(self.constants.custom_model or self.computer.real_model, self.constants).PatchVolume], + ["Change Model", cli_menu.MenuOptions(self.constants.custom_model or self.computer.real_model, self.constants).change_model], + ["Patcher Settings", cli_menu.MenuOptions(self.constants.custom_model or self.computer.real_model, self.constants).patcher_settings], + ["Installer Creation", cli_menu.MenuOptions(self.constants.custom_model or self.computer.real_model, self.constants).download_macOS], + ["Credits", cli_menu.MenuOptions(self.constants.custom_model or self.computer.real_model, self.constants).credits], + ] for option in options: menu.add_menu_option(option[0], function=option[1]) diff --git a/resources/build.py b/resources/build.py index 62d6dc5e8..72e3f37ab 100644 --- a/resources/build.py +++ b/resources/build.py @@ -1074,4 +1074,4 @@ class BuildOpenCore: print(f" {self.constants.opencore_release_folder}") print("") if self.constants.gui_mode is False: - input("Press [Enter] to go back.\n") + input("Press [Enter] to continue\n") diff --git a/resources/cli_menu.py b/resources/cli_menu.py index 2909ed233..2307b9685 100644 --- a/resources/cli_menu.py +++ b/resources/cli_menu.py @@ -1,9 +1,8 @@ # Handle misc CLI menu options # Copyright (C) 2020-2021, Dhinak G, Mykola Grymalyuk from __future__ import print_function -import subprocess -from resources import constants, utilities, defaults, sys_patch +from resources import constants, install, utilities, defaults, sys_patch, installer from data import cpu_data, smbios_data, model_array, os_data @@ -1038,6 +1037,96 @@ system_profiler SPHardwareDataType | grep 'Model Identifier' menu.add_menu_option(option[0], function=option[1]) response = menu.start() + + def download_macOS(self): + utilities.cls() + utilities.header(["Create macOS installer"]) + print( + """ +This option allows you to download and flash a macOS installer +to your USB drive. + +1. Download macOS Installer +2. Use Existing Installer +""" + ) + change_menu = input("Select an option: ") + if change_menu == "1": + self.download_macOS_installer() + elif change_menu == "2": + self.find_local_installer() + else: + self.download_macOS() + + def download_install_assistant(self, link): + installer.download_install_assistant(self.constants.payload_path, link) + # To avoid selecting the wrong installer by mistake, let user select the correct one + self.find_local_installer() + + + def download_macOS_installer(self): + response = None + while not (response and response == -1): + options = [] + title = ["Select the macOS Installer you wish to download"] + menu = utilities.TUIMenu(title, "Please select an option: ", auto_number=True, top_level=True) + avalible_installers = installer.list_downloadable_macOS_installers(self.constants.payload_path, "DeveloperSeed") + if avalible_installers: + for app in avalible_installers: + options.append([f"macOS {avalible_installers[app]['Version']} ({avalible_installers[app]['Build']} - {utilities.human_fmt(avalible_installers[app]['Size'])})", lambda x=app: self.download_install_assistant(avalible_installers[x]['Link'])]) + for option in options: + menu.add_menu_option(option[0], function=option[1]) + response = menu.start() + + def find_local_installer(self): + response = None + while not (response and response == -1): + options = [] + title = ["Select the macOS Installer you wish to use"] + menu = utilities.TUIMenu(title, "Please select an option: ", auto_number=True, top_level=True) + avalible_installers = installer.list_local_macOS_installers() + if avalible_installers: + for app in avalible_installers: + options.append([f"{avalible_installers[app]['Short Name']}: {avalible_installers[app]['Version']} ({avalible_installers[app]['Build']})", lambda: self.list_disks(avalible_installers[app]['Path'])]) + for option in options: + menu.add_menu_option(option[0], function=option[1]) + response = menu.start() + + def list_disks(self, installer_path): + disk = installer.select_disk_to_format() + if disk != None: + if installer.format_drive(disk) is True: + # Only install if OC is found + # Allows a user to create a macOS Installer without OCLP if desired + if self.constants.opencore_release_folder.exists(): + # ESP will always be the first partition when formatted by disk utility + install.tui_disk_installation.install_opencore(self, f"disk{disk}", "1") + if installer.create_installer(installer_path, "OCLP-Installer") is True: + utilities.cls() + utilities.header(["Create macOS installer"]) + print("Installer created successfully.") + input("Press enter to exit.") + self.closing_message() + else: + utilities.cls() + utilities.header(["Create macOS installer"]) + print("Installer creation failed.") + input("Press enter to return to the previous.") + return + else: + exit() + + def closing_message(self): + utilities.cls() + utilities.header(["Create macOS installer"]) + print("Thank you for using OpenCore Legacy Patcher!") + print("Reboot your machine and select EFI Boot to load OpenCore") + print("") + print("If you have any issues, remember to check the guide as well as\nour Discord server:") + print("\n\tGuide: https://dortania.github.io/OpenCore-Legacy-Patcher/") + print("\tDiscord: https://discord.gg/rqdPgH8xSN") + input("\nPress enter to exit: ") + exit() big_sur = """Patches Root volume to fix misc issues such as: diff --git a/resources/install.py b/resources/install.py index 832b9e9d4..1f7c0a4c5 100644 --- a/resources/install.py +++ b/resources/install.py @@ -12,20 +12,6 @@ from data import os_data class tui_disk_installation: def __init__(self, versions): self.constants: constants.Constants = versions - - def determine_sd_card(self, media_name): - # Array filled with common SD Card names - # Note most USB-based SD Card readers generally report as "Storage Device" - # Thus no reliable way to detect further without parsing IOService output (kUSBProductString) - if ( - "SD Card" in media_name or \ - "SD/MMC" in media_name or \ - "SDXC Reader" in media_name or \ - "SD Reader" in media_name or \ - "Card Reader" in media_name - ): - return True - return False def copy_efi(self): utilities.cls() @@ -108,8 +94,24 @@ Please build OpenCore first!""" response = menu.start() if response == -1: - return + return + self.install_opencore(disk_identifier, response) + def install_opencore(self, disk_identifier, response): + def determine_sd_card(media_name): + # Array filled with common SD Card names + # Note most USB-based SD Card readers generally report as "Storage Device" + # Thus no reliable way to detect further without parsing IOService output (kUSBProductString) + if ( + "SD Card" in media_name or \ + "SD/MMC" in media_name or \ + "SDXC Reader" in media_name or \ + "SD Reader" in media_name or \ + "Card Reader" in media_name + ): + return True + return False + # TODO: Apple Script fails in Yosemite(?) and older args = [ "osascript", @@ -134,8 +136,6 @@ Please build OpenCore first!""" ["Copying OpenCore"], "Press [Enter] to go back.\n", ["An error occurred!"] + result.stderr.decode().split("\n") + ["", "Please report this to the devs at GitHub."] ).start() return - - # TODO: Remount if readonly drive_host_info = plistlib.loads(subprocess.run(f"diskutil info -plist {disk_identifier}".split(), stdout=subprocess.PIPE).stdout.decode().strip().encode()) partition_info = plistlib.loads(subprocess.run(f"diskutil info -plist {disk_identifier}s{response}".split(), stdout=subprocess.PIPE).stdout.decode().strip().encode()) sd_type = drive_host_info["MediaName"] @@ -178,7 +178,7 @@ Please build OpenCore first!""" Path(mount_path / Path("EFI/BOOT")).mkdir() shutil.move(mount_path / Path("System/Library/CoreServices/boot.efi"), mount_path / Path("EFI/BOOT/BOOTx64.efi")) shutil.rmtree(mount_path / Path("System"), onerror=rmtree_handler) - if self.determine_sd_card(sd_type) is True: + if determine_sd_card(sd_type) is True: print("- Adding SD Card icon") shutil.copy(self.constants.icon_path_sd, mount_path) elif ssd_type is True: diff --git a/resources/installer.py b/resources/installer.py new file mode 100644 index 000000000..8aceac19b --- /dev/null +++ b/resources/installer.py @@ -0,0 +1,174 @@ +# Creates a macOS Installer +from pathlib import Path +import plistlib +import subprocess +from resources import utilities + +def list_local_macOS_installers(): + # Finds all applicable macOS installers + # within a user's /Applications folder + # Returns a list of installers + application_list = {} + + for application in Path("/Applications").iterdir(): + # Verify whether application has createinstallmedia + if (Path("/Applications") / Path(application) / Path("Contents/Resources/createinstallmedia")).exists(): + plist = plistlib.load((Path("/Applications") / Path(application) / Path("Contents/Info.plist")).open("rb")) + try: + # Doesn't reflect true OS build, but best to report SDK in the event multiple installers are found with same version + app_version = plist["DTPlatformVersion"] + clean_name = plist["CFBundleDisplayName"] + try: + app_sdk = plist["DTSDKBuild"] + except KeyError: + app_sdk = "Unknown" + application_list.update({ + application: { + "Short Name": clean_name, + "Version": app_version, + "Build": app_sdk, + "Path": application, + } + }) + except KeyError: + pass + return application_list + +def create_installer(installer_path, volume_name): + # Creates a macOS installer + # Takes a path to the installer and the Volume + # Returns boolean on success status + + createinstallmedia_path = Path("/Applications") / Path(installer_path) / Path("Contents/Resources/createinstallmedia") + + # Sanity check in the event the user somehow deleted it between the time we found it and now + if (createinstallmedia_path).exists(): + utilities.cls() + utilities.header(["Starting createinstallmedia"]) + print("This will take some time, recommend making some coffee while you wait\n") + utilities.elevated([createinstallmedia_path, "--volume", f"/Volumes/{volume_name}", "--nointeraction"]) + return True + else: + print("- Failed to find createinstallmedia") + return False + +def download_install_assistant(download_path, ia_link): + # Downloads and unpackages InstallAssistant.pkg into /Applications + utilities.download_file(ia_link, (Path(download_path) / Path("InstallAssistant.pkg"))) + print("- Installing InstallAssistant.pkg to /Applications/") + utilities.elevated(["installer", "-pkg", (Path(download_path) / Path("InstallAssistant.pkg")), "-target", "/Applications"]) + input("- Press ENTER to continue: ") + +def list_downloadable_macOS_installers(download_path, catalog): + avalible_apps = {} + if catalog == "DeveloperSeed": + link = "https://swscan.apple.com/content/catalogs/others/index-12seed-12-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog.gz" + elif catalog == "PublicSeed": + link = "https://swscan.apple.com/content/catalogs/others/index-12beta-12-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog.gz" + else: + link = "https://swscan.apple.com/content/catalogs/others/index-12customerseed-12-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog.gz" + + # Download and unzip the catalog + utilities.download_file(link, (Path(download_path) / Path("seed.sucatalog.gz"))) + subprocess.run(["gunzip", "-d", "-f", Path(download_path) / Path("seed.sucatalog.gz")]) + catalog_plist = plistlib.load((Path(download_path) / Path("seed.sucatalog")).open("rb")) + + for item in catalog_plist["Products"]: + try: + # Check if entry has SharedSupport and BuildManifest + # Ensures only Big Sur and newer Installers are listed + catalog_plist["Products"][item]["ExtendedMetaInfo"]["InstallAssistantPackageIdentifiers"]["SharedSupport"] + catalog_plist["Products"][item]["ExtendedMetaInfo"]["InstallAssistantPackageIdentifiers"]["BuildManifest"] + + for bm_package in catalog_plist["Products"][item]["Packages"]: + if "BuildManifest.plist" in bm_package["URL"]: + utilities.download_file(bm_package["URL"], (Path(download_path) / Path("BuildManifest.plist"))) + build_plist = plistlib.load((Path(download_path) / Path("BuildManifest.plist")).open("rb")) + version = build_plist["ProductVersion"] + build = build_plist["ProductBuildVersion"] + for ia_package in catalog_plist["Products"][item]["Packages"]: + if "InstallAssistant.pkg" in ia_package["URL"]: + download_link = ia_package["URL"] + size = ia_package["Size"] + integirty = ia_package["IntegrityDataURL"] + + avalible_apps.update({ + item: { + "Version": version, + "Build": build, + "Link": download_link, + "Size": size, + "integirty": integirty, + } + }) + except KeyError: + pass + return avalible_apps + +def format_drive(disk_id): + # Formats a disk for macOS install + # Takes a disk ID + # Returns boolean on success status + header = f"# Formatting disk{disk_id} for macOS installer #" + box_length = len(header) + utilities.cls() + print("#" * box_length) + print(header) + print("#" * box_length) + print("") + #print(f"- Formatting disk{disk_id} for macOS installer") + format_process = utilities.elevated(["diskutil", "eraseDisk", "HFS+", "OCLP-Installer", f"disk{disk_id}"]) + if format_process.returncode == 0: + print("- Disk formatted") + return True + else: + print("- Failed to format disk") + print(f" Error Code: {format_process.returncode}") + input("\nPress Enter to exit") + return False + +def select_disk_to_format(): + utilities.cls() + utilities.header(["Installing OpenCore to Drive"]) + + print("\nDisk picker is loading...") + + all_disks = {} + # TODO: AllDisksAndPartitions is not supported in Snow Leopard and older + try: + # High Sierra and newer + disks = plistlib.loads(subprocess.run("diskutil list -plist physical".split(), stdout=subprocess.PIPE).stdout.decode().strip().encode()) + except ValueError: + # Sierra and older + disks = plistlib.loads(subprocess.run("diskutil list -plist".split(), stdout=subprocess.PIPE).stdout.decode().strip().encode()) + for disk in disks["AllDisksAndPartitions"]: + disk_info = plistlib.loads(subprocess.run(f"diskutil info -plist {disk['DeviceIdentifier']}".split(), stdout=subprocess.PIPE).stdout.decode().strip().encode()) + try: + all_disks[disk["DeviceIdentifier"]] = {"identifier": disk_info["DeviceNode"], "name": disk_info["MediaName"], "size": disk_info["TotalSize"], "removable": disk_info["Internal"], "partitions": {}} + except KeyError: + # Avoid crashing with CDs installed + continue + menu = utilities.TUIMenu( + ["Select Disk to write the macOS Installer onto"], + "Please select the disk you would like to install OpenCore to: ", + in_between=["Missing drives? Verify they are 14GB+ and external (ie. USB)", "", "Ensure all data is backed up on selected drive, entire drive will be erased!"], + return_number_instead_of_direct_call=True, + loop=True, + ) + for disk in all_disks: + # Strip disks that are under 14GB (15,032,385,536 bytes) + # createinstallmedia isn't great at detecting if a disk has enough space + if not any(all_disks[disk]['size'] > 15032385536 for partition in all_disks[disk]): + continue + # Strip internal disks as well (avoid user formatting their SSD/HDD) + # Ensure user doesn't format their boot drive + if not any(all_disks[disk]['removable'] is False for partition in all_disks[disk]): + continue + menu.add_menu_option(f"{disk}: {all_disks[disk]['name']} ({utilities.human_fmt(all_disks[disk]['size'])})", key=disk[4:]) + + response = menu.start() + + if response == -1: + return None + + return response \ No newline at end of file From c83e926e7d956180d8278c28e52f64f658634147 Mon Sep 17 00:00:00 2001 From: Mykola Grymalyuk Date: Tue, 2 Nov 2021 19:18:45 -0600 Subject: [PATCH 2/7] Fix exit --- resources/cli_menu.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/resources/cli_menu.py b/resources/cli_menu.py index 2307b9685..e7bbde607 100644 --- a/resources/cli_menu.py +++ b/resources/cli_menu.py @@ -1,6 +1,7 @@ # Handle misc CLI menu options # Copyright (C) 2020-2021, Dhinak G, Mykola Grymalyuk from __future__ import print_function +import sys from resources import constants, install, utilities, defaults, sys_patch, installer from data import cpu_data, smbios_data, model_array, os_data @@ -1114,7 +1115,7 @@ to your USB drive. input("Press enter to return to the previous.") return else: - exit() + sys.exit() def closing_message(self): utilities.cls() @@ -1126,7 +1127,7 @@ to your USB drive. print("\n\tGuide: https://dortania.github.io/OpenCore-Legacy-Patcher/") print("\tDiscord: https://discord.gg/rqdPgH8xSN") input("\nPress enter to exit: ") - exit() + sys.exit() big_sur = """Patches Root volume to fix misc issues such as: From 924cac7577949d9ba1a2834465cc9d372753a9f6 Mon Sep 17 00:00:00 2001 From: Mykola Grymalyuk Date: Wed, 3 Nov 2021 11:20:30 -0600 Subject: [PATCH 3/7] Add 11.2.3 mirror --- OpenCore-Patcher.command | 11 +++++------ data/mirror_data.py | 11 +++++++++++ resources/cli_menu.py | 12 ++++++++---- resources/constants.py | 3 ++- resources/installer.py | 1 + resources/utilities.py | 12 +++++++++--- 6 files changed, 36 insertions(+), 14 deletions(-) create mode 100644 data/mirror_data.py diff --git a/OpenCore-Patcher.command b/OpenCore-Patcher.command index d5f3758d3..459eb4162 100755 --- a/OpenCore-Patcher.command +++ b/OpenCore-Patcher.command @@ -17,7 +17,7 @@ class OpenCoreLegacyPatcher: self.constants = constants.Constants() self.generate_base_data() if utilities.check_cli_args() is None: - self.main_menu(True) + self.main_menu(False) def generate_base_data(self): self.constants.detected_os = os_probe.detect_kernel_major() @@ -47,11 +47,9 @@ class OpenCoreLegacyPatcher: # 5. Install OpenCore to ESP # 6. flash macOS # 7. Prompt user to reboot + self.constants.walkthrough = True build.BuildOpenCore(self.constants.custom_model or self.constants.computer.real_model, self.constants).build_opencore() cli_menu.MenuOptions(self.constants.custom_model or self.computer.real_model, self.constants).download_macOS() - utilities.cls() - - sys.exit(0) def post_install(self): @@ -61,8 +59,9 @@ class OpenCoreLegacyPatcher: # 3. Install OpenCore to ESP # 4. Determine whether root patching needed # 5. Prompt user to reboot - print() - + build.BuildOpenCore(self.constants.custom_model or self.constants.computer.real_model, self.constants).build_opencore() + install.tui_disk_installation(self.constants).copy_efi() + cli_menu.MenuOptions(self.constants.custom_model or self.computer.real_model, self.constants).PatchVolume def main_menu(self, walkthrough): response = None diff --git a/data/mirror_data.py b/data/mirror_data.py new file mode 100644 index 000000000..063c50a5a --- /dev/null +++ b/data/mirror_data.py @@ -0,0 +1,11 @@ +# Mirrors of Apple's InstallAssistant.ppkg +# Currently only listing important Installers no longer on Apple's servers + +Install_macOS_Big_Sur_11_2_3 = { + "Version": "11.2.3", + "Build": "20D91", + "Link": "https://archive.org/download/install-assistant-20D91/InstallAssistant.pkg", + "Size": 11848909000, + "Source": "Archive.org", + "integirty": None, +} \ No newline at end of file diff --git a/resources/cli_menu.py b/resources/cli_menu.py index e7bbde607..407f363c5 100644 --- a/resources/cli_menu.py +++ b/resources/cli_menu.py @@ -4,7 +4,7 @@ from __future__ import print_function import sys from resources import constants, install, utilities, defaults, sys_patch, installer -from data import cpu_data, smbios_data, model_array, os_data +from data import cpu_data, smbios_data, model_array, os_data, mirror_data class MenuOptions: @@ -1073,8 +1073,10 @@ to your USB drive. menu = utilities.TUIMenu(title, "Please select an option: ", auto_number=True, top_level=True) avalible_installers = installer.list_downloadable_macOS_installers(self.constants.payload_path, "DeveloperSeed") if avalible_installers: + # Add mirror of 11.2.3 for users who want it + options.append([f"macOS {mirror_data.Install_macOS_Big_Sur_11_2_3['Version']} ({mirror_data.Install_macOS_Big_Sur_11_2_3['Build']} - {utilities.human_fmt(mirror_data.Install_macOS_Big_Sur_11_2_3['Size'])} - {mirror_data.Install_macOS_Big_Sur_11_2_3['Source']})", lambda: self.download_install_assistant(mirror_data.Install_macOS_Big_Sur_11_2_3['Link'])]) for app in avalible_installers: - options.append([f"macOS {avalible_installers[app]['Version']} ({avalible_installers[app]['Build']} - {utilities.human_fmt(avalible_installers[app]['Size'])})", lambda x=app: self.download_install_assistant(avalible_installers[x]['Link'])]) + options.append([f"macOS {avalible_installers[app]['Version']} ({avalible_installers[app]['Build']} - {utilities.human_fmt(avalible_installers[app]['Size'])} - {avalible_installers[app]['Source']})", lambda x=app: self.download_install_assistant(avalible_installers[x]['Link'])]) for option in options: menu.add_menu_option(option[0], function=option[1]) response = menu.start() @@ -1107,7 +1109,8 @@ to your USB drive. utilities.header(["Create macOS installer"]) print("Installer created successfully.") input("Press enter to exit.") - self.closing_message() + if self.constants.walkthrough is True: + self.closing_message() else: utilities.cls() utilities.header(["Create macOS installer"]) @@ -1115,7 +1118,8 @@ to your USB drive. input("Press enter to return to the previous.") return else: - sys.exit() + if self.constants.walkthrough is True: + sys.exit() def closing_message(self): utilities.cls() diff --git a/resources/constants.py b/resources/constants.py index 5dbe2ad86..25cf4c608 100644 --- a/resources/constants.py +++ b/resources/constants.py @@ -160,7 +160,8 @@ class Constants: self.disable_msr_power_ctl = False # Disable MSR Power Control (missing battery throttling) self.software_demux = False # Enable Software Demux patch set self.force_vmm = False # Force VMM patch - self.custom_sip_value = None # Set custom SIP value + self.custom_sip_value = None # Set custom SIP value + self.walkthrough = False # Enable Walkthrough self.legacy_accel_support = [ os_data.os_data.mojave, diff --git a/resources/installer.py b/resources/installer.py index 8aceac19b..1b721d834 100644 --- a/resources/installer.py +++ b/resources/installer.py @@ -99,6 +99,7 @@ def list_downloadable_macOS_installers(download_path, catalog): "Link": download_link, "Size": size, "integirty": integirty, + "Source": "Apple Inc.", } }) except KeyError: diff --git a/resources/utilities.py b/resources/utilities.py index c2dfe01cc..9e49c0cd8 100644 --- a/resources/utilities.py +++ b/resources/utilities.py @@ -293,6 +293,11 @@ def get_rom(variable: str, *, decode: bool = False): def download_file(link, location): if Path(location).exists(): Path(location).unlink() + try: + # Handle cases where Content-Length has garbage or is missing + size_string = f" of {int(requests.head(link).headers['Content-Length']) / 1024 / 1024}MB" + except KeyError: + size_string = "" response = requests.get(link, stream=True) short_link = os.path.basename(link) # SU Catalog's link is quite long, strip to make it bearable @@ -300,17 +305,18 @@ def download_file(link, location): short_link = "sucatalog.gz" header = f"# Downloading: {short_link} #" box_length = len(header) + box_string = "#" * box_length with location.open("wb") as file: count = 0 for chunk in response.iter_content(1024 * 1024 * 4): file.write(chunk) count += len(chunk) cls() - print("#" * box_length) + print(box_string) print(header) - print("#" * box_length) + print(box_string) print("") - print(f"{count / 1024 / 1024}MB Downloaded") + print(f"{count / 1024 / 1024}MB Downloaded{size_string}") checksum = hashlib.sha256() with location.open("rb") as file: chunk = file.read(1024 * 1024 * 16) From 95521e2225de445eb143ca8698d5ab2865526075 Mon Sep 17 00:00:00 2001 From: Mykola Grymalyuk Date: Wed, 3 Nov 2021 11:37:05 -0600 Subject: [PATCH 4/7] Update 11.2.3 size --- data/mirror_data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/mirror_data.py b/data/mirror_data.py index 063c50a5a..efcb73b07 100644 --- a/data/mirror_data.py +++ b/data/mirror_data.py @@ -5,7 +5,7 @@ Install_macOS_Big_Sur_11_2_3 = { "Version": "11.2.3", "Build": "20D91", "Link": "https://archive.org/download/install-assistant-20D91/InstallAssistant.pkg", - "Size": 11848909000, + "Size": 12211077798, "Source": "Archive.org", "integirty": None, } \ No newline at end of file From 9f3d3453df08f7834d0bbb36158b0d55a360ef2f Mon Sep 17 00:00:00 2001 From: Mykola Grymalyuk Date: Wed, 3 Nov 2021 11:44:29 -0600 Subject: [PATCH 5/7] Avoid Installing OC to ESP if not requested --- resources/cli_menu.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/cli_menu.py b/resources/cli_menu.py index 407f363c5..95e91c75b 100644 --- a/resources/cli_menu.py +++ b/resources/cli_menu.py @@ -1101,7 +1101,7 @@ to your USB drive. if installer.format_drive(disk) is True: # Only install if OC is found # Allows a user to create a macOS Installer without OCLP if desired - if self.constants.opencore_release_folder.exists(): + if self.constants.opencore_release_folder.exists() and self.constants.walkthrough is True: # ESP will always be the first partition when formatted by disk utility install.tui_disk_installation.install_opencore(self, f"disk{disk}", "1") if installer.create_installer(installer_path, "OCLP-Installer") is True: From b42169f980197687658b88c305b3b083c647e4c3 Mon Sep 17 00:00:00 2001 From: Mykola Grymalyuk Date: Wed, 3 Nov 2021 19:29:24 -0600 Subject: [PATCH 6/7] Strip unused functions --- OpenCore-Patcher.command | 66 +++++++++------------------------------- 1 file changed, 14 insertions(+), 52 deletions(-) diff --git a/OpenCore-Patcher.command b/OpenCore-Patcher.command index 459eb4162..e848c59b5 100755 --- a/OpenCore-Patcher.command +++ b/OpenCore-Patcher.command @@ -17,7 +17,7 @@ class OpenCoreLegacyPatcher: self.constants = constants.Constants() self.generate_base_data() if utilities.check_cli_args() is None: - self.main_menu(False) + self.main_menu() def generate_base_data(self): self.constants.detected_os = os_probe.detect_kernel_major() @@ -37,33 +37,8 @@ class OpenCoreLegacyPatcher: arguments.arguments().parse_arguments(self.constants) else: print("- No arguments present, loading TUI") - - def first_setup(self): - # Order of operations: - # 1. Build OpenCore - # 2. Download macOS - # 3. Select USB drive - # 4. Format USB drive - # 5. Install OpenCore to ESP - # 6. flash macOS - # 7. Prompt user to reboot - self.constants.walkthrough = True - build.BuildOpenCore(self.constants.custom_model or self.constants.computer.real_model, self.constants).build_opencore() - cli_menu.MenuOptions(self.constants.custom_model or self.computer.real_model, self.constants).download_macOS() - - def post_install(self): - # Order of operations: - # 1. Build OpenCore - # 2. Prompt drive to install OC to - # 3. Install OpenCore to ESP - # 4. Determine whether root patching needed - # 5. Prompt user to reboot - build.BuildOpenCore(self.constants.custom_model or self.constants.computer.real_model, self.constants).build_opencore() - install.tui_disk_installation(self.constants).copy_efi() - cli_menu.MenuOptions(self.constants.custom_model or self.computer.real_model, self.constants).PatchVolume - - def main_menu(self, walkthrough): + def main_menu(self): response = None while not (response and response == -1): title = [ @@ -92,31 +67,18 @@ class OpenCoreLegacyPatcher: menu = utilities.TUIMenu(title, "Please select an option: ", in_between=in_between, auto_number=True, top_level=True) - if walkthrough is True: - options = ( - [["First Time Setup", self.first_setup]] - if ((self.constants.custom_model or self.computer.real_model) in model_array.SupportedSMBIOS) or self.constants.allow_oc_everywhere is True - else [] - ) + [ - #["Post-Installation setup", cli_menu.MenuOptions(self.constants.custom_model or self.computer.real_model, self.constants).closing_message], - ["Change Model", cli_menu.MenuOptions(self.constants.custom_model or self.computer.real_model, self.constants).change_model], - ["Patcher Settings", cli_menu.MenuOptions(self.constants.custom_model or self.computer.real_model, self.constants).patcher_settings], - ["Credits", cli_menu.MenuOptions(self.constants.custom_model or self.computer.real_model, self.constants).credits], - ["Legacy Menu", lambda: self.main_menu(False)], - ] - else: - options = ( - [["Build OpenCore", build.BuildOpenCore(self.constants.custom_model or self.constants.computer.real_model, self.constants).build_opencore]] - if ((self.constants.custom_model or self.computer.real_model) in model_array.SupportedSMBIOS) or self.constants.allow_oc_everywhere is True - else [] - ) + [ - ["Install OpenCore to USB/internal drive", install.tui_disk_installation(self.constants).copy_efi], - ["Post-Install Volume Patch", cli_menu.MenuOptions(self.constants.custom_model or self.computer.real_model, self.constants).PatchVolume], - ["Change Model", cli_menu.MenuOptions(self.constants.custom_model or self.computer.real_model, self.constants).change_model], - ["Patcher Settings", cli_menu.MenuOptions(self.constants.custom_model or self.computer.real_model, self.constants).patcher_settings], - ["Installer Creation", cli_menu.MenuOptions(self.constants.custom_model or self.computer.real_model, self.constants).download_macOS], - ["Credits", cli_menu.MenuOptions(self.constants.custom_model or self.computer.real_model, self.constants).credits], - ] + options = ( + [["Build OpenCore", build.BuildOpenCore(self.constants.custom_model or self.constants.computer.real_model, self.constants).build_opencore]] + if ((self.constants.custom_model or self.computer.real_model) in model_array.SupportedSMBIOS) or self.constants.allow_oc_everywhere is True + else [] + ) + [ + ["Install OpenCore to USB/internal drive", install.tui_disk_installation(self.constants).copy_efi], + ["Post-Install Volume Patch", cli_menu.MenuOptions(self.constants.custom_model or self.computer.real_model, self.constants).PatchVolume], + ["Change Model", cli_menu.MenuOptions(self.constants.custom_model or self.computer.real_model, self.constants).change_model], + ["Patcher Settings", cli_menu.MenuOptions(self.constants.custom_model or self.computer.real_model, self.constants).patcher_settings], + ["Installer Creation", cli_menu.MenuOptions(self.constants.custom_model or self.computer.real_model, self.constants).download_macOS], + ["Credits", cli_menu.MenuOptions(self.constants.custom_model or self.computer.real_model, self.constants).credits], + ] for option in options: menu.add_menu_option(option[0], function=option[1]) From 6689817872ab69af38d671619ed626b172197d20 Mon Sep 17 00:00:00 2001 From: Mykola Grymalyuk Date: Fri, 5 Nov 2021 23:24:13 -0600 Subject: [PATCH 7/7] Fix Integrity typo Thanks to @J-Human --- data/mirror_data.py | 2 +- resources/installer.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/data/mirror_data.py b/data/mirror_data.py index efcb73b07..95403f154 100644 --- a/data/mirror_data.py +++ b/data/mirror_data.py @@ -7,5 +7,5 @@ Install_macOS_Big_Sur_11_2_3 = { "Link": "https://archive.org/download/install-assistant-20D91/InstallAssistant.pkg", "Size": 12211077798, "Source": "Archive.org", - "integirty": None, + "integrity": None, } \ No newline at end of file diff --git a/resources/installer.py b/resources/installer.py index 1b721d834..18b47e7f6 100644 --- a/resources/installer.py +++ b/resources/installer.py @@ -90,7 +90,7 @@ def list_downloadable_macOS_installers(download_path, catalog): if "InstallAssistant.pkg" in ia_package["URL"]: download_link = ia_package["URL"] size = ia_package["Size"] - integirty = ia_package["IntegrityDataURL"] + integrity = ia_package["IntegrityDataURL"] avalible_apps.update({ item: { @@ -98,7 +98,7 @@ def list_downloadable_macOS_installers(download_path, catalog): "Build": build, "Link": download_link, "Size": size, - "integirty": integirty, + "integrity": integrity, "Source": "Apple Inc.", } })