mirror of
https://github.com/dortania/OpenCore-Legacy-Patcher.git
synced 2026-04-14 20:58:21 +10:00
221 lines
7.4 KiB
Python
221 lines
7.4 KiB
Python
# Copyright (C) 2020-2021, Dhinak G
|
|
from __future__ import print_function
|
|
|
|
import os
|
|
import math
|
|
from pathlib import Path
|
|
import plistlib
|
|
import subprocess
|
|
|
|
from Resources import Constants
|
|
|
|
|
|
def hexswap(input_hex: str):
|
|
hex_pairs = [input_hex[i : i + 2] for i in range(0, len(input_hex), 2)]
|
|
hex_rev = hex_pairs[::-1]
|
|
hex_str = "".join(["".join(x) for x in hex_rev])
|
|
return hex_str.upper()
|
|
|
|
|
|
def header(lines):
|
|
lines = [i for i in lines if i is not None]
|
|
total_length = len(max(lines, key=len)) + 4
|
|
print("#" * (total_length))
|
|
for line in lines:
|
|
left_side = math.floor(((total_length - 2 - len(line.strip())) / 2))
|
|
print("#" + " " * left_side + line.strip() + " " * (total_length - len("#" + " " * left_side + line.strip()) - 1) + "#")
|
|
print("#" * total_length)
|
|
|
|
|
|
RECOVERY_STATUS = None
|
|
|
|
|
|
def check_recovery():
|
|
global RECOVERY_STATUS # pylint: disable=global-statement # We need to cache the result
|
|
|
|
if RECOVERY_STATUS is None:
|
|
RECOVERY_STATUS = Path("/System/Library/BaseSystem").exists()
|
|
|
|
return RECOVERY_STATUS
|
|
|
|
|
|
def get_disk_path():
|
|
root_partition_info = plistlib.loads(subprocess.run("diskutil info -plist /".split(), stdout=subprocess.PIPE).stdout.decode().strip().encode())
|
|
root_mount_path = root_partition_info["DeviceIdentifier"]
|
|
root_mount_path = root_mount_path[:-2] if root_mount_path.count("s") > 1 else root_mount_path
|
|
return root_mount_path
|
|
|
|
|
|
def csr_decode(csr_active_config):
|
|
if csr_active_config is None:
|
|
csr_active_config = b"\x00\x00\x00\x00"
|
|
sip_int = int.from_bytes(csr_active_config, byteorder="little")
|
|
i = 0
|
|
for current_sip_bit in Constants.Constants.csr_values:
|
|
if sip_int & (1 << i):
|
|
Constants.Constants.csr_values[current_sip_bit] = True
|
|
i = i + 1
|
|
|
|
# Can be adjusted to whatever OS needs patching
|
|
sip_needs_change = all(Constants.Constants.csr_values[i] for i in Constants.Constants.root_patch_sip_big_sur)
|
|
if sip_needs_change is True:
|
|
return False
|
|
else:
|
|
return True
|
|
|
|
def friendly_hex(integer: int):
|
|
return "{:02X}".format(integer)
|
|
|
|
def patching_status():
|
|
# Detection for Root Patching
|
|
sip_enabled = True # System Integrity Protection
|
|
sbm_enabled = True # Secure Boot Status (SecureBootModel)
|
|
amfi_enabled = True # Apple Mobile File Integrity
|
|
fv_enabled = True # FileVault
|
|
|
|
amfi_1 = "amfi_get_out_of_my_way=0x1"
|
|
amfi_2 = "amfi_get_out_of_my_way=1"
|
|
|
|
if get_nvram("boot-args", decode=False) and amfi_1 in get_nvram("boot-args", decode=False) or amfi_2 in get_nvram("boot-args", decode=False):
|
|
amfi_enabled = False
|
|
if get_nvram("HardwareModel", "94B73556-2197-4702-82A8-3E1337DAFBFB", decode=False) not in Constants.Constants.sbm_values:
|
|
sbm_enabled = False
|
|
|
|
if get_nvram("csr-active-config", decode=False) and csr_decode(get_nvram("csr-active-config", decode=False)) is False:
|
|
sip_enabled = False
|
|
|
|
fv_status: str = subprocess.run("fdesetup status".split(), stdout=subprocess.PIPE, stderr=subprocess.STDOUT).stdout.decode()
|
|
if fv_status.startswith("FileVault is Off"):
|
|
fv_enabled = False
|
|
|
|
return sip_enabled, sbm_enabled, amfi_enabled, fv_enabled
|
|
|
|
|
|
def cls():
|
|
if not check_recovery():
|
|
os.system("cls" if os.name == "nt" else "clear")
|
|
else:
|
|
print("\u001Bc")
|
|
|
|
|
|
def get_nvram(variable: str, uuid: str = None, *, decode: bool = False):
|
|
# TODO: Properly fix for El Capitan, which does not print the XML representation even though we say to
|
|
|
|
if uuid is not None:
|
|
uuid += ":"
|
|
else:
|
|
uuid = ""
|
|
result = subprocess.run(f"nvram -x {uuid}{variable}".split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE).stdout.strip()
|
|
try:
|
|
value = plistlib.loads(result)[f"{uuid}{variable}"]
|
|
except plistlib.InvalidFileException:
|
|
return None
|
|
if decode:
|
|
value = value.strip(b"\0").decode()
|
|
return value
|
|
|
|
|
|
# def menu(title, prompt, menu_options, add_quit=True, auto_number=False, in_between=[], top_level=False):
|
|
# return_option = ["Q", "Quit", None] if top_level else ["B", "Back", None]
|
|
# if add_quit: menu_options.append(return_option)
|
|
|
|
# cls()
|
|
# header(title)
|
|
# print()
|
|
|
|
# for i in in_between: print(i)
|
|
# if in_between: print()
|
|
|
|
# for index, option in enumerate(menu_options):
|
|
# if auto_number and not (index == (len(menu_options) - 1) and add_quit):
|
|
# option[0] = str((index + 1))
|
|
# print(option[0] + ". " + option[1])
|
|
|
|
# print()
|
|
# selected = input(prompt)
|
|
|
|
# keys = [option[0].upper() for option in menu_options]
|
|
# if not selected or selected.upper() not in keys:
|
|
# return
|
|
# if selected.upper() == return_option[0]:
|
|
# return -1
|
|
# else:
|
|
# menu_options[keys.index(selected.upper())][2]() if menu_options[keys.index(selected.upper())][2] else None
|
|
|
|
|
|
class TUIMenu:
|
|
def __init__(self, title, prompt, options=None, return_number_instead_of_direct_call=False, add_quit=True, auto_number=False, in_between=None, top_level=False, loop=False):
|
|
self.title = title
|
|
self.prompt = prompt
|
|
self.in_between = in_between or []
|
|
self.options = options or []
|
|
self.return_number_instead_of_direct_call = return_number_instead_of_direct_call
|
|
self.auto_number = auto_number
|
|
self.add_quit = add_quit
|
|
self.top_level = top_level
|
|
self.loop = loop
|
|
self.added_quit = False
|
|
|
|
def add_menu_option(self, name, description="", function=None, key=""):
|
|
self.options.append([key, name, description, function])
|
|
|
|
def start(self):
|
|
return_option = ["Q", "Quit"] if self.top_level else ["B", "Back"]
|
|
if self.add_quit and not self.added_quit:
|
|
self.add_menu_option(return_option[1], function=None, key=return_option[0])
|
|
self.added_quit = True
|
|
|
|
while True:
|
|
cls()
|
|
header(self.title)
|
|
print()
|
|
|
|
for i in self.in_between:
|
|
print(i)
|
|
if self.in_between:
|
|
print()
|
|
|
|
for index, option in enumerate(self.options):
|
|
if self.auto_number and not (index == (len(self.options) - 1) and self.add_quit):
|
|
option[0] = str((index + 1))
|
|
print(option[0] + ". " + option[1])
|
|
for i in option[2]:
|
|
print("\t" + i)
|
|
|
|
print()
|
|
selected = input(self.prompt)
|
|
|
|
keys = [option[0].upper() for option in self.options]
|
|
if not selected or selected.upper() not in keys:
|
|
if self.loop:
|
|
continue
|
|
else:
|
|
return
|
|
if self.add_quit and selected.upper() == return_option[0]:
|
|
return -1
|
|
elif self.return_number_instead_of_direct_call:
|
|
return self.options[keys.index(selected.upper())][0]
|
|
else:
|
|
self.options[keys.index(selected.upper())][3]() if self.options[keys.index(selected.upper())][3] else None
|
|
if not self.loop:
|
|
return
|
|
|
|
|
|
class TUIOnlyPrint:
|
|
def __init__(self, title, prompt, in_between=None):
|
|
self.title = title
|
|
self.prompt = prompt
|
|
self.in_between = in_between or []
|
|
|
|
def start(self):
|
|
cls()
|
|
header(self.title)
|
|
print()
|
|
|
|
for i in self.in_between:
|
|
print(i)
|
|
if self.in_between:
|
|
print()
|
|
|
|
return input(self.prompt)
|