diff --git a/data/os_data.py b/data/os_data.py index 92952c3b2..8f86929bc 100644 --- a/data/os_data.py +++ b/data/os_data.py @@ -1,3 +1,4 @@ +from curses.ascii import isdigit import enum @@ -77,4 +78,70 @@ class os_conversion: except KeyError: os_kernel = 0 - return int(os_kernel) \ No newline at end of file + return int(os_kernel) + + + def find_largest_build(build_array): + # Find the newest version within an array of versions + # These builds will have both numbers and letters in the version + # ex: + # [ + # "22A5295i", + # "22A5266r", + # "22A5286j", + # "22A5295h", + # ] + + max_length = 0 # Length of the longest build + build_array_split = [] # 'build_array', converted into individual array of elements + final_build = "" # Largest determined build + + + # Convert strings to arrays + for build in build_array: + list_build = list(build) + if len(list_build) > max_length: + max_length = len(list_build) + build_array_split.append(list_build) + + # Pad out each array to same length + for build in build_array_split: + while len(build) < max_length: + build.append("0") + + # Convert all letters to int using ord() + for build in build_array_split: + for entry in build: + if not entry.isdigit(): + build[build.index(entry)] = ord(entry) + + for build_outer_loop in build_array_split: + for build_inner_loop in list(build_array_split): + for i in range(len(build_outer_loop)): + # remove any builds that are not the largest + if int(build_outer_loop[i]) > int(build_inner_loop[i]): + build_array_split.remove(build_inner_loop) + break + if int(build_outer_loop[i]) < int(build_inner_loop[i]): + break + + # Convert array back to string + for entry in build_array_split[0]: + # Since we split per character, we know that anything above 9 is a letter + if int(entry) > 9: + # revert back to letter + final_build += chr(entry) + else: + final_build += str(entry) + + # Since we pad with 0s, we need to next determine how many 0s to remove + for build in build_array: + if final_build.startswith(build): + # Handle cases where Apple added a letter to the build + # ex. "22A5295" vs "22A5295" + remaining_strings = final_build.split(build)[1] + # If all remaining strings are 0s, then we can remove the 0s + if all(char == "0" for char in remaining_strings): + final_build = build + + return final_build \ No newline at end of file diff --git a/resources/sys_patch_helpers.py b/resources/sys_patch_helpers.py index 7db6e61fb..dcc656668 100644 --- a/resources/sys_patch_helpers.py +++ b/resources/sys_patch_helpers.py @@ -1,6 +1,7 @@ # Additional support functions for sys_patch.py # Copyright (C) 2020-2022, Dhinak G, Mykola Grymalyuk +from data import os_data from resources import generate_smbios from pathlib import Path from datetime import datetime @@ -66,12 +67,35 @@ class sys_patch_helpers: return False - def determine_kdk_present(self): + def determine_kdk_present(self, match_closest=False): # Check if KDK is present - if Path("/Library/Developer/KDKs").exists(): + # If 'match_closest' is True, will provide the closest match to the reported KDK + + kdk_array = [] + + if not Path("/Library/Developer/KDKs").exists(): + return None + + + for kdk_folder in Path("/Library/Developer/KDKs").iterdir(): + # Ensure direct match + if kdk_folder.name.endswith(f"{self.constants.detected_os_build}.kdk"): + # Verify that the KDK is valid + if (kdk_folder / Path("System/Library/Extensions/System.kext/PlugIns/Libkern.kext/Libkern")).exists(): + return kdk_folder + if match_closest is True: + # ex: KDK_13.0_22A5266r.kdk -> 22A5266r.kdk -> 22A5266r + build = kdk_folder.name.split("_")[2].split(".")[0] + if build.startswith(str(self.constants.detected_os)): + kdk_array.append(build) + + kdk_array = ['22A5295i', '22A5295h', '22A5286j', '22A5266r', '22A70'] + + if match_closest is True: + result = os_data.os_conversion.find_largest_build(kdk_array) + print(f"- Closest KDK match: {result}") for kdk_folder in Path("/Library/Developer/KDKs").iterdir(): - # We don't want to support mismatched KDKs - if self.constants.detected_os_build in kdk_folder.name: + if kdk_folder.name.endswith(f"{result}.kdk"): # Verify that the KDK is valid if (kdk_folder / Path("System/Library/Extensions/System.kext/PlugIns/Libkern.kext/Libkern")).exists(): return kdk_folder