diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ae017d71..387895de7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## 0.6.9 - Resolve BCM2046 and BCM2070 support on macOS 13.3 and newer +- Add new macOS Installer download menu (Jazzzny) - Increment Binaries: - OpenCorePkg 0.9.3 - release - Lilu 1.6.6 - release diff --git a/payloads/Icon/AppIcons/BigSur.icns b/payloads/Icon/AppIcons/BigSur.icns new file mode 100644 index 000000000..32fa0dcd3 Binary files /dev/null and b/payloads/Icon/AppIcons/BigSur.icns differ diff --git a/payloads/Icon/AppIcons/Generic.icns b/payloads/Icon/AppIcons/Generic.icns new file mode 100644 index 000000000..aab1a79ca Binary files /dev/null and b/payloads/Icon/AppIcons/Generic.icns differ diff --git a/payloads/Icon/AppIcons/Monterey.icns b/payloads/Icon/AppIcons/Monterey.icns new file mode 100644 index 000000000..9accebaf3 Binary files /dev/null and b/payloads/Icon/AppIcons/Monterey.icns differ diff --git a/payloads/Icon/AppIcons/Sonoma.icns b/payloads/Icon/AppIcons/Sonoma.icns new file mode 100644 index 000000000..ac6eb4aae Binary files /dev/null and b/payloads/Icon/AppIcons/Sonoma.icns differ diff --git a/payloads/Icon/AppIcons/Ventura.icns b/payloads/Icon/AppIcons/Ventura.icns new file mode 100644 index 000000000..7898726a9 Binary files /dev/null and b/payloads/Icon/AppIcons/Ventura.icns differ diff --git a/resources/macos_installer_handler.py b/resources/macos_installer_handler.py index 617051303..ae661631b 100644 --- a/resources/macos_installer_handler.py +++ b/resources/macos_installer_handler.py @@ -373,6 +373,7 @@ class RemoteInstallerCatalog: download_link = None integrity = None size = None + date = catalog["Products"][product]["PostDate"] for ia_package in catalog["Products"][product]["Packages"]: if "InstallAssistant.pkg" not in ia_package["URL"]: @@ -386,6 +387,7 @@ class RemoteInstallerCatalog: integrity = ia_package["IntegrityDataURL"] size = ia_package["Size"] if ia_package["Size"] else 0 + if any([version, build, download_link, size, integrity]) is None: continue @@ -400,10 +402,12 @@ class RemoteInstallerCatalog: "Variant": catalog_url, "OS": os_data.os_conversion.os_to_kernel(version), "Models": build_plist["MobileAssetProperties"]["SupportedDeviceModels"], + "Date": date } }) available_apps = {k: v for k, v in sorted(available_apps.items(), key=lambda x: x[1]['Version'])} + return available_apps @@ -493,7 +497,7 @@ class RemoteInstallerCatalog: for ia in list(newest_apps): if newest_apps[ia]["Version"].split(".")[0] not in supported_versions: newest_apps.pop(ia) - + return newest_apps diff --git a/resources/wx_gui/gui_macos_installer_download.py b/resources/wx_gui/gui_macos_installer_download.py index 483b7f9ac..2e33a9df7 100644 --- a/resources/wx_gui/gui_macos_installer_download.py +++ b/resources/wx_gui/gui_macos_installer_download.py @@ -2,6 +2,7 @@ import wx import logging import threading import webbrowser +import locale from pathlib import Path @@ -124,113 +125,195 @@ class macOSInstallerDownloadFrame(wx.Frame): """ Display available installers in frame """ + icons = [ + [wx.Bitmap(wx.Bitmap(str(self.constants.icns_resource_path / "Generic.icns"),wx.BITMAP_TYPE_ICON).ConvertToImage().Rescale(32, 32, wx.IMAGE_QUALITY_HIGH)),wx.Bitmap(wx.Bitmap(str(self.constants.icns_resource_path / "Generic.icns"),wx.BITMAP_TYPE_ICON).ConvertToImage().Rescale(64, 64, wx.IMAGE_QUALITY_HIGH))], + [wx.Bitmap(wx.Bitmap(str(self.constants.icns_resource_path / "BigSur.icns"),wx.BITMAP_TYPE_ICON).ConvertToImage().Rescale(32, 32, wx.IMAGE_QUALITY_HIGH)),wx.Bitmap(wx.Bitmap(str(self.constants.icns_resource_path / "BigSur.icns"),wx.BITMAP_TYPE_ICON).ConvertToImage().Rescale(64, 64, wx.IMAGE_QUALITY_HIGH))], + [wx.Bitmap(wx.Bitmap(str(self.constants.icns_resource_path / "Monterey.icns"),wx.BITMAP_TYPE_ICON).ConvertToImage().Rescale(32, 32, wx.IMAGE_QUALITY_HIGH)),wx.Bitmap(wx.Bitmap(str(self.constants.icns_resource_path / "Monterey.icns"),wx.BITMAP_TYPE_ICON).ConvertToImage().Rescale(64, 64, wx.IMAGE_QUALITY_HIGH))], + [wx.Bitmap(wx.Bitmap(str(self.constants.icns_resource_path / "Ventura.icns"),wx.BITMAP_TYPE_ICON).ConvertToImage().Rescale(32, 32, wx.IMAGE_QUALITY_HIGH)),wx.Bitmap(wx.Bitmap(str(self.constants.icns_resource_path / "Ventura.icns"),wx.BITMAP_TYPE_ICON).ConvertToImage().Rescale(64, 64, wx.IMAGE_QUALITY_HIGH))], + [wx.Bitmap(wx.Bitmap(str(self.constants.icns_resource_path / "Sonoma.icns"),wx.BITMAP_TYPE_ICON).ConvertToImage().Rescale(32, 32, wx.IMAGE_QUALITY_HIGH)),wx.Bitmap(wx.Bitmap(str(self.constants.icns_resource_path / "Sonoma.icns"),wx.BITMAP_TYPE_ICON).ConvertToImage().Rescale(64, 64, wx.IMAGE_QUALITY_HIGH))] + ] + + bundles = [wx.BitmapBundle.FromBitmaps(icon) for icon in icons] + self.frame_modal.Destroy() - dialog = wx.Dialog(self, title="Select macOS Installer", size=(300, 200)) + self.frame_modal = wx.Dialog(self, title="Select macOS Installer", size=(460, 500)) # Title: Select macOS Installer - title_label = wx.StaticText(dialog, label="Select macOS Installer", pos=(-1,5)) + title_label = wx.StaticText(self.frame_modal, label="Select macOS Installer", pos=(-1,-1)) title_label.SetFont(wx.Font(19, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False, ".AppleSystemUIFont")) - title_label.Centre(wx.HORIZONTAL) + + # macOS Installers list + id = wx.NewIdRef() + + self.list = wx.ListCtrl(self.frame_modal, id, style=wx.LC_REPORT | wx.LC_SINGLE_SEL | wx.LC_NO_HEADER | wx.BORDER_SUNKEN) + self.list.SetSmallImages(bundles) - # Subtitle: Installers currently available from Apple: - subtitle_label = wx.StaticText(dialog, label="Installers currently available from Apple:", pos=(-1, title_label.GetPosition()[1] + title_label.GetSize()[1] + 5)) - subtitle_label.SetFont(wx.Font(13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont")) - subtitle_label.Centre(wx.HORIZONTAL) + self.list.InsertColumn(0, "Version") + self.list.InsertColumn(1, "Size") + self.list.InsertColumn(2, "Release Date") - # List of installers installers = self.available_installers_latest if show_full is False else self.available_installers + if show_full is False: + self.frame_modal.SetSize((460, 370)) + if installers: - spacer = 0 + locale.setlocale(locale.LC_TIME, '') logging.info(f"Available installers on SUCatalog ({'All entries' if show_full else 'Latest only'}):") - for app in installers: - logging.info(f"- macOS {installers[app]['Version']} ({installers[app]['Build']}):\n - Size: {utilities.human_fmt(installers[app]['Size'])}\n - Source: {installers[app]['Source']}\n - Variant: {installers[app]['Variant']}\n - Link: {installers[app]['Link']}\n") - extra = " Beta" if installers[app]['Variant'] in ["DeveloperSeed" , "PublicSeed"] else "" + for item in installers: + extra = " Beta" if installers[item]['Variant'] in ["DeveloperSeed" , "PublicSeed"] else "" + logging.info(f"- macOS {installers[item]['Version']} ({installers[item]['Build']}):\n - Size: {utilities.human_fmt(installers[item]['Size'])}\n - Source: {installers[item]['Source']}\n - Variant: {installers[item]['Variant']}\n - Link: {installers[item]['Link']}\n") + index = self.list.InsertItem(self.list.GetItemCount(), f"macOS {installers[item]['Version']} {os_data.os_conversion.convert_kernel_to_marketing_name(int(installers[item]['Build'][:2]))}{extra} ({installers[item]['Build']})") + if int(installers[item]['Build'][:2]) > os_data.os_data.sonoma: + self.list.SetItemImage(index, 0) + else: + self.list.SetItemImage(index, int(installers[item]['Build'][:2])-19) # Darwin version to index conversion. i.e. Darwin 20 -> 1 -> BigSur.icns + self.list.SetItem(index, 1, utilities.human_fmt(installers[item]['Size'])) + self.list.SetItem(index, 2, installers[item]['Date'].strftime("%x")) + - installer_button = wx.Button(dialog, label=f"macOS {installers[app]['Version']}{extra} ({installers[app]['Build']} - {utilities.human_fmt(installers[app]['Size'])})", pos=(-1, subtitle_label.GetPosition()[1] + subtitle_label.GetSize()[1] + 5 + spacer), size=(270, 30)) - installer_button.Bind(wx.EVT_BUTTON, lambda event, temp=app: self.on_download_installer(installers[temp])) - installer_button.Centre(wx.HORIZONTAL) - spacer += 25 - - # Since installers are sorted by version, set the latest installer as the default button - # Note that on full display, the last installer is generally a beta - if show_full is False and app == list(installers.keys())[-1]: - installer_button.SetDefault() + else: logging.error("No installers found on SUCatalog") - installer_button = wx.StaticText(dialog, label="Failed to fetch catalog from Apple", pos=(-1, subtitle_label.GetPosition()[1] + subtitle_label.GetSize()[1] + 5)) - installer_button.SetFont(wx.Font(13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, False, ".AppleSystemUIFont")) - installer_button.Centre(wx.HORIZONTAL) + wx.MessageDialog(self.frame_modal, "Failed to download Installer Catalog from Apple", "Error", wx.OK | wx.ICON_ERROR).ShowModal() - # Show all available installers - show_all_button = wx.Button(dialog, label="Show all available installers" if show_full is False else "Show only latest installers", pos=(-1, installer_button.GetPosition()[1] + installer_button.GetSize()[1]), size=(200, 30)) - show_all_button.Bind(wx.EVT_BUTTON, lambda event: self._display_available_installers(event, not show_full)) - show_all_button.Centre(wx.HORIZONTAL) + self.list.SetColumnWidth(0, 280) + self.list.SetColumnWidth(1, 55) + if show_full is True: + self.list.SetColumnWidth(2, 90) + else: + self.list.SetColumnWidth(2, 104) # Hack to get the highlight to fill the ListCtrl - # Return to Main Menu - return_button = wx.Button(dialog, label="Return to Main Menu", pos=(-1, show_all_button.GetPosition()[1] + show_all_button.GetSize()[1] - 7), size=(150, 30)) + if show_full is False: + self.list.Select(-1) + + self.list.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.on_select_list) + self.list.Bind(wx.EVT_LIST_ITEM_SELECTED, self.on_select_list) + + self.select_button = wx.Button(self.frame_modal, label="Download", pos=(-1, -1), size=(150, -1)) + self.select_button.SetFont(wx.Font(13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont")) + self.select_button.Bind(wx.EVT_BUTTON, lambda event, installers=installers: self.on_download_installer(installers)) + self.select_button.SetToolTip("Download the selected macOS Installer.") + self.select_button.SetDefault() + if show_full is True: + self.select_button.Disable() + + self.copy_button = wx.Button(self.frame_modal, label="Copy Link", pos=(-1, -1), size=(80, -1)) + self.copy_button.SetFont(wx.Font(13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont")) + if show_full is True: + self.copy_button.Disable() + self.copy_button.SetToolTip("Copy the download link of the selected macOS Installer.") + self.copy_button.Bind(wx.EVT_BUTTON, lambda event, installers=installers: self.on_copy_link(installers)) + + return_button = wx.Button(self.frame_modal, label="Return to Main Menu", pos=(-1, -1), size=(150, -1)) return_button.Bind(wx.EVT_BUTTON, self.on_return_to_main_menu) - return_button.Centre(wx.HORIZONTAL) + return_button.SetFont(wx.Font(13, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL, False, ".AppleSystemUIFont")) - # Set size of frame - dialog.SetSize((-1, return_button.GetPosition()[1] + return_button.GetSize()[1] + 40)) - dialog.ShowWindowModal() - self.frame_modal = dialog + self.showolderversions_checkbox = wx.CheckBox(self.frame_modal, label="Show Older/Beta Versions", pos=(-1, -1)) + if show_full is True: + self.showolderversions_checkbox.SetValue(True) + self.showolderversions_checkbox.Bind(wx.EVT_CHECKBOX, lambda event: self._display_available_installers(event, self.showolderversions_checkbox.GetValue())) + rectbox = wx.StaticBox(self.frame_modal, -1) + rectsizer = wx.StaticBoxSizer(rectbox, wx.HORIZONTAL) + rectsizer.Add(self.copy_button, 0, wx.EXPAND | wx.RIGHT, 5) + rectsizer.Add(self.select_button, 0, wx.EXPAND | wx.LEFT, 5) - def on_download_installer(self, app: dict) -> None: + checkboxsizer = wx.BoxSizer(wx.HORIZONTAL) + checkboxsizer.Add(self.showolderversions_checkbox, 0, wx.ALIGN_CENTRE | wx.RIGHT, 5) + + sizer = wx.BoxSizer(wx.VERTICAL) + sizer.AddSpacer(10) + sizer.Add(title_label, 0, wx.ALIGN_CENTRE | wx.ALL, 0) + sizer.Add(self.list, 1, wx.EXPAND | wx.ALL, 10) + sizer.Add(rectsizer, 0, wx.ALIGN_CENTRE | wx.ALL, 0) + sizer.Add(checkboxsizer, 0, wx.ALIGN_CENTRE | wx.ALL, 15) + sizer.Add(return_button, 0, wx.ALIGN_CENTRE | wx.BOTTOM, 15) + + self.frame_modal.SetSizer(sizer) + self.frame_modal.ShowWindowModal() + + def on_copy_link(self, installers: dict) -> None: + + selected_item = self.list.GetFirstSelected() + if selected_item != -1: + clipboard = wx.Clipboard.Get() + + if not clipboard.IsOpened(): + clipboard.Open() + + clipboard.SetData(wx.TextDataObject(list(installers.values())[selected_item]['Link'])) + + clipboard.Close() + + wx.MessageDialog(self.frame_modal, "Download link copied to clipboard", "", wx.OK | wx.ICON_INFORMATION).ShowModal() + + + def on_select_list(self, event): + if self.list.GetSelectedItemCount() > 0: + self.select_button.Enable() + self.copy_button.Enable() + else: + self.select_button.Disable() + self.copy_button.Disable() + + def on_download_installer(self, installers: dict) -> None: """ Download macOS installer """ - logging.info(f"Selected macOS {app['Version']} ({app['Build']})") - # Notify user whether their model is compatible with the selected installer - problems = [] - model = self.constants.custom_model or self.constants.computer.real_model - if model in smbios_data.smbios_dictionary: - if app["OS"] >= os_data.os_data.ventura: - if smbios_data.smbios_dictionary[model]["CPU Generation"] <= cpu_data.CPUGen.penryn or model in ["MacPro4,1", "MacPro5,1", "Xserve3,1"]: - if model.startswith("MacBook"): - problems.append("Lack of internal Keyboard/Trackpad in macOS installer.") - else: - problems.append("Lack of internal Keyboard/Mouse in macOS installer.") + selected_item = self.list.GetFirstSelected() + if selected_item != -1: - if problems: - logging.warning(f"Potential issues with {model} and {app['Version']} ({app['Build']}): {problems}") - problems = "\n".join(problems) - dlg = wx.MessageDialog(self.frame_modal, f"Your model ({model}) may not be fully supported by this installer. You may encounter the following issues:\n\n{problems}\n\nFor more information, see associated page. Otherwise, we recommend using macOS Monterey", "Potential Issues", wx.YES_NO | wx.CANCEL | wx.ICON_WARNING) - dlg.SetYesNoCancelLabels("View Github Issue", "Download Anyways", "Cancel") - result = dlg.ShowModal() - if result == wx.ID_CANCEL: - return - elif result == wx.ID_YES: - webbrowser.open("https://github.com/dortania/OpenCore-Legacy-Patcher/issues/1021") + logging.info(f"Selected macOS {list(installers.values())[selected_item]['Version']} ({list(installers.values())[selected_item]['Build']})") + + # Notify user whether their model is compatible with the selected installer + problems = [] + model = self.constants.custom_model or self.constants.computer.real_model + if model in smbios_data.smbios_dictionary: + if list(installers.values())[selected_item]["OS"] >= os_data.os_data.ventura: + if smbios_data.smbios_dictionary[model]["CPU Generation"] <= cpu_data.CPUGen.penryn or model in ["MacPro4,1", "MacPro5,1", "Xserve3,1"]: + if model.startswith("MacBook"): + problems.append("Lack of internal Keyboard/Trackpad in macOS installer.") + else: + problems.append("Lack of internal Keyboard/Mouse in macOS installer.") + + if problems: + logging.warning(f"Potential issues with {model} and {list(installers.values())[selected_item]['Version']} ({list(installers.values())[selected_item]['Build']}): {problems}") + problems = "\n".join(problems) + dlg = wx.MessageDialog(self.frame_modal, f"Your model ({model}) may not be fully supported by this installer. You may encounter the following issues:\n\n{problems}\n\nFor more information, see associated page. Otherwise, we recommend using macOS Monterey", "Potential Issues", wx.YES_NO | wx.CANCEL | wx.ICON_WARNING) + dlg.SetYesNoCancelLabels("View Github Issue", "Download Anyways", "Cancel") + result = dlg.ShowModal() + if result == wx.ID_CANCEL: + return + elif result == wx.ID_YES: + webbrowser.open("https://github.com/dortania/OpenCore-Legacy-Patcher/issues/1021") + return + + host_space = utilities.get_free_space() + needed_space = list(installers.values())[selected_item]['Size'] * 2 + if host_space < needed_space: + logging.error(f"Insufficient space to download and extract: {utilities.human_fmt(host_space)} available vs {utilities.human_fmt(needed_space)} required") + dlg = wx.MessageDialog(self.frame_modal, f"You do not have enough free space to download and extract this installer. Please free up some space and try again\n\n{utilities.human_fmt(host_space)} available vs {utilities.human_fmt(needed_space)} required", "Insufficient Space", wx.OK | wx.ICON_WARNING) + dlg.ShowModal() return - host_space = utilities.get_free_space() - needed_space = app['Size'] * 2 - if host_space < needed_space: - logging.error(f"Insufficient space to download and extract: {utilities.human_fmt(host_space)} available vs {utilities.human_fmt(needed_space)} required") - dlg = wx.MessageDialog(self.frame_modal, f"You do not have enough free space to download and extract this installer. Please free up some space and try again\n\n{utilities.human_fmt(host_space)} available vs {utilities.human_fmt(needed_space)} required", "Insufficient Space", wx.OK | wx.ICON_WARNING) - dlg.ShowModal() - return + self.frame_modal.Close() - self.frame_modal.Close() + download_obj = network_handler.DownloadObject(list(installers.values())[selected_item]['Link'], self.constants.payload_path / "InstallAssistant.pkg") - download_obj = network_handler.DownloadObject(app['Link'], self.constants.payload_path / "InstallAssistant.pkg") + gui_download.DownloadFrame( + self, + title=self.title, + global_constants=self.constants, + download_obj=download_obj, + item_name=f"macOS {list(installers.values())[selected_item]['Version']} ({list(installers.values())[selected_item]['Build']})", + ) - gui_download.DownloadFrame( - self, - title=self.title, - global_constants=self.constants, - download_obj=download_obj, - item_name=f"macOS {app['Version']} ({app['Build']})", - ) + if download_obj.download_complete is False: + self.on_return_to_main_menu() + return - if download_obj.download_complete is False: - self.on_return_to_main_menu() - return - - self._validate_installer(app['integrity']) + self._validate_installer(list(installers.values())[selected_item]['integrity']) def _validate_installer(self, chunklist_link: str) -> None: