from Scripts import github from Scripts import resource_fetcher from Scripts import utils import os import tempfile import shutil import subprocess class gatheringFiles: def __init__(self): self.utils = utils.Utils() self.github = github.Github() self.fetcher = resource_fetcher.ResourceFetcher() self.dortania_builds_url = "https://raw.githubusercontent.com/dortania/build-repo/builds/latest.json" self.ocbinarydata_url = "https://github.com/acidanthera/OcBinaryData/archive/refs/heads/master.zip" self.amd_vanilla_patches_url = "https://raw.githubusercontent.com/AMD-OSX/AMD_Vanilla/beta/patches.plist" self.temporary_dir = tempfile.mkdtemp() self.ock_files_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), "OCK_Files") self.bootloader_kexts_data_path = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), "bootloader_kexts_data.json") self.download_history_file = os.path.join(self.ock_files_dir, "history.json") def get_product_index(self, product_list, target_product_name): for index, product in enumerate(product_list): if target_product_name == product.get("product_name"): return index return None def get_bootloader_kexts_data(self, kexts): download_urls = self.utils.read_file(self.bootloader_kexts_data_path) if not isinstance(download_urls, list): download_urls = [] dortania_builds_data = self.fetcher.fetch_and_parse_content(self.dortania_builds_url, "json") seen_repos = set() def add_product_to_download_urls(products): if isinstance(products, dict): products = [products] for product in products: product_index = self.get_product_index(download_urls, product.get("product_name")) if product_index is None: download_urls.append(product) else: download_urls[product_index] = product for kext in kexts: if not kext.checked: continue if kext.download_info: add_product_to_download_urls({"product_name": kext.name, **kext.download_info}) elif kext.github_repo and kext.github_repo.get("repo") not in seen_repos: name = kext.github_repo.get("repo") seen_repos.add(name) if name in dortania_builds_data: add_product_to_download_urls({ "product_name": name, "id": dortania_builds_data[name]["versions"][0]["release"]["id"], "url": dortania_builds_data[name]["versions"][0]["links"]["release"] }) else: if self.github.check_ratelimit(): latest_release = self.github.get_latest_release(kext.github_repo.get("owner"), kext.github_repo.get("repo")) or {} add_product_to_download_urls(latest_release.get("assets")) add_product_to_download_urls({ "product_name": "OpenCorePkg", "id": dortania_builds_data["OpenCorePkg"]["versions"][0]["release"]["id"], "url": dortania_builds_data["OpenCorePkg"]["versions"][0]["links"]["release"] }) sorted_download_urls = sorted(download_urls, key=lambda x:x["product_name"]) self.utils.create_folder(self.ock_files_dir) self.utils.write_file(self.bootloader_kexts_data_path, sorted_download_urls) return sorted_download_urls def move_bootloader_kexts_to_product_directory(self, product_name): if not os.path.exists(self.temporary_dir): raise FileNotFoundError("The directory {} does not exist.".format(self.temporary_dir)) if not "OpenCore" in product_name: kext_paths = self.utils.find_matching_paths(os.path.join(self.temporary_dir, product_name), extension_filter=".kext") for kext_path, type in kext_paths: source_kext_path = os.path.join(self.temporary_dir, product_name, kext_path) destination_kext_path = os.path.join(self.ock_files_dir, product_name, os.path.basename(kext_path)) if "Contents" in kext_path or "Debug".lower() in kext_path.lower(): continue shutil.move(source_kext_path, destination_kext_path) else: source_bootloader_path = os.path.join(self.temporary_dir, product_name, "X64", "EFI") if os.path.exists(source_bootloader_path): destination_efi_path = os.path.join(self.ock_files_dir, product_name, os.path.basename(source_bootloader_path)) shutil.move(source_bootloader_path, destination_efi_path) source_config_path = os.path.join(os.path.dirname(os.path.dirname(source_bootloader_path)), "Docs", "Sample.plist") destination_config_path = os.path.join(destination_efi_path, "OC", "config.plist") shutil.move(source_config_path, destination_config_path) ocbinarydata_dir = os.path.join(self.temporary_dir, "OcBinaryData", "OcBinaryData-master") background_picker_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "datasets", "background_picker.icns") if os.path.exists(ocbinarydata_dir): for name in os.listdir(ocbinarydata_dir): if name.startswith("."): continue shutil.copytree(os.path.join(ocbinarydata_dir, name), os.path.join(destination_efi_path, "OC", name), dirs_exist_ok=True) resources_image_dir = os.path.join(destination_efi_path, "OC", "Resources", "Image") picker_variants = self.utils.find_matching_paths(resources_image_dir, type_filter="dir") for picker_variant, type in picker_variants: if ".icns" in ", ".join(os.listdir(os.path.join(resources_image_dir, picker_variant))): shutil.copy(background_picker_path, os.path.join(resources_image_dir, picker_variant, "Background.icns")) macserial_paths = self.utils.find_matching_paths(os.path.join(self.temporary_dir, product_name), name_filter="macserial", type_filter="file") if macserial_paths: for macserial_path, type in macserial_paths: source_macserial_path = os.path.join(self.temporary_dir, product_name, macserial_path) destination_macserial_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), os.path.basename(macserial_path)) shutil.move(source_macserial_path, destination_macserial_path) if os.name != "nt": subprocess.run(["chmod", "+x", destination_macserial_path]) else: raise FileNotFoundError("No bootloader or kexts files found in the product directory.") return True def gather_bootloader_kexts(self, kexts, macos_version): download_history = self.utils.read_file(self.download_history_file) if not isinstance(download_history, list): download_history = [] bootloader_kext_urls = self.utils.read_file(self.bootloader_kexts_data_path) if not isinstance(bootloader_kext_urls, list): bootloader_kext_urls = self.get_bootloader_kexts_data(kexts) self.utils.create_folder(self.temporary_dir) for product in kexts + [{"Name": "OpenCorePkg"}]: if not isinstance(product, dict) and not product.checked: continue product_name = product.name if not isinstance(product, dict) else product.get("Name") if product_name == "AirportItlwm": version = macos_version[:2] if self.utils.parse_darwin_version("24.0.0") <= self.utils.parse_darwin_version(macos_version): version = "22" elif self.utils.parse_darwin_version("23.4.0") <= self.utils.parse_darwin_version(macos_version): version = "23.4" elif self.utils.parse_darwin_version("23.0.0") <= self.utils.parse_darwin_version(macos_version): version = "23.0" product_name += version elif "VoodooPS2" in product_name: product_name = "VoodooPS2" elif product_name == "BlueToolFixup" or product_name.startswith("Brcm"): product_name = "BrcmPatchRAM" elif product_name.startswith("IntelB"): product_name = "IntelBluetoothFirmware" elif product_name.startswith("VoodooI2C"): product_name = "VoodooI2C" try: product_index = self.get_product_index(bootloader_kext_urls, product_name) product_download_url = bootloader_kext_urls[product_index].get("url") product_id = bootloader_kext_urls[product_index].get("id") except: continue history_index = self.get_product_index(download_history, product_name) if history_index is not None and product_id == download_history[history_index].get("id"): continue asset_dir = os.path.join(self.ock_files_dir, product_name) self.utils.create_folder(asset_dir, remove_content=True) zip_path = os.path.join(self.temporary_dir, product_name) + ".zip" self.fetcher.download_and_save_file(product_download_url, zip_path) self.utils.extract_zip_file(zip_path) while True: zip_files = self.utils.find_matching_paths(os.path.join(self.temporary_dir, product_name), extension_filter=".zip") if not zip_files: break for zip_file, file_type in zip_files: full_zip_path = os.path.join(self.temporary_dir, product_name, zip_file) self.utils.extract_zip_file(full_zip_path) os.remove(full_zip_path) if "OpenCore" in product_name: ocbinarydata_dir = os.path.join(self.temporary_dir, "OcBinaryData") if not os.path.exists(ocbinarydata_dir): zip_path = ocbinarydata_dir + ".zip" self.fetcher.download_and_save_file(self.ocbinarydata_url, zip_path) self.utils.extract_zip_file(zip_path) if self.move_bootloader_kexts_to_product_directory(product_name): if history_index is None: download_history.append({"product_name": product_name, "id": product_id}) else: download_history[history_index]["id"] = product_id self.utils.write_file(self.download_history_file, download_history) shutil.rmtree(self.temporary_dir, ignore_errors=True) def get_amd_kernel_patches(self): self.utils.head("Gathering Files") print("") print("Please wait for download AMD Vanilla Patches") print("from " + self.amd_vanilla_patches_url) print("") try: response = self.fetcher.fetch_and_parse_content(self.amd_vanilla_patches_url, "plist") return response["Kernel"]["Patch"] except: print("Unable to download AMD Vanilla Patches at this time.") print("Please try again later or apply them manually.") print("") self.utils.request_input() return [] def gather_hardware_sniffer(self): if os.name != "nt": return self.utils.head("Gathering Files") print("") print("Please wait for download Hardware Sniffer") print("") hardware_sniffer_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "Hardware-Sniffer-CLI.exe") hardware_sniffer_cli = None try: download_history = self.utils.read_file(self.download_history_file) if not isinstance(download_history, list): download_history = [] latest_release = self.github.get_latest_release("lzhoang2801", "Hardware-Sniffer") or {} for product in latest_release.get("assets"): if product.get("product_name") == "Hardware-Sniffer-CLI": hardware_sniffer_cli = product history_index = self.get_product_index(download_history, "Hardware-Sniffer-CLI") if history_index is not None and hardware_sniffer_cli.get("id") == download_history[history_index].get("id"): if os.path.exists(hardware_sniffer_path): return hardware_sniffer_path self.fetcher.download_and_save_file(hardware_sniffer_cli.get("url"), hardware_sniffer_path) if not os.path.exists(hardware_sniffer_path): return if history_index is None: download_history.append({"product_name": "Hardware-Sniffer-CLI", "id": hardware_sniffer_cli.get("id")}) else: download_history[history_index]["id"] = hardware_sniffer_cli.get("id") self.utils.create_folder(os.path.dirname(self.download_history_file)) self.utils.write_file(self.download_history_file, download_history) return hardware_sniffer_path except: print("Could not complete download Hardware Sniffer.") print("Please download Hardware Sniffer CLI manually and place it in \"Scripts\" folder.") if hardware_sniffer_cli: print("You can manually download \"Hardware-Sniffer-CLI.exe\" from:\n {}\n".format(hardware_sniffer_cli.get("url"))) print("Alternatively, export the hardware report manually to continue.") print("") self.utils.request_input() return