Merge pull request #1029 from dortania/oop-rework

Rework additional core libraries
This commit is contained in:
Mykola Grymalyuk
2023-02-10 15:33:58 -07:00
committed by GitHub
36 changed files with 1365 additions and 2564 deletions

View File

@@ -95,7 +95,6 @@ body:
description: What variant of our software are you running?
options:
- GUI (Graphical User Interface)
- TUI (Text User Interface)
- CLI (Command Line Interface)
- Other/Non-Applicable
validations:

View File

@@ -11,12 +11,14 @@ jobs:
name: Build wxPython
runs-on: x86_64_mojave
if: github.repository_owner == 'dortania'
env:
branch: ${{ github.ref }}
commiturl: ${{ github.event.head_commit.url }}${{ github.event.release.html_url }}
commitdate: ${{ github.event.head_commit.timestamp }}${{ github.event.release.published_at }}
MAC_NOTARIZATION_USERNAME: ${{ secrets.MAC_NOTARIZATION_USERNAME }}
MAC_NOTARIZATION_PASSWORD: ${{ secrets.MAC_NOTARIZATION_PASSWORD }}
steps:
- uses: actions/checkout@v3
- run: /Library/Frameworks/Python.framework/Versions/3.10/bin/python3 Build-Binary.command --reset_binaries --branch "${{ env.branch }}" --commit "${{ env.commiturl }}" --commit_date "${{ env.commitdate }}"
@@ -30,11 +32,13 @@ jobs:
with:
name: OpenCore-Patcher.app (GUI)
path: OpenCore-Patcher-GUI.app.zip
- name: Upload Package to Artifacts
uses: actions/upload-artifact@v3
with:
name: AutoPkg-Assets.pkg
path: ./dist/AutoPkg-Assets.pkg
- name: Upload Binary to Release
if: github.event_name == 'release'
uses: svenstaro/upload-release-action@e74ff71f7d8a4c4745b560a485cc5fdb9b5b999d
@@ -43,6 +47,7 @@ jobs:
file: OpenCore-Patcher-GUI.app.zip
tag: ${{ github.ref }}
file_glob: true
- name: Upload Package to Release
if: github.event_name == 'release'
uses: svenstaro/upload-release-action@e74ff71f7d8a4c4745b560a485cc5fdb9b5b999d

View File

@@ -1,24 +0,0 @@
name: CI - Build TUI
on:
push:
workflow_dispatch:
release:
types: [published]
jobs:
build:
name: Build TUI
runs-on: x86_64_mojave
env:
branch: ${{ github.ref }}
commiturl: ${{ github.event.head_commit.url }}${{ github.event.release.html_url }}
commitdate: ${{ github.event.head_commit.timestamp }}${{ github.event.release.published_at }}
steps:
- uses: actions/checkout@v3
- run: /Library/Frameworks/Python.framework/Versions/3.10/bin/python3 Build-Binary.command --build_tui --reset_binaries --branch "${{ env.branch }}" --commit "${{ env.commiturl }}" --commit_date "${{ env.commitdate }}"
- run: 'codesign -s "Developer ID Application: Mykola Grymalyuk (S74BDJXQMD)" -v --force --deep --timestamp --entitlements ./payloads/entitlements.plist -o runtime "dist/OpenCore-Patcher.app"'
- run: cd dist; zip -r ../OpenCore-Patcher-TUI.app.zip OpenCore-Patcher.app
- name: Validate OpenCore
run: ./dist/OpenCore-Patcher.app/Contents/MacOS/OpenCore-Patcher --validate

View File

@@ -9,6 +9,7 @@ jobs:
build:
name: Build Site and Deploy
runs-on: ubuntu-latest
if: github.repository_owner == 'dortania'
steps:
- uses: actions/setup-node@v3
with:

20
.github/workflows/validate.yml vendored Normal file
View File

@@ -0,0 +1,20 @@
name: CI - Validation
on:
push:
workflow_dispatch:
release:
types: [published]
jobs:
build:
name: Validate
runs-on: x86_64_mojave
if: github.repository_owner == 'dortania'
env:
branch: ${{ github.ref }}
commiturl: ${{ github.event.head_commit.url }}${{ github.event.release.html_url }}
commitdate: ${{ github.event.head_commit.timestamp }}${{ github.event.release.published_at }}
steps:
- uses: actions/checkout@v3
- run: /Library/Frameworks/Python.framework/Versions/3.10/bin/python3 OpenCore-Patcher-GUI.command --validate

View File

@@ -1,14 +1,7 @@
#!/usr/bin/env python3
# This script's main purpose is to handle the following:
# - Download PatcherSupportPkg resources
# - Convert payloads directory into DMG (GUI only)
# - Build Binary via Pyinstaller
# - Add Launcher.sh (TUI only)
# - Patch 'LC_VERSION_MIN_MACOSX' to OS X 10.10
# - Add commit data to Info.plist
# Copyright (C) 2022 - Mykola Grymalyuk
# Generate stand alone application for OpenCore-Patcher
# Copyright (C) 2022-2023 - Mykola Grymalyuk
from pathlib import Path
import time
@@ -21,26 +14,49 @@ import sys
from resources import constants
class create_binary:
class CreateBinary:
"""
Library for creating OpenCore-Patcher application
This script's main purpose is to handle the following:
- Download external dependancies (ex. PatcherSupportPkg)
- Convert payloads directory into DMG
- Build Binary via Pyinstaller
- Patch 'LC_VERSION_MIN_MACOSX' to OS X 10.10
- Add commit data to Info.plist
"""
def __init__(self):
start = time.time()
print("- Starting build script")
self.set_cwd()
self.args = self.parse_arguments()
self.preflight_processes()
self.build_binary()
self.postflight_processes()
self.args = self._parse_arguments()
self._set_cwd()
self._preflight_processes()
self._build_binary()
self._postflight_processes()
print(f"- Build script completed in {str(round(time.time() - start, 2))} seconds")
def set_cwd(self):
def _set_cwd(self):
"""
Initialize current working directory to parent of this script
"""
os.chdir(Path(__file__).resolve().parent)
print(f"- Current Working Directory: \n\t{os.getcwd()}")
def parse_arguments(self):
def _parse_arguments(self):
"""
Parse arguments passed to script
"""
parser = argparse.ArgumentParser(description='Builds OpenCore-Patcher binary')
parser.add_argument('--build_tui', action='store_true', help='Builds TUI binary, if omitted GUI binary is built')
parser.add_argument('--branch', type=str, help='Git branch name')
parser.add_argument('--commit', type=str, help='Git commit URL')
parser.add_argument('--commit_date', type=str, help='Git commit date')
@@ -48,7 +64,12 @@ class create_binary:
args = parser.parse_args()
return args
def setup_pathing(self):
def _setup_pathing(self):
"""
Initialize pathing for pyinstaller
"""
python_path = sys.executable
python_binary = python_path.split("/")[-1]
python_bin_dir = python_path.strip(python_binary)
@@ -70,25 +91,36 @@ class create_binary:
self.pyinstaller_path = pyinstaller_path
def preflight_processes(self):
def _preflight_processes(self):
"""
Start preflight processes
"""
print("- Starting preflight processes")
self.setup_pathing()
self.delete_extra_binaries()
self.download_resources()
if not self.args.build_tui:
# payloads.dmg is only needed for GUI builds
self.generate_payloads_dmg()
self._setup_pathing()
self._delete_extra_binaries()
self._download_resources()
self._generate_payloads_dmg()
def _postflight_processes(self):
"""
Start postflight processes
"""
def postflight_processes(self):
print("- Starting postflight processes")
if self.args.build_tui:
self.move_launcher()
self.patch_load_command()
self.add_commit_data()
self.post_flight_cleanup()
self.mini_validate()
self._patch_load_command()
self._add_commit_data()
self._post_flight_cleanup()
self._mini_validate()
def _build_binary(self):
"""
Build binary via pyinstaller
"""
def build_binary(self):
if Path(f"./dist/OpenCore-Patcher.app").exists():
print("- Found OpenCore-Patcher.app, removing...")
rm_output = subprocess.run(
@@ -101,12 +133,8 @@ class create_binary:
raise Exception("Remove failed")
if self.args.build_tui:
print("- Building TUI binary...")
build_args = [self.pyinstaller_path, "./OpenCore-Patcher.spec", "--noconfirm"]
else:
print("- Building GUI binary...")
build_args = [self.pyinstaller_path, "./OpenCore-Patcher-GUI.spec", "--noconfirm"]
print("- Building GUI binary...")
build_args = [self.pyinstaller_path, "./OpenCore-Patcher-GUI.spec", "--noconfirm"]
build_result = subprocess.run(build_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if build_result.returncode != 0:
@@ -114,27 +142,52 @@ class create_binary:
print(build_result.stderr.decode('utf-8'))
raise Exception("Build failed")
def delete_extra_binaries(self):
delete_files = [
"AutoPkg-Assets.pkg",
"AutoPkg-Assets.pkg.zip",
"InstallAssistant.pkg",
"InstallAssistant.pkg.integrityDataV1",
"KDK.dmg",
def _delete_extra_binaries(self):
"""
Delete extra binaries from payloads directory
"""
whitelist_folders = [
"ACPI",
"Config",
"Drivers",
"Icon",
"InstallPackage",
"Kexts",
"OpenCore",
"Tools",
]
whitelist_files = [
"com.dortania.opencore-legacy-patcher.auto-patch.plist",
"entitlements.plist",
"launcher.sh",
"OC-Patcher-TUI.icns",
"OC-Patcher.icns",
"Universal-Binaries.zip",
]
print("- Deleting extra binaries...")
for file in Path("payloads").glob(pattern="*"):
if file.name in delete_files or file.name.startswith("OpenCore-Legacy-Patcher"):
if file.is_dir():
if file.name in whitelist_folders:
continue
print(f" - Deleting {file.name}")
file.unlink()
elif (Path(file) / Path("Contents/Resources/createinstallmedia")).exists():
print(f" - Deleting {file}")
subprocess.run(["rm", "-rf", file])
elif Path(file).is_dir() and file.name == "Universal-Binaries":
print(f" - Deleting {file}")
subprocess.run(["rm", "-rf", file])
else:
if file.name in whitelist_files:
continue
print(f" - Deleting {file.name}")
subprocess.run(["rm", "-f", file])
def _download_resources(self):
"""
Download required dependencies
"""
def download_resources(self):
patcher_support_pkg_version = constants.Constants().patcher_support_pkg_version
required_resources = [
"Universal-Binaries.zip"
@@ -181,21 +234,29 @@ class create_binary:
print(mv_output.stderr.decode('utf-8'))
raise Exception("Move failed")
def generate_payloads_dmg(self):
def _generate_payloads_dmg(self):
"""
Generate disk image containing all payloads
Disk image will be password protected due to issues with
Apple's notarization system and inclusion of kernel extensions
"""
if Path("./payloads.dmg").exists():
if self.args.reset_binaries:
print(" - Removing old payloads.dmg")
rm_output = subprocess.run(
["rm", "-rf", "./payloads.dmg"],
stdout=subprocess.PIPE, stderr=subprocess.PIPE
)
if rm_output.returncode != 0:
print("- Remove failed")
print(rm_output.stderr.decode('utf-8'))
raise Exception("Remove failed")
else:
if not self.args.reset_binaries:
print(" - payloads.dmg already exists, skipping creation")
return
print(" - Removing old payloads.dmg")
rm_output = subprocess.run(
["rm", "-rf", "./payloads.dmg"],
stdout=subprocess.PIPE, stderr=subprocess.PIPE
)
if rm_output.returncode != 0:
print("- Remove failed")
print(rm_output.stderr.decode('utf-8'))
raise Exception("Remove failed")
print(" - Generating DMG...")
dmg_output = subprocess.run([
'hdiutil', 'create', './payloads.dmg',
@@ -213,7 +274,12 @@ class create_binary:
print(" - DMG generation complete")
def add_commit_data(self):
def _add_commit_data(self):
"""
Add commit data to Info.plist
"""
if not self.args.branch and not self.args.commit and not self.args.commit_date:
print(" - No commit data provided, adding source info")
branch = "Built from source"
@@ -233,20 +299,25 @@ class create_binary:
}
plistlib.dump(plist, Path(plist_path).open("wb"), sort_keys=True)
def patch_load_command(self):
# Patches LC_VERSION_MIN_MACOSX in Load Command to report 10.10
#
# By default Pyinstaller will create binaries supporting 10.13+
# However this limitation is entirely arbitrary for our libraries
# and instead we're able to support 10.10 without issues.
#
# To verify set version:
# otool -l ./dist/OpenCore-Patcher.app/Contents/MacOS/OpenCore-Patcher
#
# cmd LC_VERSION_MIN_MACOSX
# cmdsize 16
# version 10.13
# sdk 10.9
def _patch_load_command(self):
"""
Patch LC_VERSION_MIN_MACOSX in Load Command to report 10.10
By default Pyinstaller will create binaries supporting 10.13+
However this limitation is entirely arbitrary for our libraries
and instead we're able to support 10.10 without issues.
To verify set version:
otool -l ./dist/OpenCore-Patcher.app/Contents/MacOS/OpenCore-Patcher
cmd LC_VERSION_MIN_MACOSX
cmdsize 16
version 10.13
sdk 10.9
"""
print(" - Patching LC_VERSION_MIN_MACOSX")
path = './dist/OpenCore-Patcher.app/Contents/MacOS/OpenCore-Patcher'
find = b'\x00\x0D\x0A\x00' # 10.13 (0xA0D)
@@ -257,19 +328,12 @@ class create_binary:
with open(path, 'wb') as f:
f.write(data)
def move_launcher(self):
print(" - Adding TUI launcher")
mv_output = subprocess.run(
["cp", "./payloads/launcher.sh", "./dist/OpenCore-Patcher.app/Contents/MacOS/Launcher"],
stdout=subprocess.PIPE, stderr=subprocess.PIPE
)
if mv_output.returncode != 0:
print(" - Move failed")
print(mv_output.stderr.decode('utf-8'))
raise Exception("Move failed")
def post_flight_cleanup(self):
# Remove ./dist/OpenCore-Patcher
def _post_flight_cleanup(self):
"""
Post flight cleanup
"""
path = "./dist/OpenCore-Patcher"
print(f" - Removing {path}")
rm_output = subprocess.run(
@@ -281,9 +345,12 @@ class create_binary:
print(rm_output.stderr.decode('utf-8'))
raise Exception(f"Remove failed: {path}")
def mini_validate(self):
# Ensure binary can start
# Only build a single config, TUI CI will do in-depth validation
def _mini_validate(self):
"""
Validate generated binary
"""
print(" - Validating binary")
validate_output = subprocess.run(
["./dist/OpenCore-Patcher.app/Contents/MacOS/OpenCore-Patcher", "--build", "--model", "MacPro3,1"],
@@ -294,5 +361,6 @@ class create_binary:
print(validate_output.stderr.decode('utf-8'))
raise Exception("Validation failed")
if __name__ == "__main__":
create_binary()
CreateBinary()

View File

@@ -25,7 +25,10 @@
- Allows for more reliable network calls and downloads
- Better supports network timeouts and disconnects
- Dramatically less noise in console during downloads
- Removed unused sys_patch_downloader.py module
- Removed unused modules:
- sys_patch_downloader.py
- run.py
- TUI modules
- Build Server Changes:
- Upgrade Python backend to 3.10.9
- Upgrade Python modules:

View File

@@ -3,4 +3,4 @@
from resources import main
if __name__ == '__main__':
main.OpenCoreLegacyPatcher(True)
main.OpenCoreLegacyPatcher()

View File

@@ -1,6 +0,0 @@
#!/usr/bin/env python3
# Copyright (C) 2020-2022, Dhinak G, Mykola Grymalyuk
from resources import main
if __name__ == '__main__':
main.OpenCoreLegacyPatcher()

View File

@@ -1,44 +0,0 @@
# -*- mode: python ; coding: utf-8 -*-
import sys, os
sys.path.append(os.path.abspath(os.getcwd()))
from resources import constants
block_cipher = None
a = Analysis(['OpenCore-Patcher.command'],
pathex=['resources', 'data'],
binaries=[],
datas=[('payloads', 'payloads')],
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
excludes=['wxPython', 'wxpython'],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
[],
name='OpenCore-Patcher',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=True )
app = BUNDLE(exe,
name='OpenCore-Patcher.app',
icon="payloads/OC-Patcher-TUI.icns",
bundle_identifier="com.dortania.opencore-legacy-patcher-tui",
info_plist={
"CFBundleShortVersionString": constants.Constants().patcher_version,
"CFBundleExecutable": "MacOS/Launcher",
"NSHumanReadableCopyright": constants.Constants().copyright_date,
})

View File

@@ -55,7 +55,7 @@ See `-h`/`--help` for more information on supported CLI arguments.
## Generating prebuilt binaries
The main goal of generating prebuilt binaries is to strip the requirement of a local python installation for users. For developers, there's very little benefit besides enabling dark mode support in the GUI. For development, simply use the OpenCore-Patcher.command file with a python3 installation.
The main goal of generating prebuilt binaries is to strip the requirement of a local python installation for users. For developers, there's very little benefit besides enabling dark mode support in the GUI. For development, simply use the OpenCore-Patcher-GUI.command file with a python3 installation.
* Note that due to PyInstaller's linking mechanism, binaries generated on Catalina and newer are not compatible with High Sierra and older
* To ensure the largest compatibility, generate binaries on macOS Mojave. These binaries will be compatible with macOS 10.9 to macOS 12.
@@ -68,7 +68,6 @@ pip3 install pyinstaller
cd ~/Developer/OpenCore-Legacy-Patcher/
# Create the pyinstaller based Application
# Optional Arguments
# '--build_tui': Create TUI vairant (deprecated)
# '--reset_binaries': Redownload and generate support files
python3 Build-Binary.command
# Open build folder

View File

@@ -6,19 +6,20 @@
# - 0x3 used in 11.0.1 dyld source:
# - https://github.com/apple-oss-distributions/dyld/blob/5c9192436bb195e7a8fe61f22a229ee3d30d8222/testing/test-cases/kernel-hello-world.dtest/main.c#L2
class apple_mobile_file_integrity:
import enum
class AppleMobileFileIntegrity(enum.IntEnum):
# Names set are solely for readability
# Internal names are unknown
amfi_values = {
"AMFI_ALLOW_TASK_FOR_PID": False, # 0x1 - Allow Task for PID (alt. amfi_unrestrict_task_for_pid=0x1)
"AMFI_ALLOW_INVALID_SIGNATURE": False, # 0x2 - Reduce sig enforcement (alt. amfi_allow_any_signature=0x1)
"AMFI_LV_ENFORCE_THIRD_PARTY": False, # 0x4 - Don't mark external binaries as platform binaries
"AMFI_UNKNOWN_1": False, # 0x8
"AMFI_UNKNOWN_2": False, # 0x10
"AMFI_UNKNOWN_3": False, # 0x20
"AMFI_UNKNOWN_4": False, # 0x40
"AMFI_ALLOW_EVERYTHING": False, # 0x80 - Disable sig enforcement and Library Validation (alt. amfi_get_out_of_my_way=0x1)
},
AMFI_ALLOW_TASK_FOR_PID: int = 0x1 # Allow Task for PID (alt. amfi_unrestrict_task_for_pid=0x1)
AMFI_ALLOW_INVALID_SIGNATURE: int = 0x2 # Reduce sig enforcement (alt. amfi_allow_any_signature=0x1)
AMFI_LV_ENFORCE_THIRD_PARTY: int = 0x4 # Don't mark external binaries as platform binaries
AMFI_UNKNOWN_1: int = 0x8
AMFI_UNKNOWN_2: int = 0x10
AMFI_UNKNOWN_3: int = 0x20
AMFI_UNKNOWN_4: int = 0x40
AMFI_ALLOW_EVERYTHING: int = 0x80 # Disable sig enforcement and Library Validation (alt. amfi_get_out_of_my_way=0x1)
# Internally within AMFI.kext, Apple references 0x2 and 0x80 as both 'Disable signature enforcement'
# However 0x80 is a higher privilege than 0x2, and breaks TCC support in OS (ex. Camera, Microphone, etc prompts)

View File

@@ -1,11 +0,0 @@
# Mirrors of Apple's InstallAssistant.pkg
# 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": 12211077798,
"Source": "Archive.org",
"integrity": None,
}

View File

@@ -22,7 +22,7 @@ Regarding OS support, see below:
| Support Entry | Supported OSes | Description | Comment |
| :--- | :--- | :--- | :--- |
| HostOS | macOS 10.9 - macOS 13 | Refers to OSes where running OpenCore-Patcher.app are supported | Supports 10.7+ if [Python 3.9 or higher](https://www.python.org/downloads/) is manually installed, simply run the `OpenCore-Patcher.command` located in the repo |
| HostOS | macOS 10.9 - macOS 13 | Refers to OSes where running OpenCore-Patcher.app are supported | Supports 10.7+ if [Python 3.9 or higher](https://www.python.org/downloads/) is manually installed, simply run the `OpenCore-Patcher-GUI.command` located in the repo |
| TargetOS | macOS 11 - macOS 13 | Refers to OSes that can be patched to run with OpenCore | May support 10.4 and newer (in a potentially broken state). No support provided. |
### MacBook

View File

@@ -20,14 +20,11 @@ And voila! No more USB drive required.
To do this, run the OpenCore Patcher and head to Patcher Settings:
| GUI Settings | TUI Settings
| :--- | :--- |
|![](../images/OCLP-GUI-Settings-ShowPicker.png) | ![](../images/OCLP-TUI-Settings.png) |
![](../images/OCLP-GUI-Settings-ShowPicker.png)
Here you can change different patcher settings, however the main interest is:
* Show Boot Picker (GUI)
* Set ShowPicker Mode (TUI)
* Show Boot Picker
Once you've toggled them both off, build your OpenCore EFI once again and install to your desired drive. Now to show the OpenCore selector, you can simply hold down the "ESC" key while clicking on EFI boot, and then you can release the "ESC" key when you see the cursor arrow at the top left.

View File

@@ -3,6 +3,7 @@
import enum
from resources import utilities
from data import amfi_data
class AmfiConfigDetectLevel(enum.IntEnum):
@@ -67,32 +68,31 @@ class AmfiConfigurationDetection:
amfi_value = 0
for arg in self.boot_args:
if arg.startswith("amfi="):
try:
amfi_value = arg.split("=")
if len(amfi_value) != 2:
return
amfi_value = amfi_value[1]
if amfi_value.startswith("0x"):
amfi_value = int(amfi_value, 16)
else:
amfi_value = int(amfi_value)
except:
if not arg.startswith("amfi="):
continue
try:
amfi_value = arg.split("=")
if len(amfi_value) != 2:
return
break
amfi_value = amfi_value[1]
if amfi_value.startswith("0x"):
amfi_value = int(amfi_value, 16)
else:
amfi_value = int(amfi_value)
except:
return
break
if amfi_value == 0:
return
if amfi_value & 0x1:
self.AMFI_ALLOW_TASK_FOR_PID = True
if amfi_value & 0x2:
self.AMFI_ALLOW_INVALID_SIGNATURE = True
if amfi_value & 0x4:
self.AMFI_LV_ENFORCE_THIRD_PARTY = True
if amfi_value & 0x80:
self.AMFI_ALLOW_EVERYTHING = True
self.SKIP_LIBRARY_VALIDATION = True
self.AMFI_ALLOW_TASK_FOR_PID: bool = amfi_value & amfi_data.AppleMobileFileIntegrity.AMFI_ALLOW_TASK_FOR_PID
self.AMFI_ALLOW_INVALID_SIGNATURE: bool = amfi_value & amfi_data.AppleMobileFileIntegrity.AMFI_ALLOW_INVALID_SIGNATURE
self.AMFI_LV_ENFORCE_THIRD_PARTY: bool = amfi_value & amfi_data.AppleMobileFileIntegrity.AMFI_LV_ENFORCE_THIRD_PARTY
if amfi_value & amfi_data.AppleMobileFileIntegrity.AMFI_ALLOW_EVERYTHING:
self.AMFI_ALLOW_EVERYTHING = True
self.SKIP_LIBRARY_VALIDATION = True
self.AMFI_ALLOW_INVALID_SIGNATURE = True

View File

@@ -1,116 +1,190 @@
import sys
from resources import defaults, utilities, validation
from resources.sys_patch import sys_patch, sys_patch_auto
from resources.build import build
from data import model_array
import threading
import time
import logging
import sys
from resources import defaults, utilities, validation, constants
from resources.sys_patch import sys_patch, sys_patch_auto
from resources.build import build
from data import model_array
# Generic building args
class arguments:
def __init__(self):
def __init__(self, global_constants: constants.Constants):
self.constants: constants.Constants = global_constants
self.args = utilities.check_cli_args()
def parse_arguments(self, settings):
self._parse_arguments()
def _parse_arguments(self):
"""
Parses arguments passed to the patcher
"""
if self.args.validate:
validation.validate(settings)
elif self.args.build:
self._validation_handler()
return
if self.args.build:
self._build_handler()
return
if self.args.patch_sys_vol:
self._sys_patch_handler()
return
if self.args.unpatch_sys_vol:
self._sys_unpatch_handler()
return
if self.args.auto_patch:
self._sys_patch_auto_handler()
return
def _validation_handler(self):
"""
Enter validation mode
"""
validation.PatcherValidation(self.constants)
def _sys_patch_handler(self):
"""
Start root volume patching
"""
logging.info("- Set System Volume patching")
if "Library/InstallerSandboxes/" in str(self.constants.payload_path):
logging.info("- Running from Installer Sandbox")
thread = threading.Thread(target=sys_patch.PatchSysVolume(self.constants.custom_model or self.constants.computer.real_model, self.constants, None).start_patch)
thread.start()
while thread.is_alive():
utilities.block_os_updaters()
time.sleep(1)
else:
sys_patch.PatchSysVolume(self.constants.custom_model or self.constants.computer.real_model, self.constants, None).start_patch()
def _sys_unpatch_handler(self):
"""
Start root volume unpatching
"""
logging.info("- Set System Volume unpatching")
sys_patch.PatchSysVolume(self.constants.custom_model or self.constants.computer.real_model, self.constants, None).start_unpatch()
def _sys_patch_auto_handler(self):
"""
Start root volume auto patching
"""
logging.info("- Set Auto patching")
sys_patch_auto.AutomaticSysPatch(self.constants).start_auto_patch()
def _build_handler(self):
"""
Start config building process
"""
if self.args.model:
if self.args.model:
if self.args.model:
logging.info(f"- Using custom model: {self.args.model}")
settings.custom_model = self.args.model
defaults.generate_defaults(settings.custom_model, False, settings)
elif settings.computer.real_model not in model_array.SupportedSMBIOS and settings.allow_oc_everywhere is False:
logging.info(
"""Your model is not supported by this patcher for running unsupported OSes!"
logging.info(f"- Using custom model: {self.args.model}")
self.constants.custom_model = self.args.model
defaults.GenerateDefaults(self.constants.custom_model, False, self.constants)
elif self.constants.computer.real_model not in model_array.SupportedSMBIOS and self.constants.allow_oc_everywhere is False:
logging.info(
"""Your model is not supported by this patcher for running unsupported OSes!"
If you plan to create the USB for another machine, please select the "Change Model" option in the menu."""
)
sys.exit(1)
else:
logging.info(f"- Using detected model: {settings.computer.real_model}")
defaults.generate_defaults(settings.custom_model, True, settings)
If you plan to create the USB for another machine, please select the "Change Model" option in the menu."""
)
sys.exit(1)
else:
logging.info(f"- Using detected model: {self.constants.computer.real_model}")
defaults.GenerateDefaults(self.constants.custom_model, True, self.constants)
if self.args.disk:
logging.info(f"- Install Disk set: {self.args.disk}")
settings.disk = self.args.disk
if self.args.verbose:
logging.info("- Set verbose configuration")
settings.verbose_debug = True
else:
settings.verbose_debug = False # Override Defaults detected
if self.args.debug_oc:
logging.info("- Set OpenCore DEBUG configuration")
settings.opencore_debug = True
settings.opencore_build = "DEBUG"
if self.args.debug_kext:
logging.info("- Set kext DEBUG configuration")
settings.kext_debug = True
if self.args.hide_picker:
logging.info("- Set HidePicker configuration")
settings.showpicker = False
if self.args.disable_sip:
logging.info("- Set Disable SIP configuration")
settings.sip_status = False
else:
settings.sip_status = True # Override Defaults detected
if self.args.disable_smb:
logging.info("- Set Disable SecureBootModel configuration")
settings.secure_status = False
else:
settings.secure_status = True # Override Defaults detected
if self.args.vault:
logging.info("- Set Vault configuration")
settings.vault = True
if self.args.firewire:
logging.info("- Set FireWire Boot configuration")
settings.firewire_boot = True
if self.args.nvme:
logging.info("- Set NVMe Boot configuration")
settings.nvme_boot = True
if self.args.wlan:
logging.info("- Set Wake on WLAN configuration")
settings.enable_wake_on_wlan = True
if self.args.disable_tb:
logging.info("- Set Disable Thunderbolt configuration")
settings.disable_tb = True
if self.args.force_surplus:
logging.info("- Forcing SurPlus override configuration")
settings.force_surplus = True
if self.args.moderate_smbios:
logging.info("- Set Moderate SMBIOS Patching configuration")
settings.serial_settings = "Moderate"
if self.args.smbios_spoof:
if self.args.smbios_spoof == "Minimal":
settings.serial_settings = "Minimal"
elif self.args.smbios_spoof == "Moderate":
settings.serial_settings = "Moderate"
elif self.args.smbios_spoof == "Advanced":
settings.serial_settings = "Advanced"
else:
logging.info(f"- Unknown SMBIOS arg passed: {self.args.smbios_spoof}")
if self.args.disk:
logging.info(f"- Install Disk set: {self.args.disk}")
self.constants.disk = self.args.disk
if self.args.support_all:
logging.info("- Building for natively supported model")
settings.allow_oc_everywhere = True
settings.serial_settings = "None"
build.build_opencore(settings.custom_model or settings.computer.real_model, settings).build_opencore()
elif self.args.patch_sys_vol:
logging.info("- Set System Volume patching")
if self.args.verbose:
logging.info("- Set verbose configuration")
self.constants.verbose_debug = True
else:
self.constants.verbose_debug = False # Override Defaults detected
if "Library/InstallerSandboxes/" in str(settings.payload_path):
logging.info("- Running from Installer Sandbox")
thread = threading.Thread(target=sys_patch.PatchSysVolume(settings.custom_model or settings.computer.real_model, settings, None).start_patch)
thread.start()
while thread.is_alive():
utilities.block_os_updaters()
time.sleep(1)
if self.args.debug_oc:
logging.info("- Set OpenCore DEBUG configuration")
self.constants.opencore_debug = True
self.constants.opencore_build = "DEBUG"
if self.args.debug_kext:
logging.info("- Set kext DEBUG configuration")
self.constants.kext_debug = True
if self.args.hide_picker:
logging.info("- Set HidePicker configuration")
self.constants.showpicker = False
if self.args.disable_sip:
logging.info("- Set Disable SIP configuration")
self.constants.sip_status = False
else:
self.constants.sip_status = True # Override Defaults detected
if self.args.disable_smb:
logging.info("- Set Disable SecureBootModel configuration")
self.constants.secure_status = False
else:
self.constants.secure_status = True # Override Defaults detected
if self.args.vault:
logging.info("- Set Vault configuration")
self.constants.vault = True
if self.args.firewire:
logging.info("- Set FireWire Boot configuration")
self.constants.firewire_boot = True
if self.args.nvme:
logging.info("- Set NVMe Boot configuration")
self.constants.nvme_boot = True
if self.args.wlan:
logging.info("- Set Wake on WLAN configuration")
self.constants.enable_wake_on_wlan = True
if self.args.disable_tb:
logging.info("- Set Disable Thunderbolt configuration")
self.constants.disable_tb = True
if self.args.force_surplus:
logging.info("- Forcing SurPlus override configuration")
self.constants.force_surplus = True
if self.args.moderate_smbios:
logging.info("- Set Moderate SMBIOS Patching configuration")
self.constants.serial_settings = "Moderate"
if self.args.smbios_spoof:
if self.args.smbios_spoof == "Minimal":
self.constants.serial_settings = "Minimal"
elif self.args.smbios_spoof == "Moderate":
self.constants.serial_settings = "Moderate"
elif self.args.smbios_spoof == "Advanced":
self.constants.serial_settings = "Advanced"
else:
sys_patch.PatchSysVolume(settings.custom_model or settings.computer.real_model, settings, None).start_patch()
elif self.args.unpatch_sys_vol:
logging.info("- Set System Volume unpatching")
sys_patch.PatchSysVolume(settings.custom_model or settings.computer.real_model, settings, None).start_unpatch()
elif self.args.auto_patch:
logging.info("- Set Auto patching")
sys_patch_auto.AutomaticSysPatch(settings).start_auto_patch()
logging.info(f"- Unknown SMBIOS arg passed: {self.args.smbios_spoof}")
if self.args.support_all:
logging.info("- Building for natively supported model")
self.constants.allow_oc_everywhere = True
self.constants.serial_settings = "None"
build.build_opencore(self.constants.custom_model or self.constants.computer.real_model, self.constants).build_opencore()

File diff suppressed because it is too large Load Diff

View File

@@ -1,29 +1,40 @@
# Parse Commit Info from binary's info.plist
# App Structure:
# OpenCore-Patcher.app:
# Contents:
# MacOS:
# OpenCore-Patcher
# Info.plist
from pathlib import Path
import plistlib
class commit_info:
class ParseCommitInfo:
def __init__(self, binary_path: str):
"""
Parameters:
binary_path (str): Path to binary
"""
def __init__(self, binary_path):
self.binary_path = str(binary_path)
self.plist_path = self.convert_binary_path_to_plist_path()
self.plist_path = self._convert_binary_path_to_plist_path()
def convert_binary_path_to_plist_path(self):
def _convert_binary_path_to_plist_path(self):
"""
Resolve Info.plist path from binary path
"""
if Path(self.binary_path).exists():
plist_path = self.binary_path.replace("MacOS/OpenCore-Patcher", "Info.plist")
if Path(plist_path).exists() and plist_path.endswith(".plist"):
return plist_path
return None
def generate_commit_info(self):
"""
Generate commit info from Info.plist
Returns:
tuple: (Branch, Commit Date, Commit URL)
"""
if self.plist_path:
plist_info = plistlib.load(Path(self.plist_path).open("rb"))
if "Github" in plist_info:

View File

@@ -1,36 +1,52 @@
# Generate Default Data
from resources import utilities, device_probe, generate_smbios, global_settings
from data import smbios_data, cpu_data, os_data
import subprocess
from resources import (
utilities,
device_probe,
generate_smbios,
global_settings,
constants
)
from data import (
smbios_data,
cpu_data,
os_data
)
class generate_defaults:
def __init__(self, model, host_is_target, settings):
self.model = model
self.constants = settings
self.host_is_target = host_is_target
class GenerateDefaults:
def __init__(self, model: str, host_is_target: bool, global_constants: constants.Constants):
self.constants: constants.Constants = global_constants
self.model: str = model
self.host_is_target: str = host_is_target
# Reset Variables
self.constants.sip_status = True
self.constants.secure_status = False
self.constants.disable_cs_lv = False
self.constants.disable_amfi = False
self.constants.fu_status = True
self.constants.fu_arguments = None
self.constants.sip_status: bool = True
self.constants.secure_status: bool = False
self.constants.disable_cs_lv: bool = False
self.constants.disable_amfi: bool = False
self.constants.fu_status: bool = True
self.constants.custom_serial_number = ""
self.constants.custom_board_serial_number = ""
self.constants.fu_arguments: str = None
self.general_probe()
self.nvram_probe()
self.gpu_probe()
self.networking_probe()
self.misc_hardwares_probe()
self.smbios_probe()
self.constants.custom_serial_number: str = ""
self.constants.custom_board_serial_number: str = ""
self._general_probe()
self._nvram_probe()
self._gpu_probe()
self._networking_probe()
self._misc_hardwares_probe()
self._smbios_probe()
def general_probe(self):
def _general_probe(self):
"""
General probe for data
"""
if "Book" in self.model:
self.constants.set_content_caching = False
@@ -40,11 +56,11 @@ class generate_defaults:
if self.model in ["MacBookPro8,2", "MacBookPro8,3"]:
# Users disabling TS2 most likely have a faulty dGPU
# users can override this in settings
ts2_status = global_settings.global_settings().read_property("MacBookPro_TeraScale_2_Accel")
ts2_status = global_settings.GlobalEnviromentSettings().read_property("MacBookPro_TeraScale_2_Accel")
if ts2_status is True:
self.constants.allow_ts2_accel = True
else:
global_settings.global_settings().write_property("MacBookPro_TeraScale_2_Accel", False)
global_settings.GlobalEnviromentSettings().write_property("MacBookPro_TeraScale_2_Accel", False)
self.constants.allow_ts2_accel = False
if self.model in smbios_data.smbios_dictionary:
@@ -61,14 +77,19 @@ class generate_defaults:
# Check if running in RecoveryOS
self.constants.recovery_status = utilities.check_recovery()
if global_settings.global_settings().read_property("Force_Web_Drivers") is True:
if global_settings.GlobalEnviromentSettings().read_property("Force_Web_Drivers") is True:
self.constants.force_nv_web = True
result = global_settings.global_settings().read_property("ShouldNukeKDKs")
result = global_settings.GlobalEnviromentSettings().read_property("ShouldNukeKDKs")
if result is False:
self.constants.should_nuke_kdks = False
def smbios_probe(self):
def _smbios_probe(self):
"""
SMBIOS specific probe
"""
if not self.host_is_target:
if self.model in ["MacPro4,1", "MacPro5,1"]:
# Allow H.265 on AMD
@@ -99,7 +120,11 @@ class generate_defaults:
self.constants.force_vmm = False
def nvram_probe(self):
def _nvram_probe(self):
"""
NVRAM specific probe
"""
if not self.host_is_target:
return
@@ -120,7 +145,11 @@ class generate_defaults:
self.constants.custom_cpu_model_value = custom_cpu_model_value.split("%00")[0]
def networking_probe(self):
def _networking_probe(self):
"""
Networking specific probe
"""
if self.host_is_target:
if not (
(
@@ -157,7 +186,11 @@ class generate_defaults:
self.constants.fu_status = True
self.constants.fu_arguments = " -disable_sidecar_mac"
def misc_hardwares_probe(self):
def _misc_hardwares_probe(self):
"""
Misc probe
"""
if self.host_is_target:
if self.constants.computer.usb_controllers:
if self.model in smbios_data.smbios_dictionary:
@@ -170,7 +203,11 @@ class generate_defaults:
break
def gpu_probe(self):
def _gpu_probe(self):
"""
Graphics specific probe
"""
gpu_dict = []
if self.host_is_target:
gpu_dict = self.constants.computer.gpus

View File

@@ -9,18 +9,23 @@ import logging
import os
import subprocess
class global_settings:
class GlobalEnviromentSettings:
"""
Library for querying and writing global enviroment settings
"""
def __init__(self):
self.file_name = ".com.dortania.opencore-legacy-patcher.plist"
self.global_settings_folder = "/Users/Shared"
self.global_settings_plist = f"{self.global_settings_folder}/{self.file_name}"
self.file_name: str = ".com.dortania.opencore-legacy-patcher.plist"
self.global_settings_folder: str = "/Users/Shared"
self.global_settings_plist: str = f"{self.global_settings_folder}/{self.file_name}"
self._generate_settings_file()
self._convert_defaults_to_global_settings()
self._fix_file_permission()
def read_property(self, property_name):
def read_property(self, property_name: str):
"""
Reads a property from the global settings file
"""
@@ -32,7 +37,7 @@ class global_settings:
return None
def write_property(self, property_name, property_value):
def write_property(self, property_name: str, property_value):
"""
Writes a property to the global settings file
"""

View File

@@ -33,7 +33,6 @@ from resources import (
install,
installer,
utilities,
run,
generate_smbios,
updates,
integrity_verification,
@@ -256,7 +255,7 @@ class wx_python_gui:
if local_build_date <= installed_build_date:
return False
elif updates.check_binary_updates(self.constants).check_if_build_newer(local_version, application_version) is False:
elif updates.CheckBinaryUpdates(self.constants)._check_if_build_newer(local_version, application_version) is False:
return False
# Ask user if they want to move the application to the Applications folder
@@ -307,11 +306,11 @@ class wx_python_gui:
return
did_find_update = False
ignore_updates = global_settings.global_settings().read_property("IgnoreAppUpdates")
ignore_updates = global_settings.GlobalEnviromentSettings().read_property("IgnoreAppUpdates")
if ignore_updates is not True:
self.constants.ignore_updates = False
self.constants.has_checked_updates = True
dict = updates.check_binary_updates(self.constants).check_binary_updates()
dict = updates.CheckBinaryUpdates(self.constants).check_binary_updates()
if dict:
for entry in dict:
version = dict[entry]["Version"]
@@ -331,7 +330,7 @@ class wx_python_gui:
elif response == wx.ID_NO:
logging.info("- Setting IgnoreAppUpdates to True")
self.constants.ignore_updates = True
global_settings.global_settings().write_property("IgnoreAppUpdates", True)
global_settings.GlobalEnviromentSettings().write_property("IgnoreAppUpdates", True)
else:
self.constants.ignore_updates = True
logging.info("- Ignoring App Updates due to defaults")
@@ -589,10 +588,10 @@ class wx_python_gui:
if self.constants.start_build_install is True:
self.build_install_menu()
elif "--gui_patch" in sys.argv:
self.patches = sys_patch_detect.detect_root_patch(self.computer.real_model, self.constants).detect_patch_set()
self.patches = sys_patch_detect.DetectRootPatch(self.computer.real_model, self.constants).detect_patch_set()
self.root_patch_start()
elif "--gui_unpatch" in sys.argv:
self.patches = sys_patch_detect.detect_root_patch(self.computer.real_model, self.constants).detect_patch_set()
self.patches = sys_patch_detect.DetectRootPatch(self.computer.real_model, self.constants).detect_patch_set()
self.root_patch_revert()
self.finished_auto_patch = True
self.constants.start_build_install = False
@@ -1079,7 +1078,7 @@ class wx_python_gui:
)
self.subheader.Centre(wx.HORIZONTAL)
patches = sys_patch_detect.detect_root_patch(self.computer.real_model, self.constants).detect_patch_set()
patches = sys_patch_detect.DetectRootPatch(self.computer.real_model, self.constants).detect_patch_set()
self.patches = patches
can_unpatch = patches["Validation: Unpatching Possible"]
if not any(not patch.startswith("Settings") and not patch.startswith("Validation") and patches[patch] is True for patch in patches):
@@ -2310,8 +2309,11 @@ class wx_python_gui:
def start_script(self):
utilities.disable_sleep_while_running()
args = [self.constants.oclp_helper_path, "/bin/sh", self.constants.installer_sh_path]
output, error, returncode = run.Run()._stream_output(comm=args)
args = [self.constants.oclp_helper_path, "/bin/sh", self.constants.installer_sh_path]
result = subprocess.run(args, capture_output=True, text=True)
output = result.stdout
error = result.stderr if result.stderr else ""
if "Install media now available at" in output:
logging.info("- Successfully created macOS installer")
while self.download_thread.is_alive():
@@ -2645,11 +2647,11 @@ class wx_python_gui:
if user_choice == self.computer.real_model:
logging.info(f"Using Real Model: {user_choice}")
self.constants.custom_model = None
defaults.generate_defaults(self.computer.real_model, True, self.constants)
defaults.GenerateDefaults(self.computer.real_model, True, self.constants)
else:
logging.info(f"Using Custom Model: {user_choice}")
self.constants.custom_model = user_choice
defaults.generate_defaults(self.constants.custom_model, False, self.constants)
defaults.GenerateDefaults(self.constants.custom_model, False, self.constants)
# Reload Settings
self.settings_menu(None)
@@ -2986,7 +2988,7 @@ class wx_python_gui:
else:
logging.info("Nuke KDKs disabled")
self.constants.should_nuke_kdks = False
global_settings.global_settings().write_property("ShouldNukeKDKs", self.constants.should_nuke_kdks)
global_settings.GlobalEnviromentSettings().write_property("ShouldNukeKDKs", self.constants.should_nuke_kdks)
def disable_library_validation_click(self, event):
if self.disable_library_validation_checkbox.GetValue():
@@ -3009,9 +3011,9 @@ class wx_python_gui:
def set_ignore_app_updates_click(self, event):
self.constants.ignore_updates = self.set_ignore_app_updates_checkbox.GetValue()
if self.constants.ignore_updates is True:
global_settings.global_settings().write_property("IgnoreAppUpdates", True)
global_settings.GlobalEnviromentSettings().write_property("IgnoreAppUpdates", True)
else:
global_settings.global_settings().write_property("IgnoreAppUpdates", False)
global_settings.GlobalEnviromentSettings().write_property("IgnoreAppUpdates", False)
def firewire_click(self, event=None):
if self.firewire_boot_checkbox.GetValue():
@@ -3096,21 +3098,21 @@ class wx_python_gui:
def ts2_accel_click(self, event=None):
if self.set_terascale_accel_checkbox.GetValue():
logging.info("TS2 Acceleration Enabled")
global_settings.global_settings().write_property("MacBookPro_TeraScale_2_Accel", True)
global_settings.GlobalEnviromentSettings().write_property("MacBookPro_TeraScale_2_Accel", True)
self.constants.allow_ts2_accel = True
else:
logging.info("TS2 Acceleration Disabled")
global_settings.global_settings().write_property("MacBookPro_TeraScale_2_Accel", False)
global_settings.GlobalEnviromentSettings().write_property("MacBookPro_TeraScale_2_Accel", False)
self.constants.allow_ts2_accel = False
def force_web_drivers_click(self, event=None):
if self.force_web_drivers_checkbox.GetValue():
logging.info("Force Web Drivers Enabled")
global_settings.global_settings().write_property("Force_Web_Drivers", True)
global_settings.GlobalEnviromentSettings().write_property("Force_Web_Drivers", True)
self.constants.force_nv_web = True
else:
logging.info("Force Web Drivers Disabled")
global_settings.global_settings().write_property("Force_Web_Drivers", False)
global_settings.GlobalEnviromentSettings().write_property("Force_Web_Drivers", False)
self.constants.force_nv_web = False
def windows_gmux_click(self, event=None):
@@ -3816,19 +3818,19 @@ OpenCore Legacy Patcher by default knows the most ideal
self.subheader_4.SetPosition(wx.Point(0, self.subheader2_2.GetPosition().y + self.subheader2_2.GetSize().height+ 5))
self.subheader_4.Centre(wx.HORIZONTAL)
is_dark_menu_bar = subprocess.run(["defaults", "read", "-g", "Moraea_DarkMenuBar"], stdout=subprocess.PIPE).stdout.decode("utf-8").strip()
is_dark_menu_bar = subprocess.run(["defaults", "read", "-g", "Moraea_DarkMenuBar"], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL).stdout.decode("utf-8").strip()
if is_dark_menu_bar in ["1", "true"]:
is_dark_menu_bar = True
else:
is_dark_menu_bar = False
is_blur_enabled = subprocess.run(["defaults", "read", "-g", "Moraea_BlurBeta"], stdout=subprocess.PIPE).stdout.decode("utf-8").strip()
is_blur_enabled = subprocess.run(["defaults", "read", "-g", "Moraea_BlurBeta"], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL).stdout.decode("utf-8").strip()
if is_blur_enabled in ["1", "true"]:
is_blur_enabled = True
else:
is_blur_enabled = False
is_rim_disabled = subprocess.run(["defaults", "read", "-g", "Moraea_RimBetaDisabled"], stdout=subprocess.PIPE).stdout.decode("utf-8").strip()
is_rim_disabled = subprocess.run(["defaults", "read", "-g", "Moraea_RimBetaDisabled"], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL).stdout.decode("utf-8").strip()
if is_rim_disabled in ["1", "true"]:
is_rim_disabled = True
else:

View File

@@ -8,7 +8,7 @@ import shutil
import os
import logging
from pathlib import Path
from resources import utilities, constants, tui_helpers
from resources import utilities, constants
from data import os_data
class tui_disk_installation:
@@ -75,65 +75,6 @@ class tui_disk_installation:
return supported_partitions
def copy_efi(self):
utilities.cls()
utilities.header(["Installing OpenCore to Drive"])
if not self.constants.opencore_release_folder.exists():
tui_helpers.TUIOnlyLogging.info(
["Installing OpenCore to Drive"],
"Press [Enter] to go back.\n",
[
"""OpenCore folder missing!
Please build OpenCore first!"""
],
).start()
return
logging.info("\nDisk picker is loading...")
all_disks = self.list_disks()
menu = tui_helpers.TUIMenu(
["Select Disk"],
"Please select the disk you would like to install OpenCore to: ",
in_between=["Missing disks? Ensure they have an EFI or FAT32 partition."],
return_number_instead_of_direct_call=True,
loop=True,
)
for disk in all_disks:
menu.add_menu_option(f"{disk}: {all_disks[disk]['name']} ({all_disks[disk]['size']})", key=disk[4:])
response = menu.start()
if response == -1:
return
disk_identifier = "disk" + response
selected_disk = all_disks[disk_identifier]
menu = tui_helpers.TUIMenu(
["Select Partition"],
"Please select the partition you would like to install OpenCore to: ",
return_number_instead_of_direct_call=True,
loop=True,
in_between=["Missing partitions? Ensure they are formatted as an EFI or FAT32.", "", "* denotes likely candidate."],
)
for partition in selected_disk["partitions"]:
if selected_disk["partitions"][partition]["fs"] not in ("msdos", "EFI"):
continue
text = f"{partition}: {selected_disk['partitions'][partition]['name']} ({utilities.human_fmt(selected_disk['partitions'][partition]['size'])})"
if selected_disk["partitions"][partition]["type"] == "EFI" or (
selected_disk["partitions"][partition]["type"] == "Microsoft Basic Data" and selected_disk["partitions"][partition]["size"] < 1024 * 1024 * 512
): # 512 megabytes:
text += " *"
menu.add_menu_option(text, key=partition[len(disk_identifier) + 1 :])
response = menu.start()
if response == -1:
return
self.install_opencore(f"{disk_identifier}s{response}")
def install_opencore(self, full_disk_identifier):
def determine_sd_card(media_name):
# Array filled with common SD Card names
@@ -169,18 +110,13 @@ Please build OpenCore first!"""
# cancelled prompt
return
else:
if self.constants.gui_mode is False:
tui_helpers.TUIOnlyLogging.info(
["Copying OpenCore"], "Press [Enter] to go back.\n", ["An error occurred!"] + result.stderr.decode().split("\n") + [""]
).start()
else:
logging.info("An error occurred!")
logging.info(result.stderr.decode())
logging.info("An error occurred!")
logging.info(result.stderr.decode())
# Check if we're in Safe Mode, and if so, tell user FAT32 is unsupported
if utilities.check_boot_mode() == "safe_boot":
logging.info("\nSafe Mode detected. FAT32 is unsupported by macOS in this mode.")
logging.info("Please disable Safe Mode and try again.")
# Check if we're in Safe Mode, and if so, tell user FAT32 is unsupported
if utilities.check_boot_mode() == "safe_boot":
logging.info("\nSafe Mode detected. FAT32 is unsupported by macOS in this mode.")
logging.info("Please disable Safe Mode and try again.")
return
partition_info = plistlib.loads(subprocess.run(f"diskutil info -plist {full_disk_identifier}".split(), stdout=subprocess.PIPE).stdout.decode().strip().encode())
parent_disk = partition_info["ParentWholeDisk"]
@@ -252,10 +188,7 @@ Please build OpenCore first!"""
logging.info("\nPress [Enter] to continue.\n")
input()
else:
if self.constants.gui_mode is False:
tui_helpers.TUIOnlyLogging.info(["Copying OpenCore"], "Press [Enter] to go back.\n", ["EFI failed to mount!"]).start()
else:
logging.info("EFI failed to mount!")
logging.info("EFI failed to mount!")
return False
return True

View File

@@ -4,7 +4,7 @@ import plistlib
import subprocess
import tempfile
import logging
from resources import utilities, tui_helpers, network_handler
from resources import utilities, network_handler
def list_local_macOS_installers():
# Finds all applicable macOS installers
@@ -325,52 +325,6 @@ def format_drive(disk_id):
input("\nPress Enter to exit")
return False
def select_disk_to_format():
utilities.cls()
utilities.header(["Installing OpenCore to Drive"])
logging.info("\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 = tui_helpers.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
def list_disk_to_format():

View File

@@ -15,8 +15,7 @@ import os
import logging
from resources import utilities, network_handler
from resources.constants import Constants
from resources import utilities, network_handler, constants
from data import os_data
KDK_INSTALL_PATH: str = "/Library/Developer/KDKs"
@@ -50,8 +49,8 @@ class KernelDebugKitObject:
"""
def __init__(self, constants: Constants, host_build: str, host_version: str, ignore_installed: bool = False, passive: bool = False):
self.constants: Constants = constants
def __init__(self, global_constants: constants.Constants, host_build: str, host_version: str, ignore_installed: bool = False, passive: bool = False):
self.constants: constants.Constants = global_constants
self.host_build: str = host_build # ex. 20A5384c
self.host_version: str = host_version # ex. 11.0.1
@@ -565,6 +564,8 @@ class KernelDebugKitUtilities:
logging.info(f"- Installing KDK package: {kdk_path.name}")
logging.info(f" - This may take a while...")
# TODO: Check whether enough disk space is available
result = utilities.elevated(["installer", "-pkg", kdk_path, "-target", "/"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
if result.returncode != 0:
logging.info("- Failed to install KDK:")

View File

@@ -4,21 +4,16 @@ import sys
import time
import logging
import threading
import subprocess
from pathlib import Path
from data import model_array
from resources.build import build
from resources.gui import gui_main
from resources import (
cli_menu,
constants,
utilities,
device_probe,
os_probe,
defaults,
arguments,
install,
tui_helpers,
reroute_payloads,
commit_info,
logging_handler
@@ -26,36 +21,50 @@ from resources import (
class OpenCoreLegacyPatcher:
def __init__(self, launch_gui=False):
self.constants = constants.Constants()
self.constants.wxpython_variant = launch_gui
"""
Initial entry point for starting OpenCore Legacy Patcher
"""
def __init__(self):
logging_handler.InitializeLoggingSupport()
self.constants: constants.Constants = constants.Constants()
self.constants.wxpython_variant: bool = True
logging.info(f"- Loading OpenCore Legacy Patcher v{self.constants.patcher_version}...")
self.generate_base_data()
self._generate_base_data()
if utilities.check_cli_args() is None:
if launch_gui is True:
utilities.disable_cls()
from resources.gui import gui_main
gui_main.wx_python_gui(self.constants).main_menu(None)
else:
self.main_menu()
gui_main.wx_python_gui(self.constants).main_menu(None)
def generate_base_data(self):
def _generate_base_data(self):
"""
Generate base data required for the patcher to run
"""
# Generate OS data
os_data = os_probe.OSProbe()
self.constants.detected_os = os_data.detect_kernel_major()
self.constants.detected_os_minor = os_data.detect_kernel_minor()
self.constants.detected_os_build = os_data.detect_os_build()
self.constants.detected_os_version = os_data.detect_os_version()
# Generate computer data
self.constants.computer = device_probe.Computer.probe()
self.constants.recovery_status = utilities.check_recovery()
self.computer = self.constants.computer
self.constants.booted_oc_disk = utilities.find_disk_off_uuid(utilities.clean_device_path(self.computer.opencore_path))
if self.constants.computer.firmware_vendor:
if self.constants.computer.firmware_vendor != "Apple":
self.constants.host_is_hackintosh = True
# Generate environment data
self.constants.recovery_status = utilities.check_recovery()
utilities.disable_cls()
# Generate binary data
launcher_script = None
launcher_binary = sys.executable
if "python" in launcher_binary:
@@ -65,83 +74,40 @@ class OpenCoreLegacyPatcher:
launcher_script = launcher_script.replace("/resources/main.py", "/OpenCore-Patcher-GUI.command")
self.constants.launcher_binary = launcher_binary
self.constants.launcher_script = launcher_script
self.constants.unpack_thread = threading.Thread(target=reroute_payloads.reroute_payloads(self.constants).setup_tmp_disk_image)
self.constants.unpack_thread.start()
self.constants.commit_info = commit_info.commit_info(self.constants.launcher_binary).generate_commit_info()
# Now that we have commit info, update nightly link
# Initialize working directory
self.constants.unpack_thread = threading.Thread(target=reroute_payloads.RoutePayloadDiskImage, args=(self.constants,))
self.constants.unpack_thread.start()
# Generate commit info
self.constants.commit_info = commit_info.ParseCommitInfo(self.constants.launcher_binary).generate_commit_info()
if self.constants.commit_info[0] not in ["Running from source", "Built from source"]:
# Now that we have commit info, update nightly link
branch = self.constants.commit_info[0]
branch = branch.replace("refs/heads/", "")
self.constants.installer_pkg_url_nightly = self.constants.installer_pkg_url_nightly.replace("main", branch)
defaults.generate_defaults(self.computer.real_model, True, self.constants)
# Generate defaults
defaults.GenerateDefaults(self.computer.real_model, True, self.constants)
if utilities.check_cli_args() is not None:
logging.info("- Detected arguments, switching to CLI mode")
self.constants.gui_mode = True # Assumes no user interaction is required
ignore_args = ["--auto_patch", "--gui_patch", "--gui_unpatch"]
if not any(x in sys.argv for x in ignore_args):
self.constants.current_path = Path.cwd()
self.constants.cli_mode = True
if getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS"):
logging.info("- Rerouting payloads location")
self.constants.payload_path = sys._MEIPASS / Path("payloads")
ignore_args = ignore_args.pop(0)
if not any(x in sys.argv for x in ignore_args):
while self.constants.unpack_thread.is_alive():
time.sleep(0.1)
arguments.arguments().parse_arguments(self.constants)
else:
if utilities.check_cli_args() is None:
logging.info(f"- No arguments present, loading {'GUI' if self.constants.wxpython_variant is True else 'TUI'} mode")
return
logging.info("- Detected arguments, switching to CLI mode")
self.constants.gui_mode = True # Assumes no user interaction is required
def main_menu(self):
response = None
while not (response and response == -1):
title = [
f"OpenCore Legacy Patcher v{self.constants.patcher_version}",
f"Selected Model: {self.constants.custom_model or self.computer.real_model}",
]
ignore_args = ["--auto_patch", "--gui_patch", "--gui_unpatch"]
if not any(x in sys.argv for x in ignore_args):
self.constants.current_path = Path.cwd()
self.constants.cli_mode = True
if getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS"):
logging.info("- Rerouting payloads location")
self.constants.payload_path = sys._MEIPASS / Path("payloads")
ignore_args = ignore_args.pop(0)
if (self.constants.custom_model or self.computer.real_model) not in model_array.SupportedSMBIOS and self.constants.allow_oc_everywhere is False:
in_between = [
"Your model is not supported by this patcher for running unsupported OSes!",
"",
'If you plan to create the USB for another machine, please select the \n"Change Model" option in the menu.',
"",
'If you want to run OCLP on a native Mac, please toggle \n"Allow OpenCore on native Models" in settings',
]
elif not self.constants.custom_model and self.computer.real_model == "iMac7,1" and "SSE4.1" not in self.computer.cpu.flags:
in_between = [
"Your model requires a CPU upgrade to a CPU supporting SSE4.1+ to be supported by this patcher!",
"",
f'If you plan to create the USB for another {self.computer.real_model} with SSE4.1+, please select the "Change Model" option in the menu.',
]
elif self.constants.custom_model == "iMac7,1":
in_between = ["This model is supported", "However please ensure the CPU has been upgraded to support SSE4.1+"]
else:
in_between = ["This model is supported"]
if not any(x in sys.argv for x in ignore_args):
while self.constants.unpack_thread.is_alive():
time.sleep(0.1)
menu = tui_helpers.TUIMenu(title, "Please select an option: ", in_between=in_between, auto_number=True, top_level=True)
options = (
[["Build OpenCore", build.build_opencore(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])
response = menu.start()
if getattr(sys, "frozen", False) and self.constants.recovery_status is False:
subprocess.run("""osascript -e 'tell application "Terminal" to close first window' & exit""", shell=True)
arguments.arguments(self.constants)

View File

@@ -9,21 +9,31 @@ import tempfile
import atexit
import logging
class reroute_payloads:
def __init__(self, constants):
self.constants = constants
from resources import constants
class RoutePayloadDiskImage:
def __init__(self, global_constants: constants.Constants):
self.constants: constants.Constants = global_constants
self._setup_tmp_disk_image()
def _setup_tmp_disk_image(self):
"""
Initialize temp directory and mount payloads.dmg
Create overlay for patcher to write to
Currently only applicable for GUI variant and not running from source
"""
def setup_tmp_disk_image(self):
# Create a temp directory to mount the payloads.dmg
# Then reroute r/w to this new temp directory
# Currently only applicable for GUI variant
if self.constants.wxpython_variant is True and not self.constants.launcher_script:
logging.info("- Running in Binary GUI mode, switching to tmp directory")
self.temp_dir = tempfile.TemporaryDirectory()
logging.info(f"- New payloads location: {self.temp_dir.name}")
logging.info("- Creating payloads directory")
Path(self.temp_dir.name / Path("payloads")).mkdir(parents=True, exist_ok=True)
self.unmount_active_dmgs(unmount_all_active=False)
self._unmount_active_dmgs(unmount_all_active=False)
output = subprocess.run(
[
"hdiutil", "attach", "-noverify", f"{self.constants.payload_path}.dmg",
@@ -38,16 +48,25 @@ class reroute_payloads:
logging.info("- Mounted payloads.dmg")
self.constants.current_path = Path(self.temp_dir.name)
self.constants.payload_path = Path(self.temp_dir.name) / Path("payloads")
atexit.register(self.unmount_active_dmgs, unmount_all_active=False)
atexit.register(self._unmount_active_dmgs, unmount_all_active=False)
else:
logging.info("- Failed to mount payloads.dmg")
logging.info(f"Output: {output.stdout.decode()}")
logging.info(f"Return Code: {output.returncode}")
def unmount_active_dmgs(self, unmount_all_active=True):
# Find all DMGs that are mounted, and forcefully unmount them
# If our disk image was previously mounted, we need to unmount it to use again
# This can happen if we crash during a previous secession, however 'atexit' class should hopefully avoid this
def _unmount_active_dmgs(self, unmount_all_active=True):
"""
Unmounts disk images associated with OCLP
Finds all DMGs that are mounted, and forcefully unmount them
If our disk image was previously mounted, we need to unmount it to use again
This can happen if we crash during a previous secession, however 'atexit' class should hopefully avoid this
Parameters:
unmount_all_active (bool): If True, unmount all active DMGs, otherwise only unmount our own DMG
"""
dmg_info = subprocess.run(["hdiutil", "info", "-plist"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
dmg_info = plistlib.loads(dmg_info.stdout)

View File

@@ -1,152 +0,0 @@
# Module for running processes with real time output
# Written by CorpNewt
# Source: https://github.com/corpnewt/pymodules/blob/884c3de15b6a2570afde52fe8a14a3e946ffb18a/run.py
import sys, subprocess, time, threading, shlex, logging
from queue import Queue, Empty
ON_POSIX = 'posix' in sys.builtin_module_names
class Run:
def __init__(self):
return
def _read_output(self, pipe, q):
try:
for line in iter(lambda: pipe.read(1), b''):
q.put(line)
except ValueError:
pass
pipe.close()
def _create_thread(self, output):
# Creates a new queue and thread object to watch based on the output pipe sent
q = Queue()
t = threading.Thread(target=self._read_output, args=(output, q))
t.daemon = True
return (q,t)
def _stream_output(self, comm, shell = False):
output = error = ""
p = None
try:
if shell and type(comm) is list:
comm = " ".join(shlex.quote(x) for x in comm)
if not shell and type(comm) is str:
comm = shlex.split(comm)
p = subprocess.Popen(comm, shell=shell, stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=0, universal_newlines=True, close_fds=ON_POSIX)
# Setup the stdout thread/queue
q,t = self._create_thread(p.stdout)
qe,te = self._create_thread(p.stderr)
# Start both threads
t.start()
te.start()
while True:
c = z = ""
try: c = q.get_nowait()
except Empty: pass
else:
sys.stdout.write(c)
output += c
sys.stdout.flush()
try: z = qe.get_nowait()
except Empty: pass
else:
sys.stderr.write(z)
error += z
sys.stderr.flush()
if not c==z=="": continue # Keep going until empty
# No output - see if still running
p.poll()
if p.returncode != None:
# Subprocess ended
break
# No output, but subprocess still running - stall for 20ms
time.sleep(0.02)
o, e = p.communicate()
return (output+o, error+e, p.returncode)
except:
if p:
try: o, e = p.communicate()
except: o = e = ""
return (output+o, error+e, p.returncode)
return ("", "Command not found!", 1)
def _decode(self, value, encoding="utf-8", errors="ignore"):
# Helper method to only decode if bytes type
if sys.version_info >= (3,0) and isinstance(value, bytes):
return value.decode(encoding,errors)
return value
def _run_command(self, comm, shell = False):
c = None
try:
if shell and type(comm) is list:
comm = " ".join(shlex.quote(x) for x in comm)
if not shell and type(comm) is str:
comm = shlex.split(comm)
p = subprocess.Popen(comm, shell=shell, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
c = p.communicate()
except:
if c == None:
return ("", "Command not found!", 1)
return (self._decode(c[0]), self._decode(c[1]), p.returncode)
def run(self, command_list, leave_on_fail = False):
# Command list should be an array of dicts
if type(command_list) is dict:
# We only have one command
command_list = [command_list]
output_list = []
for comm in command_list:
args = comm.get("args", [])
shell = comm.get("shell", False)
stream = comm.get("stream", False)
sudo = comm.get("sudo", False)
stdout = comm.get("stdout", False)
stderr = comm.get("stderr", False)
mess = comm.get("message", None)
show = comm.get("show", False)
if not mess == None:
logging.info(mess)
if not len(args):
# nothing to process
continue
if sudo:
# Check if we have sudo
out = self._run_command(["which", "sudo"])
if "sudo" in out[0]:
# Can sudo
if type(args) is list:
args.insert(0, out[0].replace("\n", "")) # add to start of list
elif type(args) is str:
args = out[0].replace("\n", "") + " " + args # add to start of string
if show:
logging.info(" ".join(args))
if stream:
# Stream it!
out = self._stream_output(args, shell)
else:
# Just run and gather output
out = self._run_command(args, shell)
if stdout and len(out[0]):
logging.info(out[0])
if stderr and len(out[1]):
logging.info(out[1])
# Append output
output_list.append(out)
# Check for errors
if leave_on_fail and out[2] != 0:
# Got an error - leave
break
if len(output_list) == 1:
# We only ran one command - just return that output
return output_list[0]
return output_list

View File

@@ -46,9 +46,9 @@ from data import os_data
class PatchSysVolume:
def __init__(self, model, versions, hardware_details=None):
def __init__(self, model: str, global_constants: constants.Constants, hardware_details: list = None):
self.model = model
self.constants: constants.Constants() = versions
self.constants: constants.Constants = global_constants
self.computer = self.constants.computer
self.root_mount_path = None
self.root_supports_snapshot = utilities.check_if_root_is_apfs_snapshot()
@@ -61,9 +61,9 @@ class PatchSysVolume:
# GUI will detect hardware patches before starting PatchSysVolume()
# However the TUI will not, so allow for data to be passed in manually avoiding multiple calls
if hardware_details is None:
hardware_details = sys_patch_detect.detect_root_patch(self.computer.real_model, self.constants).detect_patch_set()
hardware_details = sys_patch_detect.DetectRootPatch(self.computer.real_model, self.constants).detect_patch_set()
self.hardware_details = hardware_details
self.init_pathing(custom_root_mount_path=None, custom_data_mount_path=None)
self._init_pathing(custom_root_mount_path=None, custom_data_mount_path=None)
self.skip_root_kmutil_requirement = self.hardware_details["Settings: Supports Auxiliary Cache"]
@@ -72,7 +72,15 @@ class PatchSysVolume:
if Path(self.constants.payload_local_binaries_root_path).exists():
shutil.rmtree(self.constants.payload_local_binaries_root_path)
def init_pathing(self, custom_root_mount_path=None, custom_data_mount_path=None):
def _init_pathing(self, custom_root_mount_path: Path = None, custom_data_mount_path: Path = None):
"""
Initializes the pathing for root volume patching
Parameters:
custom_root_mount_path (Path): Custom path to mount the root volume
custom_data_mount_path (Path): Custom path to mount the data volume
"""
if custom_root_mount_path and custom_data_mount_path:
self.mount_location = custom_root_mount_path
self.data_mount_location = custom_data_mount_path
@@ -83,11 +91,12 @@ class PatchSysVolume:
else:
self.mount_location = ""
self.mount_location_data = ""
self.mount_extensions = f"{self.mount_location}/System/Library/Extensions"
self.mount_application_support = f"{self.mount_location_data}/Library/Application Support"
def mount_root_vol(self):
def _mount_root_vol(self):
# Returns boolean if Root Volume is available
self.root_mount_path = utilities.get_disk_path()
if self.root_mount_path.startswith("disk"):
@@ -113,7 +122,7 @@ class PatchSysVolume:
return False
def merge_kdk_with_root(self, save_hid_cs=False):
def _merge_kdk_with_root(self, save_hid_cs=False):
if self.skip_root_kmutil_requirement is True:
return
if self.constants.detected_os < os_data.os_data.ventura:
@@ -211,7 +220,7 @@ class PatchSysVolume:
utilities.elevated(["rm", "-rf", f"{self.constants.payload_path}/IOHIDEventDriver_CodeSignature.bak"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
def unpatch_root_vol(self):
def _unpatch_root_vol(self):
if self.constants.detected_os > os_data.os_data.catalina and self.root_supports_snapshot is True:
logging.info("- Reverting to last signed APFS snapshot")
result = utilities.elevated(["bless", "--mount", self.mount_location, "--bootefi", "--last-sealed-snapshot"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
@@ -221,18 +230,18 @@ class PatchSysVolume:
logging.info(result.stdout.decode())
logging.info("- Failed to revert snapshot via Apple's 'bless' command")
else:
self.clean_skylight_plugins()
self.delete_nonmetal_enforcement()
self.clean_auxiliary_kc()
self._clean_skylight_plugins()
self._delete_nonmetal_enforcement()
self._clean_auxiliary_kc()
self.constants.root_patcher_succeeded = True
logging.info("- Unpatching complete")
logging.info("\nPlease reboot the machine for patches to take effect")
def rebuild_snapshot(self):
if self.rebuild_kernel_collection() is True:
def _rebuild_snapshot(self):
if self._rebuild_kernel_collection() is True:
self.update_preboot_kernel_cache()
self.rebuild_dyld_shared_cache()
if self.create_new_apfs_snapshot() is True:
self._rebuild_dyld_shared_cache()
if self._create_new_apfs_snapshot() is True:
logging.info("- Patching complete")
logging.info("\nPlease reboot the machine for patches to take effect")
if self.needs_kmutil_exemptions is True:
@@ -241,7 +250,7 @@ class PatchSysVolume:
if self.constants.gui_mode is False:
input("\nPress [ENTER] to continue")
def rebuild_kernel_collection(self):
def _rebuild_kernel_collection(self):
logging.info("- Rebuilding Kernel Cache (This may take some time)")
if self.constants.detected_os > os_data.os_data.catalina:
# Base Arguments
@@ -328,15 +337,15 @@ class PatchSysVolume:
return False
for file in ["KextPolicy", "KextPolicy-shm", "KextPolicy-wal"]:
self.remove_file("/private/var/db/SystemPolicyConfiguration/", file)
self._remove_file("/private/var/db/SystemPolicyConfiguration/", file)
else:
# Install RSRHelper utility to handle desynced KCs
sys_patch_helpers.sys_patch_helpers(self.constants).install_rsr_repair_binary()
sys_patch_helpers.SysPatchHelpers(self.constants).install_rsr_repair_binary()
logging.info("- Successfully built new kernel cache")
return True
def create_new_apfs_snapshot(self):
def _create_new_apfs_snapshot(self):
if self.root_supports_snapshot is True:
logging.info("- Creating new APFS snapshot")
bless = utilities.elevated(
@@ -353,24 +362,25 @@ class PatchSysVolume:
if "Can't use last-sealed-snapshot or create-snapshot on non system volume" in bless.stdout.decode():
logging.info("- This is an APFS bug with Monterey and newer! Perform a clean installation to ensure your APFS volume is built correctly")
return False
self.unmount_drive()
self._unmount_drive()
return True
def unmount_drive(self):
def _unmount_drive(self):
logging.info("- Unmounting Root Volume (Don't worry if this fails)")
utilities.elevated(["diskutil", "unmount", self.root_mount_path], stdout=subprocess.PIPE).stdout.decode().strip().encode()
def rebuild_dyld_shared_cache(self):
if self.constants.detected_os <= os_data.os_data.catalina:
logging.info("- Rebuilding dyld shared cache")
utilities.process_status(utilities.elevated(["update_dyld_shared_cache", "-root", f"{self.mount_location}/"]))
def _rebuild_dyld_shared_cache(self):
if self.constants.detected_os > os_data.os_data.catalina:
return
logging.info("- Rebuilding dyld shared cache")
utilities.process_status(utilities.elevated(["update_dyld_shared_cache", "-root", f"{self.mount_location}/"]))
def update_preboot_kernel_cache(self):
if self.constants.detected_os == os_data.os_data.catalina:
logging.info("- Rebuilding preboot kernel cache")
utilities.process_status(utilities.elevated(["kcditto"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
def clean_skylight_plugins(self):
def _clean_skylight_plugins(self):
if (Path(self.mount_application_support) / Path("SkyLightPlugins/")).exists():
logging.info("- Found SkylightPlugins folder, removing old plugins")
utilities.process_status(utilities.elevated(["rm", "-Rf", f"{self.mount_application_support}/SkyLightPlugins"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
@@ -379,14 +389,14 @@ class PatchSysVolume:
logging.info("- Creating SkylightPlugins folder")
utilities.process_status(utilities.elevated(["mkdir", "-p", f"{self.mount_application_support}/SkyLightPlugins/"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
def delete_nonmetal_enforcement(self):
def _delete_nonmetal_enforcement(self):
for arg in ["useMetal", "useIOP"]:
result = subprocess.run(["defaults", "read", "/Library/Preferences/com.apple.CoreDisplay", arg], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL).stdout.decode("utf-8").strip()
if result in ["0", "false", "1", "true"]:
logging.info(f"- Removing non-Metal Enforcement Preference: {arg}")
utilities.elevated(["defaults", "delete", "/Library/Preferences/com.apple.CoreDisplay", arg])
def clean_auxiliary_kc(self):
def _clean_auxiliary_kc(self):
# When reverting root volume patches, the AuxKC will still retain the UUID
# it was built against. Thus when Boot/SysKC are reverted, Aux will break
# To resolve this, delete all installed kexts in /L*/E* and rebuild the AuxKC
@@ -407,7 +417,7 @@ class PatchSysVolume:
for file in oclp_plist_data[key]["Install"][location]:
if not file.endswith(".kext"):
continue
self.remove_file("/Library/Extensions", file)
self._remove_file("/Library/Extensions", file)
# Handle situations where users migrated from older OSes with a lot of garbage in /L*/E*
# ex. Nvidia Web Drivers, NetUSB, dosdude1's patches, etc.
@@ -431,17 +441,17 @@ class PatchSysVolume:
# ex. Symlinks pointing to symlinks pointing to dead files
pass
def write_patchset(self, patchset):
def _write_patchset(self, patchset):
destination_path = f"{self.mount_location}/System/Library/CoreServices"
file_name = "OpenCore-Legacy-Patcher.plist"
destination_path_file = f"{destination_path}/{file_name}"
if sys_patch_helpers.sys_patch_helpers(self.constants).generate_patchset_plist(patchset, file_name, self.kdk_path):
if sys_patch_helpers.SysPatchHelpers(self.constants).generate_patchset_plist(patchset, file_name, self.kdk_path):
logging.info("- Writing patchset information to Root Volume")
if Path(destination_path_file).exists():
utilities.process_status(utilities.elevated(["rm", destination_path_file], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
utilities.process_status(utilities.elevated(["cp", f"{self.constants.payload_path}/{file_name}", destination_path], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
def add_auxkc_support(self, install_file, source_folder_path, install_patch_directory, destination_folder_path):
def _add_auxkc_support(self, install_file, source_folder_path, install_patch_directory, destination_folder_path):
# In macOS Ventura, KDKs are required to build new Boot and System KCs
# However for some patch sets, we're able to use the Auxiliary KCs with '/Library/Extensions'
@@ -477,11 +487,11 @@ class PatchSysVolume:
plist_data["OSBundleRequired"] = "Auxiliary"
plistlib.dump(plist_data, plist_path.open("wb"))
self.check_kexts_needs_authentication(install_file)
self._check_kexts_needs_authentication(install_file)
return updated_install_location
def check_kexts_needs_authentication(self, kext_name):
def _check_kexts_needs_authentication(self, kext_name):
# Verify whether the user needs to authenticate in System Preferences
# Specifically under 'private/var/db/KernelManagement/AuxKC/CurrentAuxKC/com.apple.kcgen.instructions.plist'
# ["kextsToBuild"][i]:
@@ -503,21 +513,21 @@ class PatchSysVolume:
logging.info(f" - {kext_name} requires authentication in System Preferences")
self.constants.needs_to_open_preferences = True # Notify in GUI to open System Preferences
def patch_root_vol(self):
def _patch_root_vol(self):
logging.info(f"- Running patches for {self.model}")
if self.patch_set_dictionary != {}:
self.execute_patchset(self.patch_set_dictionary)
self._execute_patchset(self.patch_set_dictionary)
else:
self.execute_patchset(sys_patch_detect.detect_root_patch(self.computer.real_model, self.constants).generate_patchset(self.hardware_details))
self._execute_patchset(sys_patch_detect.DetectRootPatch(self.computer.real_model, self.constants).generate_patchset(self.hardware_details))
if self.constants.wxpython_variant is True and self.constants.detected_os >= os_data.os_data.big_sur:
sys_patch_auto.AutomaticSysPatch(self.constants).install_auto_patcher_launch_agent()
self.rebuild_snapshot()
self._rebuild_snapshot()
def execute_patchset(self, required_patches):
def _execute_patchset(self, required_patches):
source_files_path = str(self.constants.payload_local_binaries_root_path)
self.preflight_checks(required_patches, source_files_path)
self._preflight_checks(required_patches, source_files_path)
for patch in required_patches:
logging.info("- Installing Patchset: " + patch)
if "Remove" in required_patches[patch]:
@@ -525,7 +535,7 @@ class PatchSysVolume:
logging.info("- Remove Files at: " + remove_patch_directory)
for remove_patch_file in required_patches[patch]["Remove"][remove_patch_directory]:
destination_folder_path = str(self.mount_location) + remove_patch_directory
self.remove_file(destination_folder_path, remove_patch_file)
self._remove_file(destination_folder_path, remove_patch_file)
for method_install in ["Install", "Install Non-Root"]:
@@ -539,10 +549,10 @@ class PatchSysVolume:
else:
if install_patch_directory == "/Library/Extensions":
self.needs_kmutil_exemptions = True
self.check_kexts_needs_authentication(install_file)
self._check_kexts_needs_authentication(install_file)
destination_folder_path = str(self.mount_location_data) + install_patch_directory
updated_destination_folder_path = self.add_auxkc_support(install_file, source_folder_path, install_patch_directory, destination_folder_path)
updated_destination_folder_path = self._add_auxkc_support(install_file, source_folder_path, install_patch_directory, destination_folder_path)
if destination_folder_path != updated_destination_folder_path:
# Update required_patches to reflect the new destination folder path
@@ -553,7 +563,7 @@ class PatchSysVolume:
destination_folder_path = updated_destination_folder_path
self.install_new_file(source_folder_path, destination_folder_path, install_file)
self._install_new_file(source_folder_path, destination_folder_path, install_file)
if "Processes" in required_patches[patch]:
for process in required_patches[patch]["Processes"]:
@@ -566,24 +576,24 @@ class PatchSysVolume:
logging.info(f"- Running Process:\n{process}")
utilities.process_status(subprocess.run(process, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True))
if any(x in required_patches for x in ["AMD Legacy GCN", "AMD Legacy Polaris", "AMD Legacy Vega"]):
sys_patch_helpers.sys_patch_helpers(self.constants).disable_window_server_caching()
sys_patch_helpers.SysPatchHelpers(self.constants).disable_window_server_caching()
if any(x in required_patches for x in ["Intel Ivy Bridge", "Intel Haswell"]):
sys_patch_helpers.sys_patch_helpers(self.constants).remove_news_widgets()
self.write_patchset(required_patches)
sys_patch_helpers.SysPatchHelpers(self.constants).remove_news_widgets()
self._write_patchset(required_patches)
def preflight_checks(self, required_patches, source_files_path):
def _preflight_checks(self, required_patches, source_files_path):
logging.info("- Running Preflight Checks before patching")
# Make sure old SkyLight plugins aren't being used
self.clean_skylight_plugins()
self._clean_skylight_plugins()
# Make sure non-Metal Enforcement preferences are not present
self.delete_nonmetal_enforcement()
self._delete_nonmetal_enforcement()
# Make sure we clean old kexts in /L*/E* that are not in the patchset
self.clean_auxiliary_kc()
self._clean_auxiliary_kc()
# Make sure SNB kexts are compatible with the host
if "Intel Sandy Bridge" in required_patches:
sys_patch_helpers.sys_patch_helpers(self.constants).snb_board_id_patch(source_files_path)
sys_patch_helpers.SysPatchHelpers(self.constants).snb_board_id_patch(source_files_path)
for patch in required_patches:
# Check if all files are present
@@ -599,11 +609,11 @@ class PatchSysVolume:
should_save_cs = False
if "Legacy USB 1.1" in required_patches:
should_save_cs = True
self.merge_kdk_with_root(save_hid_cs=should_save_cs)
self._merge_kdk_with_root(save_hid_cs=should_save_cs)
logging.info("- Finished Preflight, starting patching")
def install_new_file(self, source_folder, destination_folder, file_name):
def _install_new_file(self, source_folder, destination_folder, file_name):
# .frameworks are merged
# .kexts and .apps are deleted and replaced
file_name_str = str(file_name)
@@ -616,7 +626,7 @@ class PatchSysVolume:
# merge with rsync
logging.info(f" - Installing: {file_name}")
utilities.elevated(["rsync", "-r", "-i", "-a", f"{source_folder}/{file_name}", f"{destination_folder}/"], stdout=subprocess.PIPE)
self.fix_permissions(destination_folder + "/" + file_name)
self._fix_permissions(destination_folder + "/" + file_name)
elif Path(source_folder + "/" + file_name_str).is_dir():
# Applicable for .kext, .app, .plugin, .bundle, all of which are directories
if Path(destination_folder + "/" + file_name).exists():
@@ -625,7 +635,7 @@ class PatchSysVolume:
else:
logging.info(f" - Installing: {file_name}")
utilities.process_status(utilities.elevated(["cp", "-R", f"{source_folder}/{file_name}", destination_folder], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
self.fix_permissions(destination_folder + "/" + file_name)
self._fix_permissions(destination_folder + "/" + file_name)
else:
# Assume it's an individual file, replace as normal
if Path(destination_folder + "/" + file_name).exists():
@@ -634,9 +644,9 @@ class PatchSysVolume:
else:
logging.info(f" - Installing: {file_name}")
utilities.process_status(utilities.elevated(["cp", f"{source_folder}/{file_name}", destination_folder], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
self.fix_permissions(destination_folder + "/" + file_name)
self._fix_permissions(destination_folder + "/" + file_name)
def remove_file(self, destination_folder, file_name):
def _remove_file(self, destination_folder, file_name):
if Path(destination_folder + "/" + file_name).exists():
logging.info(f" - Removing: {file_name}")
if Path(destination_folder + "/" + file_name).is_dir():
@@ -645,7 +655,7 @@ class PatchSysVolume:
utilities.process_status(utilities.elevated(["rm", f"{destination_folder}/{file_name}"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
def fix_permissions(self, destination_file):
def _fix_permissions(self, destination_file):
chmod_args = ["chmod", "-Rf", "755", destination_file]
chown_args = ["chown", "-Rf", "root:wheel", destination_file]
if not Path(destination_file).is_dir():
@@ -656,7 +666,7 @@ class PatchSysVolume:
utilities.process_status(utilities.elevated(chown_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT))
def check_files(self):
def _check_files(self):
if Path(self.constants.payload_local_binaries_root_path).exists():
logging.info("- Local PatcherSupportPkg resources available, continuing...")
return True
@@ -675,7 +685,7 @@ class PatchSysVolume:
def start_patch(self):
logging.info("- Starting Patch Process")
logging.info(f"- Determining Required Patch set for Darwin {self.constants.detected_os}")
self.patch_set_dictionary = sys_patch_detect.detect_root_patch(self.computer.real_model, self.constants).generate_patchset(self.hardware_details)
self.patch_set_dictionary = sys_patch_detect.DetectRootPatch(self.computer.real_model, self.constants).generate_patchset(self.hardware_details)
if self.patch_set_dictionary == {}:
change_menu = None
@@ -689,11 +699,11 @@ class PatchSysVolume:
logging.info("- Continuing root patching")
if change_menu in ["y", "Y"]:
logging.info("- Verifying whether Root Patching possible")
if sys_patch_detect.detect_root_patch(self.computer.real_model, self.constants).verify_patch_allowed(print_errors=not self.constants.wxpython_variant) is True:
if sys_patch_detect.DetectRootPatch(self.computer.real_model, self.constants).verify_patch_allowed(print_errors=not self.constants.wxpython_variant) is True:
logging.info("- Patcher is capable of patching")
if self.check_files():
if self.mount_root_vol() is True:
self.patch_root_vol()
if self._check_files():
if self._mount_root_vol() is True:
self._patch_root_vol()
if self.constants.gui_mode is False:
input("\nPress [ENTER] to return to the main menu")
else:
@@ -708,9 +718,9 @@ class PatchSysVolume:
def start_unpatch(self):
logging.info("- Starting Unpatch Process")
if sys_patch_detect.detect_root_patch(self.computer.real_model, self.constants).verify_patch_allowed(print_errors=True) is True:
if self.mount_root_vol() is True:
self.unpatch_root_vol()
if sys_patch_detect.DetectRootPatch(self.computer.real_model, self.constants).verify_patch_allowed(print_errors=True) is True:
if self._mount_root_vol() is True:
self._unpatch_root_vol()
if self.constants.gui_mode is False:
input("\nPress [ENTER] to return to the main menu")
else:

View File

@@ -1,29 +1,42 @@
# Auto Patching's main purpose is to try and tell the user they're missing root patches
# New users may not realize OS updates remove our patches, so we try and run when nessasary
# Conditions for running:
# - Verify running GUI (TUI users can write their own scripts)
# - Verify the Snapshot Seal is intact (if not, assume user is running patches)
# - Verify this model needs patching (if not, assume user upgraded hardware and OCLP was not removed)
# - Verify there are no updates for OCLP (ensure we have the latest patch sets)
# If all these tests pass, start Root Patcher
# Copyright (C) 2022, Mykola Grymalyuk
from pathlib import Path
import plistlib
import subprocess
import webbrowser
import logging
from resources import utilities, updates, global_settings, network_handler
from pathlib import Path
from resources import utilities, updates, global_settings, network_handler, constants
from resources.sys_patch import sys_patch_detect
from resources.gui import gui_main
class AutomaticSysPatch:
def __init__(self, constants):
self.constants = constants
class AutomaticSysPatch:
"""
Library of functions for launch agent, including automatic patching
"""
def __init__(self, global_constants: constants.Constants):
self.constants: constants.Constants = global_constants
def start_auto_patch(self):
"""
Initiates automatic patching
Auto Patching's main purpose is to try and tell the user they're missing root patches
New users may not realize OS updates remove our patches, so we try and run when nessasary
Conditions for running:
- Verify running GUI (TUI users can write their own scripts)
- Verify the Snapshot Seal is intact (if not, assume user is running patches)
- Verify this model needs patching (if not, assume user upgraded hardware and OCLP was not removed)
- Verify there are no updates for OCLP (ensure we have the latest patch sets)
If all these tests pass, start Root Patcher
"""
logging.info("- Starting Automatic Patching")
if self.constants.wxpython_variant is False:
logging.info("- Auto Patch option is not supported on TUI, please use GUI")
@@ -31,7 +44,7 @@ class AutomaticSysPatch:
if utilities.check_seal() is True:
logging.info("- Detected Snapshot seal intact, detecting patches")
patches = sys_patch_detect.detect_root_patch(self.constants.computer.real_model, self.constants).detect_patch_set()
patches = sys_patch_detect.DetectRootPatch(self.constants.computer.real_model, self.constants).detect_patch_set()
if not any(not patch.startswith("Settings") and not patch.startswith("Validation") and patches[patch] is True for patch in patches):
patches = []
if patches:
@@ -46,7 +59,7 @@ class AutomaticSysPatch:
if patches[patch] is True and not patch.startswith("Settings") and not patch.startswith("Validation"):
patch_string += f"- {patch}\n"
# Check for updates
dict = updates.check_binary_updates(self.constants).check_binary_updates()
dict = updates.CheckBinaryUpdates(self.constants).check_binary_updates()
if not dict:
logging.info("- No new binaries found on Github, proceeding with patching")
if self.constants.launcher_script is None:
@@ -113,26 +126,35 @@ class AutomaticSysPatch:
else:
logging.info("- Detected Snapshot seal not intact, skipping")
if self.determine_if_versions_match() is False:
self.determine_if_boot_matches()
if self._determine_if_versions_match():
self._determine_if_boot_matches()
def determine_if_versions_match(self):
def _determine_if_versions_match(self):
"""
Determine if the booted version of OCLP matches the installed version
ie. Installed app is 0.2.0, but EFI version is 0.1.0
Returns:
bool: True if versions match, False if not
"""
logging.info("- Checking booted vs installed OCLP Build")
if self.constants.computer.oclp_version is None:
logging.info("- Booted version not found")
return False
return True
if self.constants.computer.oclp_version == self.constants.patcher_version:
logging.info("- Versions match")
return False
return True
# Check if installed version is newer than booted version
if updates.check_binary_updates(self.constants).check_if_build_newer(
if updates.CheckBinaryUpdates(self.constants)._check_if_build_newer(
self.constants.computer.oclp_version.split("."), self.constants.patcher_version.split(".")
) is True:
logging.info("- Installed version is newer than booted version")
return False
return True
args = [
"osascript",
@@ -150,18 +172,25 @@ class AutomaticSysPatch:
self.constants.start_build_install = True
gui_main.wx_python_gui(self.constants).main_menu(None)
return True
return False
def determine_if_boot_matches(self):
# Goal of this function is to determine whether the user
# is using a USB drive to Boot OpenCore but macOS does not
# reside on the same drive as the USB.
# If we determine them to be mismatched, notify the user
# and ask if they want to install to install to disk
def _determine_if_boot_matches(self):
"""
Determine if the boot drive matches the macOS drive
ie. Booted from USB, but macOS is on internal disk
Goal of this function is to determine whether the user
is using a USB drive to Boot OpenCore but macOS does not
reside on the same drive as the USB.
If we determine them to be mismatched, notify the user
and ask if they want to install to install to disk.
"""
logging.info("- Determining if macOS drive matches boot drive")
should_notify = global_settings.global_settings().read_property("AutoPatch_Notify_Mismatched_Disks")
should_notify = global_settings.GlobalEnviromentSettings().read_property("AutoPatch_Notify_Mismatched_Disks")
if should_notify is False:
logging.info("- Skipping due to user preference")
return
@@ -223,9 +252,16 @@ class AutomaticSysPatch:
def install_auto_patcher_launch_agent(self):
# Installs the following:
# - OpenCore-Patcher.app in /Library/Application Support/Dortania/
# - com.dortania.opencore-legacy-patcher.auto-patch.plist in /Library/LaunchAgents/
"""
Install the Auto Patcher Launch Agent
Installs the following:
- OpenCore-Patcher.app in /Library/Application Support/Dortania/
- com.dortania.opencore-legacy-patcher.auto-patch.plist in /Library/LaunchAgents/
See start_auto_patch() comments for more info
"""
if self.constants.launcher_script is not None:
logging.info("- Skipping Auto Patcher Launch Agent, not supported when running from source")
return

View File

@@ -3,19 +3,39 @@
# Used when supplying data to sys_patch.py
# Copyright (C) 2020-2022, Dhinak G, Mykola Grymalyuk
from resources import constants, device_probe, utilities, amfi_detect, network_handler, kdk_handler
from resources.sys_patch import sys_patch_helpers
from data import model_array, os_data, sip_data, sys_patch_dict, smbios_data, cpu_data
import py_sip_xnu
from pathlib import Path
import plistlib
import logging
import py_sip_xnu
from pathlib import Path
from resources import (
constants,
device_probe,
utilities,
amfi_detect,
network_handler,
kdk_handler
)
from data import (
model_array,
os_data,
sip_data,
sys_patch_dict,
smbios_data,
cpu_data
)
class DetectRootPatch:
"""
Library for querying root volume patches applicable for booted system
"""
def __init__(self, model: str, global_constants: constants.Constants):
self.model: str = model
self.constants: constants.Constants = global_constants
class detect_root_patch:
def __init__(self, model, versions):
self.model = model
self.constants: constants.Constants() = versions
self.computer = self.constants.computer
# GPU Patch Detection
@@ -63,7 +83,12 @@ class detect_root_patch:
self.missing_nv_web_opengl = False
self.missing_nv_compat = False
def detect_gpus(self):
def _detect_gpus(self):
"""
Query GPUs and set flags for applicable patches
"""
gpus = self.constants.computer.gpus
non_metal_os = os_data.os_data.catalina
for i, gpu in enumerate(gpus):
@@ -75,7 +100,7 @@ class detect_root_patch:
self.amfi_must_disable = True
if os_data.os_data.ventura in self.constants.legacy_accel_support:
self.amfi_shim_bins = True
self.legacy_keyboard_backlight = self.check_legacy_keyboard_backlight()
self.legacy_keyboard_backlight = self._check_legacy_keyboard_backlight()
self.requires_root_kc = True
elif gpu.arch == device_probe.NVIDIA.Archs.Kepler and self.constants.force_nv_web is False:
if self.constants.detected_os > os_data.os_data.big_sur:
@@ -165,7 +190,7 @@ class detect_root_patch:
self.amfi_must_disable = True
if os_data.os_data.ventura in self.constants.legacy_accel_support:
self.amfi_shim_bins = True
self.legacy_keyboard_backlight = self.check_legacy_keyboard_backlight()
self.legacy_keyboard_backlight = self._check_legacy_keyboard_backlight()
self.requires_root_kc = True
elif gpu.arch == device_probe.Intel.Archs.Sandy_Bridge:
if self.constants.detected_os > non_metal_os:
@@ -173,7 +198,7 @@ class detect_root_patch:
self.amfi_must_disable = True
if os_data.os_data.ventura in self.constants.legacy_accel_support:
self.amfi_shim_bins = True
self.legacy_keyboard_backlight = self.check_legacy_keyboard_backlight()
self.legacy_keyboard_backlight = self._check_legacy_keyboard_backlight()
self.requires_root_kc = True
elif gpu.arch == device_probe.Intel.Archs.Ivy_Bridge:
if self.constants.detected_os > os_data.os_data.big_sur:
@@ -220,17 +245,21 @@ class detect_root_patch:
self.requires_root_kc = True
else:
if self.requires_root_kc is True:
self.missing_kdk = not self.check_kdk()
self.missing_kdk = not self._check_kdk()
self.check_networking_support()
self._check_networking_support()
def check_networking_support(self):
# On macOS Ventura, networking support is required to download KDKs.
# However for machines such as BCM94322, BCM94328 and Atheros chipsets,
# users may only have wifi as their only supported network interface.
# Thus we'll allow for KDK-less installs for these machines on first run.
# On subsequent runs, we'll require networking to be enabled.
def _check_networking_support(self):
"""
Query for network requirement, ex. KDK downloading
On macOS Ventura, networking support is required to download KDKs.
However for machines such as BCM94322, BCM94328 and Atheros chipsets,
users may only have wifi as their only supported network interface.
Thus we'll allow for KDK-less installs for these machines on first run.
On subsequent runs, we'll require networking to be enabled.
"""
if self.constants.detected_os < os_data.os_data.ventura:
return
@@ -272,7 +301,11 @@ class detect_root_patch:
self.legacy_keyboard_backlight = False
def check_dgpu_status(self):
def _check_dgpu_status(self):
"""
Query whether system has an active dGPU
"""
dgpu = self.constants.computer.dgpu
if dgpu:
if dgpu.class_code and dgpu.class_code == 0xFFFFFFFF:
@@ -281,25 +314,45 @@ class detect_root_patch:
return True
return False
def detect_demux(self):
def _detect_demux(self):
"""
Query whether system has been demuxed (ex. MacBookPro8,2, disabled dGPU)
"""
# If GFX0 is missing, assume machine was demuxed
# -wegnoegpu would also trigger this, so ensure arg is not present
if not "-wegnoegpu" in (utilities.get_nvram("boot-args", decode=True) or ""):
igpu = self.constants.computer.igpu
dgpu = self.check_dgpu_status()
dgpu = self._check_dgpu_status()
if igpu and not dgpu:
return True
return False
def check_legacy_keyboard_backlight(self):
def _check_legacy_keyboard_backlight(self):
"""
Query whether system has a legacy keyboard backlight
Returns:
bool: True if legacy keyboard backlight, False otherwise
"""
# iMac12,x+ have an 'ACPI0008' device, but it's not a keyboard backlight
# Best to assume laptops will have a keyboard backlight
if self.model.startswith("MacBook"):
return self.constants.computer.ambient_light_sensor
return False
def check_nv_web_nvram(self):
# First check boot-args, then dedicated nvram variable
def _check_nv_web_nvram(self):
"""
Query for Nvidia Web Driver property: nvda_drv_vrl or nvda_drv
Returns:
bool: True if property is present, False otherwise
"""
nv_on = utilities.get_nvram("boot-args", decode=True)
if nv_on:
if "nvda_drv_vrl=" in nv_on:
@@ -309,8 +362,17 @@ class detect_root_patch:
return True
return False
def check_nv_web_opengl(self):
# First check boot-args, then whether property exists on GPU
def _check_nv_web_opengl(self):
"""
Query for Nvidia Web Driver property: ngfxgl
Verify Web Drivers will run in OpenGL mode
Returns:
bool: True if property is present, False otherwise
"""
nv_on = utilities.get_nvram("boot-args", decode=True)
if nv_on:
if "ngfxgl=" in nv_on:
@@ -321,8 +383,17 @@ class detect_root_patch:
return True
return False
def check_nv_compat(self):
# Check for 'nv_web' in boot-args, then whether property exists on GPU
def _check_nv_compat(self):
"""
Query for Nvidia Web Driver property: ngfxcompat
Verify Web Drivers will skip NVDAStartupWeb compatibility check
Returns:
bool: True if property is present, False otherwise
"""
nv_on = utilities.get_nvram("boot-args", decode=True)
if nv_on:
if "ngfxcompat=" in nv_on:
@@ -333,13 +404,37 @@ class detect_root_patch:
return True
return False
def check_whatevergreen(self):
def _check_whatevergreen(self):
"""
Query whether WhateverGreen.kext is loaded
Returns:
bool: True if loaded, False otherwise
"""
return utilities.check_kext_loaded("WhateverGreen", self.constants.detected_os)
def check_kdk(self):
def _check_kdk(self):
"""
Query whether Kernel Debug Kit is installed
Returns:
bool: True if installed, False otherwise
"""
return kdk_handler.KernelDebugKitObject(self.constants, self.constants.detected_os_build, self.constants.detected_os_version, passive=True).kdk_already_installed
def check_sip(self):
def _check_sip(self):
"""
Query System Integrity checks required for patching
Returns:
tuple: (list, str, str) of SIP values, SIP hex, SIP error message
"""
if self.constants.detected_os > os_data.os_data.catalina:
if self.nvidia_web is True:
sip = sip_data.system_integrity_protection.root_patch_sip_big_sur_3rd_part_kexts
@@ -365,7 +460,15 @@ class detect_root_patch:
sip_value = f"For Hackintoshes, please set csr-active-config to '03060000' ({sip_hex})\nFor non-OpenCore Macs, please run 'csrutil disable' in RecoveryOS"
return (sip, sip_value, sip_hex)
def check_uhci_ohci(self):
def _check_uhci_ohci(self):
"""
Query whether host has UHCI/OHCI controllers, and requires USB 1.1 patches
Returns:
bool: True if UHCI/OHCI patches required, False otherwise
"""
if self.constants.detected_os < os_data.os_data.ventura:
return False
@@ -399,10 +502,19 @@ class detect_root_patch:
return False
# Entry point for patch set detection
def detect_patch_set(self):
"""
Query patch sets required for host
Returns:
dict: Dictionary of patch sets
"""
self.has_network = network_handler.NetworkUtilities().verify_network_connection()
if self.check_uhci_ohci() is True:
if self._check_uhci_ohci() is True:
self.legacy_uhci_ohci = True
self.requires_root_kc = True
@@ -435,12 +547,12 @@ class detect_root_patch:
if self.constants.detected_os > os_data.os_data.high_sierra:
if self.model in ["MacBookPro8,2", "MacBookPro8,3"]:
# Ref: https://doslabelectronics.com/Demux.html
if self.detect_demux() is True:
if self._detect_demux() is True:
self.legacy_gmux = True
else:
self.legacy_gmux = True
self.detect_gpus()
self._detect_gpus()
self.root_patch_dict = {
"Graphics: Nvidia Tesla": self.nvidia_tesla,
@@ -467,11 +579,11 @@ class detect_root_patch:
"Settings: Supports Auxiliary Cache": not self.requires_root_kc,
"Settings: Kernel Debug Kit missing": self.missing_kdk if self.constants.detected_os >= os_data.os_data.ventura.value else False,
"Validation: Patching Possible": self.verify_patch_allowed(),
"Validation: Unpatching Possible": self.verify_unpatch_allowed(),
f"Validation: SIP is enabled (Required: {self.check_sip()[2]} or higher)": self.sip_enabled,
"Validation: Unpatching Possible": self._verify_unpatch_allowed(),
f"Validation: SIP is enabled (Required: {self._check_sip()[2]} or higher)": self.sip_enabled,
f"Validation: Currently Booted SIP: ({hex(py_sip_xnu.SipXnu().get_sip_status().value)})": self.sip_enabled,
"Validation: SecureBootModel is enabled": self.sbm_enabled,
f"Validation: {'AMFI' if self.constants.host_is_hackintosh is True or self.get_amfi_level_needed() > 2 else 'Library Validation'} is enabled": self.amfi_enabled if self.amfi_must_disable is True else False,
f"Validation: {'AMFI' if self.constants.host_is_hackintosh is True or self._get_amfi_level_needed() > 2 else 'Library Validation'} is enabled": self.amfi_enabled if self.amfi_must_disable is True else False,
"Validation: FileVault is enabled": self.fv_enabled,
"Validation: System is dosdude1 patched": self.dosdude_patched,
"Validation: WhateverGreen.kext missing": self.missing_whatever_green if self.nvidia_web is True else False,
@@ -483,30 +595,53 @@ class detect_root_patch:
return self.root_patch_dict
def get_amfi_level_needed(self):
if self.amfi_must_disable is True:
if self.constants.detected_os > os_data.os_data.catalina:
if self.constants.detected_os >= os_data.os_data.ventura:
if self.amfi_shim_bins is True:
# Currently we require AMFI outright disabled
# in Ventura to work with shim'd binaries
return amfi_detect.AmfiConfigDetectLevel.ALLOW_ALL
return amfi_detect.AmfiConfigDetectLevel.LIBRARY_VALIDATION
return amfi_detect.AmfiConfigDetectLevel.NO_CHECK
def verify_patch_allowed(self, print_errors=False):
sip_dict = self.check_sip()
def _get_amfi_level_needed(self):
"""
Query the AMFI level needed for the patcher to work
Returns:
int: AMFI level needed
"""
if self.amfi_must_disable is False:
return amfi_detect.AmfiConfigDetectLevel.NO_CHECK
if self.constants.detected_os < os_data.os_data.big_sur:
return amfi_detect.AmfiConfigDetectLevel.NO_CHECK
if self.constants.detected_os >= os_data.os_data.ventura:
if self.amfi_shim_bins is True:
# Currently we require AMFI outright disabled
# in Ventura to work with shim'd binaries
return amfi_detect.AmfiConfigDetectLevel.ALLOW_ALL
return amfi_detect.AmfiConfigDetectLevel.LIBRARY_VALIDATION
def verify_patch_allowed(self, print_errors: bool = False):
"""
Validate that the patcher can be run
Parameters:
print_errors (bool): Print errors to console
Returns:
bool: True if patching is allowed, False otherwise
"""
sip_dict = self._check_sip()
sip = sip_dict[0]
sip_value = sip_dict[1]
self.sip_enabled, self.sbm_enabled, self.fv_enabled, self.dosdude_patched = utilities.patching_status(sip, self.constants.detected_os)
self.amfi_enabled = not amfi_detect.AmfiConfigurationDetection().check_config(self.get_amfi_level_needed())
self.amfi_enabled = not amfi_detect.AmfiConfigurationDetection().check_config(self._get_amfi_level_needed())
if self.nvidia_web is True:
self.missing_nv_web_nvram = not self.check_nv_web_nvram()
self.missing_nv_web_opengl = not self.check_nv_web_opengl()
self.missing_nv_compat = not self.check_nv_compat()
self.missing_whatever_green = not self.check_whatevergreen()
self.missing_nv_web_nvram = not self._check_nv_web_nvram()
self.missing_nv_web_opengl = not self._check_nv_web_opengl()
self.missing_nv_compat = not self._check_nv_compat()
self.missing_whatever_green = not self._check_whatevergreen()
if print_errors is True:
if self.sip_enabled is True:
@@ -574,28 +709,54 @@ class detect_root_patch:
]
):
return False
else:
return True
def verify_unpatch_allowed(self, print_errors=False):
# Must be called after verify_patch_allowed
return True
def _verify_unpatch_allowed(self):
"""
Validate that the unpatcher can be run
Preconditions:
Must be called after verify_patch_allowed()
Returns:
bool: True if unpatching is allowed, False otherwise
"""
return not self.sip_enabled
def generate_patchset(self, hardware_details):
all_hardware_patchset = sys_patch_dict.SystemPatchDictionary(self.constants.detected_os, self.constants.detected_os_minor, self.constants.legacy_accel_support)
required_patches = {}
def generate_patchset(self, hardware_details: dict):
"""
Generate Patchset dictionary for the current system
Parameters:
hardware_details (dict): Dictionary of hardware details generated by detect_patch_set()
Returns:
dict: Dictionary of patches to be applied from sys_patch_dict.py
"""
all_hardware_patchset: dict = sys_patch_dict.SystemPatchDictionary(self.constants.detected_os, self.constants.detected_os_minor, self.constants.legacy_accel_support)
required_patches: dict = {}
utilities.cls()
logging.info("- The following patches will be applied:")
if hardware_details["Graphics: Intel Ironlake"] is True:
required_patches.update({"Non-Metal Common": all_hardware_patchset["Graphics"]["Non-Metal Common"]})
required_patches.update({"WebKit Monterey Common": all_hardware_patchset["Graphics"]["WebKit Monterey Common"]})
required_patches.update({"Intel Ironlake": all_hardware_patchset["Graphics"]["Intel Ironlake"]})
if hardware_details["Graphics: Intel Sandy Bridge"] is True:
required_patches.update({"Non-Metal Common": all_hardware_patchset["Graphics"]["Non-Metal Common"]})
required_patches.update({"Non-Metal ColorSync Workaround": all_hardware_patchset["Graphics"]["Non-Metal ColorSync Workaround"]})
required_patches.update({"High Sierra GVA": all_hardware_patchset["Graphics"]["High Sierra GVA"]})
required_patches.update({"WebKit Monterey Common": all_hardware_patchset["Graphics"]["WebKit Monterey Common"]})
required_patches.update({"Intel Sandy Bridge": all_hardware_patchset["Graphics"]["Intel Sandy Bridge"]})
if hardware_details["Graphics: Intel Ivy Bridge"] is True:
required_patches.update({"Metal 3802 Common": all_hardware_patchset["Graphics"]["Metal 3802 Common"]})
required_patches.update({"Catalina GVA": all_hardware_patchset["Graphics"]["Catalina GVA"]})
@@ -603,23 +764,28 @@ class detect_root_patch:
required_patches.update({"Big Sur OpenCL": all_hardware_patchset["Graphics"]["Big Sur OpenCL"]})
required_patches.update({"WebKit Monterey Common": all_hardware_patchset["Graphics"]["WebKit Monterey Common"]})
required_patches.update({"Intel Ivy Bridge": all_hardware_patchset["Graphics"]["Intel Ivy Bridge"]})
if hardware_details["Graphics: Intel Haswell"] is True:
required_patches.update({"Metal 3802 Common": all_hardware_patchset["Graphics"]["Metal 3802 Common"]})
required_patches.update({"Monterey GVA": all_hardware_patchset["Graphics"]["Monterey GVA"]})
required_patches.update({"Monterey OpenCL": all_hardware_patchset["Graphics"]["Monterey OpenCL"]})
required_patches.update({"Intel Haswell": all_hardware_patchset["Graphics"]["Intel Haswell"]})
if hardware_details["Graphics: Intel Broadwell"] is True:
required_patches.update({"Monterey GVA": all_hardware_patchset["Graphics"]["Monterey GVA"]})
required_patches.update({"Monterey OpenCL": all_hardware_patchset["Graphics"]["Monterey OpenCL"]})
required_patches.update({"Intel Broadwell": all_hardware_patchset["Graphics"]["Intel Broadwell"]})
if hardware_details["Graphics: Intel Skylake"] is True:
required_patches.update({"Monterey GVA": all_hardware_patchset["Graphics"]["Monterey GVA"]})
required_patches.update({"Monterey OpenCL": all_hardware_patchset["Graphics"]["Monterey OpenCL"]})
required_patches.update({"Intel Skylake": all_hardware_patchset["Graphics"]["Intel Skylake"]})
if hardware_details["Graphics: Nvidia Tesla"] is True:
required_patches.update({"Non-Metal Common": all_hardware_patchset["Graphics"]["Non-Metal Common"]})
required_patches.update({"WebKit Monterey Common": all_hardware_patchset["Graphics"]["WebKit Monterey Common"]})
required_patches.update({"Nvidia Tesla": all_hardware_patchset["Graphics"]["Nvidia Tesla"]})
if hardware_details["Graphics: Nvidia Web Drivers"] is True:
required_patches.update({"Non-Metal Common": all_hardware_patchset["Graphics"]["Non-Metal Common"]})
required_patches.update({"Non-Metal IOAccelerator Common": all_hardware_patchset["Graphics"]["Non-Metal IOAccelerator Common"]})
@@ -627,6 +793,7 @@ class detect_root_patch:
required_patches.update({"WebKit Monterey Common": all_hardware_patchset["Graphics"]["WebKit Monterey Common"]})
required_patches.update({"Nvidia Web Drivers": all_hardware_patchset["Graphics"]["Nvidia Web Drivers"]})
required_patches.update({"Non-Metal Enforcement": all_hardware_patchset["Graphics"]["Non-Metal Enforcement"]})
if hardware_details["Graphics: Nvidia Kepler"] is True:
required_patches.update({"Revert Metal Downgrade": all_hardware_patchset["Graphics"]["Revert Metal Downgrade"]})
required_patches.update({"Metal 3802 Common": all_hardware_patchset["Graphics"]["Metal 3802 Common"]})
@@ -641,11 +808,13 @@ class detect_root_patch:
if "Catalina GVA" in required_patches:
del(required_patches["Catalina GVA"])
break
if hardware_details["Graphics: AMD TeraScale 1"] is True:
required_patches.update({"Non-Metal Common": all_hardware_patchset["Graphics"]["Non-Metal Common"]})
required_patches.update({"WebKit Monterey Common": all_hardware_patchset["Graphics"]["WebKit Monterey Common"]})
required_patches.update({"AMD TeraScale Common": all_hardware_patchset["Graphics"]["AMD TeraScale Common"]})
required_patches.update({"AMD TeraScale 1": all_hardware_patchset["Graphics"]["AMD TeraScale 1"]})
if hardware_details["Graphics: AMD TeraScale 2"] is True:
required_patches.update({"Non-Metal Common": all_hardware_patchset["Graphics"]["Non-Metal Common"]})
required_patches.update({"Non-Metal IOAccelerator Common": all_hardware_patchset["Graphics"]["Non-Metal IOAccelerator Common"]})
@@ -656,6 +825,7 @@ class detect_root_patch:
# TeraScale 2 MacBooks with faulty GPUs are highly prone to crashing with AMDRadeonX3000 attached
# Additionally, AMDRadeonX3000 requires IOAccelerator downgrade which is not installed without 'Non-Metal IOAccelerator Common'
del(required_patches["AMD TeraScale 2"]["Install"]["/System/Library/Extensions"]["AMDRadeonX3000.kext"])
if hardware_details["Graphics: AMD Legacy GCN"] is True or hardware_details["Graphics: AMD Legacy Polaris"] is True:
required_patches.update({"Revert Metal Downgrade": all_hardware_patchset["Graphics"]["Revert Metal Downgrade"]})
required_patches.update({"Monterey GVA": all_hardware_patchset["Graphics"]["Monterey GVA"]})
@@ -666,6 +836,7 @@ class detect_root_patch:
required_patches.update({"AMD Legacy Polaris": all_hardware_patchset["Graphics"]["AMD Legacy Polaris"]})
if "AVX2" not in self.constants.computer.cpu.leafs:
required_patches.update({"AMD OpenCL": all_hardware_patchset["Graphics"]["AMD OpenCL"]})
if hardware_details["Graphics: AMD Legacy Vega"] is True:
required_patches.update({"Monterey GVA": all_hardware_patchset["Graphics"]["Monterey GVA"]})
required_patches.update({"Monterey OpenCL": all_hardware_patchset["Graphics"]["Monterey OpenCL"]})
@@ -673,20 +844,26 @@ class detect_root_patch:
required_patches.update({"AMD OpenCL": all_hardware_patchset["Graphics"]["AMD OpenCL"]})
if hardware_details["Graphics: AMD Legacy GCN"] is True:
required_patches.update({"AMD Legacy Vega Extended": all_hardware_patchset["Graphics"]["AMD Legacy Vega Extended"]})
if hardware_details["Brightness: Legacy Backlight Control"] is True:
required_patches.update({"Legacy Backlight Control": all_hardware_patchset["Brightness"]["Legacy Backlight Control"]})
if hardware_details["Audio: Legacy Realtek"] is True:
if self.model in ["iMac7,1", "iMac8,1"]:
required_patches.update({"Legacy Realtek": all_hardware_patchset["Audio"]["Legacy Realtek"]})
else:
required_patches.update({"Legacy Non-GOP": all_hardware_patchset["Audio"]["Legacy Non-GOP"]})
if hardware_details["Networking: Legacy Wireless"] is True:
required_patches.update({"Legacy Wireless": all_hardware_patchset["Networking"]["Legacy Wireless"]})
required_patches.update({"Legacy Wireless Extended": all_hardware_patchset["Networking"]["Legacy Wireless Extended"]})
if hardware_details["Miscellaneous: Legacy GMUX"] is True:
required_patches.update({"Legacy GMUX": all_hardware_patchset["Miscellaneous"]["Legacy GMUX"]})
if hardware_details["Miscellaneous: Legacy Keyboard Backlight"] is True:
required_patches.update({"Legacy Keyboard Backlight": all_hardware_patchset["Miscellaneous"]["Legacy Keyboard Backlight"]})
if hardware_details["Miscellaneous: Legacy USB 1.1"] is True:
required_patches.update({"Legacy USB 1.1": all_hardware_patchset["Miscellaneous"]["Legacy USB 1.1"]})

View File

@@ -1,58 +1,86 @@
# Additional support functions for sys_patch.py
# Copyright (C) 2020-2022, Dhinak G, Mykola Grymalyuk
# Copyright (C) 2020-2023, Dhinak G, Mykola Grymalyuk
import subprocess
import tempfile
from data import os_data
from resources import generate_smbios, utilities
from pathlib import Path
from datetime import datetime
import plistlib
import os
import logging
import subprocess
from pathlib import Path
from datetime import datetime
from resources import kdk_handler, bplist
class sys_patch_helpers:
def __init__(self, constants):
self.constants = constants
from data import os_data
from resources import bplist, constants, generate_smbios, utilities
def snb_board_id_patch(self, source_files_path):
# AppleIntelSNBGraphicsFB hard codes the supported Board IDs for Sandy Bridge iGPUs
# Because of this, the kext errors out on unsupported systems
# This function simply patches in a supported Board ID, using 'determine_best_board_id_for_sandy()'
# to supplement the ideal Board ID
class SysPatchHelpers:
"""
Library of helper functions for sys_patch.py and related libraries
"""
def __init__(self, global_constants: constants.Constants):
self.constants: constants.Constants = global_constants
def snb_board_id_patch(self, source_files_path: str):
"""
Patch AppleIntelSNBGraphicsFB.kext to support unsupported Board IDs
AppleIntelSNBGraphicsFB hard codes the supported Board IDs for Sandy Bridge iGPUs
Because of this, the kext errors out on unsupported systems
This function simply patches in a supported Board ID, using 'determine_best_board_id_for_sandy()'
to supplement the ideal Board ID
Parameters:
source_files_path (str): Path to the source files
"""
source_files_path = str(source_files_path)
if self.constants.computer.reported_board_id not in self.constants.sandy_board_id_stock:
logging.info(f"- Found unsupported Board ID {self.constants.computer.reported_board_id}, performing AppleIntelSNBGraphicsFB bin patching")
board_to_patch = generate_smbios.determine_best_board_id_for_sandy(self.constants.computer.reported_board_id, self.constants.computer.gpus)
logging.info(f"- Replacing {board_to_patch} with {self.constants.computer.reported_board_id}")
board_to_patch_hex = bytes.fromhex(board_to_patch.encode('utf-8').hex())
reported_board_hex = bytes.fromhex(self.constants.computer.reported_board_id.encode('utf-8').hex())
if self.constants.computer.reported_board_id in self.constants.sandy_board_id_stock:
return
if len(board_to_patch_hex) > len(reported_board_hex):
# Pad the reported Board ID with zeros to match the length of the board to patch
reported_board_hex = reported_board_hex + bytes(len(board_to_patch_hex) - len(reported_board_hex))
elif len(board_to_patch_hex) < len(reported_board_hex):
logging.info(f"- Error: Board ID {self.constants.computer.reported_board_id} is longer than {board_to_patch}")
raise Exception("Host's Board ID is longer than the kext's Board ID, cannot patch!!!")
logging.info(f"- Found unsupported Board ID {self.constants.computer.reported_board_id}, performing AppleIntelSNBGraphicsFB bin patching")
path = source_files_path + "/10.13.6/System/Library/Extensions/AppleIntelSNBGraphicsFB.kext/Contents/MacOS/AppleIntelSNBGraphicsFB"
if Path(path).exists():
with open(path, 'rb') as f:
data = f.read()
data = data.replace(board_to_patch_hex, reported_board_hex)
with open(path, 'wb') as f:
f.write(data)
else:
logging.info(f"- Error: Could not find {path}")
raise Exception("Failed to find AppleIntelSNBGraphicsFB.kext, cannot patch!!!")
board_to_patch = generate_smbios.determine_best_board_id_for_sandy(self.constants.computer.reported_board_id, self.constants.computer.gpus)
logging.info(f"- Replacing {board_to_patch} with {self.constants.computer.reported_board_id}")
board_to_patch_hex = bytes.fromhex(board_to_patch.encode('utf-8').hex())
reported_board_hex = bytes.fromhex(self.constants.computer.reported_board_id.encode('utf-8').hex())
if len(board_to_patch_hex) > len(reported_board_hex):
# Pad the reported Board ID with zeros to match the length of the board to patch
reported_board_hex = reported_board_hex + bytes(len(board_to_patch_hex) - len(reported_board_hex))
elif len(board_to_patch_hex) < len(reported_board_hex):
logging.info(f"- Error: Board ID {self.constants.computer.reported_board_id} is longer than {board_to_patch}")
raise Exception("Host's Board ID is longer than the kext's Board ID, cannot patch!!!")
path = source_files_path + "/10.13.6/System/Library/Extensions/AppleIntelSNBGraphicsFB.kext/Contents/MacOS/AppleIntelSNBGraphicsFB"
if not Path(path).exists():
logging.info(f"- Error: Could not find {path}")
raise Exception("Failed to find AppleIntelSNBGraphicsFB.kext, cannot patch!!!")
with open(path, 'rb') as f:
data = f.read()
data = data.replace(board_to_patch_hex, reported_board_hex)
with open(path, 'wb') as f:
f.write(data)
def generate_patchset_plist(self, patchset, file_name, kdk_used):
def generate_patchset_plist(self, patchset: dict, file_name: str, kdk_used: Path):
"""
Generate patchset file for user reference
Parameters:
patchset (dict): Dictionary of patchset, see sys_patch_detect.py and sys_patch_dict.py
file_name (str): Name of the file to write to
kdk_used (Path): Path to the KDK used, if any
Returns:
bool: True if successful, False if not
"""
source_path = f"{self.constants.payload_path}"
source_path_file = f"{source_path}/{file_name}"
@@ -68,23 +96,35 @@ class sys_patch_helpers:
"Kernel Debug Kit Used": f"{kdk_string}",
"OS Version": f"{self.constants.detected_os}.{self.constants.detected_os_minor} ({self.constants.detected_os_build})",
}
data.update(patchset)
if Path(source_path_file).exists():
os.remove(source_path_file)
# Need to write to a safe location
plistlib.dump(data, Path(source_path_file).open("wb"), sort_keys=False)
if Path(source_path_file).exists():
return True
return False
def disable_window_server_caching(self):
# On legacy GCN GPUs, the WindowServer cache generated creates
# corrupted Opaque shaders.
# To work-around this, we disable WindowServer caching
# And force macOS into properly generating the Opaque shaders
"""
Disable WindowServer's asset caching
On legacy GCN GPUs, the WindowServer cache generated creates
corrupted Opaque shaders.
To work-around this, we disable WindowServer caching
And force macOS into properly generating the Opaque shaders
"""
if self.constants.detected_os < os_data.os_data.ventura:
return
logging.info("- Disabling WindowServer Caching")
# Invoke via 'bash -c' to resolve pathing
utilities.elevated(["bash", "-c", "rm -rf /private/var/folders/*/*/*/WindowServer/com.apple.WindowServer"])
@@ -96,11 +136,17 @@ class sys_patch_helpers:
def remove_news_widgets(self):
# On Ivy Bridge and Haswell iGPUs, RenderBox will crash the News Widgets in
# Notification Centre. To ensure users can access Notifications normally,
# we manually remove all News Widgets
"""
Remove News Widgets from Notification Centre
On Ivy Bridge and Haswell iGPUs, RenderBox will crash the News Widgets in
Notification Centre. To ensure users can access Notifications normally,
we manually remove all News Widgets
"""
if self.constants.detected_os < os_data.os_data.ventura:
return
logging.info("- Parsing Notification Centre Widgets")
file_path = "~/Library/Containers/com.apple.notificationcenterui/Data/Library/Preferences/com.apple.notificationcenterui.plist"
file_path = Path(file_path).expanduser()
@@ -112,22 +158,27 @@ class sys_patch_helpers:
did_find = False
with open(file_path, "rb") as f:
data = plistlib.load(f)
if "widgets" in data:
if "instances" in data["widgets"]:
for widget in list(data["widgets"]["instances"]):
widget_data = bplist.BPListReader(widget).parse()
for entry in widget_data:
if not 'widget' in entry:
continue
sub_data = bplist.BPListReader(widget_data[entry]).parse()
for sub_entry in sub_data:
if not '$object' in sub_entry:
continue
if not b'com.apple.news' in sub_data[sub_entry][2]:
continue
logging.info(f" - Found News Widget to remove: {sub_data[sub_entry][2].decode('ascii')}")
data["widgets"]["instances"].remove(widget)
did_find = True
if "widgets" not in data:
return
if "instances" not in data["widgets"]:
return
for widget in list(data["widgets"]["instances"]):
widget_data = bplist.BPListReader(widget).parse()
for entry in widget_data:
if 'widget' not in entry:
continue
sub_data = bplist.BPListReader(widget_data[entry]).parse()
for sub_entry in sub_data:
if not '$object' in sub_entry:
continue
if not b'com.apple.news' in sub_data[sub_entry][2]:
continue
logging.info(f" - Found News Widget to remove: {sub_data[sub_entry][2].decode('ascii')}")
data["widgets"]["instances"].remove(widget)
did_find = True
if did_find:
with open(file_path, "wb") as f:
plistlib.dump(data, f, sort_keys=False)
@@ -135,16 +186,23 @@ class sys_patch_helpers:
def install_rsr_repair_binary(self):
# With macOS 13.2, Apple implemented the Rapid Security Response System
# However Apple added a half baked snapshot reversion system if seal was broken,
# which forgets to handle Preboot BootKC syncing
"""
Installs RSRRepair
# Thus this application will try to re-sync the BootKC with SysKC in the event of a panic
# Reference: https://github.com/dortania/OpenCore-Legacy-Patcher/issues/1019
RSRRepair is a utility that will sync the SysKC and BootKC in the event of a panic
# This is a (hopefully) temporary work-around, however likely to stay.
# RSRRepair has the added bonus of fixing desynced KCs from 'bless', so useful in Big Sur+
# https://github.com/flagersgit/RSRRepair
With macOS 13.2, Apple implemented the Rapid Security Response System
However Apple added a half baked snapshot reversion system if seal was broken,
which forgets to handle Preboot BootKC syncing.
Thus this application will try to re-sync the BootKC with SysKC in the event of a panic
Reference: https://github.com/dortania/OpenCore-Legacy-Patcher/issues/1019
This is a (hopefully) temporary work-around, however likely to stay.
RSRRepair has the added bonus of fixing desynced KCs from 'bless', so useful in Big Sur+
Source: https://github.com/flagersgit/RSRRepair
"""
if self.constants.detected_os < os_data.os_data.big_sur:
return

View File

@@ -1,78 +0,0 @@
# Copyright (C) 2020-2022, Dhinak G, Mykola Grymalyuk
from resources import utilities
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:
utilities.cls()
utilities.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):
utilities.cls()
utilities.header(self.title)
print()
for i in self.in_between:
print(i)
if self.in_between:
print()
return input(self.prompt)

View File

@@ -5,21 +5,31 @@
import requests
import logging
from resources import network_handler
from resources import network_handler, constants
REPO_LATEST_RELEASE_URL: str = "https://api.github.com/repos/dortania/OpenCore-Legacy-Patcher/releases/latest"
class check_binary_updates:
def __init__(self, constants):
self.constants = constants
self.binary_version = self.constants.patcher_version
self.binary_version_array = self.binary_version.split(".")
self.binary_version_array = [int(x) for x in self.binary_version_array]
self.binary_url = "https://api.github.com/repos/dortania/OpenCore-Legacy-Patcher/releases/latest"
class CheckBinaryUpdates:
def __init__(self, global_constants: constants.Constants):
self.constants: constants.Constants = global_constants
self.available_binaries = {}
self.binary_version = self.constants.patcher_version
self.binary_version_array = [int(x) for x in self.binary_version.split(".")]
def check_if_build_newer(self, remote_version=None, local_version=None):
def _check_if_build_newer(self, remote_version: list = None, local_version: list = None):
"""
Check if the remote version is newer than the local version
Parameters:
remote_version (list): Remote version to compare against
local_version (list): Local version to compare against
Returns:
bool: True if remote version is newer, False if not
"""
if remote_version is None:
remote_version = self.remote_version_array
if local_version is None:
@@ -39,13 +49,32 @@ class check_binary_updates:
return False
def determine_local_build_type(self):
def _determine_local_build_type(self):
"""
Check if the local build is a GUI or TUI build
Returns:
str: "GUI" or "TUI"
"""
if self.constants.wxpython_variant is True:
return "GUI"
else:
return "TUI"
def determine_remote_type(self, remote_name):
def _determine_remote_type(self, remote_name: str):
"""
Check if the remote build is a GUI or TUI build
Parameters:
remote_name (str): Name of the remote build
Returns:
str: "GUI" or "TUI"
"""
if "TUI" in remote_name:
return "TUI"
elif "GUI" in remote_name:
@@ -53,45 +82,43 @@ class check_binary_updates:
else:
return "Unknown"
def check_binary_updates(self):
# logging.info("- Checking for updates...")
if network_handler.NetworkUtilities(self.binary_url).verify_network_connection():
# logging.info("- Network connection functional")
response = requests.get(self.binary_url)
data_set = response.json()
# logging.info("- Retrieved latest version data")
self.remote_version = data_set["tag_name"]
# logging.info(f"- Latest version: {self.remote_version}")
self.remote_version_array = self.remote_version.split(".")
self.remote_version_array = [
int(x) for x in self.remote_version_array
]
if self.check_if_build_newer() is True:
# logging.info("- Remote version is newer")
for asset in data_set["assets"]:
logging.info(f"- Found asset: {asset['name']}")
if self.determine_remote_type(asset["name"]) == self.determine_local_build_type():
# logging.info(f"- Found matching asset: {asset['name']}")
self.available_binaries.update({
asset['name']: {
"Name":
asset["name"],
"Version":
self.remote_version,
"Link":
asset["browser_download_url"],
"Type":
self.determine_remote_type(asset["name"]),
"Github Link":
f"https://github.com/dortania/OpenCore-Legacy-Patcher/releases/{self.remote_version}"
}
})
break
if self.available_binaries:
return self.available_binaries
else:
# logging.info("- No matching binaries available")
return None
# else:
# logging.info("- Failed to connect to GitHub API")
"""
Check if any updates are available for the OpenCore Legacy Patcher binary
Returns:
dict: Dictionary with Link and Version of the latest binary update if available
"""
available_binaries: list = {}
if not network_handler.NetworkUtilities(REPO_LATEST_RELEASE_URL).verify_network_connection():
return None
response = requests.get(REPO_LATEST_RELEASE_URL)
data_set = response.json()
self.remote_version = data_set["tag_name"]
self.remote_version_array = self.remote_version.split(".")
self.remote_version_array = [int(x) for x in self.remote_version_array]
if self._check_if_build_newer() is False:
return None
for asset in data_set["assets"]:
logging.info(f"- Found asset: {asset['name']}")
if self._determine_remote_type(asset["name"]) == self._determine_local_build_type():
available_binaries.update({
asset['name']: {
"Name": asset["name"],
"Version": self.remote_version,
"Link": asset["browser_download_url"],
"Type": self._determine_remote_type(asset["name"]),
"Github Link": f"https://github.com/dortania/OpenCore-Legacy-Patcher/releases/{self.remote_version}"
}
})
return available_binaries
return None

View File

@@ -9,7 +9,6 @@ import os
import binascii
import argparse
import atexit
import requests
import shutil
import py_sip_xnu

View File

@@ -1,51 +1,70 @@
import logging
import subprocess
from pathlib import Path
from resources.sys_patch import sys_patch_helpers
from resources.build import build
from resources import constants
from data import example_data, model_array, sys_patch_dict, os_data
from pathlib import Path
import logging
def validate(settings):
# Runs through ocvalidate to check for errors
class PatcherValidation:
"""
Validation class for the patcher
valid_dumps = [
example_data.MacBookPro.MacBookPro92_Stock,
example_data.MacBookPro.MacBookPro111_Stock,
example_data.MacBookPro.MacBookPro133_Stock,
# example_data.MacBookPro.MacBookPro171_Stock,
example_data.Macmini.Macmini52_Stock,
example_data.Macmini.Macmini61_Stock,
example_data.Macmini.Macmini71_Stock,
# example_data.Macmini.Macmini91_Stock,
example_data.iMac.iMac81_Stock,
example_data.iMac.iMac112_Stock,
example_data.iMac.iMac122_Upgraded,
example_data.iMac.iMac122_Upgraded_Nvidia,
example_data.iMac.iMac151_Stock,
example_data.MacPro.MacPro31_Stock,
example_data.MacPro.MacPro31_Upgrade,
example_data.MacPro.MacPro31_Modern_AMD,
example_data.MacPro.MacPro31_Modern_Kepler,
example_data.MacPro.MacPro41_Upgrade,
example_data.MacPro.MacPro41_Modern_AMD,
example_data.MacPro.MacPro41_51__Flashed_Modern_AMD,
example_data.MacPro.MacPro41_51_Flashed_NVIDIA_WEB_DRIVERS,
]
Primarily for Continuous Integration
"""
valid_dumps_native = [
example_data.iMac.iMac201_Stock,
example_data.MacBookPro.MacBookPro141_SSD_Upgrade,
]
def __init__(self, global_constants: constants.Constants):
self.constants: constants.Constants = global_constants
settings.validate = True
self.constants.validate = True
self.valid_dumps = [
example_data.MacBookPro.MacBookPro92_Stock,
example_data.MacBookPro.MacBookPro111_Stock,
example_data.MacBookPro.MacBookPro133_Stock,
example_data.Macmini.Macmini52_Stock,
example_data.Macmini.Macmini61_Stock,
example_data.Macmini.Macmini71_Stock,
example_data.iMac.iMac81_Stock,
example_data.iMac.iMac112_Stock,
example_data.iMac.iMac122_Upgraded,
example_data.iMac.iMac122_Upgraded_Nvidia,
example_data.iMac.iMac151_Stock,
example_data.MacPro.MacPro31_Stock,
example_data.MacPro.MacPro31_Upgrade,
example_data.MacPro.MacPro31_Modern_AMD,
example_data.MacPro.MacPro31_Modern_Kepler,
example_data.MacPro.MacPro41_Upgrade,
example_data.MacPro.MacPro41_Modern_AMD,
example_data.MacPro.MacPro41_51__Flashed_Modern_AMD,
example_data.MacPro.MacPro41_51_Flashed_NVIDIA_WEB_DRIVERS,
]
self.valid_dumps_native = [
example_data.iMac.iMac201_Stock,
example_data.MacBookPro.MacBookPro141_SSD_Upgrade,
]
self._validate_configs()
self._validate_sys_patch()
def _build_prebuilt(self):
"""
Generate a build for each predefined model
Then validate against ocvalidate
"""
def build_prebuilt():
for model in model_array.SupportedSMBIOS:
logging.info(f"Validating predefined model: {model}")
settings.custom_model = model
build.build_opencore(settings.custom_model, settings).build_opencore()
result = subprocess.run([settings.ocvalidate_path, f"{settings.opencore_release_folder}/EFI/OC/config.plist"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
self.constants.custom_model = model
build.build_opencore(self.constants.custom_model, self.constants).build_opencore()
result = subprocess.run([self.constants.ocvalidate_path, f"{self.constants.opencore_release_folder}/EFI/OC/config.plist"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
if result.returncode != 0:
logging.info("Error on build!")
logging.info(result.stdout.decode())
@@ -53,24 +72,35 @@ def validate(settings):
else:
logging.info(f"Validation succeeded for predefined model: {model}")
def build_dumps():
for model in valid_dumps:
settings.computer = model
settings.custom_model = ""
logging.info(f"Validating dumped model: {settings.computer.real_model}")
build.build_opencore(settings.computer.real_model, settings).build_opencore()
result = subprocess.run([settings.ocvalidate_path, f"{settings.opencore_release_folder}/EFI/OC/config.plist"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
def _build_dumps(self):
"""
Generate a build for each predefined model
Then validate against ocvalidate
"""
for model in self.valid_dumps:
self.constants.computer = model
self.constants.custom_model = ""
logging.info(f"Validating dumped model: {self.constants.computer.real_model}")
build.build_opencore(self.constants.computer.real_model, self.constants).build_opencore()
result = subprocess.run([self.constants.ocvalidate_path, f"{self.constants.opencore_release_folder}/EFI/OC/config.plist"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
if result.returncode != 0:
logging.info("Error on build!")
logging.info(result.stdout.decode())
raise Exception(f"Validation failed for predefined model: {settings.computer.real_model}")
raise Exception(f"Validation failed for predefined model: {self.constants.computer.real_model}")
else:
logging.info(f"Validation succeeded for predefined model: {settings.computer.real_model}")
logging.info(f"Validation succeeded for predefined model: {self.constants.computer.real_model}")
def validate_root_patch_files(major_kernel, minor_kernel):
patchset = sys_patch_dict.SystemPatchDictionary(major_kernel, minor_kernel, settings.legacy_accel_support)
def _validate_root_patch_files(self, major_kernel, minor_kernel):
"""
Validate that all files in the patchset are present in the payload
"""
patchset = sys_patch_dict.SystemPatchDictionary(major_kernel, minor_kernel, self.constants.legacy_accel_support)
host_os_float = float(f"{major_kernel}.{minor_kernel}")
for patch_subject in patchset:
for patch_core in patchset[patch_subject]:
patch_os_min_float = float(f'{patchset[patch_subject][patch_core]["OS Support"]["Minimum OS Support"]["OS Major"]}.{patchset[patch_subject][patch_core]["OS Support"]["Minimum OS Support"]["OS Minor"]}')
@@ -81,58 +111,83 @@ def validate(settings):
if install_type in patchset[patch_subject][patch_core]:
for install_directory in patchset[patch_subject][patch_core][install_type]:
for install_file in patchset[patch_subject][patch_core][install_type][install_directory]:
source_file = str(settings.payload_local_binaries_root_path) + "/" + patchset[patch_subject][patch_core][install_type][install_directory][install_file] + install_directory + "/" + install_file
source_file = str(self.constants.payload_local_binaries_root_path) + "/" + patchset[patch_subject][patch_core][install_type][install_directory][install_file] + install_directory + "/" + install_file
if not Path(source_file).exists():
logging.info(f"File not found: {source_file}")
raise Exception(f"Failed to find {source_file}")
logging.info(f"- Validating against Darwin {major_kernel}.{minor_kernel}")
if not sys_patch_helpers.sys_patch_helpers(settings).generate_patchset_plist(patchset, f"OpenCore-Legacy-Patcher-{major_kernel}.{minor_kernel}.plist", None):
if not sys_patch_helpers.SysPatchHelpers(self.constants).generate_patchset_plist(patchset, f"OpenCore-Legacy-Patcher-{major_kernel}.{minor_kernel}.plist", None):
raise Exception("Failed to generate patchset plist")
# Remove the plist file after validation
Path(settings.payload_path / f"OpenCore-Legacy-Patcher-{major_kernel}.{minor_kernel}.plist").unlink()
Path(self.constants.payload_path / f"OpenCore-Legacy-Patcher-{major_kernel}.{minor_kernel}.plist").unlink()
def validate_sys_patch():
if Path(settings.payload_local_binaries_root_path_zip).exists():
def _validate_sys_patch(self):
"""
Validates sys_patch modules
"""
if Path(self.constants.payload_local_binaries_root_path_zip).exists():
logging.info("Validating Root Patch File integrity")
if not Path(settings.payload_local_binaries_root_path).exists():
subprocess.run(["ditto", "-V", "-x", "-k", "--sequesterRsrc", "--rsrc", settings.payload_local_binaries_root_path_zip, settings.payload_path], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
if not Path(self.constants.payload_local_binaries_root_path).exists():
subprocess.run(
[
"ditto", "-V", "-x", "-k", "--sequesterRsrc", "--rsrc",
self.constants.payload_local_binaries_root_path_zip,
self.constants.payload_path
],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT
)
for supported_os in [os_data.os_data.big_sur, os_data.os_data.monterey, os_data.os_data.ventura]:
for i in range(0, 10):
validate_root_patch_files(supported_os, i)
self._validate_root_patch_files(supported_os, i)
logging.info("Validating SNB Board ID patcher")
settings.computer.reported_board_id = "Mac-7BA5B2DFE22DDD8C"
sys_patch_helpers.sys_patch_helpers(settings).snb_board_id_patch(settings.payload_local_binaries_root_path)
self.constants.computer.reported_board_id = "Mac-7BA5B2DFE22DDD8C"
sys_patch_helpers.SysPatchHelpers(self.constants).snb_board_id_patch(self.constants.payload_local_binaries_root_path)
# Clean up
subprocess.run(
[
"rm", "-rf", self.constants.payload_local_binaries_root_path
],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT
)
else:
logging.info("- Skipping Root Patch File integrity validation")
def validate_configs():
def _validate_configs(self):
"""
Validates build modules
"""
# First run is with default settings
build_prebuilt()
build_dumps()
self._build_prebuilt()
self._build_dumps()
# Second run, flip all settings
settings.verbose_debug = True
settings.opencore_debug = True
settings.opencore_build = "DEBUG"
settings.kext_debug = True
settings.kext_variant = "DEBUG"
settings.kext_debug = True
settings.showpicker = False
settings.sip_status = False
settings.secure_status = True
settings.firewire_boot = True
settings.nvme_boot = True
settings.enable_wake_on_wlan = True
settings.disable_tb = True
settings.force_surplus = True
settings.software_demux = True
settings.serial_settings = "Minimal"
build_prebuilt()
build_dumps()
self.constants.verbose_debug = True
self.constants.opencore_debug = True
self.constants.opencore_build = "DEBUG"
self.constants.kext_debug = True
self.constants.kext_variant = "DEBUG"
self.constants.kext_debug = True
self.constants.showpicker = False
self.constants.sip_status = False
self.constants.secure_status = True
self.constants.firewire_boot = True
self.constants.nvme_boot = True
self.constants.enable_wake_on_wlan = True
self.constants.disable_tb = True
self.constants.force_surplus = True
self.constants.software_demux = True
self.constants.serial_settings = "Minimal"
self._build_prebuilt()
self._build_dumps()
validate_configs()
validate_sys_patch()
subprocess.run(["rm", "-rf", self.constants.build_path], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)