diff --git a/.github/workflows/build-app-wxpython.yml b/.github/workflows/build-app-wxpython.yml index 8f3dc2acf..87ade3834 100644 --- a/.github/workflows/build-app-wxpython.yml +++ b/.github/workflows/build-app-wxpython.yml @@ -18,7 +18,6 @@ jobs: - uses: actions/checkout@v3 - run: python3 create_offline_build.py - run: /Library/Frameworks/Python.framework/Versions/3.9/bin/pyinstaller OpenCore-Patcher-GUI.spec - - run: python3 ./payloads/binary.py $branch $commiturl $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-wxPython.app.zip OpenCore-Patcher.app - run: ./../sign-wxpython.sh diff --git a/.github/workflows/build-app.yml b/.github/workflows/build-app.yml index 226203808..34dcc5bef 100644 --- a/.github/workflows/build-app.yml +++ b/.github/workflows/build-app.yml @@ -18,7 +18,6 @@ jobs: - uses: actions/checkout@v3 - run: python3 create_offline_build.py - run: /Library/Frameworks/Python.framework/Versions/3.9/bin/pyinstaller OpenCore-Patcher.spec - - run: python3 ./payloads/binary.py $branch $commiturl $commitdate - run: ./after_pyinstaller.sh - 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 diff --git a/CHANGELOG.md b/CHANGELOG.md index 584e0cbf2..1b65fc970 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - Fix backported to 0.4.4 release binaries - Add Macmini8,1 FeatureUnlock support - Drops CPU check, supports all machines +- Refactor Root Patching System ## 0.4.4 - Lower SIP requirement for Root Patching diff --git a/create_offline_build.py b/create_offline_build.py index 2bf4e8472..88e6175c0 100644 --- a/create_offline_build.py +++ b/create_offline_build.py @@ -2,7 +2,7 @@ import subprocess from resources import constants patcher_support_pkg_version = constants.Constants().patcher_support_pkg_version -binary_packages = ["11-Big-Sur", "12-Monterey", "Universal-Extensions"] +binary_packages = ["Universal-Binaries"] for binary_package in binary_packages: print(f"- Downloading {binary_package}...") diff --git a/data/sys_patch_data.py b/data/sys_patch_data.py deleted file mode 100644 index eb234852a..000000000 --- a/data/sys_patch_data.py +++ /dev/null @@ -1,287 +0,0 @@ -# Lists Root patches used by sys_patch.py -# Copyright (C) 2020-2021, Dhinak G, Mykola Grymalyuk -DeleteNvidiaAccel11 = [ - "AMDRadeonX4000.kext", - "AMDRadeonX4000HWServices.kext", - "AMDRadeonX5000.kext", - "AMDRadeonX5000HWServices.kext", - "AMDRadeonX6000.kext", - "AMDRadeonX6000Framebuffer.kext", - "AMDRadeonX6000HWServices.kext", - "AppleIntelBDWGraphics.kext", - "AppleIntelBDWGraphicsFramebuffer.kext", - "AppleIntelCFLGraphicsFramebuffer.kext", - "AppleIntelHD4000Graphics.kext", - "AppleIntelHD5000Graphics.kext", - "AppleIntelICLGraphics.kext", - "AppleIntelICLLPGraphicsFramebuffer.kext", - "AppleIntelKBLGraphics.kext", - "AppleIntelKBLGraphicsFramebuffer.kext", - "AppleIntelSKLGraphics.kext", - "AppleIntelSKLGraphicsFramebuffer.kext", - "AppleIntelFramebufferAzul.kext", - "AppleIntelFramebufferCapri.kext", - "AppleParavirtGPU.kext", - "GeForce.kext", - "IOAcceleratorFamily2.kext", - "IOGPUFamily.kext", -] - -DeleteAMDAccel11 = [ - "AMDRadeonX4000.kext", - "AMDRadeonX4000HWServices.kext", - "AMDRadeonX5000.kext", - "AMDRadeonX5000HWServices.kext", - "AMDRadeonX6000.kext", - "AMDRadeonX6000Framebuffer.kext", - "AMDRadeonX6000HWServices.kext", - "AMD7000Controller.kext", # AMDSupport Dependency - "AMD8000Controller.kext", # AMDSupport Dependency - "AMD9000Controller.kext", # AMDSupport Dependency - "AMD9500Controller.kext", # AMDSupport Dependency - "AMD10000Controller.kext", # AMDSupport Dependency - "AppleIntelBDWGraphics.kext", - "AppleIntelBDWGraphicsFramebuffer.kext", - "AppleIntelCFLGraphicsFramebuffer.kext", - "AppleIntelHD4000Graphics.kext", - "AppleIntelHD5000Graphics.kext", - "AppleIntelICLGraphics.kext", - "AppleIntelICLLPGraphicsFramebuffer.kext", - "AppleIntelKBLGraphics.kext", - "AppleIntelKBLGraphicsFramebuffer.kext", - "AppleIntelSKLGraphics.kext", - "AppleIntelSKLGraphicsFramebuffer.kext", - "AppleIntelFramebufferAzul.kext", - "AppleIntelFramebufferCapri.kext", - "AppleParavirtGPU.kext", - "GeForce.kext", - "IOGPUFamily.kext", -] - -DeleteAMDAccel11TS2 = [ - "AppleCameraInterface.kext", -] - -AddNvidiaAccel11 = [ - "GeForceGA.bundle", - "GeForceTesla.kext", - "GeForceTeslaGLDriver.bundle", - "GeForceTeslaVADriver.bundle", - "NVDANV50HalTesla.kext", - "NVDAResmanTesla.kext", - "IOSurface.kext", -] - -AddNvidiaBrightness = [ - "GeForceGA.bundle", - "GeForceTesla.kext", - "GeForceTeslaGLDriver.bundle", - "GeForceTeslaVADriver.bundle", - "NVDANV50HalTesla.kext", - "NVDAResmanTesla.kext", -] - -AddNvidiaKeplerAccel11 = [ - "GeForce.kext", - "GeForceAIRPlugin.bundle", - "GeForceGLDriver.bundle", - "GeForceMTLDriver.bundle", - "GeForceVADriver.bundle", - "NVDAGF100Hal.kext", - "NVDAGK100Hal.kext", - "NVDAResman.kext", - "NVDAStartup.kext", -] - -AddNvidiaTeslaAccel12 = [ - "NVDAStartup.kext", -] - -AddAMDAccel11 = [ - "AMD2400Controller.kext", - "AMD2600Controller.kext", - "AMD3800Controller.kext", - "AMD4600Controller.kext", - "AMD4800Controller.kext", - "AMD5000Controller.kext", - "AMD6000Controller.kext", - "AMDFramebuffer.kext", - "AMDLegacyFramebuffer.kext", - "AMDLegacySupport.kext", - "AMDRadeonVADriver.bundle", - "AMDRadeonVADriver2.bundle", - "AMDRadeonX3000.kext", - "AMDRadeonX3000GLDriver.bundle", - "AMDShared.bundle", - "AMDSupport.kext", - "ATIRadeonX2000.kext", - "ATIRadeonX2000GA.plugin", - "ATIRadeonX2000GLDriver.bundle", - "ATIRadeonX2000VADriver.bundle", -] - -AddAMDBrightness = [ - "AMD2400Controller.kext", - "AMD2600Controller.kext", - "AMD3800Controller.kext", - "AMD4600Controller.kext", - "AMD4800Controller.kext", - "AMD5000Controller.kext", - "AMD6000Controller.kext", - "AMDLegacyFramebuffer.kext", - "AMDLegacySupport.kext", - "AMDRadeonVADriver.bundle", - "AMDRadeonVADriver2.bundle", - # "AMDRadeonX3000.kext", - # "AMDRadeonX3000GLDriver.bundle", - "AMDShared.bundle", - "ATIRadeonX2000.kext", - "ATIRadeonX2000GA.plugin", - "ATIRadeonX2000GLDriver.bundle", - "ATIRadeonX2000VADriver.bundle", -] - -AddAMDAccel11TS2 = [ - "IOSurface.kext", - "IOAcceleratorFamily2.kext", -] - -AddIntelGen1Accel = [ - "AppleIntelHDGraphics.kext", - "AppleIntelHDGraphicsFB.kext", - "AppleIntelHDGraphicsGA.plugin", - "AppleIntelHDGraphicsGLDriver.bundle", - "AppleIntelHDGraphicsVADriver.bundle", -] - -AddIntelGen2Accel = [ - "AppleIntelHD3000Graphics.kext", - "AppleIntelHD3000GraphicsGA.plugin", - "AppleIntelHD3000GraphicsGLDriver.bundle", - "AppleIntelHD3000GraphicsVADriver.bundle", - # "AppleIntelSNBGraphicsFB.kext", - "AppleIntelSNBVA.bundle", -] - -AddIntelGen2AccelStock = [ - "AppleIntelSNBGraphicsFB-Clean.kext", -] - -AddIntelGen2AccelPatched = [ - "AppleIntelSNBGraphicsFB.kext", -] - -AddIntelGen3Accel = [ - "AppleIntelFramebufferCapri.kext", - "AppleIntelHD4000Graphics.kext", - "AppleIntelHD4000GraphicsGLDriver.bundle", - "AppleIntelHD4000GraphicsMTLDriver.bundle", - "AppleIntelHD4000GraphicsVADriver.bundle", - "AppleIntelIVBVA.bundle", - "AppleIntelGraphicsShared.bundle", -] - -AddGeneralAccel = ["IOAcceleratorFamily2.kext", "IOSurface.kext"] - -DeleteBrightness = ["AppleGraphicsControl.kext/Contents/PlugIns/AGDCBacklightControl.kext"] - -DeleteDemux = [ - "AppleGraphicsControl.kext/Contents/PlugIns/AGDCBacklightControl.kext", - "AppleGraphicsControl.kext/Contents/PlugIns/AppleMuxControl.kext", - "AppleBacklight.kext", -] - -AddDemux = [ - "AppleMuxControl.kext", -] - -AddBrightness = [ - "AppleBacklight.kext", - "AppleBacklightExpert.kext", -] - -AddVolumeControl = [ - "AppleHDA.kext", - "IOAudioFamily.kext", -] - -AddVolumeControlv2 = [ - "AppleHDA.kext", -] - -DeleteVolumeControl = [ - "AppleVirtIO.kext", - "AppleVirtualGraphics.kext", - "AppleVirtualPlatform.kext", - "ApplePVPanic.kext", - "AppleVirtIOStorage.kext", -] - -AddNvidiaAccelLegacy = [ - # "GeForceGA.bundle", - "GeForceTesla.kext", - "GeForceTeslaGLDriver.bundle", - "GeForceTeslaVADriver.bundle", - "NVDANV50HalTesla.kext", - "NVDAResmanTesla.kext", -] - -AddAMDAccelLegacy = [ - "AMD2400Controller.kext", - "AMD2600Controller.kext", - "AMD3800Controller.kext", - "AMD4600Controller.kext", - "AMD4800Controller.kext", - "AMD5000Controller.kext", - "AMD6000Controller.kext", - "AMDFramebuffer.kext", - "AMDLegacyFramebuffer.kext", - "AMDLegacySupport.kext", - "AMDRadeonVADriver.bundle", - "AMDRadeonVADriver2.bundle", - "AMDRadeonX3000.kext", - "AMDRadeonX3000GLDriver.bundle", - "AMDRadeonX4000HWServices.kext", - "AMDRadeonX4000.kext", - "AMDRadeonX4000GLDriver.bundle", - "AMDShared.bundle", - "AMDSupport.kext", - "ATIRadeonX2000.kext", - "ATIRadeonX2000GA.plugin", - "ATIRadeonX2000GLDriver.bundle", - "ATIRadeonX2000VADriver.bundle", -] - -AddGeneralAccelCatalina = [ - "AppleGraphicsControl.kext", - "AppleGraphicsPowerManagement.kext", - "AppleMCCSControl.kext", - "IOGraphicsFamily.kext", - "IONDRVSupport.kext", - "IOSurface.kext", -] - -AddGeneralAccelMojave = [ - "IONDRVSupport.kext", - "AppleGraphicsControl.kext", - "AppleGraphicsPowerManagement.kext", - "AppleMCCSControl.kext", - "IOAccelerator2D.plugin", - "IOAcceleratorFamily2.kext", - "IOGraphicsFamily.kext", -] - -BackupLocations = [ - "System/Library/Extensions", - "System/Library/Frameworks/CoreDisplay.framework", - "System/Library/Frameworks/IOSurface.framework", - "System/Library/Frameworks/OpenGL.framework", - "System/Library/Frameworks/WebKit.framework", - "System/Library/LaunchDaemons", - "System/Library/PrivateFrameworks/DisplayServices.framework", - "System/Library/PrivateFrameworks/GPUSupport.framework", - "System/Library/PrivateFrameworks/SkyLight.framework", - "System/Library/PrivateFrameworks/IOAccelerator.framework", - "System/Library/PrivateFrameworks/AppleGVA.framework", - "System/Library/PrivateFrameworks/AppleGVACore.framework", -] diff --git a/data/sys_patch_dict.py b/data/sys_patch_dict.py new file mode 100644 index 000000000..79669449a --- /dev/null +++ b/data/sys_patch_dict.py @@ -0,0 +1,335 @@ +# Dictionary defining patch sets used during Root Volume patching (sys_patch.py) +# Copyright (C) 2022, Mykola Grymalyuk + +# Schema for sys_patch_dict.py: +# Supports 3 types of higher level keys: +# - Install: Install to root volume - Dictionary of strings with value of source +# - Install Non-Root: Install to data partition - Dictionary of strings with value of source +# - Remove: Files to remove - Array of strings +# - Processes: Additional processes to run - Array of strings + +# File Storage is based off the origin, ie. '10.13.6/System/Library/Extensions/IOSurface.kext' +# Stubbed binaries are OS specific, this use the 'os_major' variable to denounce which folder to use + +from data import os_data + +def SystemPatchDictionary(os_major): + sys_patch_dict = { + "Graphics": { + "Non-Metal Common": { + "Install": { + "/System/Library/Extensions": { + "IOSurface.kext": "10.15.7", + }, + "/System/Library/Frameworks": { + "OpenGL.framework": "10.14.3", + "CoreDisplay.framework": f"10.14.4-{os_major}", + "IOSurface.framework": f"10.15.7-{os_major}", + "QuartzCore.framework": f"10.15.7-{os_major}", + }, + "/System/Library/PrivateFrameworks": { + "GPUSupport.framework": "10.14.3", + "SkyLight.framework": f"10.14.6-{os_major}", + }, + }, + "Remove": { + "/System/Library/Extensions": [ + "AMDRadeonX4000.kext", + "AMDRadeonX4000HWServices.kext", + "AMDRadeonX5000.kext", + "AMDRadeonX5000HWServices.kext", + "AMDRadeonX6000.kext", + "AMDRadeonX6000Framebuffer.kext", + "AMDRadeonX6000HWServices.kext", + "AppleIntelBDWGraphics.kext", + "AppleIntelBDWGraphicsFramebuffer.kext", + "AppleIntelCFLGraphicsFramebuffer.kext", + "AppleIntelHD4000Graphics.kext", + "AppleIntelHD5000Graphics.kext", + "AppleIntelICLGraphics.kext", + "AppleIntelICLLPGraphicsFramebuffer.kext", + "AppleIntelKBLGraphics.kext", + "AppleIntelKBLGraphicsFramebuffer.kext", + "AppleIntelSKLGraphics.kext", + "AppleIntelSKLGraphicsFramebuffer.kext", + "AppleIntelFramebufferAzul.kext", + "AppleIntelFramebufferCapri.kext", + "AppleParavirtGPU.kext", + "GeForce.kext", + "IOAcceleratorFamily2.kext", + "IOGPUFamily.kext", + ], + }, + "Install Non-Root": { + "/Library/Application Support/SkyLightPlugins": { + **({ "DropboxHack.dylib": "SkyLightPlugins" } if os_major >= os_data.os_data.monterey else {}), + **({ "DropboxHack.txt": "SkyLightPlugins" } if os_major >= os_data.os_data.monterey else {}), + }, + }, + }, + "Metal Common": { + "Install": { + "/System/Library/Frameworks": { + "OpenCL.framework": "11.6", + "WebKit.framework": "11.6", + }, + "/System/Library/PrivateFrameworks": { + "AppleGVA.framework": "10.15.7", + "AppleGVACore.framework": "10.15.7", + }, + }, + }, + + "Legacy GVA": { + "Install": { + "/System/Library/PrivateFrameworks": { + "AppleGVA.framework": "10.13.6", + "AppleGVACore.framework": "10.15.7", + }, + }, + }, + + "Nvidia Tesla": { + "Install": { + "/System/Library/Extensions": { + "GeForceGA.bundle": "10.13.6", + "GeForceTesla.kext": "10.13.6", + "GeForceTeslaGLDriver.bundle": "10.13.6", + "GeForceTeslaVADriver.bundle": "10.13.6", + "NVDANV50HalTesla.kext": "10.13.6", + "NVDAResmanTesla.kext": "10.13.6", + **({ "NVDAStartup.kext": "12.0 Beta 6" } if os_major >= os_data.os_data.monterey else {}) + }, + }, + }, + "Nvidia Kepler": { + "Install": { + "/System/Library/Extensions": { + "GeForceAIRPlugin.bundle": "11.0 Beta 3", + "GeForceGLDriver.bundle": "11.0 Beta 3", + "GeForceMTLDriver.bundle": "11.0 Beta 3", + "GeForce.kext": "12.0 Beta 6", + "GeForceVADriver.bundle": "12.0 Beta 6", + "NVDAGF100Hal.kext": "12.0 Beta 6", + "NVDAGK100Hal.kext": "12.0 Beta 6", + "NVDAResman.kext": "12.0 Beta 6", + "NVDAStartup.kext": "12.0 Beta 6", + }, + }, + }, + "Nvidia Web Drivers": { + "Install": { + "/System/Library/Extensions": { + "NVDAStartupWeb.kext": "10.13.6", + "GeForceTeslaWeb.kext": "10.13.6", + "GeForceWeb.kext": "10.13.6", + "NVDAGF100HalWeb.kext": "10.13.6", + "NVDAGK100HalWeb.kext": "10.13.6", + "NVDAGM100HalWeb.kext": "10.13.6", + "NVDAGP100HalWeb.kext": "10.13.6", + "NVDANV50HalTeslaWeb.kext": "10.13.6", + "NVDAResmanTeslaWeb.kext": "10.13.6", + "NVDAResmanWeb.kext": "10.13.6", + "GeForceVADriverWeb.bundle": "10.13.6", + "GeForceAIRPluginWeb.bundle": "10.13.6", + "GeForceGLDriverWeb.bundle": "10.13.6", + "GeForceMTLDriverWeb.bundle": "10.13.6", + "GeForceTeslaGAWeb.bundle": "10.13.6", + "GeForceTeslaGLDriverWeb.bundle": "10.13.6", + "GeForceTeslaVADriverWeb.bundle": "10.13.6", + }, + }, + }, + "AMD Non-Metal Common": { + "Install": { + "/System/Library/Extensions": { + "AMDFramebuffer.kext": "10.13.6", + "AMDLegacyFramebuffer.kext": "10.13.6", + "AMDLegacySupport.kext": "10.13.6", + "AMDShared.bundle": "10.13.6", + "AMDSupport.kext": "10.13.6", + }, + }, + "Remove": { + "/System/Library/Extensions": [ + "AMD7000Controller.kext", + "AMD8000Controller.kext", + "AMD9000Controller.kext", + "AMD9500Controller.kext", + "AMD10000Controller.kext", + ], + }, + }, + + "AMD TeraScale 1": { + "Install": { + "/System/Library/Extensions": { + "AMD2400Controller.kext": "10.13.6", + "AMD2600Controller.kext": "10.13.6", + "AMD3800Controller.kext": "10.13.6", + "AMD4600Controller.kext": "10.13.6", + "AMD4800Controller.kext": "10.13.6", + "ATIRadeonX2000.kext": "10.13.6", + "ATIRadeonX2000GA.plugin": "10.13.6", + "ATIRadeonX2000GLDriver.bundle": "10.13.6", + "ATIRadeonX2000VADriver.bundle": "10.13.6", + }, + }, + }, + "AMD TeraScale 2": { + "Install": { + "/System/Library/Extensions": { + "AMD5000Controller.kext": "10.13.6", + "AMD6000Controller.kext": "10.13.6", + "AMDRadeonVADriver.bundle": "10.13.6", + "AMDRadeonVADriver2.bundle": "10.13.6", + "AMDRadeonX3000.kext": "10.13.6", + "AMDRadeonX3000GLDriver.bundle": "10.13.6", + "IOAcceleratorFamily2.kext": "10.13.6", + "IOSurface.kext": "10.14.6", + }, + "/System/Library/Frameworks": { + "OpenCL.framework": "10.13.6", + "IOSurface.framework": f"10.14.6-{os_major}", + }, + "/System/Library/PrivateFrameworks": { + "GPUSupport.framework": "10.13.6", + "IOAccelerator.framework": f"10.13.6-{os_major}", + }, + }, + "Remove": { + "/System/Library/Extensions": { + "AppleCameraInterface.kext", + }, + }, + }, + "Intel Ironlake": { + "Install": { + "/System/Library/Extensions": { + "AppleIntelHDGraphics.kext": "10.13.6", + "AppleIntelHDGraphicsFB.kext": "10.13.6", + "AppleIntelHDGraphicsGA.plugin": "10.13.6", + "AppleIntelHDGraphicsGLDriver.bundle": "10.13.6", + "AppleIntelHDGraphicsVADriver.bundle": "10.13.6", + }, + }, + }, + "Intel Sandy Bridge": { + "Install": { + "/System/Library/Extensions": { + "AppleIntelHD3000Graphics.kext": "10.13.6", + "AppleIntelHD3000GraphicsGA.plugin": "10.13.6", + "AppleIntelHD3000GraphicsGLDriver.bundle": "10.13.6", + "AppleIntelHD3000GraphicsVADriver.bundle": "10.13.6", + "AppleIntelSNBGraphicsFB.kext": "10.13.6", + "AppleIntelSNBVA.bundle": "10.13.6", + }, + }, + }, + "Intel Ivy Bridge": { + "Processes": { + "defaults write com.apple.coremedia hardwareVideoDecoder -string enable": False, + }, + "Install": { + "/System/Library/Extensions": { + "AppleIntelHD4000GraphicsGLDriver.bundle": "11.0 Beta 6", + "AppleIntelHD4000GraphicsMTLDriver.bundle": "11.0 Beta 6", + "AppleIntelHD4000GraphicsVADriver.bundle": "11.3 Beta 1", + "AppleIntelFramebufferCapri.kext": "11.4", + "AppleIntelHD4000Graphics.kext": "11.4", + "AppleIntelIVBVA.bundle": "11.4", + "AppleIntelGraphicsShared.bundle": "11.4", # libIGIL-Metal.dylib pulled from 11.0 Beta 6 + }, + }, + }, + }, + "Audio": { + "Legacy Realtek": { + # For iMac7,1 and iMac8,1 units with legacy Realtek HD Audio + "Install": { + "/System/Library/Extensions": { + "AppleHDA.kext": "10.11.6", + "IOAudioFamily.kext": "10.11.6", + }, + }, + "Remove": { + "/System/Library/Extensions": [ + "AppleVirtIO.kext", + "AppleVirtualGraphics.kext", + "AppleVirtualPlatform.kext", + "ApplePVPanic.kext", + "AppleVirtIOStorage.kext", + ], + }, + }, + # For Mac Pros with non-UGA/GOP GPUs + "Legacy Non-GOP": { + "Install": { + "/System/Library/Extensions": { + "AppleHDA.kext": "10.13.6", + }, + }, + }, + }, + "Networking": { + "Legacy WiFi": { + "Install": { + "/usr/libexec": { + "airportd": "11.5.2", + }, + "/System/Library/CoreServices": { + "WiFiAgent.app": "11.5.2", + }, + }, + "Install Non-Root": { + "/Library/Application Support/SkyLightPlugins": { + **({ "CoreWLAN.dylib": "SkyLightPlugins" } if os_major >= os_data.os_data.monterey else {}), + **({ "CoreWLAN.txt": "SkyLightPlugins" } if os_major >= os_data.os_data.monterey else {}), + }, + }, + }, + }, + "Brightness": { + "Legacy Brightness": { + "Install": { + "/System/Library/Extensions": { + "AppleBacklight.kext": "10.12.6", + "AppleBacklightExpert.kext": "10.12.6", + }, + "/System/Library/PrivateFrameworks": { + "DisplayServices.framework": "10.12.6", + }, + }, + "Remove": { + "/System/Library/Extensions/AppleGraphicsControl.kext/Contents/PlugIns": [ + "AGDCBacklightControl.kext", + ], + }, + }, + }, + "Miscellaneous": { + "Legacy GMUX": { + "Install": { + "/System/Library/Extensions/AppleGraphicsControl.kext/Contents/PlugIns": { + "AppleMuxControl.kext": "10.12.6", + }, + }, + "Remove": { + "/System/Library/Extensions": [ + "AppleBacklight.kext", + ], + "/System/Library/Extensions/AppleGraphicsControl.kext/Contents/PlugIns": [ + "AGDCBacklightControl.kext", + "AppleMuxControl.kext", + ], + }, + }, + "Legacy Keyboard Backlight": { + "Processes": { + "defaults write /Library/Preferences/.GlobalPreferences.plist Moraea_BacklightHack -bool true": True, + }, + }, + }, + } + + return sys_patch_dict \ No newline at end of file diff --git a/gui/gui_main.py b/gui/gui_main.py index b5fc206c5..d273d58f5 100644 --- a/gui/gui_main.py +++ b/gui/gui_main.py @@ -899,7 +899,7 @@ class wx_python_gui: # Download resources sys.stdout=menu_redirect.RedirectLabel(self.developer_note) - download_result, os_ver, extensions_link, framework_link = sys_patch_download.grab_patcher_support_pkg(self.constants).download_files() + download_result, link = sys_patch_download.grab_patcher_support_pkg(self.constants).download_files() sys.stdout=sys.__stdout__ if download_result is None: diff --git a/resources/cli_menu.py b/resources/cli_menu.py index d0b6c26c5..136bc2866 100644 --- a/resources/cli_menu.py +++ b/resources/cli_menu.py @@ -3,7 +3,7 @@ import sys import subprocess -from resources import constants, install, utilities, defaults, sys_patch, installer +from resources import constants, install, utilities, defaults, sys_patch, installer, tui_helpers from data import cpu_data, smbios_data, model_array, os_data, mirror_data @@ -1014,7 +1014,7 @@ AssetCache. self.set_cc_support() def credits(self): - utilities.TUIOnlyPrint( + tui_helpers.TUIOnlyPrint( ["Credits"], "Press [Enter] to go back.\n", [ @@ -1085,7 +1085,7 @@ system_profiler SPHardwareDataType | grep 'Model Identifier' response = None while not (response and response == -1): title = ["Adjust Advanced Patcher Settings, for developers ONLY"] - menu = utilities.TUIMenu(title, "Please select an option: ", auto_number=True, top_level=True) + menu = tui_helpers.TUIMenu(title, "Please select an option: ", auto_number=True, top_level=True) options = [ [f"Set Metal GPU Status:\t\tCurrently {self.constants.imac_vendor}", MenuOptions(self.constants.custom_model or self.constants.computer.real_model, self.constants).change_metal], [f"Set DRM Preferences:\t\tCurrently {self.constants.drm_support}", MenuOptions(self.constants.custom_model or self.constants.computer.real_model, self.constants).drm_setting], @@ -1105,7 +1105,7 @@ system_profiler SPHardwareDataType | grep 'Model Identifier' response = None while not (response and response == -1): title = ["Adjust Patcher Settings"] - menu = utilities.TUIMenu(title, "Please select an option: ", auto_number=True, top_level=True) + menu = tui_helpers.TUIMenu(title, "Please select an option: ", auto_number=True, top_level=True) options = [ ["Debug Settings", self.patcher_setting_debug], ["Security Settings", self.patcher_settings_security], @@ -1129,7 +1129,7 @@ system_profiler SPHardwareDataType | grep 'Model Identifier' response = None while not (response and response == -1): title = ["Adjust Debug Settings"] - menu = utilities.TUIMenu(title, "Please select an option: ", auto_number=True, top_level=True) + menu = tui_helpers.TUIMenu(title, "Please select an option: ", auto_number=True, top_level=True) options = [ [f"Enable Verbose Mode:\tCurrently {self.constants.verbose_debug}", MenuOptions(self.constants.custom_model or self.constants.computer.real_model, self.constants).change_verbose], [f"Enable OpenCore DEBUG:\tCurrently {self.constants.opencore_debug}", MenuOptions(self.constants.custom_model or self.constants.computer.real_model, self.constants).change_oc], @@ -1149,7 +1149,7 @@ system_profiler SPHardwareDataType | grep 'Model Identifier' response = None while not (response and response == -1): title = ["Adjust Security Settings"] - menu = utilities.TUIMenu(title, "Please select an option: ", auto_number=True, top_level=True) + menu = tui_helpers.TUIMenu(title, "Please select an option: ", auto_number=True, top_level=True) options = [ # [ # f"Set Apple Mobile File Integrity (AMFI):\tCurrently {self.constants.amfi_status}", @@ -1175,7 +1175,7 @@ system_profiler SPHardwareDataType | grep 'Model Identifier' response = None while not (response and response == -1): title = ["Adjust SMBIOS Settings"] - menu = utilities.TUIMenu(title, "Please select an option: ", auto_number=True, top_level=True) + menu = tui_helpers.TUIMenu(title, "Please select an option: ", auto_number=True, top_level=True) options = [ [f"Set SMBIOS Spoof Level:\tCurrently {self.constants.serial_settings}", MenuOptions(self.constants.custom_model or self.constants.computer.real_model, self.constants).change_serial], [f"Set SMBIOS Spoof Model:\tCurrently {self.constants.override_smbios}", MenuOptions(self.constants.custom_model or self.constants.computer.real_model, self.constants).set_smbios], @@ -1192,7 +1192,7 @@ system_profiler SPHardwareDataType | grep 'Model Identifier' response = None while not (response and response == -1): title = ["Adjust Bootable Volume Settings"] - menu = utilities.TUIMenu(title, "Please select an option: ", auto_number=True, top_level=True) + menu = tui_helpers.TUIMenu(title, "Please select an option: ", auto_number=True, top_level=True) options = [ [f"Set FireWire Boot:\t\tCurrently {self.constants.firewire_boot}", MenuOptions(self.constants.custom_model or self.constants.computer.real_model, self.constants).allow_firewire], [f"Set XHCI Boot:\t\tCurrently {self.constants.xhci_boot}", MenuOptions(self.constants.custom_model or self.constants.computer.real_model, self.constants).allow_xhci], @@ -1209,7 +1209,7 @@ system_profiler SPHardwareDataType | grep 'Model Identifier' response = None while not (response and response == -1): title = ["Adjust Miscellaneous Settings"] - menu = utilities.TUIMenu(title, "Please select an option: ", auto_number=True, top_level=True) + menu = tui_helpers.TUIMenu(title, "Please select an option: ", auto_number=True, top_level=True) options = [ [f"Set ShowPicker Mode:\tCurrently {self.constants.showpicker}", MenuOptions(self.constants.custom_model or self.constants.computer.real_model, self.constants).change_showpicker], [f"Set Wake on WLAN:\t\tCurrently {self.constants.enable_wake_on_wlan}", MenuOptions(self.constants.custom_model or self.constants.computer.real_model, self.constants).allow_wowl], @@ -1246,7 +1246,7 @@ system_profiler SPHardwareDataType | grep 'Model Identifier' response = None while not (response and response == -1): title = ["Adjust Advanced Patcher Settings, for developers ONLY"] - menu = utilities.TUIMenu(title, "Please select an option: ", auto_number=True, top_level=True) + menu = tui_helpers.TUIMenu(title, "Please select an option: ", auto_number=True, top_level=True) options = [ [f"Set Metal GPU Status:\t\tCurrently {self.constants.imac_vendor}", MenuOptions(self.constants.custom_model or self.constants.computer.real_model, self.constants).change_metal], [f"Set DRM Preferences:\t\tCurrently {self.constants.drm_support}", MenuOptions(self.constants.custom_model or self.constants.computer.real_model, self.constants).drm_setting], @@ -1301,7 +1301,7 @@ B. Exit while not (response and response == -1): options = [] title = ["Select the macOS Installer you wish to download"] - menu = utilities.TUIMenu(title, "Please select an option: ", auto_number=True, top_level=True) + menu = tui_helpers.TUIMenu(title, "Please select an option: ", auto_number=True, top_level=True) available_installers = installer.list_downloadable_macOS_installers(self.constants.payload_path, "DeveloperSeed") if available_installers: # Add mirror of 11.2.3 for users who want it @@ -1321,7 +1321,7 @@ B. Exit while not (response and response == -1): options = [] title = ["Select the macOS Installer you wish to use"] - menu = utilities.TUIMenu(title, "Please select an option: ", auto_number=True, top_level=True) + menu = tui_helpers.TUIMenu(title, "Please select an option: ", auto_number=True, top_level=True) available_installers = installer.list_local_macOS_installers() if available_installers: for app in available_installers: diff --git a/resources/constants.py b/resources/constants.py index 1e797822a..7e69e7748 100644 --- a/resources/constants.py +++ b/resources/constants.py @@ -15,7 +15,7 @@ class Constants: def __init__(self): # Patcher Versioning self.patcher_version = "0.4.5" # OpenCore-Legacy-Patcher - self.patcher_support_pkg_version = "0.3.9" # PatcherSupportPkg + self.patcher_support_pkg_version = "0.4.0" # PatcherSupportPkg self.url_patcher_support_pkg = "https://github.com/dortania/PatcherSupportPkg/releases/download/" self.nightly_url_patcher_support_pkg = "https://nightly.link/dortania/PatcherSupportPkg/workflows/build/master/" self.discord_link = "https://discord.gg/rqdPgH8xSN" @@ -209,11 +209,6 @@ class Constants: @property def plist_template(self): return self.payload_path / Path("Config/config.plist") - - # Mount Location - @property - def payload_mnt1_path(self): - return self.payload_path / Path("mnt1") # Launch Agent @property @@ -571,167 +566,14 @@ class Constants: return self.payload_path / Path("AutoPkg-Assets.pkg.zip") # Apple Payloads Paths - @property - def payload_apple_root_path_zip(self): - return self.payload_path / Path("Apple.zip") + def payload_local_binaries_root_path(self): + return self.payload_path / Path("Universal-Binaries") @property - def payload_universal_extensions_zip_path(self): - return self.payload_path / Path("Universal-Extensions.zip") + def payload_local_binaries_root_path_zip(self): + return self.payload_path / Path("Universal-Binaries.zip") - @property - def payload_apple_root_path(self): - return self.payload_path / Path("Apple") - - @property - def payload_apple_kexts_path(self): - return self.payload_path / Path("Universal-Extensions") - - @property - def payload_apple_coreservices_path(self): - return self.payload_apple_root_path / Path("CoreServices") - - @property - def payload_apple_usr_path(self): - return self.payload_apple_root_path / Path("usr") - - @property - def payload_apple_libexec_path(self): - return self.payload_apple_usr_path / Path("libexec") - - @property - def payload_apple_application_support(self): - return self.payload_apple_root_path / Path("Application Support") - - @property - def payload_apple_private_path(self): - return self.payload_apple_root_path / Path("private") - - @property - def payload_apple_etc_path(self): - return self.payload_apple_private_path / Path("etc") - - @property - def payload_apple_frameworks_path(self): - return self.payload_apple_root_path / Path("Frameworks") - - @property - def payload_apple_frameworks_path_accel(self): - return self.payload_apple_frameworks_path / Path("Graphics-Acceleration-Non-Metal") - - @property - def payload_apple_frameworks_path_accel_ts2(self): - return self.payload_apple_frameworks_path / Path("Graphics-Acceleration-TeraScale-2") - - @property - def payload_apple_frameworks_path_accel_ivy(self): - return self.payload_apple_frameworks_path / Path("Graphics-Acceleration-Ivy-Bridge") - - @property - def payload_apple_frameworks_path_accel_kepler(self): - return self.payload_apple_frameworks_path / Path("Graphics-Acceleration-Kepler") - - @property - def payload_apple_lauchd_path(self): - return self.payload_apple_root_path / Path("LaunchDaemons") - - @property - def payload_apple_private_frameworks_path(self): - return self.payload_apple_root_path / Path("PrivateFrameworks") - - @property - def payload_apple_private_frameworks_path_accel(self): - return self.payload_apple_private_frameworks_path / Path("Graphics-Acceleration-Non-Metal") - - @property - def payload_apple_private_frameworks_path_accel_ts2(self): - return self.payload_apple_private_frameworks_path / Path("Graphics-Acceleration-TeraScale-2") - - @property - def payload_apple_private_frameworks_path_accel_ivy(self): - return self.payload_apple_private_frameworks_path / Path("Graphics-Acceleration-Ivy-Bridge") - - @property - def payload_apple_private_frameworks_path_brightness(self): - return self.payload_apple_private_frameworks_path / Path("Brightness-Control") - - @property - def payload_apple_private_frameworks_path_legacy_drm(self): - return self.payload_apple_private_frameworks_path / Path("Legacy-GVA") - - # Apple Extensions - # El Capitan Extensions - @property - def audio_path(self): - return self.payload_apple_kexts_path / Path("Audio") - - # High Sierra Extensions - @property - def audio_v2_path(self): - return self.payload_apple_kexts_path / Path("Audio-v2") - - # GPU Kexts and Bundles - - @property - def legacy_graphics(self): - return self.payload_apple_kexts_path / Path("Graphics-Acceleration") - - @property - def legacy_nvidia_path(self): - return self.legacy_graphics / Path("Nvidia-Tesla") - - @property - def legacy_nvidia_kepler_path(self): - return self.legacy_graphics / Path("Nvidia-Kepler") - - @property - def legacy_amd_path(self): - return self.legacy_graphics / Path("AMD-TeraScale") - - @property - def legacy_amd_path_ts2(self): - return self.legacy_graphics / Path("AMD-TeraScale-2") - - @property - def legacy_intel_gen1_path(self): - return self.legacy_graphics / Path("Intel-Gen5-Ironlake") - - @property - def legacy_intel_gen2_path(self): - return self.legacy_graphics / Path("Intel-Gen6-SandyBridge") - - @property - def legacy_intel_gen3_path(self): - return self.legacy_graphics / Path("Intel-Gen7-IvyBridge") - - @property - def legacy_general_path(self): - return self.legacy_graphics / Path("General-Patches") - - @property - def legacy_brightness(self): - return self.payload_apple_kexts_path / Path("Brightness-Control") - - @property - def legacy_mux_path(self): - return self.payload_apple_kexts_path / Path("Legacy-Mux") - - @property - def legacy_wifi_coreservices(self): - return self.payload_apple_coreservices_path / Path("Legacy-Wifi") - - @property - def legacy_wifi_libexec(self): - return self.payload_apple_libexec_path / Path("Legacy-Wifi") - - @property - def legacy_wifi_support(self): - return self.payload_apple_application_support / Path("Legacy-Wifi") - - @property - def legacy_dropbox_support(self): - return self.payload_apple_application_support / Path("Dropbox") sbm_values = [ "j137ap", # iMacPro1,1 @@ -753,15 +595,6 @@ class Constants: # "x86legacy", # non-T2 Macs/VMs, Monterey's boot.efi enforces this on all Macs ] - sandy_board_id = [ - "Mac-E43C1C25D4880AD6", # MacBookPro12,1 - "Mac-06F11F11946D27C5", # MacBookPro11,5 - "Mac-9F18E312C5C2BF0B", # MacBookAir7,1 - "Mac-937CB26E2E02BB01", # MacBookAir7,2 - "Mac-35C5E08120C7EEAF", # Macmini7,1 - "Mac-7BA5B2D9E42DDD94", # iMacPro1,1 - ] - sandy_board_id_stock = [ "Mac-94245B3640C91C81", # MacBookPro8,1 "Mac-94245A3940C91C80", # MacBookPro8,2 diff --git a/resources/generate_smbios.py b/resources/generate_smbios.py index 188bd67a5..5377ebb7b 100644 --- a/resources/generate_smbios.py +++ b/resources/generate_smbios.py @@ -105,6 +105,13 @@ def find_model_off_board(board): return key return None +def find_board_off_model(model): + if model in smbios_data.smbios_dictionary: + return smbios_data.smbios_dictionary[model]["Board ID"] + else: + return None + + def check_firewire(model): # MacBooks never supported FireWire # Pre-Thunderbolt MacBook Airs as well @@ -116,4 +123,54 @@ def check_firewire(model): elif model.startswith("MacBook"): return False else: - return True \ No newline at end of file + return True + +def determine_best_board_id_for_sandy(current_board_id, gpus): + # This function is mainly for users who are either spoofing or using hackintoshes + # Generally hackintosh will use whatever the latest SMBIOS is, so we need to determine + # the best Board ID to patch inside of AppleIntelSNBGraphicsFB + + # Currently the kext supports the following models: + # MacBookPro8,1 - Mac-94245B3640C91C81 (13") + # MacBookPro8,2 - Mac-94245A3940C91C80 (15") + # MacBookPro8,3 - Mac-942459F5819B171B (17") + # MacBookAir4,1 - Mac-C08A6BB70A942AC2 (11") + # MacBookAir4,2 - Mac-742912EFDBEE19B3 (13") + # Macmini5,1 - Mac-8ED6AF5B48C039E1 + # Macmini5,2 - Mac-4BC72D62AD45599E (headless) + # Macmini5,3 - Mac-7BA5B2794B2CDB12 + # iMac12,1 - Mac-942B5BF58194151B (headless) + # iMac12,2 - Mac-942B59F58194171B (headless) + # Unknown(MBP) - Mac-94245AF5819B141B + # Unknown(iMac) - Mac-942B5B3A40C91381 (headless) + if current_board_id: + model = find_model_off_board(current_board_id) + if model: + if model.startswith("MacBook"): + try: + size = int(smbios_data.smbios_dictionary[model]["Screen Size"]) + except KeyError: + size = 13 # Assume 13 if it's missing + if model.startswith("MacBookPro"): + if size >= 17: + return find_board_off_model("MacBookPro8,3") + elif size >= 15: + return find_board_off_model("MacBookPro8,2") + else: + return find_board_off_model("MacBookPro8,1") + else: # MacBook and MacBookAir + if size >= 13: + return find_board_off_model("MacBookAir4,2") + else: + return find_board_off_model("MacBookAir4,1") + else: + # We're working with a desktop, so need to figure out whether the unit is running headless or not + if len(gpus) > 1: + # More than 1 GPU detected, assume headless + if model.startswith("Macmini"): + return find_board_off_model("Macmini5,2") + else: + return find_board_off_model("iMac12,2") + else: + return find_board_off_model("Macmini5,1") + return find_board_off_model("Macmini5,1") # Safest bet if we somehow don't know the model \ No newline at end of file diff --git a/resources/install.py b/resources/install.py index 1d07a81db..3adcf789a 100644 --- a/resources/install.py +++ b/resources/install.py @@ -7,7 +7,7 @@ import subprocess import shutil import os from pathlib import Path -from resources import utilities, constants +from resources import utilities, constants, tui_helpers from data import os_data class tui_disk_installation: @@ -79,7 +79,7 @@ class tui_disk_installation: utilities.header(["Installing OpenCore to Drive"]) if not self.constants.opencore_release_folder.exists(): - utilities.TUIOnlyPrint( + tui_helpers.TUIOnlyPrint( ["Installing OpenCore to Drive"], "Press [Enter] to go back.\n", [ @@ -92,7 +92,7 @@ Please build OpenCore first!""" print("\nDisk picker is loading...") all_disks = self.list_disks() - menu = utilities.TUIMenu( + 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."], @@ -110,7 +110,7 @@ Please build OpenCore first!""" disk_identifier = "disk" + response selected_disk = all_disks[disk_identifier] - menu = utilities.TUIMenu( + menu = tui_helpers.TUIMenu( ["Select Partition"], "Please select the partition you would like to install OpenCore to: ", return_number_instead_of_direct_call=True, @@ -169,7 +169,7 @@ Please build OpenCore first!""" return else: if self.constants.gui_mode is False: - utilities.TUIOnlyPrint( + tui_helpers.TUIOnlyPrint( ["Copying OpenCore"], "Press [Enter] to go back.\n", ["An error occurred!"] + result.stderr.decode().split("\n") + [""] ).start() else: @@ -252,7 +252,7 @@ Please build OpenCore first!""" input() else: if self.constants.gui_mode is False: - utilities.TUIOnlyPrint(["Copying OpenCore"], "Press [Enter] to go back.\n", ["EFI failed to mount!"]).start() + tui_helpers.TUIOnlyPrint(["Copying OpenCore"], "Press [Enter] to go back.\n", ["EFI failed to mount!"]).start() else: print("EFI failed to mount!") diff --git a/resources/installer.py b/resources/installer.py index 8da2e8f7c..fadaeb91d 100644 --- a/resources/installer.py +++ b/resources/installer.py @@ -3,7 +3,7 @@ from pathlib import Path import plistlib import subprocess import requests -from resources import utilities +from resources import utilities, tui_helpers def list_local_macOS_installers(): # Finds all applicable macOS installers @@ -264,7 +264,7 @@ def select_disk_to_format(): except KeyError: # Avoid crashing with CDs installed continue - menu = utilities.TUIMenu( + 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!"], diff --git a/resources/main.py b/resources/main.py index 79c7599af..2a5362072 100644 --- a/resources/main.py +++ b/resources/main.py @@ -6,7 +6,7 @@ import subprocess import sys from pathlib import Path -from resources import build, cli_menu, constants, utilities, device_probe, os_probe, defaults, arguments, install +from resources import build, cli_menu, constants, utilities, device_probe, os_probe, defaults, arguments, install, tui_helpers from data import model_array class OpenCoreLegacyPatcher: @@ -79,7 +79,7 @@ class OpenCoreLegacyPatcher: else: in_between = ["This model is supported"] - menu = utilities.TUIMenu(title, "Please select an option: ", in_between=in_between, auto_number=True, top_level=True) + menu = tui_helpers.TUIMenu(title, "Please select an option: ", in_between=in_between, auto_number=True, top_level=True) options = ( [["Build OpenCore", build.BuildOpenCore(self.constants.custom_model or self.constants.computer.real_model, self.constants).build_opencore]] diff --git a/resources/sys_patch.py b/resources/sys_patch.py index c3e95ff09..94eebae2e 100644 --- a/resources/sys_patch.py +++ b/resources/sys_patch.py @@ -1,18 +1,33 @@ # Framework for mounting and patching macOS root volume # Copyright (C) 2020-2022, Dhinak G, Mykola Grymalyuk -# Missing Features: -# - Full System/Library Snapshotting (need to research how Apple achieves this) -# - Temporary Work-around: sudo bless --mount /System/Volumes/Update/mnt1 --bootefi --last-sealed-snapshot -# - Work-around battery throttling on laptops with no battery (IOPlatformPluginFamily.kext/Contents/PlugIns/ACPI_SMC_PlatformPlugin.kext/Contents/Resources/) -import os +# System based off of Apple's Kernel Development Kit (KDK) +# - https://developer.apple.com/download/all/ + +# The system relies on mounting the APFS volume as a live read/write volume +# We perform our required edits, then create a new snapshot for the system boot + +# The manual process is as follows: +# 1. Mount the APFS volume as a read/write volume +# 'sudo mount -o nobrowse -t apfs /dev/disk5s5 /System/Volumes/Update/mnt1' +# 2. Perform edits to the system (ie. create new KernelCollection) +# 'sudo kmutil install --volume-root /System/Volumes/Update/mnt1/ --update-all' +# 3. Create a new snapshot for the system boot +# 'sudo bless --folder /System/Volumes/Update/mnt1/System/Library/CoreServices --bootefi --create-snapshot' + +# Additionally Apple's APFS snapshot system supports system rollbacks: +# 'sudo bless --mount /System/Volumes/Update/mnt1 --bootefi --last-sealed-snapshot' +# Note: root volume rollbacks are unstable in Big Sur due to quickly discarding the original snapshot +# - Generally within 2~ boots, the original snapshot is discarded +# - Monterey always preserves the original snapshot allowing for reliable rollbacks + + import shutil import subprocess -import zipfile from pathlib import Path -from resources import constants, utilities, generate_smbios, sys_patch_download, sys_patch_detect -from data import sip_data, sys_patch_data, os_data +from resources import constants, generate_smbios, utilities, sys_patch_download, sys_patch_detect, sys_patch_auto +from data import os_data class PatchSysVolume: @@ -21,42 +36,17 @@ class PatchSysVolume: self.constants: constants.Constants() = versions self.computer = self.constants.computer self.root_mount_path = None - self.validate = False - self.added_legacy_kexts = False self.root_supports_snapshot = utilities.check_if_root_is_apfs_snapshot() self.constants.root_patcher_succeded = False # Reset Variable each time we start + self.patch_set_dictionary = {} - # GUI will detect hardware patches betfore starting 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() - self.init_hardware_patches(hardware_details) + self.hardware_details = hardware_details self.init_pathing(custom_root_mount_path=None, custom_data_mount_path=None) - def init_hardware_patches(self, hardware_details): - - self.amfi_must_disable = hardware_details["Settings: Requires AMFI exemption"] - self.check_board_id = hardware_details["Settings: Requires Board ID validation"] - self.sip_enabled = hardware_details["Validation: SIP is enabled"] - self.sbm_enabled = hardware_details["Validation: SBM is enabled"] - self.amfi_enabled = hardware_details["Validation: AMFI is enabled"] - self.fv_enabled = hardware_details["Validation: FileVault is enabled"] - self.dosdude_patched = hardware_details["Validation: System is dosdude1 patched"] - self.bad_board_id = hardware_details[f"Validation: Board ID is unsupported \n({self.computer.reported_board_id})"] - - self.nvidia_legacy = hardware_details["Graphics: Nvidia Tesla"] - self.kepler_gpu = hardware_details["Graphics: Nvidia Kepler"] - self.amd_ts1 = hardware_details["Graphics: AMD TeraScale 1"] - self.amd_ts2 = hardware_details["Graphics: AMD TeraScale 2"] - self.iron_gpu = hardware_details["Graphics: Intel Ironlake"] - self.sandy_gpu = hardware_details["Graphics: Intel Sandy Bridge"] - self.ivy_gpu = hardware_details["Graphics: Intel Ivy Bridge"] - self.brightness_legacy = hardware_details["Brightness: Legacy Backlight Control"] - self.legacy_audio = hardware_details["Audio: Legacy Realtek"] - self.legacy_wifi = hardware_details["Networking: Legacy Wireless"] - self.legacy_gmux = hardware_details["Miscellaneous: Legacy GMUX"] - self.legacy_keyboard_backlight = hardware_details["Miscellaneous: Legacy Keyboard Backlight"] - def init_pathing(self, custom_root_mount_path=None, custom_data_mount_path=None): if custom_root_mount_path and custom_data_mount_path: self.mount_location = custom_root_mount_path @@ -68,25 +58,12 @@ class PatchSysVolume: else: self.mount_location = "" self.mount_location_data = "" - self.mount_coreservices = f"{self.mount_location}/System/Library/CoreServices" self.mount_extensions = f"{self.mount_location}/System/Library/Extensions" - self.mount_frameworks = f"{self.mount_location}/System/Library/Frameworks" - self.mount_lauchd = f"{self.mount_location}/System/Library/LaunchDaemons" - self.mount_private_frameworks = f"{self.mount_location}/System/Library/PrivateFrameworks" - self.mount_libexec = f"{self.mount_location}/usr/libexec" - self.mount_extensions_mux = f"{self.mount_location}/System/Library/Extensions/AppleGraphicsControl.kext/Contents/PlugIns/" - self.mount_private_etc = f"{self.mount_location_data}/private/etc" self.mount_application_support = f"{self.mount_location_data}/Library/Application Support" def find_mount_root_vol(self, patch): self.root_mount_path = utilities.get_disk_path() if self.root_mount_path.startswith("disk"): - if ( - self.constants.detected_os == os_data.os_data.catalina or - (self.constants.detected_os > os_data.os_data.catalina and self.root_supports_snapshot is False) - ): - print("- Mounting Dedicated Root Volume as writable") - utilities.elevated(["mount", "-uw", f"{self.mount_location}/"], stdout=subprocess.PIPE).stdout.decode().strip().encode() print(f"- Found Root Volume at: {self.root_mount_path}") if Path(self.mount_extensions).exists(): print("- Root Volume is already mounted") @@ -137,28 +114,24 @@ class PatchSysVolume: def rebuild_snapshot(self): print("- Rebuilding Kernel Cache (This may take some time)") - if self.constants.detected_os > os_data.os_data.catalina: - result = utilities.elevated(["kmutil", "install", "--volume-root", self.mount_location, "--update-all"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - else: - result = utilities.elevated(["kextcache", "-i", f"{self.mount_location}/"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + result = utilities.elevated(["kmutil", "install", "--volume-root", self.mount_location, "--update-all"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) # kextcache notes: # - kextcache always returns 0, even if it fails # - Check the output for 'KernelCache ID' to see if the cache was successfully rebuilt # kmutil notes: # - will return 71 on failure to build KCs + # - will return 31 on 'No binaries or codeless kexts were provided' # - will return -10 if the volume is missing (ie. unmounted by another process) - if result.returncode != 0 or (self.constants.detected_os < os_data.os_data.catalina and "KernelCache ID" not in result.stdout.decode()): - self.success_status = False + if result.returncode != 0: print("- Unable to build new kernel cache") - print(f"\nReason for Patch Failure({result.returncode}):") + print(f"\nReason for Patch Failure ({result.returncode}):") print(result.stdout.decode()) print("") print("\nPlease reboot the machine to avoid potential issues rerunning the patcher") if self.constants.gui_mode is False: input("Press [ENTER] to continue") else: - self.success_status = True print("- Successfully built new kernel cache") if self.root_supports_snapshot is True: print("- Creating new APFS snapshot") @@ -174,13 +147,6 @@ class PatchSysVolume: return else: self.unmount_drive() - else: - if self.constants.detected_os == os_data.os_data.catalina: - print("- Merging kernel cache") - utilities.process_status(utilities.elevated(["kcditto"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) - if self.constants.detected_os in [os_data.os_data.mojave, os_data.os_data.catalina]: - print("- Merging dyld cache") - utilities.process_status(utilities.elevated(["update_dyld_shared_cache", "-root", f"{self.mount_location}/"])) print("- Patching complete") print("\nPlease reboot the machine for patches to take effect") self.constants.root_patcher_succeded = True @@ -191,434 +157,162 @@ class PatchSysVolume: print("- 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 delete_old_binaries(self, vendor_patch): - for delete_current_kext in vendor_patch: - delete_path = Path(self.mount_extensions) / Path(delete_current_kext) - if Path(delete_path).exists(): - print(f"- Deleting {delete_current_kext}") - utilities.process_status(utilities.elevated(["rm", "-R", delete_path], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) - else: - print(f"- Couldn't find {delete_current_kext}, skipping") - - def add_new_binaries(self, vendor_patch, vendor_location): - for add_current_kext in vendor_patch: - existing_path = Path(self.mount_extensions) / Path(add_current_kext) - if Path(existing_path).exists(): - print(f"- Found conflicting kext, Deleting Root Volume's {add_current_kext}") - utilities.process_status(utilities.elevated(["rm", "-R", existing_path], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) - print(f"- Adding {add_current_kext}") - utilities.process_status(utilities.elevated(["cp", "-R", f"{vendor_location}/{add_current_kext}", self.mount_extensions], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) - utilities.process_status(utilities.elevated(["chmod", "-Rf", "755", f"{self.mount_extensions}/{add_current_kext}"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) - utilities.process_status(utilities.elevated(["chown", "-Rf", "root:wheel", f"{self.mount_extensions}/{add_current_kext}"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) - - 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/ - if self.constants.launcher_script is None: - # Verify our binary isn't located in '/Library/Application Support/Dortania/' - # As we'd simply be duplicating ourselves - if not self.constants.launcher_binary.startswith("/Library/Application Support/Dortania/"): - print("- Installing Auto Patcher Launch Agent") - - if not Path("Library/Application Support/Dortania").exists(): - print("- Creating /Library/Application Support/Dortania/") - utilities.process_status(utilities.elevated(["mkdir", "-p", "/Library/Application Support/Dortania"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) - - print("- Copying OpenCore Patcher to /Library/Application Support/Dortania/") - if Path("/Library/Application Support/Dortania/OpenCore-Patcher.app").exists(): - print("- Deleting existing OpenCore-Patcher") - utilities.process_status(utilities.elevated(["rm", "-R", "/Library/Application Support/Dortania/OpenCore-Patcher.app"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) - - # Strip everything after OpenCore-Patcher.app - path = str(self.constants.launcher_binary).split("/Contents/MacOS/OpenCore-Patcher")[0] - print(f"- Copying {path} to /Library/Application Support/Dortania/") - utilities.process_status(utilities.elevated(["cp", "-R", path, "/Library/Application Support/Dortania/"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) - - if not Path("/Library/Application Support/Dortania/OpenCore-Patcher.app").exists(): - # Sometimes the binary the user launches maye have a suffix (ie. OpenCore-Patcher 3.app) - # We'll want to rename it to OpenCore-Patcher.app - path = path.split("/")[-1] - print(f"- Renaming {path} to OpenCore-Patcher.app") - utilities.process_status(utilities.elevated(["mv", f"/Library/Application Support/Dortania/{path}", "/Library/Application Support/Dortania/OpenCore-Patcher.app"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) - - # Copy over our launch agent - print("- Copying auto-patch.plist Launch Agent to /Library/LaunchAgents/") - if Path("/Library/LaunchAgents/com.dortania.opencore-legacy-patcher.auto-patch.plist").exists(): - print("- Deleting existing auto-patch.plist") - utilities.process_status(utilities.elevated(["rm", "/Library/LaunchAgents/com.dortania.opencore-legacy-patcher.auto-patch.plist"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) - utilities.process_status(utilities.elevated(["cp", self.constants.auto_patch_launch_agent_path, "/Library/LaunchAgents/"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) - - # Set the permissions on the com.dortania.opencore-legacy-patcher.auto-patch.plist - print("- Setting permissions on auto-patch.plist") - utilities.process_status(utilities.elevated(["chmod", "644", "/Library/LaunchAgents/com.dortania.opencore-legacy-patcher.auto-patch.plist"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) - utilities.process_status(utilities.elevated(["chown", "root:wheel", "/Library/LaunchAgents/com.dortania.opencore-legacy-patcher.auto-patch.plist"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) - - # Making app alias - # Simply an easy way for users to notice the app - # If there's already an alias or exiting app, skip - if not Path("/Applications/OpenCore-Patcher.app").exists(): - print("- Making app alias") - utilities.process_status(utilities.elevated(["ln", "-s", "/Library/Application Support/Dortania/OpenCore-Patcher.app", "/Applications/OpenCore-Patcher.app"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) - else: - print("- Skipping Auto Patcher Launch Agent, not supported when running from source") def clean_skylight_plugins(self): if (Path(self.mount_application_support) / Path("SkyLightPlugins/")).exists(): - print("- Found SkylightPlugins folder, removing") - utilities.process_status(utilities.elevated(["rm", "-rf", f"{self.mount_application_support}/SkyLightPlugins"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) - - def add_brightness_patch(self): - self.delete_old_binaries(sys_patch_data.DeleteBrightness) - self.add_new_binaries(sys_patch_data.AddBrightness, self.constants.legacy_brightness) - utilities.elevated(["rsync", "-r", "-i", "-a", f"{self.constants.payload_apple_private_frameworks_path_brightness}/", self.mount_private_frameworks], stdout=subprocess.PIPE) - utilities.process_status(utilities.elevated(["chmod", "-Rf", "755", f"{self.mount_private_frameworks}/DisplayServices.framework"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) - utilities.process_status(utilities.elevated(["chown", "-Rf", "root:wheel", f"{self.mount_private_frameworks}/DisplayServices.framework"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) - - def add_audio_patch(self): - if self.model in ["iMac7,1", "iMac8,1"]: - self.delete_old_binaries(sys_patch_data.DeleteVolumeControl) - self.add_new_binaries(sys_patch_data.AddVolumeControl, self.constants.audio_path) + print("- 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)) + utilities.process_status(utilities.elevated(["mkdir", f"{self.mount_application_support}/SkyLightPlugins"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) else: - self.add_new_binaries(sys_patch_data.AddVolumeControlv2, self.constants.audio_v2_path) - - def add_wifi_patch(self): - print("- Merging Wireless CoreSerices patches") - utilities.elevated(["rsync", "-r", "-i", "-a", f"{self.constants.legacy_wifi_coreservices}/", self.mount_coreservices], stdout=subprocess.PIPE) - utilities.process_status(utilities.elevated(["chmod", "-Rf", "755", f"{self.mount_coreservices}/WiFiAgent.app"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) - utilities.process_status(utilities.elevated(["chown", "-Rf", "root:wheel", f"{self.mount_coreservices}/WiFiAgent.app"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) - print("- Merging Wireless usr/libexec patches") - utilities.elevated(["rsync", "-r", "-i", "-a", f"{self.constants.legacy_wifi_libexec}/", self.mount_libexec], stdout=subprocess.PIPE) - utilities.process_status(utilities.elevated(["chmod", "755", f"{self.mount_libexec}/airportd"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) - utilities.process_status(utilities.elevated(["chown", "root:wheel", f"{self.mount_libexec}/airportd"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) - - # dylib patch to resolve password crash prompt - # Note requires ASentientBot's SkyLight to function - # Thus Metal machines do not benefit from this patch, however install anyways as harmless - print("- Merging Wireless SkyLightPlugins") - utilities.elevated(["rsync", "-r", "-i", "-a", f"{self.constants.legacy_wifi_support}/", self.mount_application_support], stdout=subprocess.PIPE) - - def add_legacy_mux_patch(self): - self.delete_old_binaries(sys_patch_data.DeleteDemux) - print("- Merging Legacy Mux Kext patches") - utilities.process_status( - utilities.elevated(["cp", "-R", f"{self.constants.legacy_mux_path}/AppleMuxControl.kext", self.mount_extensions_mux], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - ) - - def add_legacy_keyboard_backlight_patch(self): - print("- Enabling Keyboard Backlight delay") - utilities.process_status( - utilities.elevated(["defaults", "write", "/Library/Preferences/.GlobalPreferences.plist", "Moraea_BacklightHack", "-bool", "true"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - ) - - def add_legacy_dropbox_patch(self): - print("- Merging DropboxHack SkyLightPlugins") - utilities.elevated(["rsync", "-r", "-i", "-a", f"{self.constants.legacy_dropbox_support}/", self.mount_application_support], stdout=subprocess.PIPE) - - def gpu_accel_legacy(self): - if self.constants.detected_os == os_data.os_data.mojave: - print("- Installing General Acceleration Kext patches for Mojave") - self.add_new_binaries(sys_patch_data.AddGeneralAccelMojave, self.constants.legacy_general_path) - elif self.constants.detected_os == os_data.os_data.catalina: - print("- Installing General Acceleration Kext patches for Catalina") - self.add_new_binaries(sys_patch_data.AddGeneralAccelCatalina, self.constants.legacy_general_path) - elif self.constants.detected_os in [os_data.os_data.big_sur, os_data.os_data.monterey]: - print("- Installing General Acceleration Kext patches for Big Sur/Monterey") - self.add_new_binaries(sys_patch_data.AddGeneralAccel, self.constants.legacy_general_path) - - # Nvidia - def gpu_accel_legacy_nvidia_master(self): - if self.constants.detected_os in [os_data.os_data.mojave, os_data.os_data.catalina]: - print("- Installing Nvidia Acceleration Kext patches for Mojave/Catalina") - self.gpu_accel_legacy() - self.add_new_binaries(sys_patch_data.AddNvidiaAccelLegacy, self.constants.legacy_nvidia_path) - elif self.constants.detected_os in [os_data.os_data.big_sur, os_data.os_data.monterey]: - print("- Installing Nvidia Acceleration Kext patches for Big Sur/Monterey") - self.delete_old_binaries(sys_patch_data.DeleteNvidiaAccel11) - self.gpu_accel_legacy() - self.add_new_binaries(sys_patch_data.AddNvidiaAccel11, self.constants.legacy_nvidia_path) - if self.constants.detected_os == os_data.os_data.monterey and self.constants.detected_os_minor > 0: - # Beta 7+ removes NVDAStartup - self.add_new_binaries(sys_patch_data.AddNvidiaTeslaAccel12, self.constants.legacy_nvidia_kepler_path) - else: - print("- Installing basic Nvidia Framebuffer Kext patches for generic OS") - self.add_new_binaries(sys_patch_data.AddNvidiaBrightness, self.constants.legacy_nvidia_path) - - # AMD/ATI - def gpu_accel_legacy_ts1_master(self): - if self.constants.detected_os in [os_data.os_data.mojave, os_data.os_data.catalina]: - print("- Installing TeraScale 1 Acceleration Kext patches for Mojave/Catalina") - self.gpu_accel_legacy() - self.add_new_binaries(sys_patch_data.AddAMDAccelLegacy, self.constants.legacy_amd_path) - elif self.constants.detected_os in [os_data.os_data.big_sur, os_data.os_data.monterey]: - print("- Installing TeraScale 1 Acceleration Kext patches for Big Sur/Monterey") - self.delete_old_binaries(sys_patch_data.DeleteAMDAccel11) - self.gpu_accel_legacy() - self.add_new_binaries(sys_patch_data.AddAMDAccel11, self.constants.legacy_amd_path) - else: - print("- Installing basic TeraScale 1 Framebuffer Kext patches for generic OS") - self.add_new_binaries(sys_patch_data.AddAMDBrightness, self.constants.legacy_amd_path) - - def gpu_accel_legacy_ts2_master(self): - if self.constants.detected_os in [os_data.os_data.mojave, os_data.os_data.catalina] and self.constants.allow_ts2_accel is True: - print("- Installing TeraScale 2 Acceleration Kext patches for Mojave/Catalina") - self.gpu_accel_legacy() - self.add_new_binaries(sys_patch_data.AddAMDAccelLegacy, self.constants.legacy_amd_path) - elif self.constants.detected_os in [os_data.os_data.big_sur, os_data.os_data.monterey] and self.constants.allow_ts2_accel is True: - print("- Installing TeraScale 2 Acceleration Kext patches for Big Sur") - self.delete_old_binaries(sys_patch_data.DeleteAMDAccel11) - self.delete_old_binaries(sys_patch_data.DeleteAMDAccel11TS2) - self.gpu_accel_legacy() - self.add_new_binaries(sys_patch_data.AddAMDAccel11, self.constants.legacy_amd_path) - else: - print("- Installing basic TeraScale 2 Framebuffer Kext patches for generic OS") - self.add_new_binaries(sys_patch_data.AddAMDBrightness, self.constants.legacy_amd_path) - - # Intel - def gpu_accel_legacy_ironlake_master(self): - if self.constants.detected_os in [os_data.os_data.mojave, os_data.os_data.catalina]: - print("- Installing Ironlake Acceleration Kext patches for Mojave/Catalina") - self.gpu_accel_legacy() - self.add_new_binaries(sys_patch_data.AddIntelGen1Accel, self.constants.legacy_intel_gen1_path) - elif self.constants.detected_os in [os_data.os_data.big_sur, os_data.os_data.monterey]: - print("- Installing Ironlake Acceleration Kext patches for Big Sur/Monterey") - self.delete_old_binaries(sys_patch_data.DeleteNvidiaAccel11) - self.gpu_accel_legacy() - self.add_new_binaries(sys_patch_data.AddIntelGen1Accel, self.constants.legacy_intel_gen1_path) - else: - print("- Installing basic Ironlake Framebuffer Kext patches for generic OS") - self.add_new_binaries(sys_patch_data.AddIntelGen1Accel, self.constants.legacy_intel_gen1_path) - - def gpu_accel_legacy_sandybridge_master(self): - if self.constants.detected_os in [os_data.os_data.mojave, os_data.os_data.catalina]: - print("- Installing Sandy Bridge Acceleration Kext patches for Mojave/Catalina") - self.gpu_accel_legacy() - self.add_new_binaries(sys_patch_data.AddIntelGen2Accel, self.constants.legacy_intel_gen2_path) - self.gpu_accel_legacy_sandybridge_board_id() - elif self.constants.detected_os in [os_data.os_data.big_sur, os_data.os_data.monterey]: - print("- Installing Sandy Bridge Acceleration Kext patches for Big Sur/Monterey") - self.delete_old_binaries(sys_patch_data.DeleteNvidiaAccel11) - self.gpu_accel_legacy() - self.add_new_binaries(sys_patch_data.AddIntelGen2Accel, self.constants.legacy_intel_gen2_path) - self.gpu_accel_legacy_sandybridge_board_id() - self.gpu_accel_legacy_gva() - else: - print("- Installing basic Sandy Bridge Framebuffer Kext patches for generic OS") - self.add_new_binaries(sys_patch_data.AddIntelGen2Accel, self.constants.legacy_intel_gen2_path) - self.gpu_accel_legacy_sandybridge_board_id() - - def gpu_accel_legacy_sandybridge_board_id(self): - if self.constants.computer.reported_board_id in self.constants.sandy_board_id_stock: - print("- Using stock AppleIntelSNBGraphicsFB") - # TODO: Clean this function up - # add_new_binaries() and delete_old_binaries() have a bug when the passed array has a single element - # 'TypeError: expected str, bytes or os.PathLike object, not list' - # This is a temporary workaround to fix that - utilities.elevated(["rm", "-r", f"{self.mount_extensions}/AppleIntelSNBGraphicsFB-Clean.kext"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - utilities.elevated(["rm", "-r", f"{self.mount_extensions}/AppleIntelSNBGraphicsFB.kext"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - # Add kext - print("- Adding AppleIntelSNBGraphicsFB.kext") - utilities.elevated( - ["cp", "-r", f"{self.constants.legacy_intel_gen2_path}/AppleIntelSNBGraphicsFB-Clean.kext", f"{self.mount_extensions}"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT - ) - # Rename kext - utilities.elevated( - ["mv", f"{self.mount_extensions}/AppleIntelSNBGraphicsFB-Clean.kext", f"{self.mount_extensions}/AppleIntelSNBGraphicsFB.kext"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT - ) - # Fix permissions - utilities.elevated(["chown", "-Rf", "root:wheel", f"{self.mount_extensions}/AppleIntelSNBGraphicsFB.kext"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - utilities.elevated(["chmod", "-Rf", "755", f"{self.mount_extensions}/AppleIntelSNBGraphicsFB.kext"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - - else: - # Adjust board ID for spoofs - print("- Using Board ID patched AppleIntelSNBGraphicsFB") - utilities.elevated(["rm", "-r", f"{self.mount_extensions}/AppleIntelSNBGraphicsFB-Clean.kext"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - utilities.elevated(["rm", "-r", f"{self.mount_extensions}/AppleIntelSNBGraphicsFB.kext"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - # Add kext - print("- Adding AppleIntelSNBGraphicsFB.kext") - utilities.elevated(["cp", "-r", f"{self.constants.legacy_intel_gen2_path}/AppleIntelSNBGraphicsFB.kext", f"{self.mount_extensions}"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - # Fix permissions - utilities.elevated(["chown", "-Rf", "root:wheel", f"{self.mount_extensions}/AppleIntelSNBGraphicsFB.kext"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - utilities.elevated(["chmod", "-Rf", "755", f"{self.mount_extensions}/AppleIntelSNBGraphicsFB.kext"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - - def gpu_framebuffer_ivybridge_master(self): - if self.constants.detected_os == os_data.os_data.monterey: - print("- Installing IvyBridge Acceleration Kext patches for Monterey") - self.add_new_binaries(sys_patch_data.AddIntelGen3Accel, self.constants.legacy_intel_gen3_path) - if self.validate is False: - print("- Fixing Acceleration in CoreMedia") - utilities.process_status(subprocess.run(["defaults", "write", "com.apple.coremedia", "hardwareVideoDecoder", "-string", "enable"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) - print("- Merging Ivy Bridge Frameworks") - utilities.elevated(["rsync", "-r", "-i", "-a", f"{self.constants.payload_apple_frameworks_path_accel_ivy}/", self.mount_frameworks], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - print("- Merging Ivy Bridge PrivateFrameworks") - utilities.elevated(["rsync", "-r", "-i", "-a", f"{self.constants.payload_apple_private_frameworks_path_accel_ivy}/", self.mount_private_frameworks], stdout=subprocess.PIPE) - else: - print("- Installing basic Ivy Bridge Kext patches for generic OS") - self.add_new_binaries(sys_patch_data.AddIntelGen3Accel, self.constants.legacy_intel_gen3_path) - - def gpu_framebuffer_kepler_master(self): - if self.constants.detected_os == os_data.os_data.monterey: - print("- Installing Kepler Acceleration Kext patches for Monterey") - self.add_new_binaries(sys_patch_data.AddNvidiaKeplerAccel11, self.constants.legacy_nvidia_kepler_path) - print("- Merging Kepler Frameworks") - utilities.elevated(["rsync", "-r", "-i", "-a", f"{self.constants.payload_apple_frameworks_path_accel_kepler}/", self.mount_frameworks], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - else: - print("- Installing Kepler Kext patches for generic OS") - self.add_new_binaries(sys_patch_data.AddNvidiaKeplerAccel11, self.constants.legacy_nvidia_kepler_path) - - def gpu_accel_legacy_gva(self): - print("- Merging AppleGVA Hardware Accel patches for non-Metal") - utilities.elevated(["rsync", "-r", "-i", "-a", f"{self.constants.payload_apple_private_frameworks_path_legacy_drm}/", self.mount_private_frameworks], stdout=subprocess.PIPE) - - def gpu_accel_legacy_extended(self): - if self.constants.detected_os == os_data.os_data.monterey: - self.add_legacy_dropbox_patch() - - if self.legacy_keyboard_backlight is True: - self.add_legacy_keyboard_backlight_patch() - - print("- Merging general legacy Frameworks") - utilities.elevated(["rsync", "-r", "-i", "-a", f"{self.constants.payload_apple_frameworks_path_accel}/", self.mount_frameworks], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - if self.constants.detected_os > os_data.os_data.big_sur: - print("- Merging Monterey WebKit patch") - utilities.elevated(["rsync", "-r", "-i", "-a", f"{self.constants.payload_apple_frameworks_path_accel_ivy}/", self.mount_frameworks], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - - print("- Merging general legacy PrivateFrameworks") - utilities.elevated(["rsync", "-r", "-i", "-a", f"{self.constants.payload_apple_private_frameworks_path_accel}/", self.mount_private_frameworks], stdout=subprocess.PIPE) - if self.constants.detected_os > os_data.os_data.catalina: - # With PatcherSupportPkg v0.2.0, IOHID-Fixup.plist is deprecated and integrated into SkyLight patch set - if (Path(self.mount_lauchd) / Path("IOHID-Fixup.plist")).exists(): - print("- Stripping legacy IOHID-Fixup.plist") - utilities.process_status( - utilities.elevated(["rm", "-f", f"{self.mount_lauchd}/IOHID-Fixup.plist"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - ) - else: - print("- Disabling Library Validation") - utilities.process_status( - utilities.elevated( - ["defaults", "write", "/Library/Preferences/com.apple.security.libraryvalidation.plist", "DisableLibraryValidation", "-bool", "true"], - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - ) - ) - - def gpu_accel_legacy_extended_ts2(self): - print("- Merging TeraScale 2 legacy Frameworks") - utilities.elevated(["rsync", "-r", "-i", "-a", f"{self.constants.payload_apple_frameworks_path_accel_ts2}/", self.mount_frameworks], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - - print("- Merging TeraScale 2 PrivateFrameworks") - utilities.elevated(["rsync", "-r", "-i", "-a", f"{self.constants.payload_apple_private_frameworks_path_accel_ts2}/", self.mount_private_frameworks], stdout=subprocess.PIPE) - if self.validate is False: - print("- Fixing Acceleration in CMIO") - utilities.process_status(subprocess.run(["defaults", "write", "com.apple.cmio", "CMIO_Unit_Input_ASC.DoNotUseOpenCL", "-bool", "true"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) + print("- Creating SkylightPlugins folder") + utilities.process_status(utilities.elevated(["mkdir", f"{self.mount_application_support}/SkyLightPlugins/"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) + def patch_root_vol(self): print(f"- Running patches for {self.model}") - - # Before starting, clean out old plugins - self.clean_skylight_plugins() - - # Graphics patches - if self.nvidia_legacy is True: - print("- Installing legacy Nvidia Patches") - if self.constants.detected_os in self.constants.legacy_accel_support: - print("- Detected supported OS, installing Acceleration Patches") - self.added_legacy_kexts = True - else: - print("- Detected unsupported OS, installing Basic Framebuffer") - self.gpu_accel_legacy_nvidia_master() - - elif self.kepler_gpu is True: - print("- Installing Kepler Patches") - if self.constants.detected_os == os_data.os_data.monterey: - print("- Detected supported OS, installing Acceleration Patches") - else: - print("- Detected unsupported OS, installing Basic Framebuffer") - self.gpu_framebuffer_kepler_master() - - elif self.amd_ts1 is True: - print("- Installing legacy TeraScale 1 Patches") - if self.constants.detected_os in self.constants.legacy_accel_support: - print("- Detected supported OS, installing Acceleration Patches") - self.added_legacy_kexts = True - else: - print("- Detected unsupported OS, installing Basic Framebuffer") - self.gpu_accel_legacy_ts1_master() - - elif self.amd_ts2 is True: - print("- Installing legacy TeraScale 2 Patches") - if self.constants.detected_os in self.constants.legacy_accel_support: - print("- Detected supported OS, installing Acceleration Patches") - self.added_legacy_kexts = True - else: - print("- Detected unsupported OS, installing Basic Framebuffer") - self.gpu_accel_legacy_ts2_master() - - if self.iron_gpu is True: - print("- Installing legacy Ironlake Patches") - if self.constants.detected_os in self.constants.legacy_accel_support: - print("- Detected supported OS, installing Acceleration Patches") - self.added_legacy_kexts = True - else: - print("- Detected unsupported OS, installing Basic Framebuffer") - self.gpu_accel_legacy_ironlake_master() - - elif self.sandy_gpu is True: - print("- Installing legacy Sandy Bridge Patches") - if self.constants.detected_os in self.constants.legacy_accel_support: - print("- Detected supported OS, installing Acceleration Patches") - self.added_legacy_kexts = True - else: - print("- Detected unsupported OS, installing Basic Framebuffer") - self.gpu_accel_legacy_sandybridge_master() - - elif self.ivy_gpu is True: - print("- Installing Ivy Bridge Patches") - if self.constants.detected_os == os_data.os_data.monterey: - print("- Detected supported OS, installing Acceleration Patches") - else: - print("- Detected unsupported OS, installing Basic Framebuffer") - self.gpu_framebuffer_ivybridge_master() - - if self.amd_ts2 is True and self.constants.detected_os in self.constants.legacy_accel_support and self.constants.allow_ts2_accel is True: - # TeraScale 2 patches must be installed after Intel HD3000 - self.add_new_binaries(sys_patch_data.AddAMDAccel11TS2, self.constants.legacy_amd_path_ts2) - - if self.added_legacy_kexts is True and self.constants.detected_os in self.constants.legacy_accel_support: - self.gpu_accel_legacy_extended() - if self.amd_ts2 is True and self.constants.allow_ts2_accel is True: - self.gpu_accel_legacy_extended_ts2() - - # Misc patches - if self.brightness_legacy is True: - print("- Installing legacy Brightness Control") - self.add_brightness_patch() - - if self.legacy_audio is True: - print("- Fixing Volume Control Support") - self.add_audio_patch() - - if self.legacy_wifi is True: - print("- Installing legacy Wireless support") - self.add_wifi_patch() - - if self.legacy_gmux is True: - print("- Installing Legacy Mux Brightness support") - self.add_legacy_mux_patch() + if 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)) if self.constants.wxpython_variant is True and self.constants.detected_os >= os_data.os_data.big_sur: - self.install_auto_patcher_launch_agent() + sys_patch_auto.AutomaticSysPatch.install_auto_patcher_launch_agent(self.constants) + + self.rebuild_snapshot() + + 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) + for patch in required_patches: + print("- Installing Patchset: " + patch) + if "Remove" in required_patches[patch]: + for remove_patch_directory in required_patches[patch]["Remove"]: + print("- 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) + + + for method_install in ["Install", "Install Non-Root"]: + if method_install in required_patches[patch]: + for install_patch_directory in required_patches[patch][method_install]: + print(f"- Handling Installs in: {install_patch_directory}") + for install_file in required_patches[patch][method_install][install_patch_directory]: + source_folder_path = source_files_path + "/" + required_patches[patch][method_install][install_patch_directory][install_file] + install_patch_directory + if method_install == "Install": + destination_folder_path = str(self.mount_location) + install_patch_directory + else: + destination_folder_path = str(self.mount_location_data) + install_patch_directory + 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"]: + # Some processes need sudo, however we cannot directly call sudo in some scenarios + # Instead, call elevated funtion and strip sudo from argument + process_array = process.split(" ") + if required_patches[patch]["Processes"][process] is True: + utilities.process_status(utilities.elevated(process_array, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) + else: + utilities.process_status(subprocess.run(process_array, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) + + def preflight_checks(self, required_patches, source_files_path): + print("- Running Preflight Checks before patching") + + # Make sure old SkyLight plugins aren't being used + self.clean_skylight_plugins() + + # Make sure SNB kexts are compatible with the host + if "Intel Sandy Bridge" in required_patches: + if self.computer.reported_board_id not in self.constants.sandy_board_id_stock: + print(f"- Found unspported Board ID {self.computer.reported_board_id}, performing AppleIntelSNBGraphicsFB bin patching") + board_to_patch = generate_smbios.determine_best_board_id_for_sandy(self.computer.reported_board_id, self.computer.gpus) + print(f"- Replacing {board_to_patch} with {self.computer.reported_board_id}") + + board_to_patch_hex = bytes.fromhex(board_to_patch.encode('utf-8').hex()) + reported_board_hex = bytes.fromhex(self.computer.reported_board_id.encode('utf-8').hex()) + + if len(board_to_patch_hex) != len(reported_board_hex): + print(f"- Error: Board ID {self.computer.reported_board_id} is not the same length as {board_to_patch}") + raise Exception("Host's Board ID is not the same length as the kext's Board ID, cannot patch!!!") + else: + 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: + raise Exception("Failed to find AppleIntelSNBGraphicsFB.kext, cannot patch!!!") + + # Check all the files are present + for patch in required_patches: + for method_type in ["Install", "Install Non-Root"]: + if method_type in required_patches[patch]: + for install_patch_directory in required_patches[patch][method_type]: + for install_file in required_patches[patch][method_type][install_patch_directory]: + source_file = source_files_path + "/" + required_patches[patch][method_type][install_patch_directory][install_file] + install_patch_directory + "/" + install_file + if not Path(source_file).exists: + raise Exception(f"Failed to find {source_file}") + + print("- Finished Preflight, starting patching") + + 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) + + if file_name_str.endswith(".framework"): + # merge with rsync + print(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) + 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(): + print(f" - Found existing {file_name}, overwritting...") + utilities.process_status(utilities.elevated(["rm", "-R", f"{destination_folder}/{file_name}"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) + else: + print(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) + else: + # Assume it's an individual file, replace as normal + if Path(destination_folder + "/" + file_name).exists(): + print(f" - Found existing {file_name}, overwritting...") + utilities.process_status(utilities.elevated(["rm", f"{destination_folder}/{file_name}"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) + else: + print(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) + + def remove_file(self, destination_folder, file_name): + if Path(destination_folder + "/" + file_name).exists(): + print(f" - Removing: {file_name}") + if Path(destination_folder + "/" + file_name).is_dir(): + utilities.process_status(utilities.elevated(["rm", "-R", f"{destination_folder}/{file_name}"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) + else: + utilities.process_status(utilities.elevated(["rm", f"{destination_folder}/{file_name}"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) + + + 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(): + # Strip recursive arguments + chmod_args.pop(1) + chown_args.pop(1) + utilities.process_status(utilities.elevated(chmod_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) + utilities.process_status(utilities.elevated(chown_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) - if self.validate is False: - self.rebuild_snapshot() def check_files(self): - if Path(self.constants.payload_apple_root_path).exists(): + if Path(self.constants.payload_local_binaries_root_path).exists(): print("- Found local Apple Binaries") if self.constants.gui_mode is False: patch_input = input("Would you like to redownload?(y/n): ") if patch_input in {"y", "Y", "yes", "Yes"}: - shutil.rmtree(Path(self.constants.payload_apple_root_path)) + shutil.rmtree(Path(self.constants.payload_local_binaries_root_path)) output = self.download_files() else: output = self.download_files() @@ -628,140 +322,34 @@ class PatchSysVolume: def download_files(self): if self.constants.gui_mode is False or "Library/InstallerSandboxes/" in str(self.constants.payload_path): - download_result, os_ver, extensions_link, framework_link = sys_patch_download.grab_patcher_support_pkg(self.constants).download_files() + download_result, link = sys_patch_download.grab_patcher_support_pkg(self.constants).download_files() else: download_result = True - os_ver, extensions_link, framework_link = sys_patch_download.grab_patcher_support_pkg(self.constants).generate_pkg_link() + link = sys_patch_download.grab_patcher_support_pkg(self.constants).generate_pkg_link() - if download_result and self.constants.payload_apple_root_path_zip.exists() and self.constants.payload_universal_extensions_zip_path.exists(): - print("- Download completed") - print("- Unzipping download...") - try: - utilities.process_status(subprocess.run(["unzip", self.constants.payload_apple_root_path_zip], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=self.constants.payload_path)) - utilities.process_status(subprocess.run(["unzip", self.constants.payload_universal_extensions_zip_path], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=self.constants.payload_path)) - print("- Renaming folder") - os.rename(self.constants.payload_path / Path(os_ver), self.constants.payload_apple_root_path) - Path(self.constants.payload_apple_root_path_zip).unlink() - Path(self.constants.payload_universal_extensions_zip_path).unlink() - print("- Binaries downloaded to:") - print(self.constants.payload_path) - return self.constants.payload_apple_root_path - except zipfile.BadZipFile: - print("- Couldn't unzip") - return None + if download_result and self.constants.payload_local_binaries_root_path_zip.exists(): + print("- Unzipping binaries...") + utilities.process_status(subprocess.run(["ditto", "-V", "-x", "-k", "--sequesterRsrc", "--rsrc", self.constants.payload_local_binaries_root_path_zip, self.constants.payload_path])) + print("- Binaries downloaded to:") + print(self.constants.payload_path) + return self.constants.payload_local_binaries_root_path else: if self.constants.gui_mode is True: - print("- Download failed, please verify the below links work:") - print(framework_link) - print(extensions_link) + print("- Download failed, please verify the below link work:") + print(link) print("\nIf you continue to have issues, try using the Offline builds") print("located on Github next to the other builds") else: input("\nPress enter to continue") return None - - def detect_patch_set(self): - utilities.cls() - print("The following patches will be applied:") - if self.nvidia_legacy is True: - print("- Add Legacy Nvidia Tesla Graphics Patch") - elif self.kepler_gpu is True: - print("- Add Legacy Nvidia Kepler Graphics Patch") - elif self.amd_ts1 is True: - print("- Add Legacy ATI TeraScale 1 Graphics Patch") - elif self.amd_ts2 is True: - print("- Add Legacy ATI TeraScale 2 Graphics Patch") - if self.iron_gpu is True: - print("- Add Legacy Intel IronLake Graphics Patch") - elif self.sandy_gpu is True: - print("- Add Legacy Intel Sandy Bridge Graphics Patch") - elif self.ivy_gpu is True: - print("- Add Legacy Intel Ivy Bridge Graphics Patch") - if self.brightness_legacy is True: - print("- Add Legacy Brightness Control") - if self.legacy_audio is True: - print("- Add legacy Audio Control") - if self.legacy_wifi is True: - print("- Add legacy WiFi Control") - if self.legacy_gmux is True: - print("- Add Legacy Mux Brightness Control") - if self.legacy_keyboard_backlight is True: - print("- Add Legacy Keyboard Backlight Control") - - self.no_patch = not any( - [ - self.nvidia_legacy, - self.kepler_gpu, - self.amd_ts1, - self.amd_ts2, - self.iron_gpu, - self.sandy_gpu, - self.ivy_gpu, - self.brightness_legacy, - self.legacy_audio, - self.legacy_wifi, - self.legacy_gmux, - ] - ) - - def verify_patch_allowed(self): - sip = sip_data.system_integrity_protection.root_patch_sip_big_sur if self.constants.detected_os > os_data.os_data.catalina else sip_data.system_integrity_protection.root_patch_sip_mojave - if sip == sip_data.system_integrity_protection.root_patch_sip_mojave: - sip_value = "For Hackintoshes, please set csr-active-config to '03060000' (0x603)\nFor non-OpenCore Macs, please run 'csrutil disable' in RecoveryOS" - else: - sip_value = ( - "For Hackintoshes, please set csr-active-config to '02080000' (0x802)\nFor non-OpenCore Macs, please run 'csrutil disable' and \n'csrutil authenticated-root disable' in RecoveryOS" - ) - if self.sip_enabled is True: - print("\nCannot patch! Please disable System Integrity Protection (SIP).") - print("Disable SIP in Patcher Settings and Rebuild OpenCore\n") - print("Ensure the following bits are set for csr-active-config:") - print("\n".join(sip)) - print(sip_value) - - if self.sbm_enabled is True: - print("\nCannot patch! Please disable Apple Secure Boot.") - print("Disable SecureBootModel in Patcher Settings and Rebuild OpenCore") - print("For Hackintoshes, set SecureBootModel to Disabled") - - if self.fv_enabled is True: - print("\nCannot patch! Please disable FileVault.") - print("For OCLP Macs, please rebuild your config with 0.2.5 or newer") - print("For others, Go to System Preferences -> Security and disable FileVault") - - if self.amfi_enabled is True and self.amfi_must_disable is True: - print("\nCannot patch! Please disable AMFI.") - print("For Hackintoshes, please add amfi_get_out_of_my_way=1 to boot-args") - - if self.check_board_id is True and (self.computer.reported_board_id not in self.constants.sandy_board_id and self.computer.reported_board_id not in self.constants.sandy_board_id_stock): - print("\nCannot patch! Board ID not supported by AppleIntelSNBGraphicsFB") - print(f"Detected Board ID: {self.computer.reported_board_id}") - print("Please ensure your Board ID is listed below:") - for board in self.constants.sandy_board_id: - print(f"- {board} ({generate_smbios.find_model_off_board(board)})") - for board in self.constants.sandy_board_id_stock: - print(f"- {board} ({generate_smbios.find_model_off_board(board)})") - - self.bad_board_id = True - - if self.dosdude_patched is True: - print("\nCannot patch! Detected machine has already been patched by another patcher") - print("Please ensure your install is either clean or patched with OpenCore Legacy Patcher") - - if any( - [self.sip_enabled, self.sbm_enabled, self.fv_enabled, self.dosdude_patched, self.amfi_enabled if self.amfi_must_disable else False, self.bad_board_id if self.check_board_id else False] - ): - return False - else: - return True - # Entry Function def start_patch(self): print("- Starting Patch Process") print(f"- Determining Required Patch set for Darwin {self.constants.detected_os}") - self.detect_patch_set() - if self.no_patch is True: + self.patch_set_dictionary = sys_patch_detect.detect_root_patch(self.computer.real_model, self.constants).generate_patchset(self.hardware_details) + + if self.patch_set_dictionary == {}: change_menu = None print("- No Root Patches required for your machine!") if self.constants.gui_mode is False: @@ -772,9 +360,8 @@ class PatchSysVolume: change_menu = "y" print("- Continuing root patching") if change_menu in ["y", "Y"]: - print("- Continuing with Patching") print("- Verifying whether Root Patching possible") - if self.verify_patch_allowed() is True: + 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: print("- Patcher is capable of patching") if self.check_files(): self.find_mount_root_vol(True) @@ -786,7 +373,7 @@ class PatchSysVolume: def start_unpatch(self): print("- Starting Unpatch Process") - if self.verify_patch_allowed() is True: + if sys_patch_detect.detect_root_patch(self.computer.real_model, self.constants).verify_patch_allowed(print_errors=True) is True: self.find_mount_root_vol(False) if self.constants.gui_mode is False: input("\nPress [ENTER] to return to the main menu") diff --git a/resources/sys_patch_auto.py b/resources/sys_patch_auto.py index 65bd532b2..84889d32a 100644 --- a/resources/sys_patch_auto.py +++ b/resources/sys_patch_auto.py @@ -8,6 +8,7 @@ # If all these tests pass, start Root Patcher # Copyright (C) 2022, Mykola Grymalyuk +from pathlib import Path import plistlib import subprocess import webbrowser @@ -162,4 +163,57 @@ class AutomaticSysPatch: print("- Unable to determine if boot disk is removable, skipping prompt") else: - print("- Failed to find disk OpenCore launched from") \ No newline at end of file + print("- Failed to find disk OpenCore launched from") + + + def install_auto_patcher_launch_agent(settings): + # Installs the following: + # - OpenCore-Patcher.app in /Library/Application Support/Dortania/ + # - com.dortania.opencore-legacy-patcher.auto-patch.plist in /Library/LaunchAgents/ + if settings.launcher_script is None: + # Verify our binary isn't located in '/Library/Application Support/Dortania/' + # As we'd simply be duplicating ourselves + if not settings.launcher_binary.startswith("/Library/Application Support/Dortania/"): + print("- Installing Auto Patcher Launch Agent") + + if not Path("Library/Application Support/Dortania").exists(): + print("- Creating /Library/Application Support/Dortania/") + utilities.process_status(utilities.elevated(["mkdir", "-p", "/Library/Application Support/Dortania"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) + + print("- Copying OpenCore Patcher to /Library/Application Support/Dortania/") + if Path("/Library/Application Support/Dortania/OpenCore-Patcher.app").exists(): + print("- Deleting existing OpenCore-Patcher") + utilities.process_status(utilities.elevated(["rm", "-R", "/Library/Application Support/Dortania/OpenCore-Patcher.app"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) + + # Strip everything after OpenCore-Patcher.app + path = str(settings.launcher_binary).split("/Contents/MacOS/OpenCore-Patcher")[0] + print(f"- Copying {path} to /Library/Application Support/Dortania/") + utilities.process_status(utilities.elevated(["cp", "-R", path, "/Library/Application Support/Dortania/"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) + + if not Path("/Library/Application Support/Dortania/OpenCore-Patcher.app").exists(): + # Sometimes the binary the user launches maye have a suffix (ie. OpenCore-Patcher 3.app) + # We'll want to rename it to OpenCore-Patcher.app + path = path.split("/")[-1] + print(f"- Renaming {path} to OpenCore-Patcher.app") + utilities.process_status(utilities.elevated(["mv", f"/Library/Application Support/Dortania/{path}", "/Library/Application Support/Dortania/OpenCore-Patcher.app"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) + + # Copy over our launch agent + print("- Copying auto-patch.plist Launch Agent to /Library/LaunchAgents/") + if Path("/Library/LaunchAgents/com.dortania.opencore-legacy-patcher.auto-patch.plist").exists(): + print("- Deleting existing auto-patch.plist") + utilities.process_status(utilities.elevated(["rm", "/Library/LaunchAgents/com.dortania.opencore-legacy-patcher.auto-patch.plist"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) + utilities.process_status(utilities.elevated(["cp", settings.auto_patch_launch_agent_path, "/Library/LaunchAgents/"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) + + # Set the permissions on the com.dortania.opencore-legacy-patcher.auto-patch.plist + print("- Setting permissions on auto-patch.plist") + utilities.process_status(utilities.elevated(["chmod", "644", "/Library/LaunchAgents/com.dortania.opencore-legacy-patcher.auto-patch.plist"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) + utilities.process_status(utilities.elevated(["chown", "root:wheel", "/Library/LaunchAgents/com.dortania.opencore-legacy-patcher.auto-patch.plist"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) + + # Making app alias + # Simply an easy way for users to notice the app + # If there's already an alias or exiting app, skip + if not Path("/Applications/OpenCore-Patcher.app").exists(): + print("- Making app alias") + utilities.process_status(utilities.elevated(["ln", "-s", "/Library/Application Support/Dortania/OpenCore-Patcher.app", "/Applications/OpenCore-Patcher.app"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)) + else: + print("- Skipping Auto Patcher Launch Agent, not supported when running from source") \ No newline at end of file diff --git a/resources/sys_patch_detect.py b/resources/sys_patch_detect.py index dc4b4e04a..54499e8f1 100644 --- a/resources/sys_patch_detect.py +++ b/resources/sys_patch_detect.py @@ -4,7 +4,7 @@ # Copyright (C) 2020-2022, Dhinak G, Mykola Grymalyuk from resources import constants, device_probe, utilities -from data import model_array, os_data, sip_data +from data import model_array, os_data, sip_data, sys_patch_dict class detect_root_patch: def __init__(self, model, versions): @@ -13,7 +13,7 @@ class detect_root_patch: self.computer = self.constants.computer # GPU Patch Detection - self.nvidia_legacy= False + self.nvidia_tesla= False self.kepler_gpu= False self.amd_ts1= False self.amd_ts2= False @@ -30,7 +30,6 @@ class detect_root_patch: # Patch Requirements self.amfi_must_disable= False - self.check_board_id= False self.supports_metal= False # Validation Checks @@ -39,7 +38,6 @@ class detect_root_patch: self.amfi_enabled = False self.fv_enabled = False self.dosdude_patched = False - self.bad_board_id = False def detect_gpus(self): @@ -50,7 +48,7 @@ class detect_root_patch: print(f"- Found GPU ({i}): {utilities.friendly_hex(gpu.vendor_id)}:{utilities.friendly_hex(gpu.device_id)}") if gpu.arch in [device_probe.NVIDIA.Archs.Tesla, device_probe.NVIDIA.Archs.Fermi]: if self.constants.detected_os > non_metal_os: - self.nvidia_legacy = True + self.nvidia_tesla = True self.amfi_must_disable = True self.legacy_keyboard_backlight = self.check_legacy_keyboard_backlight() elif gpu.arch == device_probe.NVIDIA.Archs.Kepler: @@ -80,7 +78,6 @@ class detect_root_patch: if self.constants.detected_os > non_metal_os: self.sandy_gpu = True self.amfi_must_disable = True - self.check_board_id = True self.legacy_keyboard_backlight = self.check_legacy_keyboard_backlight() elif gpu.arch == device_probe.Intel.Archs.Ivy_Bridge: if self.constants.detected_os > os_data.os_data.big_sur: @@ -89,7 +86,7 @@ class detect_root_patch: if self.supports_metal is True: # Avoid patching Metal and non-Metal GPUs if both present, prioritize Metal GPU # Main concerns are for iMac12,x with Sandy iGPU and Kepler dGPU - self.nvidia_legacy = False + self.nvidia_tesla = False self.amd_ts1 = False self.amd_ts2 = False self.iron_gpu = False @@ -156,7 +153,7 @@ class detect_root_patch: self.legacy_gmux = True self.root_patch_dict = { - "Graphics: Nvidia Tesla": self.nvidia_legacy, + "Graphics: Nvidia Tesla": self.nvidia_tesla, "Graphics: Nvidia Kepler": self.kepler_gpu, "Graphics: AMD TeraScale 1": self.amd_ts1, "Graphics: AMD TeraScale 2": self.amd_ts2, @@ -170,28 +167,116 @@ class detect_root_patch: "Miscellaneous: Legacy GMUX": self.legacy_gmux, "Miscellaneous: Legacy Keyboard Backlight": self.legacy_keyboard_backlight, "Settings: Requires AMFI exemption": self.amfi_must_disable, - "Settings: Requires Board ID validation": self.check_board_id, "Validation: Patching Possible": self.verify_patch_allowed(), "Validation: SIP is enabled": self.sip_enabled, "Validation: SBM is enabled": self.sbm_enabled, "Validation: AMFI is enabled": self.amfi_enabled if self.amfi_must_disable else False, "Validation: FileVault is enabled": self.fv_enabled, "Validation: System is dosdude1 patched": self.dosdude_patched, - f"Validation: Board ID is unsupported \n({self.computer.reported_board_id})": self.bad_board_id, } return self.root_patch_dict - def verify_patch_allowed(self): + def verify_patch_allowed(self, print_errors=False): sip = sip_data.system_integrity_protection.root_patch_sip_big_sur if self.constants.detected_os > os_data.os_data.catalina else sip_data.system_integrity_protection.root_patch_sip_mojave self.sip_enabled, self.sbm_enabled, self.amfi_enabled, self.fv_enabled, self.dosdude_patched = utilities.patching_status(sip, self.constants.detected_os) + if sip == sip_data.system_integrity_protection.root_patch_sip_mojave: + sip_value = "For Hackintoshes, please set csr-active-config to '03060000' (0x603)\nFor non-OpenCore Macs, please run 'csrutil disable' in RecoveryOS" + else: + sip_value = ( + "For Hackintoshes, please set csr-active-config to '02080000' (0x802)\nFor non-OpenCore Macs, please run 'csrutil disable' and \n'csrutil authenticated-root disable' in RecoveryOS" + ) + if print_errors is True: + if self.sip_enabled is True: + print("\nCannot patch! Please disable System Integrity Protection (SIP).") + print("Disable SIP in Patcher Settings and Rebuild OpenCore\n") + print("Ensure the following bits are set for csr-active-config:") + print("\n".join(sip)) + print(sip_value) - if self.check_board_id is True and (self.computer.reported_board_id not in self.constants.sandy_board_id and self.computer.reported_board_id not in self.constants.sandy_board_id_stock): - self.bad_board_id = True + if self.sbm_enabled is True: + print("\nCannot patch! Please disable Apple Secure Boot.") + print("Disable SecureBootModel in Patcher Settings and Rebuild OpenCore") + print("For Hackintoshes, set SecureBootModel to Disabled") + if self.fv_enabled is True: + print("\nCannot patch! Please disable FileVault.") + print("For OCLP Macs, please rebuild your config with 0.2.5 or newer") + print("For others, Go to System Preferences -> Security and disable FileVault") + + if self.amfi_enabled is True and self.amfi_must_disable is True: + print("\nCannot patch! Please disable AMFI.") + print("For Hackintoshes, please add amfi_get_out_of_my_way=1 to boot-args") + + if self.dosdude_patched is True: + print("\nCannot patch! Detected machine has already been patched by another patcher") + print("Please ensure your install is either clean or patched with OpenCore Legacy Patcher") + if any( - [self.sip_enabled, self.sbm_enabled, self.fv_enabled, self.dosdude_patched, self.amfi_enabled if self.amfi_must_disable else False, self.bad_board_id if self.check_board_id else False] + [self.sip_enabled, self.sbm_enabled, self.fv_enabled, self.dosdude_patched, self.amfi_enabled if self.amfi_must_disable else False] ): return False else: - return True \ No newline at end of file + return True + + def generate_patchset(self, hardware_details): + all_hardware_patchset = sys_patch_dict.SystemPatchDictionary(self.constants.detected_os) + required_patches = {} + utilities.cls() + print("- The following patches will be applied:") + if hardware_details["Graphics: Intel Ironlake"] is True: + print(" - Graphics: Intel Ironlake") + required_patches.update({"Non-Metal Common": all_hardware_patchset["Graphics"]["Non-Metal Common"]}) + required_patches.update({"Intel Ironlake": all_hardware_patchset["Graphics"]["Intel Ironlake"]}) + if hardware_details["Graphics: Intel Sandy Bridge"] is True: + print(" - Graphics: Intel Sandy Bridge") + required_patches.update({"Non-Metal Common": all_hardware_patchset["Graphics"]["Non-Metal Common"]}) + required_patches.update({"Legacy GVA": all_hardware_patchset["Graphics"]["Legacy GVA"]}) + required_patches.update({"Intel Sandy Bridge": all_hardware_patchset["Graphics"]["Intel Sandy Bridge"]}) + if hardware_details["Graphics: Intel Ivy Bridge"] is True: + print(" - Graphics: Intel Ivy Bridge") + required_patches.update({"Metal Common": all_hardware_patchset["Graphics"]["Metal Common"]}) + required_patches.update({"Intel Ivy Bridge": all_hardware_patchset["Graphics"]["Intel Ivy Bridge"]}) + if hardware_details["Graphics: Nvidia Tesla"] is True: + print(" - Graphics: Nvidia Tesla") + required_patches.update({"Non-Metal Common": all_hardware_patchset["Graphics"]["Non-Metal Common"]}) + required_patches.update({"Nvidia Tesla": all_hardware_patchset["Graphics"]["Nvidia Tesla"]}) + required_patches.update({"Nvidia Web Drivers": all_hardware_patchset["Graphics"]["Nvidia Web Drivers"]}) + if hardware_details["Graphics: Nvidia Kepler"] is True: + print(" - Graphics: Nvidia Kepler") + required_patches.update({"Metal Common": all_hardware_patchset["Graphics"]["Metal Common"]}) + required_patches.update({"Nvidia Kepler": all_hardware_patchset["Graphics"]["Nvidia Kepler"]}) + if hardware_details["Graphics: AMD TeraScale 1"] is True: + print(" - Graphics: AMD TeraScale 1") + required_patches.update({"Non-Metal Common": all_hardware_patchset["Graphics"]["Non-Metal Common"]}) + required_patches.update({"AMD Non-Metal Common": all_hardware_patchset["Graphics"]["AMD Non-Metal Common"]}) + required_patches.update({"AMD TeraScale 1": all_hardware_patchset["Graphics"]["AMD TeraScale 1"]}) + if hardware_details["Graphics: AMD TeraScale 2"] is True: + print(" - Graphics: AMD TeraScale 2") + required_patches.update({"Non-Metal Common": all_hardware_patchset["Graphics"]["Non-Metal Common"]}) + required_patches.update({"AMD Non-Metal Common": all_hardware_patchset["Graphics"]["AMD Non-Metal Common"]}) + required_patches.update({"AMD TeraScale 2": all_hardware_patchset["Graphics"]["AMD TeraScale 2"]}) + if hardware_details["Brightness: Legacy Backlight Control"] is True: + print(" - Brightness: Legacy Brightness") + required_patches.update({"Legacy Brightness": all_hardware_patchset["Brightness"]["Legacy Brightness"]}) + if hardware_details["Audio: Legacy Realtek"] is True: + if self.model in ["iMac7,1", "iMac8,1"]: + print(" - Audio: Legacy Realtek Audio") + required_patches.update({"Legacy Realtek": all_hardware_patchset["Audio"]["Legacy Realtek"]}) + else: + print(" - Audio: Legacy non-GOP Audio") + required_patches.update({"Legacy Non-GOP": all_hardware_patchset["Audio"]["Legacy Non-GOP"]}) + if hardware_details["Networking: Legacy Wireless"] is True: + print(" - Networking: Legacy WiFi") + required_patches.update({"Legacy WiFi": all_hardware_patchset["Networking"]["Legacy WiFi"]}) + if hardware_details["Miscellaneous: Legacy GMUX"] is True: + print(" - Miscellaneous: Legacy GMUX") + required_patches.update({"Legacy GMUX": all_hardware_patchset["Miscellaneous"]["Legacy GMUX"]}) + if hardware_details["Miscellaneous: Legacy Keyboard Backlight"]: + print(" - Miscellaneous: Legacy Keyboard Backlight") + required_patches.update({"Legacy Keyboard Backlight": all_hardware_patchset["Miscellaneous"]["Legacy Keyboard Backlight"]}) + + if not required_patches: + print(" - No patch sets found for booted model") + + return required_patches \ No newline at end of file diff --git a/resources/sys_patch_download.py b/resources/sys_patch_download.py index 0577c1bac..23d1746c6 100644 --- a/resources/sys_patch_download.py +++ b/resources/sys_patch_download.py @@ -12,41 +12,23 @@ class grab_patcher_support_pkg: self.constants = constants def generate_pkg_link(self): - if self.constants.detected_os == os_data.os_data.monterey: - os_ver = "12-Monterey" - elif self.constants.detected_os == os_data.os_data.big_sur: - os_ver = "11-Big-Sur" - elif self.constants.detected_os == os_data.os_data.catalina: - os_ver = "10.15-Catalina" - elif self.constants.detected_os == os_data.os_data.mojave: - os_ver = "10.14-Mojave" - else: - raise Exception(f"Unsupported OS: {self.constants.detected_os}") - framework_link = f"{self.constants.url_patcher_support_pkg}{self.constants.patcher_support_pkg_version}/{os_ver}.zip" - extensions_link = f"{self.constants.url_patcher_support_pkg}{self.constants.patcher_support_pkg_version}/Universal-Extensions.zip" - return os_ver, extensions_link, framework_link + link = f"{self.constants.url_patcher_support_pkg}{self.constants.patcher_support_pkg_version}/Universal-Binaries.zip" + return link def download_files(self): - os_ver, extensions_link, framework_link = self.generate_pkg_link() - if Path(self.constants.payload_apple_root_path).exists(): + link = self.generate_pkg_link() + if Path(self.constants.payload_local_binaries_root_path).exists(): print("- Removing old Apple Binaries folder") # Delete folder - shutil.rmtree(self.constants.payload_apple_root_path) - if Path(self.constants.payload_apple_root_path_zip).exists(): - print("- Removing old Apple Binaries zip") - Path(self.constants.payload_apple_root_path_zip).unlink() + shutil.rmtree(self.constants.payload_local_binaries_root_path) download_result = None - local_framework_zip = Path(self.constants.payload_path) / f"{os_ver}.zip" - local_extensions_zip = Path(self.constants.payload_path) / f"Universal-Extensions.zip" - if Path(local_framework_zip).exists() and Path(local_extensions_zip).exists(): - print(f"- Found local {os_ver} zip, skipping download") - print(f"- Duplicating into Apple.zip") - shutil.copy(local_framework_zip, self.constants.payload_apple_root_path_zip) + local_zip = Path(self.constants.payload_path) / f"Universal-Binaries.zip" + if Path(local_zip).exists(): + print(f"- Found local {local_zip} zip, skipping download") download_result = True else: - print(f"- No local version found, donwloading...") - if utilities.download_file(extensions_link, self.constants.payload_universal_extensions_zip_path): - download_result = utilities.download_file(framework_link, self.constants.payload_apple_root_path_zip) + print(f"- No local version found, downloading...") + download_result = utilities.download_file(link, self.constants.payload_local_binaries_root_path_zip) - return download_result, os_ver, extensions_link, framework_link \ No newline at end of file + return download_result, link \ No newline at end of file diff --git a/resources/tui_helpers.py b/resources/tui_helpers.py new file mode 100644 index 000000000..92b24602a --- /dev/null +++ b/resources/tui_helpers.py @@ -0,0 +1,78 @@ +# Copyright (C) 2020-2022, Dhinak G, Mykola Grymaluk +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) \ No newline at end of file diff --git a/resources/utilities.py b/resources/utilities.py index b33197b70..a126592d3 100644 --- a/resources/utilities.py +++ b/resources/utilities.py @@ -567,108 +567,3 @@ def check_cli_args(): return None else: return args - - -# def menu(title, prompt, menu_options, add_quit=True, auto_number=False, in_between=[], top_level=False): -# return_option = ["Q", "Quit", None] if top_level else ["B", "Back", None] -# if add_quit: menu_options.append(return_option) - -# cls() -# header(title) -# print() - -# for i in in_between: print(i) -# if in_between: print() - -# for index, option in enumerate(menu_options): -# if auto_number and not (index == (len(menu_options) - 1) and add_quit): -# option[0] = str((index + 1)) -# print(option[0] + ". " + option[1]) - -# print() -# selected = input(prompt) - -# keys = [option[0].upper() for option in menu_options] -# if not selected or selected.upper() not in keys: -# return -# if selected.upper() == return_option[0]: -# return -1 -# else: -# menu_options[keys.index(selected.upper())][2]() if menu_options[keys.index(selected.upper())][2] else None - - -class TUIMenu: - def __init__(self, title, prompt, options=None, return_number_instead_of_direct_call=False, add_quit=True, auto_number=False, in_between=None, top_level=False, loop=False): - self.title = title - self.prompt = prompt - self.in_between = in_between or [] - self.options = options or [] - self.return_number_instead_of_direct_call = return_number_instead_of_direct_call - self.auto_number = auto_number - self.add_quit = add_quit - self.top_level = top_level - self.loop = loop - self.added_quit = False - - def add_menu_option(self, name, description="", function=None, key=""): - self.options.append([key, name, description, function]) - - def start(self): - return_option = ["Q", "Quit"] if self.top_level else ["B", "Back"] - if self.add_quit and not self.added_quit: - self.add_menu_option(return_option[1], function=None, key=return_option[0]) - self.added_quit = True - - while True: - cls() - header(self.title) - print() - - for i in self.in_between: - print(i) - if self.in_between: - print() - - for index, option in enumerate(self.options): - if self.auto_number and not (index == (len(self.options) - 1) and self.add_quit): - option[0] = str((index + 1)) - print(option[0] + ". " + option[1]) - for i in option[2]: - print("\t" + i) - - print() - selected = input(self.prompt) - - keys = [option[0].upper() for option in self.options] - if not selected or selected.upper() not in keys: - if self.loop: - continue - else: - return - if self.add_quit and selected.upper() == return_option[0]: - return -1 - elif self.return_number_instead_of_direct_call: - return self.options[keys.index(selected.upper())][0] - else: - self.options[keys.index(selected.upper())][3]() if self.options[keys.index(selected.upper())][3] else None - if not self.loop: - return - - -class TUIOnlyPrint: - def __init__(self, title, prompt, in_between=None): - self.title = title - self.prompt = prompt - self.in_between = in_between or [] - - def start(self): - cls() - header(self.title) - print() - - for i in self.in_between: - print(i) - if self.in_between: - print() - - return input(self.prompt)