all of nugget's features
43
README.md
@ -1,26 +1,47 @@
|
||||
# CODENAME Nugget
|
||||
# Nugget
|
||||
Unlock your device's full potential! Works on all versions iOS 17.0+
|
||||
|
||||
This uses the sparserestore exploit to write to files outside of the intended restore location, like mobilegestalt.
|
||||
|
||||
Note: I am not responsible if your device bootloops. Please back up your data before using.
|
||||
|
||||
## Features
|
||||
- Enable Dynamic Island on any device
|
||||
- Change Device Model Name
|
||||
- Enable iPhone X gestures on iPhone SEs
|
||||
- Change Device Model Name (ie what shows in the Settings app)
|
||||
- Enable Boot Chime
|
||||
- Enable Charge Limit
|
||||
- Enable Tap to Wake on unsupported devices (ie iPhone SEs)
|
||||
- Enable iPhone 16 Settings
|
||||
- Enable Collision SOS
|
||||
- Enable Stage Manager
|
||||
- Disable the Wallpaper Parallax
|
||||
- Disable Region Restrictions (ie. Shutter Sound)
|
||||
- Note: This does not include enabling EU sideloading outside the EU. That will come later.
|
||||
- Enable AOD on any device
|
||||
- Show the Apple Pencil options in Settings app
|
||||
- Show the Action Button options in Settings app
|
||||
- Show Internal Storage info (Might cause problems on some devices, use at your own risk)
|
||||
- Enabling lock screen clock animation, lock screen page duplication button, and more!
|
||||
- Disabling the new iOS 18 Photos UI
|
||||
- EU Enabler
|
||||
|
||||
## Running the Program
|
||||
Requirements:
|
||||
- pymobiledevice3
|
||||
- Python 3.8 or newer
|
||||
|
||||
Note: It is highly recommended to use a virtual environment:
|
||||
```
|
||||
python -m venv .env # only needed once
|
||||
source .env/bin/activate
|
||||
pip install -r requirements.txt # only needed once
|
||||
python main_app.py
|
||||
python3 -m venv .env # only needed once
|
||||
# macOS/Linux: source .env/bin/activate
|
||||
# Windows: .env/Scripts/activate.bat
|
||||
pip3 install -r requirements.txt # only needed once
|
||||
python3 main_app.py
|
||||
```
|
||||
Note: It may be either `python`/`pip` or `python3`/`pip3` depending on your path.
|
||||
|
||||
The CLI version can be ran with `python3 cli_app.py`.
|
||||
|
||||
## Getting the File
|
||||
You need to get the mobilegestalt file that is specific to your device. To do that, follow these steps:
|
||||
@ -29,7 +50,17 @@ You need to get the mobilegestalt file that is specific to your device. To do th
|
||||
3. Save the file and share it to your computer.
|
||||
4. Place it in the same folder as the python file (or specify the path in the program)
|
||||
|
||||
## Building
|
||||
To compile `mainwindow.ui` for Python, run the following command:
|
||||
`pyside6-uic qt/mainwindow.ui -o qt/ui_mainwindow.py`
|
||||
|
||||
To compile the resources file for Python, run the following command:
|
||||
`pyside6-rcc qt/resources.qrc -o resources_rc.py`
|
||||
|
||||
The application itself can be compiled by running `compile.py`.
|
||||
|
||||
## Credits
|
||||
- [JJTech](https://github.com/JJTech0130) for Sparserestore/[TrollRestore](https://github.com/JJTech0130/TrollRestore)
|
||||
- [pymobiledevice3](https://github.com/doronz88/pymobiledevice3)
|
||||
- [disfordottie](https://x.com/disfordottie) for some global flag features
|
||||
|
||||
|
||||
215
cli_app.py
Normal file
@ -0,0 +1,215 @@
|
||||
from exploit.restore import restore_files, FileToRestore, restore_file
|
||||
from tweaks.tweaks import tweaks, TweakModifyType, FeatureFlagTweak, EligibilityTweak
|
||||
from devicemanagement.constants import Device
|
||||
|
||||
from pymobiledevice3.exceptions import PyMobileDevice3Exception
|
||||
from pymobiledevice3.services.diagnostics import DiagnosticsService
|
||||
from pymobiledevice3 import usbmux
|
||||
from pymobiledevice3.lockdown import create_using_usbmux
|
||||
|
||||
from pathlib import Path
|
||||
import plistlib
|
||||
import traceback
|
||||
|
||||
running = True
|
||||
passed_check = False
|
||||
num_tweaks = len(tweaks)
|
||||
|
||||
gestalt_path = Path.joinpath(Path.cwd(), "com.apple.MobileGestalt.plist")
|
||||
flags_path = Path.joinpath(Path.cwd(), "Global.plist")
|
||||
device = None
|
||||
|
||||
def print_option(num: int, active: bool, message: str):
|
||||
txt = str(num) + ". "
|
||||
if active:
|
||||
txt = txt + "[Y] "
|
||||
txt = txt + message
|
||||
print(txt)
|
||||
|
||||
def get_apply_number(num: int) -> int:
|
||||
return num + 5-num%5
|
||||
|
||||
while running:
|
||||
print("""\n\n\n\n
|
||||
|
||||
,--.
|
||||
,--.'| ___
|
||||
,--,: : | ,--.'|_
|
||||
,`--.'`| ' : ,--, | | :,'
|
||||
| : : | | ,'_ /| ,----._,. ,----._,. : : ' :
|
||||
: | \\ | : .--. | | : / / ' / / / ' / ,---. .;__,' /
|
||||
| : ' '; |,'_ /| : . || : || : | / \\ | | |
|
||||
' ' ;. ;| ' | | . .| | .\\ .| | .\\ . / / |:__,'| :
|
||||
| | | \\ || | ' | | |. ; '; |. ; '; |. ' / | ' : |__
|
||||
' : | ; .': | : ; ; |' . . |' . . |' ; /| | | '.'|
|
||||
| | '`--' ' : `--' \\`---`-'| | `---`-'| |' | / | ; : ;
|
||||
' : | : , .-./.'__/\\_: | .'__/\\_: || : | | , /
|
||||
; |.' `--`----' | : : | : : \\ \\ / ---`-'
|
||||
'---' \\ \\ / \\ \\ / `----'
|
||||
`--`-' `--`-'
|
||||
""")
|
||||
print("CLI v2.2")
|
||||
print("by LeminLimez")
|
||||
print("Thanks @disfordottie for the clock animation and @lrdsnow for EU Enabler\n")
|
||||
print("Please back up your device before using!")
|
||||
|
||||
while device == None:
|
||||
connected_devices = usbmux.list_devices()
|
||||
# Connect via usbmuxd
|
||||
for current_device in connected_devices:
|
||||
if current_device.is_usb:
|
||||
try:
|
||||
ld = create_using_usbmux(serial=current_device.serial)
|
||||
vals = ld.all_values
|
||||
device = Device(uuid=current_device.serial, name=vals['DeviceName'], version=vals['ProductVersion'], model=vals['ProductType'], locale=ld.locale, ld=ld)
|
||||
except Exception as e:
|
||||
print(traceback.format_exc())
|
||||
input("Press Enter to continue...")
|
||||
|
||||
if device == None:
|
||||
print("Please connect your device and try again!")
|
||||
input("Press Enter to continue...")
|
||||
|
||||
print(f"Connected to {device.name}\niOS {device.version}\n")
|
||||
|
||||
if not passed_check and Path.exists(gestalt_path) and Path.is_file(gestalt_path):
|
||||
passed_check = True
|
||||
|
||||
if passed_check:
|
||||
count = 0
|
||||
for key in tweaks:
|
||||
count += 1
|
||||
# do not show if the tweak is not compatible
|
||||
if tweaks[key].is_compatible(device.version):
|
||||
print_option(count, tweaks[key].enabled, tweaks[key].label)
|
||||
if tweaks[key].divider_below:
|
||||
print()
|
||||
|
||||
# apply will still be the number of tweaks just to keep consistency
|
||||
print(f"\n{get_apply_number(num_tweaks + 1)}. Apply")
|
||||
print(f"{get_apply_number(num_tweaks + 1) + 1}. Remove All Tweaks")
|
||||
print(f"{get_apply_number(num_tweaks + 1) + 2}. Reset Mobile Gestalt")
|
||||
print("0. Exit\n")
|
||||
page = int(input("Enter a number: "))
|
||||
if page == get_apply_number(num_tweaks + 1) or page == get_apply_number(num_tweaks + 1) + 1:
|
||||
# either apply or reset tweaks
|
||||
print()
|
||||
resetting = page == (get_apply_number(num_tweaks + 1) + 1)
|
||||
# set the tweaks and apply
|
||||
# first open the file in read mode
|
||||
with open(gestalt_path, 'rb') as in_fp:
|
||||
gestalt_plist = plistlib.load(in_fp)
|
||||
# create the other plists
|
||||
flag_plist: dict = {}
|
||||
eligibility_files = None
|
||||
|
||||
# verify the device credentials before continuing
|
||||
if gestalt_plist["CacheExtra"]["qNNddlUK+B/YlooNoymwgA"] != device.version or gestalt_plist["CacheExtra"]["0+nc/Udy4WNG8S+Q7a/s1A"] != device.model:
|
||||
print("com.apple.mobilegestalt.plist does not match the device!")
|
||||
print("Please make sure you are using the correct file!")
|
||||
print("If you believe this is a mistake, you can override this check.")
|
||||
override = input("Do you want to overrride? (y/n) ")
|
||||
if override.lower() != 'y':
|
||||
continue # break applying and return to the main page
|
||||
|
||||
# set the plist keys
|
||||
if not resetting:
|
||||
for tweak in tweaks.values:
|
||||
if isinstance(tweak, FeatureFlagTweak):
|
||||
flag_plist = tweak.apply_tweak(flag_plist)
|
||||
elif isinstance(tweak, EligibilityTweak):
|
||||
tweak.set_region_code(device.locale[-2:])
|
||||
eligibility_files = tweak.apply_tweak()
|
||||
else:
|
||||
gestalt_plist = tweak.apply_tweak(gestalt_plist)
|
||||
|
||||
# create the restore file list
|
||||
files_to_restore = [
|
||||
FileToRestore(
|
||||
contents=plistlib.dumps(gestalt_plist),
|
||||
restore_path="/var/containers/Shared/SystemGroup/systemgroup.com.apple.mobilegestaltcache/Library/Caches/",
|
||||
restore_name="com.apple.MobileGestalt.plist"
|
||||
),
|
||||
FileToRestore(
|
||||
contents=plistlib.dumps(flag_plist),
|
||||
restore_path="/var/preferences/FeatureFlags/",
|
||||
restore_name="Global.plist"
|
||||
)
|
||||
]
|
||||
if eligibility_files != None:
|
||||
files_to_restore += eligibility_files
|
||||
# restore to the device
|
||||
try:
|
||||
restore_files(files=files_to_restore, reboot=True, lockdown_client=device.ld)
|
||||
except Exception as e:
|
||||
print(traceback.format_exc())
|
||||
finally:
|
||||
input("Press Enter to exit...")
|
||||
running = False
|
||||
elif page == get_apply_number(num_tweaks + 1) + 2:
|
||||
# reset mobilegestalt
|
||||
# restore to the device
|
||||
try:
|
||||
restore_files(files=[FileToRestore(
|
||||
contents=b"",
|
||||
restore_path="/var/containers/Shared/SystemGroup/systemgroup.com.apple.mobilegestaltcache/Library/Caches/",
|
||||
restore_name="com.apple.MobileGestalt.plist"
|
||||
)], reboot=True, lockdown_client=device.ld)
|
||||
except Exception as e:
|
||||
print(traceback.format_exc())
|
||||
finally:
|
||||
input("Press Enter to exit...")
|
||||
running = False
|
||||
elif page == 0:
|
||||
# exit the panel
|
||||
print("Goodbye!")
|
||||
running = False
|
||||
elif page == 42:
|
||||
# restore_file(fp=Path.joinpath(Path.cwd(), "telephony_test.png"), restore_path="/var/mobile/Library/Caches/TelephonyUI-9/", restore_name="en-0---white.png", reboot=True, lockdown_client=device.ld)
|
||||
restore_files(files=[FileToRestore(
|
||||
contents=b"",
|
||||
restore_path="/var/Managed Preferences/mobile/",
|
||||
restore_name="com.apple.purplebuddy.plist"
|
||||
)], reboot=True, lockdown_client=device.ld)
|
||||
else:
|
||||
tweak = list(tweaks.values())[page-1]
|
||||
if page > 0 and page <= num_tweaks and tweak.is_compatible(device.version):
|
||||
if tweak.edit_type == TweakModifyType.TEXT:
|
||||
# text input
|
||||
# for now it is just for set model, deal with a fix later
|
||||
print("\n\nSet Model Name")
|
||||
print("Leave blank to turn off custom name.\n")
|
||||
name = input("Enter Model Name: ")
|
||||
if name == "":
|
||||
tweak.set_enabled(False)
|
||||
else:
|
||||
tweak.set_value(name)
|
||||
elif tweak.edit_type == TweakModifyType.PICKER:
|
||||
# pick between values
|
||||
print("\n\nSelect a value.")
|
||||
print("If you do not know which to try, start with the first option.")
|
||||
values = tweak.value
|
||||
for option in range(len(values)):
|
||||
print_option(
|
||||
num=option+1,
|
||||
active=(tweak.enabled and tweak.get_selected_option() == option),
|
||||
message=str(values[option])
|
||||
)
|
||||
print_option(num=len(values)+1, active=(not tweak.enabled), message="Disable")
|
||||
picker_choice = int(input("Select option: "))
|
||||
if picker_choice > 0 and picker_choice <= len(values):
|
||||
tweak.set_selected_option(picker_choice-1)
|
||||
elif picker_choice == len(values)+1:
|
||||
tweak.set_enabled(False)
|
||||
else:
|
||||
tweak.toggle_enabled()
|
||||
else:
|
||||
print("No MobileGestalt file found!")
|
||||
print(f"Please place the file in \'{Path.cwd()}\' with the name \'com.apple.MobileGestalt.plist\'")
|
||||
print("Remember to make a backup of the file!!\n")
|
||||
print("1. Retry")
|
||||
print("2. Enter path\n")
|
||||
choice = int(input("Enter number: "))
|
||||
if choice == 2:
|
||||
new_path = input("Enter new path to file: ")
|
||||
gestalt_path = Path(new_path)
|
||||
12
compile.py
@ -6,10 +6,18 @@ args = [
|
||||
'main_app.py',
|
||||
# '--hidden-import=ipsw_parser',
|
||||
'--hidden-import=zeroconf',
|
||||
'--hidden-import=pyimg4',
|
||||
'--hidden-import=zeroconf._utils.ipaddress',
|
||||
'--hidden-import=zeroconf._handlers.answers',
|
||||
'--add-data=files/:./files',
|
||||
'--copy-metadata=pyimg4',
|
||||
'--onedir',
|
||||
'--name=CODENAME Nugget',
|
||||
'--name=Nugget',
|
||||
'--icon=nugget.ico'
|
||||
]
|
||||
|
||||
PyInstaller.__main__.run(args)
|
||||
if platform == "darwin":
|
||||
# add --windowed arg for macOS
|
||||
args.append('--windowed')
|
||||
|
||||
PyInstaller.__main__.run(args)
|
||||
|
||||
BIN
credits/LeminLimez.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
credits/big_nugget.png
Normal file
|
After Width: | Height: | Size: 125 KiB |
0
devicemanagement/__init__.py
Normal file
BIN
devicemanagement/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
devicemanagement/__pycache__/constants.cpython-312.pyc
Normal file
BIN
devicemanagement/__pycache__/data_singleton.cpython-312.pyc
Normal file
BIN
devicemanagement/__pycache__/device_manager.cpython-312.pyc
Normal file
94
devicemanagement/constants.py
Normal file
@ -0,0 +1,94 @@
|
||||
from enum import Enum
|
||||
from pymobiledevice3.lockdown import LockdownClient
|
||||
|
||||
class Device:
|
||||
def __init__(self, uuid: int, name: str, version: str, model: str, locale: str, ld: LockdownClient):
|
||||
self.uuid = uuid
|
||||
self.name = name
|
||||
self.version = version
|
||||
self.model = model
|
||||
self.locale = locale
|
||||
self.ld = ld
|
||||
|
||||
class Version:
|
||||
def __init__(self, major: int, minor: int = 0, patch: int = 0):
|
||||
self.major = major
|
||||
self.minor = minor
|
||||
self.patch = patch
|
||||
|
||||
def __init__(self, ver: str):
|
||||
nums: list[str] = ver.split(".")
|
||||
self.major = int(nums[0])
|
||||
self.minor = int(nums[1]) if len(nums) > 1 else 0
|
||||
self.patch = int(nums[2]) if len(nums) > 2 else 0
|
||||
|
||||
# Comparison Functions
|
||||
def compare_to(self, other) -> int:
|
||||
if self.major > other.major:
|
||||
return 1
|
||||
elif self.major < other.major:
|
||||
return -1
|
||||
if self.minor > other.minor:
|
||||
return 1
|
||||
elif self.minor < other.minor:
|
||||
return -1
|
||||
if self.patch > other.patch:
|
||||
return 1
|
||||
elif self.patch < other.patch:
|
||||
return -1
|
||||
return 0
|
||||
|
||||
def __gt__(self, other) -> bool:
|
||||
return self.compare_to(other) == 1
|
||||
def __ge__(self, other) -> bool:
|
||||
comp: int = self.compare_to(other)
|
||||
return comp == 0 or comp == 1
|
||||
|
||||
def __lt__(self, other) -> bool:
|
||||
return self.compare_to(other) == -1
|
||||
def __le__(self, other) -> bool:
|
||||
comp: int = self.compare_to(other)
|
||||
return comp == 0 or comp == -1
|
||||
|
||||
def __eq__(self, other) -> bool:
|
||||
return self.compare_to(other) == 0
|
||||
|
||||
class Tweak(Enum):
|
||||
StatusBar = 'Status Bar'
|
||||
SpringboardOptions = 'Springboard Options'
|
||||
InternalOptions = 'Internal Options'
|
||||
SkipSetup = 'Setup Options'
|
||||
|
||||
class FileLocation(Enum):
|
||||
# Control Center
|
||||
mute = "ControlCenter/ManagedPreferencesDomain/mobile/com.apple.control-center.MuteModule.plist"
|
||||
focus = "ControlCenter/ManagedPreferencesDomain/mobile/com.apple.FocusUIModule.plist"
|
||||
spoken = "ControlCenter/ManagedPreferencesDomain/mobile/com.apple.siri.SpokenNotificationsModule.plist"
|
||||
module_config = "ControlCenter/HomeDomain/Library/ControlCenter/ModuleConfiguration.plist"
|
||||
replay_kit_audio = "ControlCenter/ManagedPreferencesDomain/mobile/com.apple.replaykit.AudioConferenceControlCenterModule.plist"
|
||||
replay_kit_video = "ControlCenter/ManagedPreferencesDomain/mobile/com.apple.replaykit.VideoConferenceControlCenterModule.plist"
|
||||
|
||||
# Status Bar
|
||||
status_bar = "StatusBar/HomeDomain/Library/SpringBoard/statusBarOverrides"
|
||||
|
||||
# Springboard Options
|
||||
springboard = "SpringboardOptions/ManagedPreferencesDomain/mobile/com.apple.springboard.plist"
|
||||
footnote = "SpringboardOptions/ConfigProfileDomain/Library/ConfigurationProfiles/SharedDeviceConfiguration.plist"
|
||||
wifi = "SpringboardOptions/SystemPreferencesDomain/SystemConfiguration/com.apple.wifi.plist"
|
||||
uikit = "SpringboardOptions/ManagedPreferencesDomain/mobile/com.apple.UIKit.plist"
|
||||
accessibility = "SpringboardOptions/ManagedPreferencesDomain/mobile/com.apple.Accessibility.plist"
|
||||
wifi_debug = "SpringboardOptions/ManagedPreferencesDomain/mobile/com.apple.MobileWiFi.debug.plist"
|
||||
airdrop = "SpringboardOptions/ManagedPreferencesDomain/mobile/com.apple.sharingd.plist"
|
||||
|
||||
# Internal Options
|
||||
global_prefs = "InternalOptions/ManagedPreferencesDomain/mobile/hiddendotGlobalPreferences.plist"
|
||||
app_store = "InternalOptions/ManagedPreferencesDomain/mobile/com.apple.AppStore.plist"
|
||||
backboardd = "InternalOptions/ManagedPreferencesDomain/mobile/com.apple.backboardd.plist"
|
||||
core_motion = "InternalOptions/ManagedPreferencesDomain/mobile/com.apple.CoreMotion.plist"
|
||||
pasteboard = "InternalOptions/HomeDomain/Library/Preferences/com.apple.Pasteboard.plist"
|
||||
notes = "InternalOptions/ManagedPreferencesDomain/mobile/com.apple.mobilenotes.plist"
|
||||
maps = "InternalOptions/AppDomain-com.apple.Maps/Library/Preferences/com.apple.Maps.plist"
|
||||
weather = "InternalOptions/AppDomain-com.apple.weather/Library/Preferences/com.apple.weather.plist"
|
||||
|
||||
# Setup Options
|
||||
cloud_config = "SkipSetup/ConfigProfileDomain/Library/ConfigurationProfiles/CloudConfigurationDetails.plist"
|
||||
9
devicemanagement/data_singleton.py
Normal file
@ -0,0 +1,9 @@
|
||||
from pathlib import Path
|
||||
|
||||
from devicemanagement.constants import Device, Tweak
|
||||
|
||||
class DataSingleton:
|
||||
def __init__(self):
|
||||
self.current_device: Device
|
||||
self.device_available: bool = False
|
||||
self.gestalt_path = None
|
||||
182
devicemanagement/device_manager.py
Normal file
@ -0,0 +1,182 @@
|
||||
import traceback
|
||||
import plistlib
|
||||
from pathlib import Path
|
||||
|
||||
from PySide6.QtWidgets import QMessageBox
|
||||
|
||||
from pymobiledevice3 import usbmux
|
||||
from pymobiledevice3.lockdown import create_using_usbmux
|
||||
|
||||
from devicemanagement.constants import Device, Version
|
||||
from devicemanagement.data_singleton import DataSingleton
|
||||
|
||||
from tweaks.tweaks import tweaks, FeatureFlagTweak, EligibilityTweak
|
||||
from exploit.restore import restore_files, FileToRestore
|
||||
|
||||
def show_error_msg(txt: str):
|
||||
detailsBox = QMessageBox()
|
||||
detailsBox.setIcon(QMessageBox.Critical)
|
||||
detailsBox.setWindowTitle("Error!")
|
||||
detailsBox.setText(txt)
|
||||
detailsBox.setDetailedText(str(traceback.format_exc()))
|
||||
detailsBox.exec()
|
||||
|
||||
class DeviceManager:
|
||||
min_version: Version = Version("17")
|
||||
|
||||
## Class Functions
|
||||
def __init__(self):
|
||||
self.devices: list[Device] = []
|
||||
self.data_singleton = DataSingleton()
|
||||
self.current_device_index = 0
|
||||
self.apply_over_wifi = True
|
||||
|
||||
def get_devices(self):
|
||||
self.devices.clear()
|
||||
connected_devices = usbmux.list_devices()
|
||||
# Connect via usbmuxd
|
||||
for device in connected_devices:
|
||||
if self.apply_over_wifi or device.is_usb:
|
||||
try:
|
||||
ld = create_using_usbmux(serial=device.serial)
|
||||
vals = ld.all_values
|
||||
dev = Device(
|
||||
uuid=device.serial,
|
||||
name=vals['DeviceName'],
|
||||
version=vals['ProductVersion'],
|
||||
model=vals['ProductType'],
|
||||
locale=ld.locale,
|
||||
ld=ld
|
||||
)
|
||||
self.devices.append(dev)
|
||||
except Exception as e:
|
||||
print(f"ERROR with lockdown device with UUID {device.serial}")
|
||||
show_error_msg(type(e).__name__)
|
||||
|
||||
if len(connected_devices) > 0:
|
||||
self.set_current_device(index=0)
|
||||
else:
|
||||
self.set_current_device(index=None)
|
||||
|
||||
## CURRENT DEVICE
|
||||
def set_current_device(self, index: int = None):
|
||||
if index == None:
|
||||
self.data_singleton.current_device = None
|
||||
self.data_singleton.device_available = False
|
||||
self.data_singleton.gestalt_path = None
|
||||
self.current_device_index = 0
|
||||
else:
|
||||
self.data_singleton.current_device = self.devices[index]
|
||||
if Version(self.devices[index].version) < DeviceManager.min_version:
|
||||
self.data_singleton.device_available = False
|
||||
self.data_singleton.gestalt_path = None
|
||||
else:
|
||||
self.data_singleton.device_available = True
|
||||
self.current_device_index = index
|
||||
|
||||
def get_current_device_name(self) -> str:
|
||||
if self.data_singleton.current_device == None:
|
||||
return "No Device"
|
||||
else:
|
||||
return self.data_singleton.current_device.name
|
||||
|
||||
def get_current_device_version(self) -> str:
|
||||
if self.data_singleton.current_device == None:
|
||||
return ""
|
||||
else:
|
||||
return self.data_singleton.current_device.version
|
||||
|
||||
def get_current_device_uuid(self) -> str:
|
||||
if self.data_singleton.current_device == None:
|
||||
return ""
|
||||
else:
|
||||
return self.data_singleton.current_device.uuid
|
||||
|
||||
|
||||
## APPLYING OR REMOVING TWEAKS AND RESTORING
|
||||
def apply_changes(self, resetting: bool = False, update_label=lambda x: None):
|
||||
# set the tweaks and apply
|
||||
# first open the file in read mode
|
||||
update_label("Applying changes to files...")
|
||||
gestalt_plist = None
|
||||
if self.data_singleton.gestalt_path != None:
|
||||
with open(self.data_singleton.gestalt_path, 'rb') as in_fp:
|
||||
gestalt_plist = plistlib.load(in_fp)
|
||||
# create the other plists
|
||||
flag_plist: dict = {}
|
||||
eligibility_files = None
|
||||
|
||||
# set the plist keys
|
||||
if not resetting:
|
||||
for tweak_name in tweaks:
|
||||
tweak = tweaks[tweak_name]
|
||||
if isinstance(tweak, FeatureFlagTweak):
|
||||
flag_plist = tweak.apply_tweak(flag_plist)
|
||||
elif isinstance(tweak, EligibilityTweak):
|
||||
eligibility_files = tweak.apply_tweak()
|
||||
else:
|
||||
if gestalt_plist != None:
|
||||
gestalt_plist = tweak.apply_tweak(gestalt_plist)
|
||||
|
||||
# Generate backup
|
||||
update_label("Generating backup...")
|
||||
# create the restore file list
|
||||
files_to_restore = [
|
||||
FileToRestore(
|
||||
contents=plistlib.dumps(flag_plist),
|
||||
restore_path="/var/preferences/FeatureFlags/",
|
||||
restore_name="Global.plist"
|
||||
)
|
||||
]
|
||||
if gestalt_plist != None:
|
||||
files_to_restore.append(FileToRestore(
|
||||
contents=plistlib.dumps(gestalt_plist),
|
||||
restore_path="/var/containers/Shared/SystemGroup/systemgroup.com.apple.mobilegestaltcache/Library/Caches/",
|
||||
restore_name="com.apple.MobileGestalt.plist"
|
||||
))
|
||||
if eligibility_files:
|
||||
files_to_restore += eligibility_files
|
||||
|
||||
# restore to the device
|
||||
update_label("Restoring to device...")
|
||||
try:
|
||||
restore_files(files=files_to_restore, reboot=True, lockdown_client=self.data_singleton.current_device.ld)
|
||||
QMessageBox.information(None, "Success!", "All done! Your device will now restart.")
|
||||
update_label("Success!")
|
||||
except Exception as e:
|
||||
if "Find My" in str(e):
|
||||
detailsBox = QMessageBox()
|
||||
detailsBox.setIcon(QMessageBox.Critical)
|
||||
detailsBox.setWindowTitle("Error!")
|
||||
detailsBox.setText("Find My must be disabled in order to use this tool.")
|
||||
detailsBox.setDetailedText("Disable Find My from Settings (Settings -> [Your Name] -> Find My) and then try again.")
|
||||
detailsBox.exec()
|
||||
else:
|
||||
print(traceback.format_exc())
|
||||
update_label("Failed to restore")
|
||||
show_error_msg(type(e).__name__)
|
||||
|
||||
## RESETTING MOBILE GESTALT
|
||||
def reset_mobilegestalt(self, update_label=lambda x: None):
|
||||
# restore to the device
|
||||
update_label("Restoring to device...")
|
||||
try:
|
||||
restore_files(files=[FileToRestore(
|
||||
contents=b"",
|
||||
restore_path="/var/containers/Shared/SystemGroup/systemgroup.com.apple.mobilegestaltcache/Library/Caches/",
|
||||
restore_name="com.apple.MobileGestalt.plist"
|
||||
)], reboot=True, lockdown_client=self.data_singleton.current_device.ld)
|
||||
QMessageBox.information(None, "Success!", "All done! Your device will now restart.")
|
||||
update_label("Success!")
|
||||
except Exception as e:
|
||||
if "Find My" in str(e):
|
||||
detailsBox = QMessageBox()
|
||||
detailsBox.setIcon(QMessageBox.Critical)
|
||||
detailsBox.setWindowTitle("Error!")
|
||||
detailsBox.setText("Find My must be disabled in order to use this tool.")
|
||||
detailsBox.setDetailedText("Disable Find My from Settings (Settings -> [Your Name] -> Find My) and then try again.")
|
||||
detailsBox.exec()
|
||||
else:
|
||||
print(traceback.format_exc())
|
||||
update_label("Failed to restore")
|
||||
show_error_msg(type(e).__name__)
|
||||
@ -1 +1,33 @@
|
||||
from . import backup
|
||||
from tempfile import TemporaryDirectory
|
||||
from pathlib import Path
|
||||
|
||||
from pymobiledevice3.lockdown import create_using_usbmux
|
||||
from pymobiledevice3.services.mobilebackup2 import Mobilebackup2Service
|
||||
from pymobiledevice3.exceptions import PyMobileDevice3Exception
|
||||
from pymobiledevice3.services.diagnostics import DiagnosticsService
|
||||
from pymobiledevice3.lockdown import LockdownClient
|
||||
|
||||
from . import backup
|
||||
|
||||
def perform_restore(backup: backup.Backup, reboot: bool = False, lockdown_client: LockdownClient = None):
|
||||
try:
|
||||
with TemporaryDirectory() as backup_dir:
|
||||
backup.write_to_directory(Path(backup_dir))
|
||||
|
||||
if lockdown_client == None:
|
||||
lockdown_client = create_using_usbmux()
|
||||
with Mobilebackup2Service(lockdown_client) as mb:
|
||||
mb.restore(backup_dir, system=True, reboot=False, copy=False, source=".")
|
||||
except PyMobileDevice3Exception as e:
|
||||
if "Find My" in str(e):
|
||||
print("Find My must be disabled in order to use this tool.")
|
||||
print("Disable Find My from Settings (Settings -> [Your Name] -> Find My) and then try again.")
|
||||
raise e
|
||||
elif "crash_on_purpose" not in str(e):
|
||||
raise e
|
||||
else:
|
||||
if reboot and lockdown_client != None:
|
||||
print("Success! Rebooting your device...")
|
||||
with DiagnosticsService(lockdown_client) as diagnostics_service:
|
||||
diagnostics_service.restart()
|
||||
print("Remember to turn Find My back on!")
|
||||
@ -1,31 +1,113 @@
|
||||
from exploit import backup
|
||||
from pymobiledevice3.lockdown import create_using_usbmux
|
||||
from pymobiledevice3.services.mobilebackup2 import Mobilebackup2Service
|
||||
from tempfile import TemporaryDirectory
|
||||
from pathlib import Path
|
||||
from . import backup, perform_restore
|
||||
from pymobiledevice3.lockdown import LockdownClient
|
||||
|
||||
def restore_file(fp: str, restore_path: str, restore_name: str):
|
||||
class FileToRestore:
|
||||
def __init__(self, contents: str, restore_path: str, restore_name: str, owner: int = 501, group: int = 501):
|
||||
self.contents = contents
|
||||
self.restore_path = restore_path
|
||||
self.restore_name = restore_name
|
||||
self.owner = owner
|
||||
self.group = group
|
||||
|
||||
# files is a list of FileToRestore objects
|
||||
def restore_files(files: list, reboot: bool = False, lockdown_client: LockdownClient = None):
|
||||
# create the files to be backed up
|
||||
files_list = [
|
||||
backup.Directory("", "RootDomain"),
|
||||
backup.Directory("Library", "RootDomain"),
|
||||
backup.Directory("Library/Preferences", "RootDomain"),
|
||||
]
|
||||
# create the links
|
||||
for file_num in range(len(files)):
|
||||
files_list.append(backup.ConcreteFile(
|
||||
f"Library/Preferences/temp{file_num}",
|
||||
"RootDomain",
|
||||
owner=files[file_num].owner,
|
||||
group=files[file_num].group,
|
||||
contents=files[file_num].contents,
|
||||
inode=file_num
|
||||
))
|
||||
# add the file paths
|
||||
for file_num in range(len(files)):
|
||||
file = files[file_num]
|
||||
base_path = "/var/backup"
|
||||
# set it to work in the separate volumes (prevents a bootloop)
|
||||
if file.restore_path.startswith("/var/mobile/"):
|
||||
# required on iOS 17.0+ since /var/mobile is on a separate partition
|
||||
base_path = "/var/mobile/backup"
|
||||
elif file.restore_path.startswith("/private/var/mobile/"):
|
||||
base_path = "/private/var/mobile/backup"
|
||||
elif file.restore_path.startswith("/private/var/"):
|
||||
base_path = "/private/var/backup"
|
||||
files_list.append(backup.Directory(
|
||||
"",
|
||||
f"SysContainerDomain-../../../../../../../..{base_path}{file.restore_path}",
|
||||
owner=file.owner,
|
||||
group=file.group
|
||||
))
|
||||
files_list.append(backup.ConcreteFile(
|
||||
"",
|
||||
f"SysContainerDomain-../../../../../../../..{base_path}{file.restore_path}{file.restore_name}",
|
||||
owner=file.owner,
|
||||
group=file.group,
|
||||
contents=b"",
|
||||
inode=file_num
|
||||
))
|
||||
# break the hard links
|
||||
for file_num in range(len(files)):
|
||||
files_list.append(backup.ConcreteFile(
|
||||
"",
|
||||
f"SysContainerDomain-../../../../../../../../var/.backup.i/var/root/Library/Preferences/temp{file_num}",
|
||||
owner=501,
|
||||
group=501,
|
||||
contents=b"",
|
||||
)) # Break the hard link
|
||||
files_list.append(backup.ConcreteFile("", "SysContainerDomain-../../../../../../../.." + "/crash_on_purpose", contents=b""))
|
||||
|
||||
# create the backup
|
||||
back = backup.Backup(files=files_list)
|
||||
|
||||
perform_restore(backup=back, reboot=reboot, lockdown_client=lockdown_client)
|
||||
|
||||
|
||||
def restore_file(fp: str, restore_path: str, restore_name: str, reboot: bool = False, lockdown_client: LockdownClient = None):
|
||||
# open the file and read the contents
|
||||
contents = open(fp, "rb").read()
|
||||
|
||||
base_path = "/var/backup"
|
||||
if restore_path.startswith("/var/mobile/"):
|
||||
# required on iOS 17.0+ since /var/mobile is on a separate partition
|
||||
base_path = "/var/mobile/backup"
|
||||
|
||||
# create the backup
|
||||
back = backup.Backup(files=[
|
||||
backup.Directory("", "RootDomain", owner=501, group=501),
|
||||
backup.Directory("Library", "RootDomain", owner=501, group=501),
|
||||
backup.Directory("Library/Preferences", "RootDomain", owner=501, group=501),
|
||||
backup.ConcreteFile("Library/Preferences/Hello", "RootDomain", owner=501, group=501, contents=contents),
|
||||
backup.Directory("", f"SysContainerDomain-../../../../../../../../var/.backup.i{restore_path}", owner=501, group=501),
|
||||
backup.ConcreteFile("", f"SysContainerDomain-../../../../../../../../var/.backup.i{restore_path}{restore_name}", owner=501, group=501, contents=contents),
|
||||
backup.Directory("", "SysContainerDomain-../../../../../../../../var/.backup.i/var/root/Library/Preferences/Hello", owner=501, group=501),
|
||||
# backup.Directory("", "HomeDomain"),
|
||||
# backup.Directory("Library", "HomeDomain"),
|
||||
# backup.Directory("Library/Preferences", "HomeDomain"),
|
||||
# backup.ConcreteFile("Library/Preferences/temp", "HomeDomain", owner=501, group=501, contents=contents, inode=0),
|
||||
backup.Directory(
|
||||
"",
|
||||
f"HomeDomain-../../../../../../..{base_path}{restore_path}",
|
||||
owner=501,
|
||||
group=501
|
||||
),
|
||||
backup.ConcreteFile(
|
||||
"",
|
||||
f"HomeDomain-../../../../../../..{base_path}{restore_path}{restore_name}",
|
||||
owner=501,
|
||||
group=501,
|
||||
contents=contents#b"",
|
||||
# inode=0
|
||||
),
|
||||
# backup.ConcreteFile(
|
||||
# "",
|
||||
# "SysContainerDomain-../../../../../../../../var/.backup.i/var/root/Library/Preferences/temp",
|
||||
# owner=501,
|
||||
# group=501,
|
||||
# contents=b"",
|
||||
# ), # Break the hard link
|
||||
backup.ConcreteFile("", "SysContainerDomain-../../../../../../../.." + "/crash_on_purpose", contents=b""),
|
||||
])
|
||||
|
||||
# get a temporary dir to store the backup
|
||||
with TemporaryDirectory() as backup_dir:
|
||||
backup_dir_path = Path(backup_dir)
|
||||
back.write_to_directory(backup_dir_path)
|
||||
print(f"Backup written to {backup_dir}")
|
||||
input("Press Enter to continue...")
|
||||
|
||||
lockdown = create_using_usbmux()
|
||||
with Mobilebackup2Service(lockdown) as mb:
|
||||
mb.restore(backup_dir, system=True, reboot=False, copy=False, source=".")
|
||||
|
||||
perform_restore(backup=back, reboot=reboot, lockdown_client=lockdown_client)
|
||||
BIN
files/eligibility/Config.plist
Normal file
546
files/eligibility/eligibility.plist
Normal file
@ -0,0 +1,546 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>OS_ELIGIBILITY_DOMAIN_ALUMINUM</key>
|
||||
<dict>
|
||||
<key>os_eligibility_answer_source_t</key>
|
||||
<integer>1</integer>
|
||||
<key>os_eligibility_answer_t</key>
|
||||
<integer>2</integer>
|
||||
<key>status</key>
|
||||
<dict>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_BILLING</key>
|
||||
<integer>2</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_LOCATION</key>
|
||||
<integer>2</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_DEVICE_CLASS</key>
|
||||
<integer>2</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>OS_ELIGIBILITY_DOMAIN_ARGON</key>
|
||||
<dict>
|
||||
<key>os_eligibility_answer_source_t</key>
|
||||
<integer>4</integer>
|
||||
<key>os_eligibility_answer_t</key>
|
||||
<integer>4</integer>
|
||||
<key>status</key>
|
||||
<dict>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_BILLING</key>
|
||||
<integer>4</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_LOCATION</key>
|
||||
<integer>4</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_DEVICE_CLASS</key>
|
||||
<integer>4</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>OS_ELIGIBILITY_DOMAIN_BERYLLIUM</key>
|
||||
<dict>
|
||||
<key>os_eligibility_answer_source_t</key>
|
||||
<integer>1</integer>
|
||||
<key>os_eligibility_answer_t</key>
|
||||
<integer>2</integer>
|
||||
<key>status</key>
|
||||
<dict>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_BILLING</key>
|
||||
<integer>2</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_LOCATION</key>
|
||||
<integer>2</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_DEVICE_CLASS</key>
|
||||
<integer>3</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>OS_ELIGIBILITY_DOMAIN_BORON</key>
|
||||
<dict>
|
||||
<key>os_eligibility_answer_source_t</key>
|
||||
<integer>1</integer>
|
||||
<key>os_eligibility_answer_t</key>
|
||||
<integer>2</integer>
|
||||
<key>status</key>
|
||||
<dict>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_BILLING</key>
|
||||
<integer>2</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_LOCATION</key>
|
||||
<integer>2</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_DEVICE_CLASS</key>
|
||||
<integer>3</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>OS_ELIGIBILITY_DOMAIN_CARBON</key>
|
||||
<dict>
|
||||
<key>os_eligibility_answer_source_t</key>
|
||||
<integer>4</integer>
|
||||
<key>os_eligibility_answer_t</key>
|
||||
<integer>4</integer>
|
||||
<key>status</key>
|
||||
<dict>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_BILLING</key>
|
||||
<integer>4</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_LOCATION</key>
|
||||
<integer>4</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_DEVICE_CLASS</key>
|
||||
<integer>4</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>OS_ELIGIBILITY_DOMAIN_CHLORINE</key>
|
||||
<dict>
|
||||
<key>os_eligibility_answer_source_t</key>
|
||||
<integer>1</integer>
|
||||
<key>os_eligibility_answer_t</key>
|
||||
<integer>2</integer>
|
||||
<key>status</key>
|
||||
<dict>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_BILLING</key>
|
||||
<integer>2</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_DEVICE_CLASS</key>
|
||||
<integer>3</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_DEVICE_LOCALE</key>
|
||||
<integer>3</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>OS_ELIGIBILITY_DOMAIN_CHROMIUM</key>
|
||||
<dict>
|
||||
<key>os_eligibility_answer_source_t</key>
|
||||
<integer>1</integer>
|
||||
<key>os_eligibility_answer_t</key>
|
||||
<integer>2</integer>
|
||||
<key>status</key>
|
||||
<dict>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_BILLING</key>
|
||||
<integer>3</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_DEVICE_CLASS</key>
|
||||
<integer>2</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>OS_ELIGIBILITY_DOMAIN_COBALT</key>
|
||||
<dict>
|
||||
<key>os_eligibility_answer_source_t</key>
|
||||
<integer>1</integer>
|
||||
<key>os_eligibility_answer_t</key>
|
||||
<integer>4</integer>
|
||||
<key>status</key>
|
||||
<dict>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_BILLING</key>
|
||||
<integer>3</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_LOCATION</key>
|
||||
<integer>3</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>OS_ELIGIBILITY_DOMAIN_COPPER</key>
|
||||
<dict>
|
||||
<key>os_eligibility_answer_source_t</key>
|
||||
<integer>1</integer>
|
||||
<key>os_eligibility_answer_t</key>
|
||||
<integer>2</integer>
|
||||
<key>status</key>
|
||||
<dict/>
|
||||
</dict>
|
||||
<key>OS_ELIGIBILITY_DOMAIN_FLUORINE</key>
|
||||
<dict>
|
||||
<key>os_eligibility_answer_source_t</key>
|
||||
<integer>1</integer>
|
||||
<key>os_eligibility_answer_t</key>
|
||||
<integer>2</integer>
|
||||
<key>status</key>
|
||||
<dict>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_BILLING</key>
|
||||
<integer>3</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_DEVICE_CLASS</key>
|
||||
<integer>2</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>OS_ELIGIBILITY_DOMAIN_HELIUM</key>
|
||||
<dict>
|
||||
<key>os_eligibility_answer_source_t</key>
|
||||
<integer>4</integer>
|
||||
<key>os_eligibility_answer_t</key>
|
||||
<integer>4</integer>
|
||||
<key>status</key>
|
||||
<dict>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_BILLING</key>
|
||||
<integer>4</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_LOCATION</key>
|
||||
<integer>4</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_DEVICE_CLASS</key>
|
||||
<integer>4</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>OS_ELIGIBILITY_DOMAIN_HYDROGEN</key>
|
||||
<dict>
|
||||
<key>os_eligibility_answer_source_t</key>
|
||||
<integer>4</integer>
|
||||
<key>os_eligibility_answer_t</key>
|
||||
<integer>4</integer>
|
||||
<key>status</key>
|
||||
<dict>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_BILLING</key>
|
||||
<integer>4</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_LOCATION</key>
|
||||
<integer>4</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_DEVICE_CLASS</key>
|
||||
<integer>4</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>OS_ELIGIBILITY_DOMAIN_IRON</key>
|
||||
<dict>
|
||||
<key>os_eligibility_answer_source_t</key>
|
||||
<integer>1</integer>
|
||||
<key>os_eligibility_answer_t</key>
|
||||
<integer>2</integer>
|
||||
<key>status</key>
|
||||
<dict>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_BILLING</key>
|
||||
<integer>3</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_LOCATION</key>
|
||||
<integer>3</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_DEVICE_CLASS</key>
|
||||
<integer>2</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>OS_ELIGIBILITY_DOMAIN_KRYPTON</key>
|
||||
<dict>
|
||||
<key>context</key>
|
||||
<dict>
|
||||
<key>OS_ELIGIBILITY_CONTEXT_COUNTRY_BILLING</key>
|
||||
<string>US</string>
|
||||
</dict>
|
||||
<key>os_eligibility_answer_source_t</key>
|
||||
<integer>1</integer>
|
||||
<key>os_eligibility_answer_t</key>
|
||||
<integer>2</integer>
|
||||
<key>status</key>
|
||||
<dict>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_BILLING</key>
|
||||
<integer>2</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_LOCATION</key>
|
||||
<integer>2</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>OS_ELIGIBILITY_DOMAIN_LITHIUM</key>
|
||||
<dict>
|
||||
<key>os_eligibility_answer_source_t</key>
|
||||
<integer>4</integer>
|
||||
<key>os_eligibility_answer_t</key>
|
||||
<integer>4</integer>
|
||||
<key>status</key>
|
||||
<dict>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_BILLING</key>
|
||||
<integer>4</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_LOCATION</key>
|
||||
<integer>4</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_DEVICE_CLASS</key>
|
||||
<integer>4</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>OS_ELIGIBILITY_DOMAIN_LOTX</key>
|
||||
<dict>
|
||||
<key>os_eligibility_answer_source_t</key>
|
||||
<integer>1</integer>
|
||||
<key>os_eligibility_answer_t</key>
|
||||
<integer>2</integer>
|
||||
<key>status</key>
|
||||
<dict>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_LOCATION</key>
|
||||
<integer>2</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>OS_ELIGIBILITY_DOMAIN_MAGNESIUM</key>
|
||||
<dict>
|
||||
<key>os_eligibility_answer_source_t</key>
|
||||
<integer>1</integer>
|
||||
<key>os_eligibility_answer_t</key>
|
||||
<integer>2</integer>
|
||||
<key>status</key>
|
||||
<dict>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_BILLING</key>
|
||||
<integer>2</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_DEVICE_CLASS</key>
|
||||
<integer>2</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>OS_ELIGIBILITY_DOMAIN_MANGANESE</key>
|
||||
<dict>
|
||||
<key>os_eligibility_answer_source_t</key>
|
||||
<integer>1</integer>
|
||||
<key>os_eligibility_answer_t</key>
|
||||
<integer>2</integer>
|
||||
<key>status</key>
|
||||
<dict>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_BILLING</key>
|
||||
<integer>3</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_DEVICE_CLASS</key>
|
||||
<integer>2</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>OS_ELIGIBILITY_DOMAIN_NEON</key>
|
||||
<dict>
|
||||
<key>os_eligibility_answer_source_t</key>
|
||||
<integer>1</integer>
|
||||
<key>os_eligibility_answer_t</key>
|
||||
<integer>2</integer>
|
||||
<key>status</key>
|
||||
<dict>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_BILLING</key>
|
||||
<integer>3</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_LOCATION</key>
|
||||
<integer>3</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_DEVICE_CLASS</key>
|
||||
<integer>2</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>OS_ELIGIBILITY_DOMAIN_NICKEL</key>
|
||||
<dict>
|
||||
<key>os_eligibility_answer_source_t</key>
|
||||
<integer>1</integer>
|
||||
<key>os_eligibility_answer_t</key>
|
||||
<integer>2</integer>
|
||||
<key>status</key>
|
||||
<dict>
|
||||
<key>OS_ELIGIBILITY_INPUT_DEVICE_CLASS</key>
|
||||
<integer>2</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_DEVICE_LOCALE</key>
|
||||
<integer>3</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>OS_ELIGIBILITY_DOMAIN_NITROGEN</key>
|
||||
<dict>
|
||||
<key>os_eligibility_answer_source_t</key>
|
||||
<integer>1</integer>
|
||||
<key>os_eligibility_answer_t</key>
|
||||
<integer>2</integer>
|
||||
<key>status</key>
|
||||
<dict>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_BILLING</key>
|
||||
<integer>2</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_LOCATION</key>
|
||||
<integer>2</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_DEVICE_CLASS</key>
|
||||
<integer>2</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>OS_ELIGIBILITY_DOMAIN_OXYGEN</key>
|
||||
<dict>
|
||||
<key>os_eligibility_answer_source_t</key>
|
||||
<integer>1</integer>
|
||||
<key>os_eligibility_answer_t</key>
|
||||
<integer>2</integer>
|
||||
<key>status</key>
|
||||
<dict>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_BILLING</key>
|
||||
<integer>3</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_LOCATION</key>
|
||||
<integer>3</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_DEVICE_CLASS</key>
|
||||
<integer>2</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>OS_ELIGIBILITY_DOMAIN_PHOSPHORUS</key>
|
||||
<dict>
|
||||
<key>context</key>
|
||||
<dict>
|
||||
<key>OS_ELIGIBILITY_CONTEXT_COUNTRY_BILLING</key>
|
||||
<string>US</string>
|
||||
</dict>
|
||||
<key>os_eligibility_answer_source_t</key>
|
||||
<integer>4</integer>
|
||||
<key>os_eligibility_answer_t</key>
|
||||
<integer>4</integer>
|
||||
<key>status</key>
|
||||
<dict>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_BILLING</key>
|
||||
<integer>4</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_LOCATION</key>
|
||||
<integer>4</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_DEVICE_CLASS</key>
|
||||
<integer>4</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>OS_ELIGIBILITY_DOMAIN_PODCASTS_TRANSCRIPTS</key>
|
||||
<dict>
|
||||
<key>os_eligibility_answer_source_t</key>
|
||||
<integer>1</integer>
|
||||
<key>os_eligibility_answer_t</key>
|
||||
<integer>4</integer>
|
||||
<key>status</key>
|
||||
<dict>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_BILLING</key>
|
||||
<integer>3</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>OS_ELIGIBILITY_DOMAIN_POTASSIUM</key>
|
||||
<dict>
|
||||
<key>os_eligibility_answer_source_t</key>
|
||||
<integer>4</integer>
|
||||
<key>os_eligibility_answer_t</key>
|
||||
<integer>4</integer>
|
||||
<key>status</key>
|
||||
<dict>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_BILLING</key>
|
||||
<integer>4</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_LOCATION</key>
|
||||
<integer>4</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_DEVICE_CLASS</key>
|
||||
<integer>4</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>OS_ELIGIBILITY_DOMAIN_SCANDIUM</key>
|
||||
<dict>
|
||||
<key>os_eligibility_answer_source_t</key>
|
||||
<integer>1</integer>
|
||||
<key>os_eligibility_answer_t</key>
|
||||
<integer>2</integer>
|
||||
<key>status</key>
|
||||
<dict>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_BILLING</key>
|
||||
<integer>2</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_LOCATION</key>
|
||||
<integer>2</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_DEVICE_CLASS</key>
|
||||
<integer>3</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>OS_ELIGIBILITY_DOMAIN_SEARCH_MARKETPLACES</key>
|
||||
<dict>
|
||||
<key>os_eligibility_answer_source_t</key>
|
||||
<integer>1</integer>
|
||||
<key>os_eligibility_answer_t</key>
|
||||
<integer>2</integer>
|
||||
<key>status</key>
|
||||
<dict>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_BILLING</key>
|
||||
<integer>2</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_LOCATION</key>
|
||||
<integer>2</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_DEVICE_CLASS</key>
|
||||
<integer>3</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>OS_ELIGIBILITY_DOMAIN_SILICON</key>
|
||||
<dict>
|
||||
<key>os_eligibility_answer_source_t</key>
|
||||
<integer>1</integer>
|
||||
<key>os_eligibility_answer_t</key>
|
||||
<integer>2</integer>
|
||||
<key>status</key>
|
||||
<dict>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_BILLING</key>
|
||||
<integer>2</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_DEVICE_CLASS</key>
|
||||
<integer>2</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>OS_ELIGIBILITY_DOMAIN_SODIUM</key>
|
||||
<dict>
|
||||
<key>os_eligibility_answer_source_t</key>
|
||||
<integer>1</integer>
|
||||
<key>os_eligibility_answer_t</key>
|
||||
<integer>2</integer>
|
||||
<key>status</key>
|
||||
<dict>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_BILLING</key>
|
||||
<integer>2</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_LOCATION</key>
|
||||
<integer>2</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_DEVICE_CLASS</key>
|
||||
<integer>2</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>OS_ELIGIBILITY_DOMAIN_SULFUR</key>
|
||||
<dict>
|
||||
<key>context</key>
|
||||
<dict>
|
||||
<key>OS_ELIGIBILITY_CONTEXT_COUNTRY_BILLING</key>
|
||||
<string>US</string>
|
||||
</dict>
|
||||
<key>os_eligibility_answer_source_t</key>
|
||||
<integer>1</integer>
|
||||
<key>os_eligibility_answer_t</key>
|
||||
<integer>2</integer>
|
||||
<key>status</key>
|
||||
<dict>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_BILLING</key>
|
||||
<integer>2</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_DEVICE_CLASS</key>
|
||||
<integer>3</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>OS_ELIGIBILITY_DOMAIN_SWIFT_ASSIST</key>
|
||||
<dict>
|
||||
<key>os_eligibility_answer_source_t</key>
|
||||
<integer>1</integer>
|
||||
<key>os_eligibility_answer_t</key>
|
||||
<integer>2</integer>
|
||||
<key>status</key>
|
||||
<dict>
|
||||
<key>OS_ELIGIBILITY_INPUT_DEVICE_CLASS</key>
|
||||
<integer>2</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_DEVICE_REGION_CODE</key>
|
||||
<integer>3</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_EXTERNAL_BOOT_DRIVE</key>
|
||||
<integer>3</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>OS_ELIGIBILITY_DOMAIN_TITANIUM</key>
|
||||
<dict>
|
||||
<key>os_eligibility_answer_source_t</key>
|
||||
<integer>1</integer>
|
||||
<key>os_eligibility_answer_t</key>
|
||||
<integer>2</integer>
|
||||
<key>status</key>
|
||||
<dict>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_BILLING</key>
|
||||
<integer>2</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_LOCATION</key>
|
||||
<integer>2</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_DEVICE_CLASS</key>
|
||||
<integer>3</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>OS_ELIGIBILITY_DOMAIN_VANADIUM</key>
|
||||
<dict>
|
||||
<key>os_eligibility_answer_source_t</key>
|
||||
<integer>1</integer>
|
||||
<key>os_eligibility_answer_t</key>
|
||||
<integer>2</integer>
|
||||
<key>status</key>
|
||||
<dict>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_BILLING</key>
|
||||
<integer>2</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_LOCATION</key>
|
||||
<integer>2</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_DEVICE_CLASS</key>
|
||||
<integer>3</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>OS_ELIGIBILITY_DOMAIN_XCODE_LLM</key>
|
||||
<dict>
|
||||
<key>os_eligibility_answer_source_t</key>
|
||||
<integer>1</integer>
|
||||
<key>os_eligibility_answer_t</key>
|
||||
<integer>2</integer>
|
||||
<key>status</key>
|
||||
<dict>
|
||||
<key>OS_ELIGIBILITY_INPUT_DEVICE_CLASS</key>
|
||||
<integer>2</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_DEVICE_REGION_CODE</key>
|
||||
<integer>3</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_EXTERNAL_BOOT_DRIVE</key>
|
||||
<integer>3</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>OS_ELIGIBILITY_DOMAIN_ZINC</key>
|
||||
<dict>
|
||||
<key>os_eligibility_answer_source_t</key>
|
||||
<integer>1</integer>
|
||||
<key>os_eligibility_answer_t</key>
|
||||
<integer>2</integer>
|
||||
<key>status</key>
|
||||
<dict>
|
||||
<key>OS_ELIGIBILITY_INPUT_COUNTRY_BILLING</key>
|
||||
<integer>2</integer>
|
||||
<key>OS_ELIGIBILITY_INPUT_DEVICE_LANGUAGE</key>
|
||||
<integer>2</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
0
gui/__init__.py
Normal file
BIN
gui/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
gui/__pycache__/gestalt_dialog.cpython-312.pyc
Normal file
BIN
gui/__pycache__/main_window.cpython-312.pyc
Normal file
BIN
gui/__pycache__/ui_mainwindow.cpython-312.pyc
Normal file
27
gui/gestalt_dialog.py
Normal file
@ -0,0 +1,27 @@
|
||||
from PySide6.QtWidgets import QDialog, QDialogButtonBox, QLabel, QVBoxLayout
|
||||
|
||||
class GestaltDialog(QDialog):
|
||||
def __init__(self, device_manager, gestalt_label, selected_file, parent=None):
|
||||
super().__init__(parent)
|
||||
self.device_manager = device_manager
|
||||
self.gestalt_label = gestalt_label
|
||||
self.selected_file = selected_file
|
||||
|
||||
QBtn = (
|
||||
QDialogButtonBox.Ok | QDialogButtonBox.Cancel
|
||||
)
|
||||
|
||||
self.buttonBox = QDialogButtonBox(QBtn)
|
||||
self.buttonBox.accepted.connect(self.accept)
|
||||
self.buttonBox.rejected.connect(self.reject)
|
||||
|
||||
layout = QVBoxLayout()
|
||||
message = QLabel("The gestalt file looks like it was made for a different device.\nAre you sure you want to use this one?")
|
||||
layout.addWidget(message)
|
||||
layout.addWidget(self.buttonBox)
|
||||
self.setLayout(layout)
|
||||
|
||||
def accept(self):
|
||||
self.device_manager.data_singleton.gestalt_path = self.selected_file
|
||||
self.gestalt_label.setText(self.selected_file)
|
||||
super().accept()
|
||||
413
gui/main_window.py
Normal file
@ -0,0 +1,413 @@
|
||||
from PySide6 import QtCore, QtWidgets
|
||||
from enum import Enum
|
||||
import webbrowser
|
||||
import plistlib
|
||||
|
||||
from pymobiledevice3.lockdown import create_using_usbmux
|
||||
|
||||
from qt.ui_mainwindow import Ui_Nugget
|
||||
|
||||
from devicemanagement.constants import Version
|
||||
from devicemanagement.device_manager import DeviceManager
|
||||
|
||||
from gui.gestalt_dialog import GestaltDialog
|
||||
|
||||
from tweaks.tweaks import tweaks
|
||||
|
||||
class Page(Enum):
|
||||
Home = 0
|
||||
Explore = 1
|
||||
LocSim = 2
|
||||
CustomOperations = 5
|
||||
Themes = 3
|
||||
Gestalt = 4
|
||||
Settings = 6
|
||||
FeatureFlags = 7
|
||||
EUEnabler = 8
|
||||
Apply = 9
|
||||
|
||||
class MainWindow(QtWidgets.QMainWindow):
|
||||
def __init__(self, device_manager: DeviceManager):
|
||||
super(MainWindow, self).__init__()
|
||||
self.device_manager = device_manager
|
||||
self.ui = Ui_Nugget()
|
||||
self.ui.setupUi(self)
|
||||
self.show_uuid = False
|
||||
self.loadSettings()
|
||||
|
||||
## DEVICE BAR
|
||||
self.refresh_devices()
|
||||
|
||||
self.ui.refreshBtn.clicked.connect(self.refresh_devices)
|
||||
self.ui.devicePicker.currentIndexChanged.connect(self.change_selected_device)
|
||||
|
||||
## SIDE BAR ACTIONS
|
||||
self.ui.homePageBtn.clicked.connect(self.on_homePageBtn_clicked)
|
||||
self.ui.gestaltPageBtn.clicked.connect(self.on_gestaltPageBtn_clicked)
|
||||
self.ui.featureFlagsPageBtn.clicked.connect(self.on_featureFlagsPageBtn_clicked)
|
||||
self.ui.euEnablerPageBtn.clicked.connect(self.on_euEnablerPageBtn_clicked)
|
||||
self.ui.applyPageBtn.clicked.connect(self.on_applyPageBtn_clicked)
|
||||
self.ui.settingsPageBtn.clicked.connect(self.on_settingsPageBtn_clicked)
|
||||
|
||||
## HOME PAGE ACTIONS
|
||||
self.ui.phoneVersionLbl.linkActivated.connect(self.toggle_version_label)
|
||||
|
||||
## HOME PAGE LINKS
|
||||
self.ui.bigNuggetBtn.clicked.connect(self.on_bigNuggetBtn_clicked)
|
||||
|
||||
# self.ui.leminGitHubBtn.clicked.connect(self.on_leminGitHubBtn_clicked)
|
||||
self.ui.leminTwitterBtn.clicked.connect(self.on_leminTwitterBtn_clicked)
|
||||
self.ui.leminKoFiBtn.clicked.connect(self.on_leminKoFiBtn_clicked)
|
||||
|
||||
self.ui.jjtechBtn.clicked.connect(self.on_jjtechBtn_clicked)
|
||||
self.ui.disfordottieBtn.clicked.connect(self.on_disfordottieBtn_clicked)
|
||||
self.ui.lrdsnowBtn.clicked.connect(self.on_lrdsnowBtn_clicked)
|
||||
|
||||
self.ui.libiBtn.clicked.connect(self.on_libiBtn_clicked)
|
||||
self.ui.qtBtn.clicked.connect(self.on_qtBtn_clicked)
|
||||
|
||||
self.ui.discordBtn.clicked.connect(self.on_discordBtn_clicked)
|
||||
|
||||
## EU ENABLER PAGE ACTIONS
|
||||
self.ui.euEnablerEnabledChk.toggled.connect(self.on_euEnablerEnabledChk_toggled)
|
||||
self.ui.methodChoiceDrp.activated.connect(self.on_methodChoiceDrp_activated)
|
||||
self.ui.regionCodeTxt.textEdited.connect(self.on_regionCodeTxt_textEdited)
|
||||
|
||||
## FEATURE FLAGS PAGE
|
||||
self.ui.clockAnimChk.toggled.connect(self.on_clockAnimChk_toggled)
|
||||
self.ui.lockscreenChk.toggled.connect(self.on_lockscreenChk_clicked)
|
||||
self.ui.photosChk.toggled.connect(self.on_photosChk_clicked)
|
||||
self.ui.aiChk.toggled.connect(self.on_aiChk_clicked)
|
||||
|
||||
## APPLY PAGE ACTIONS
|
||||
self.ui.applyTweaksBtn.clicked.connect(self.on_applyPageBtn_clicked)
|
||||
self.ui.removeTweaksBtn.clicked.connect(self.on_removeTweaksBtn_clicked)
|
||||
self.ui.chooseGestaltBtn.clicked.connect(self.on_chooseGestaltBtn_clicked)
|
||||
self.ui.resetGestaltBtn.clicked.connect(self.on_resetGestaltBtn_clicked)
|
||||
|
||||
## MOBILE GESTALT PAGE ACTIONS
|
||||
self.ui.dynamicIslandDrp.activated.connect(self.on_dynamicIslandDrp_activated)
|
||||
self.ui.modelNameChk.toggled.connect(self.on_modelNameChk_clicked)
|
||||
self.ui.modelNameTxt.textEdited.connect(self.on_modelNameTxt_textEdited)
|
||||
|
||||
self.ui.bootChimeChk.clicked.connect(self.on_bootChimeChk_clicked)
|
||||
self.ui.chargeLimitChk.clicked.connect(self.on_chargeLimitChk_clicked)
|
||||
self.ui.tapToWakeChk.clicked.connect(self.on_tapToWakeChk_clicked)
|
||||
self.ui.iphone16SettingsChk.clicked.connect(self.on_iphone16SettingsChk_clicked)
|
||||
self.ui.parallaxChk.clicked.connect(self.on_parallaxChk_clicked)
|
||||
self.ui.stageManagerChk.clicked.connect(self.on_stageManagerChk_clicked)
|
||||
self.ui.ipadAppsChk.clicked.connect(self.on_ipadAppsChk_clicked)
|
||||
self.ui.shutterChk.clicked.connect(self.on_shutterChk_clicked)
|
||||
self.ui.pencilChk.clicked.connect(self.on_pencilChk_clicked)
|
||||
self.ui.actionButtonChk.clicked.connect(self.on_actionButtonChk_clicked)
|
||||
|
||||
self.ui.internalInstallChk.clicked.connect(self.on_internalInstallChk_clicked)
|
||||
self.ui.internalStorageChk.clicked.connect(self.on_internalStorageChk_clicked)
|
||||
self.ui.collisionSOSChk.clicked.connect(self.on_collisionSOSChk_clicked)
|
||||
self.ui.aodChk.clicked.connect(self.on_aodChk_clicked)
|
||||
self.ui.sleepApneaChk.clicked.connect(self.on_sleepApneaChk_clicked)
|
||||
|
||||
|
||||
## GENERAL INTERFACE FUNCTIONS
|
||||
def updateInterfaceForNewDevice(self):
|
||||
# update the home page
|
||||
self.updatePhoneInfo()
|
||||
|
||||
|
||||
## DEVICE BAR FUNCTIONS
|
||||
@QtCore.Slot()
|
||||
def refresh_devices(self):
|
||||
# get the devices
|
||||
self.device_manager.get_devices()
|
||||
# clear the picker
|
||||
self.ui.devicePicker.clear()
|
||||
self.ui.restoreProgressBar.hide()
|
||||
if len(self.device_manager.devices) == 0:
|
||||
self.ui.devicePicker.setEnabled(False)
|
||||
self.ui.devicePicker.addItem('None')
|
||||
self.ui.pages.setCurrentIndex(Page.Home.value)
|
||||
self.ui.homePageBtn.setChecked(True)
|
||||
|
||||
# hide all pages
|
||||
self.ui.explorePageBtn.hide()
|
||||
self.ui.customOperationsPageBtn.hide()
|
||||
self.ui.locSimPageBtn.hide()
|
||||
self.ui.sidebarDiv1.hide()
|
||||
self.ui.gestaltPageBtn.hide()
|
||||
self.ui.featureFlagsPageBtn.hide()
|
||||
self.ui.euEnablerPageBtn.hide()
|
||||
self.ui.internalOptionsPageBtn.hide()
|
||||
self.ui.sidebarDiv2.hide()
|
||||
self.ui.applyPageBtn.hide()
|
||||
else:
|
||||
self.ui.devicePicker.setEnabled(True)
|
||||
# populate the ComboBox with device names
|
||||
for device in self.device_manager.devices:
|
||||
self.ui.devicePicker.addItem(device.name)
|
||||
|
||||
# show all pages
|
||||
self.ui.explorePageBtn.hide()
|
||||
self.ui.customOperationsPageBtn.hide()
|
||||
self.ui.locSimPageBtn.hide()
|
||||
self.ui.sidebarDiv1.show()
|
||||
self.ui.gestaltPageBtn.show()
|
||||
# self.ui.featureFlagsPageBtn.show()
|
||||
self.ui.euEnablerPageBtn.show()
|
||||
self.ui.internalOptionsPageBtn.hide()
|
||||
self.ui.sidebarDiv2.show()
|
||||
self.ui.applyPageBtn.show()
|
||||
self.ui.gestaltPageContent.setDisabled(False)
|
||||
self.ui.featureFlagsPageContent.setDisabled(False)
|
||||
|
||||
# update the selected device
|
||||
self.ui.devicePicker.setCurrentIndex(0)
|
||||
self.change_selected_device(0)
|
||||
|
||||
# update the interface
|
||||
self.updateInterfaceForNewDevice()
|
||||
|
||||
def change_selected_device(self, index):
|
||||
if len(self.device_manager.devices) > 0:
|
||||
self.device_manager.set_current_device(index=index)
|
||||
# hide options that are for newer versions
|
||||
# remove the new dynamic island options
|
||||
try:
|
||||
self.ui.dynamicIslandDrp.removeItem(6)
|
||||
self.ui.dynamicIslandDrp.removeItem(5)
|
||||
except:
|
||||
pass
|
||||
if Version(self.device_manager.data_singleton.current_device.version) >= Version("18.0"):
|
||||
self.ui.aodChk.show()
|
||||
self.ui.sleepApneaChk.show()
|
||||
self.ui.featureFlagsPageBtn.show()
|
||||
# show the other dynamic island options
|
||||
self.ui.dynamicIslandDrp.addItem("2622 (iPhone 16 Pro Dynamic Island)")
|
||||
self.ui.dynamicIslandDrp.addItem("2868 (iPhone 16 Pro Max Dynamic Island)")
|
||||
else:
|
||||
self.ui.aodChk.hide()
|
||||
self.ui.sleepApneaChk.hide()
|
||||
self.ui.featureFlagsPageBtn.hide()
|
||||
else:
|
||||
self.device_manager.set_current_device(index=None)
|
||||
self.ui.featureFlagsPageBtn.hide()
|
||||
|
||||
def loadSettings(self):
|
||||
self.settings = QtCore.QSettings()
|
||||
try:
|
||||
# load the settings
|
||||
apply_over_wifi = self.settings.value("apply_over_wifi", True, type=bool)
|
||||
self.ui.allowWifiApplyingChk.setChecked(apply_over_wifi)
|
||||
self.device_manager.apply_over_wifi = apply_over_wifi
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
## SIDE BAR FUNCTIONS
|
||||
def on_homePageBtn_clicked(self):
|
||||
self.ui.pages.setCurrentIndex(Page.Home.value)
|
||||
|
||||
def on_gestaltPageBtn_clicked(self):
|
||||
self.ui.pages.setCurrentIndex(Page.Gestalt.value)
|
||||
|
||||
def on_featureFlagsPageBtn_clicked(self):
|
||||
self.ui.pages.setCurrentIndex(Page.FeatureFlags.value)
|
||||
|
||||
def on_euEnablerPageBtn_clicked(self):
|
||||
self.ui.pages.setCurrentIndex(Page.EUEnabler.value)
|
||||
|
||||
def on_applyPageBtn_clicked(self):
|
||||
self.ui.pages.setCurrentIndex(Page.Apply.value)
|
||||
|
||||
def on_settingsPageBtn_clicked(self):
|
||||
self.ui.pages.setCurrentIndex(Page.Settings.value)
|
||||
|
||||
def update_side_btn_color(self, btn: QtWidgets.QToolButton, toggled: bool):
|
||||
if toggled:
|
||||
btn.setStyleSheet("QToolButton {\ncolor: #00FF00;\n}")
|
||||
else:
|
||||
btn.setStyleSheet("")
|
||||
|
||||
|
||||
## HOME PAGE
|
||||
def updatePhoneInfo(self):
|
||||
# name label
|
||||
self.ui.phoneNameLbl.setText(self.device_manager.get_current_device_name())
|
||||
# version label
|
||||
ver = self.device_manager.get_current_device_version()
|
||||
self.show_uuid = False
|
||||
if ver != "":
|
||||
self.show_version_text(version=ver)
|
||||
else:
|
||||
self.ui.phoneVersionLbl.setText("Please connect a device.")
|
||||
|
||||
def toggle_version_label(self):
|
||||
if self.show_uuid:
|
||||
self.show_uuid = False
|
||||
ver = self.device_manager.get_current_device_version()
|
||||
if ver != "":
|
||||
self.show_version_text(version=ver)
|
||||
else:
|
||||
self.show_uuid = True
|
||||
uuid = self.device_manager.get_current_device_uuid()
|
||||
if uuid != "":
|
||||
self.ui.phoneVersionLbl.setText(f"<a style=\"text-decoration:none; color: white\" href=\"#\">{uuid}</a>")
|
||||
|
||||
def show_version_text(self, version: str):
|
||||
parsed_ver: Version = Version(version)
|
||||
support_str: str = "<span style=\"color: #32d74b;\">Supported!</span></a>"
|
||||
if parsed_ver < DeviceManager.min_version:
|
||||
support_str = "<span style=\"color: #ff0000;\">Not Supported.</span></a>"
|
||||
self.ui.phoneVersionLbl.setText(f"<a style=\"text-decoration:none; color: white;\" href=\"#\">iOS {version} {support_str}")
|
||||
|
||||
## HOME PAGE LINKS
|
||||
def on_bigMilkBtn_clicked(self):
|
||||
webbrowser.open_new_tab("https://cowabun.ga")
|
||||
|
||||
def on_leminGitHubBtn_clicked(self):
|
||||
webbrowser.open_new_tab("https://github.com/leminlimez")
|
||||
def on_leminTwitterBtn_clicked(self):
|
||||
webbrowser.open_new_tab("https://twitter.com/LeminLimez")
|
||||
def on_leminKoFiBtn_clicked(self):
|
||||
webbrowser.open_new_tab("https://ko-fi.com/leminlimez")
|
||||
|
||||
def on_jjtechBtn_clicked(self):
|
||||
webbrowser.open_new_tab("https://github.com/JJTech0130/TrollRestore")
|
||||
def on_disfordottieBtn_clicked(self):
|
||||
webbrowser.open_new_tab("https://twitter.com/disfordottie")
|
||||
def on_lrdsnowBtn_clicked(self):
|
||||
webbrowser.open_new_tab("https://github.com/Lrdsnow/EUEnabler")
|
||||
|
||||
def on_libiBtn_clicked(self):
|
||||
webbrowser.open_new_tab("https://github.com/doronz88/pymobiledevice3")
|
||||
def on_qtBtn_clicked(self):
|
||||
webbrowser.open_new_tab("https://www.qt.io/product/development-tools")
|
||||
|
||||
def on_discordBtn_clicked(self):
|
||||
webbrowser.open_new_tab("https://discord.gg/MN8JgqSAqT")
|
||||
def on_bigNuggetBtn_clicked(self):
|
||||
webbrowser.open_new_tab("https://cowabun.ga")
|
||||
|
||||
|
||||
## MOBILE GESTALT PAGE
|
||||
def on_dynamicIslandDrp_activated(self, index: int):
|
||||
if index == 0:
|
||||
tweaks["DynamicIsland"].set_enabled(False)
|
||||
else:
|
||||
tweaks["DynamicIsland"].set_selected_option(index - 1)
|
||||
|
||||
def on_modelNameChk_clicked(self, checked: bool):
|
||||
tweaks["ModelName"].set_enabled(checked)
|
||||
def on_modelNameTxt_textEdited(self, text: str):
|
||||
tweaks["ModelName"].set_value(text, toggle_enabled=False)
|
||||
|
||||
def on_bootChimeChk_clicked(self, checked: bool):
|
||||
tweaks["BootChime"].set_enabled(checked)
|
||||
def on_chargeLimitChk_clicked(self, checked: bool):
|
||||
tweaks["ChargeLimit"].set_enabled(checked)
|
||||
def on_tapToWakeChk_clicked(self, checked: bool):
|
||||
tweaks["TapToWake"].set_enabled(checked)
|
||||
def on_iphone16SettingsChk_clicked(self, checked: bool):
|
||||
tweaks["CameraButton"].set_enabled(checked)
|
||||
def on_parallaxChk_clicked(self, checked: bool):
|
||||
tweaks["Parallax"].set_enabled(checked)
|
||||
|
||||
def on_stageManagerChk_clicked(self, checked: bool):
|
||||
tweaks["StageManager"].set_enabled(checked)
|
||||
def on_ipadAppsChk_clicked(self, checked: bool):
|
||||
tweaks["iPadApps"].set_enabled(checked)
|
||||
def on_shutterChk_clicked(self, checked: bool):
|
||||
# TODO: allow the user to select the region
|
||||
tweaks["Shutter"].set_enabled(checked)
|
||||
def on_pencilChk_clicked(self, checked: bool):
|
||||
tweaks["Pencil"].set_enabled(checked)
|
||||
def on_actionButtonChk_clicked(self, checked: bool):
|
||||
tweaks["ActionButton"].set_enabled(checked)
|
||||
|
||||
def on_internalInstallChk_clicked(self, checked: bool):
|
||||
tweaks["InternalInstall"].set_enabled(checked)
|
||||
def on_internalStorageChk_clicked(self, checked: bool):
|
||||
tweaks["InternalStorage"].set_enabled(checked)
|
||||
|
||||
def on_collisionSOSChk_clicked(self, checked: bool):
|
||||
tweaks["CollisionSOS"].set_enabled(checked)
|
||||
def on_aodChk_clicked(self, checked: bool):
|
||||
tweaks["AOD"].set_enabled(checked)
|
||||
def on_sleepApneaChk_clicked(self, checked: bool):
|
||||
tweaks["SleepApnea"].set_enabled(checked)
|
||||
|
||||
|
||||
## FEATURE FLAGS PAGE
|
||||
def on_clockAnimChk_toggled(self, checked: bool):
|
||||
tweaks["ClockAnim"].set_enabled(checked)
|
||||
def on_lockscreenChk_clicked(self, checked: bool):
|
||||
tweaks["Lockscreen"].set_enabled(checked)
|
||||
|
||||
def on_photosChk_clicked(self, checked: bool):
|
||||
tweaks["PhotoUI"].set_enabled(checked)
|
||||
def on_aiChk_clicked(self, checked: bool):
|
||||
tweaks["AI"].set_enabled(checked)
|
||||
|
||||
|
||||
## EU ENABLER PAGE
|
||||
def on_euEnablerEnabledChk_toggled(self, checked: bool):
|
||||
tweaks["EUEnabler"].set_enabled(checked)
|
||||
self.ui.euEnablerPageContent.setDisabled(not checked)
|
||||
def on_methodChoiceDrp_activated(self, index: int):
|
||||
tweaks["EUEnabler"].set_selected_option(index)
|
||||
def on_regionCodeTxt_textEdited(self, text: str):
|
||||
tweaks["EUEnabler"].set_region_code(text)
|
||||
|
||||
|
||||
## SETTINGS PAGE
|
||||
def on_allowWifiApplyingChk_toggled(self, checked: bool):
|
||||
self.device_manager.apply_over_wifi = checked
|
||||
# save the setting
|
||||
self.settings.setValue("apply_over_wifi", checked)
|
||||
|
||||
|
||||
## APPLY PAGE
|
||||
def on_chooseGestaltBtn_clicked(self):
|
||||
selected_file, _ = QtWidgets.QFileDialog.getOpenFileName(self, "Select Mobile Gestalt File", "", "Plist Files (*.plist)", options=QtWidgets.QFileDialog.ReadOnly)
|
||||
if selected_file == "" or selected_file == None:
|
||||
self.device_manager.data_singleton.gestalt_path = None
|
||||
self.ui.gestaltLocationLbl.setText("None")
|
||||
else:
|
||||
# verify that the gestalt is correct and compatible
|
||||
with open(selected_file, 'rb') as in_fp:
|
||||
gestalt_plist = plistlib.load(in_fp)
|
||||
if not "CacheExtra" in gestalt_plist:
|
||||
detailsBox = QtWidgets.QMessageBox()
|
||||
detailsBox.setIcon(QtWidgets.QMessageBox.Critical)
|
||||
detailsBox.setWindowTitle("Error!")
|
||||
detailsBox.setText("The file is not a mobile gestalt file!")
|
||||
detailsBox.exec()
|
||||
return
|
||||
if (
|
||||
not "qNNddlUK+B/YlooNoymwgA" in gestalt_plist["CacheExtra"]
|
||||
or gestalt_plist["CacheExtra"]["qNNddlUK+B/YlooNoymwgA"] != self.device_manager.data_singleton.current_device.version
|
||||
or gestalt_plist["CacheExtra"]["0+nc/Udy4WNG8S+Q7a/s1A"] != self.device_manager.data_singleton.current_device.model
|
||||
or not "0+nc/Udy4WNG8S+Q7a/s1A" in gestalt_plist["CacheExtra"]
|
||||
):
|
||||
dialog = GestaltDialog(
|
||||
device_manager=self.device_manager,
|
||||
gestalt_label=self.ui.gestaltLocationLbl,
|
||||
selected_file=selected_file
|
||||
)
|
||||
dialog.exec()
|
||||
self.device_manager.data_singleton.gestalt_path = selected_file
|
||||
self.ui.gestaltLocationLbl.setText(selected_file)
|
||||
|
||||
def update_label(self, txt: str):
|
||||
self.ui.statusLbl.setText(txt)
|
||||
def update_bar(self, percent):
|
||||
self.ui.restoreProgressBar.setValue(int(percent))
|
||||
def on_removeTweaksBtn_clicked(self):
|
||||
# TODO: Add safety here
|
||||
self.device_manager.apply_changes(resetting=True, update_label=self.update_label)
|
||||
def on_resetGestaltBtn_clicked(self):
|
||||
self.device_manager.reset_mobilegestalt(update_label=self.update_label)
|
||||
|
||||
@QtCore.Slot()
|
||||
def on_applyTweaksBtn_clicked(self):
|
||||
# TODO: Add safety here
|
||||
self.device_manager.apply_changes(update_label=self.update_label)
|
||||
4
icon/app-indicator.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="white" class="bi bi-app-indicator" viewBox="0 0 16 16">
|
||||
<path d="M5.5 2A3.5 3.5 0 0 0 2 5.5v5A3.5 3.5 0 0 0 5.5 14h5a3.5 3.5 0 0 0 3.5-3.5V8a.5.5 0 0 1 1 0v2.5a4.5 4.5 0 0 1-4.5 4.5h-5A4.5 4.5 0 0 1 1 10.5v-5A4.5 4.5 0 0 1 5.5 1H8a.5.5 0 0 1 0 1H5.5z"/>
|
||||
<path d="M16 3a3 3 0 1 1-6 0 3 3 0 0 1 6 0z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 380 B |
4
icon/arrow-clockwise.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="white" class="bi bi-arrow-clockwise" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2v1z"/>
|
||||
<path d="M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 345 B |
3
icon/brush.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="white" class="bi bi-brush" viewBox="0 0 16 16">
|
||||
<path d="M15.825.12a.5.5 0 0 1 .132.584c-1.53 3.43-4.743 8.17-7.095 10.64a6.067 6.067 0 0 1-2.373 1.534c-.018.227-.06.538-.16.868-.201.659-.667 1.479-1.708 1.74a8.118 8.118 0 0 1-3.078.132 3.659 3.659 0 0 1-.562-.135 1.382 1.382 0 0 1-.466-.247.714.714 0 0 1-.204-.288.622.622 0 0 1 .004-.443c.095-.245.316-.38.461-.452.394-.197.625-.453.867-.826.095-.144.184-.297.287-.472l.117-.198c.151-.255.326-.54.546-.848.528-.739 1.201-.925 1.746-.896.126.007.243.025.348.048.062-.172.142-.38.238-.608.261-.619.658-1.419 1.187-2.069 2.176-2.67 6.18-6.206 9.117-8.104a.5.5 0 0 1 .596.04zM4.705 11.912a1.23 1.23 0 0 0-.419-.1c-.246-.013-.573.05-.879.479-.197.275-.355.532-.5.777l-.105.177c-.106.181-.213.362-.32.528a3.39 3.39 0 0 1-.76.861c.69.112 1.736.111 2.657-.12.559-.139.843-.569.993-1.06a3.122 3.122 0 0 0 .126-.75l-.793-.792zm1.44.026c.12-.04.277-.1.458-.183a5.068 5.068 0 0 0 1.535-1.1c1.9-1.996 4.412-5.57 6.052-8.631-2.59 1.927-5.566 4.66-7.302 6.792-.442.543-.795 1.243-1.042 1.826-.121.288-.214.54-.275.72v.001l.575.575zm-4.973 3.04.007-.005a.031.031 0 0 1-.007.004zm3.582-3.043.002.001h-.002z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
3
icon/caret-down-fill.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="white" class="bi bi-caret-down-fill" viewBox="0 0 16 16">
|
||||
<path d="M7.247 11.14 2.451 5.658C1.885 5.013 2.345 4 3.204 4h9.592a1 1 0 0 1 .753 1.659l-4.796 5.48a1 1 0 0 1-1.506 0z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 259 B |
4
icon/check-circle.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="white" class="bi bi-check-circle" viewBox="0 0 16 16">
|
||||
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z"/>
|
||||
<path d="M10.97 4.97a.235.235 0 0 0-.02.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-1.071-1.05z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 380 B |
4
icon/compass.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="white" class="bi bi-compass" viewBox="0 0 16 16">
|
||||
<path d="M8 16.016a7.5 7.5 0 0 0 1.962-14.74A1 1 0 0 0 9 0H7a1 1 0 0 0-.962 1.276A7.5 7.5 0 0 0 8 16.016zm6.5-7.5a6.5 6.5 0 1 1-13 0 6.5 6.5 0 0 1 13 0z"/>
|
||||
<path d="m6.94 7.44 4.95-2.83-2.83 4.95-4.949 2.83 2.828-4.95z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 351 B |
3
icon/currency-dollar.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="white" class="bi bi-currency-dollar" viewBox="0 0 16 16">
|
||||
<path d="M4 10.781c.148 1.667 1.513 2.85 3.591 3.003V15h1.043v-1.216c2.27-.179 3.678-1.438 3.678-3.3 0-1.59-.947-2.51-2.956-3.028l-.722-.187V3.467c1.122.11 1.879.714 2.07 1.616h1.47c-.166-1.6-1.54-2.748-3.54-2.875V1H7.591v1.233c-1.939.23-3.27 1.472-3.27 3.156 0 1.454.966 2.483 2.661 2.917l.61.162v4.031c-1.149-.17-1.94-.8-2.131-1.718H4zm3.391-3.836c-1.043-.263-1.6-.825-1.6-1.616 0-.944.704-1.641 1.8-1.828v3.495l-.2-.05zm1.591 1.872c1.287.323 1.852.859 1.852 1.769 0 1.097-.826 1.828-2.2 1.939V8.73l.348.086z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 649 B |
3
icon/discord.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="white" class="bi bi-discord" viewBox="0 0 16 16">
|
||||
<path d="M13.545 2.907a13.227 13.227 0 0 0-3.257-1.011.05.05 0 0 0-.052.025c-.141.25-.297.577-.406.833a12.19 12.19 0 0 0-3.658 0 8.258 8.258 0 0 0-.412-.833.051.051 0 0 0-.052-.025c-1.125.194-2.22.534-3.257 1.011a.041.041 0 0 0-.021.018C.356 6.024-.213 9.047.066 12.032c.001.014.01.028.021.037a13.276 13.276 0 0 0 3.995 2.02.05.05 0 0 0 .056-.019c.308-.42.582-.863.818-1.329a.05.05 0 0 0-.01-.059.051.051 0 0 0-.018-.011 8.875 8.875 0 0 1-1.248-.595.05.05 0 0 1-.02-.066.051.051 0 0 1 .015-.019c.084-.063.168-.129.248-.195a.05.05 0 0 1 .051-.007c2.619 1.196 5.454 1.196 8.041 0a.052.052 0 0 1 .053.007c.08.066.164.132.248.195a.051.051 0 0 1-.004.085 8.254 8.254 0 0 1-1.249.594.05.05 0 0 0-.03.03.052.052 0 0 0 .003.041c.24.465.515.909.817 1.329a.05.05 0 0 0 .056.019 13.235 13.235 0 0 0 4.001-2.02.049.049 0 0 0 .021-.037c.334-3.451-.559-6.449-2.366-9.106a.034.034 0 0 0-.02-.019Zm-8.198 7.307c-.789 0-1.438-.724-1.438-1.612 0-.889.637-1.613 1.438-1.613.807 0 1.45.73 1.438 1.613 0 .888-.637 1.612-1.438 1.612Zm5.316 0c-.788 0-1.438-.724-1.438-1.612 0-.889.637-1.613 1.438-1.613.807 0 1.451.73 1.438 1.613 0 .888-.631 1.612-1.438 1.612Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
4
icon/file-earmark-zip.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="white" class="bi bi-file-earmark-zip" viewBox="0 0 16 16">
|
||||
<path d="M5 7.5a1 1 0 0 1 1-1h1a1 1 0 0 1 1 1v.938l.4 1.599a1 1 0 0 1-.416 1.074l-.93.62a1 1 0 0 1-1.11 0l-.929-.62a1 1 0 0 1-.415-1.074L5 8.438V7.5zm2 0H6v.938a1 1 0 0 1-.03.243l-.4 1.598.93.62.929-.62-.4-1.598A1 1 0 0 1 7 8.438V7.5z"/>
|
||||
<path d="M14 4.5V14a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2h5.5L14 4.5zm-3 0A1.5 1.5 0 0 1 9.5 3V1h-2v1h-1v1h1v1h-1v1h1v1H6V5H5V4h1V3H5V2h1V1H4a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V4.5h-2z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 584 B |
11
icon/flag.svg
Normal file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--Generator: Apple Native CoreSVG 326-->
|
||||
<!DOCTYPE svg
|
||||
PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 14.209 15.2637">
|
||||
<g>
|
||||
<rect height="15.2637" opacity="0" width="14.209" x="0" y="0"/>
|
||||
<path d="M1.01562 15.2637C1.37695 15.2637 1.66992 14.9707 1.66992 14.6094L1.66992 10.4785C1.8457 10.3809 2.56836 10.1172 3.64258 10.1172C6.51367 10.1172 8.33008 11.5137 11.0254 11.5137C12.2559 11.5137 12.6855 11.3867 13.291 11.1133C13.8477 10.8691 14.209 10.4297 14.209 9.6582L14.209 1.78711C14.209 1.34766 13.8184 1.08398 13.3203 1.08398C12.9395 1.08398 12.2363 1.39648 10.9277 1.39648C8.23242 1.39648 6.41602 0 3.54492 0C2.31445 0 1.88477 0.126953 1.2793 0.400391C0.722656 0.644531 0.351562 1.08398 0.351562 1.86523L0.351562 14.6094C0.351562 14.9609 0.654297 15.2637 1.01562 15.2637ZM11.0254 10.1953C8.50586 10.1953 6.65039 8.80859 3.64258 8.80859C2.77344 8.80859 1.99219 8.91602 1.66992 9.0625L1.66992 1.91406C1.76758 1.67969 2.35352 1.31836 3.54492 1.31836C6.24023 1.31836 8.0957 2.71484 10.9277 2.71484C11.7969 2.71484 12.4805 2.60742 12.9004 2.49023L12.9004 9.59961C12.793 9.84375 12.2168 10.1953 11.0254 10.1953Z" fill="white" fill-opacity="0.85"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
3
icon/folder.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="white" class="bi bi-folder" viewBox="0 0 16 16">
|
||||
<path d="M.54 3.87.5 3a2 2 0 0 1 2-2h3.672a2 2 0 0 1 1.414.586l.828.828A2 2 0 0 0 9.828 3h3.982a2 2 0 0 1 1.992 2.181l-.637 7A2 2 0 0 1 13.174 14H2.826a2 2 0 0 1-1.991-1.819l-.637-7a1.99 1.99 0 0 1 .342-1.31zM2.19 4a1 1 0 0 0-.996 1.09l.637 7a1 1 0 0 0 .995.91h10.348a1 1 0 0 0 .995-.91l.637-7A1 1 0 0 0 13.81 4H2.19zm4.69-1.707A1 1 0 0 0 6.172 2H2.5a1 1 0 0 0-1 .981l.006.139C1.72 3.042 1.95 3 2.19 3h5.396l-.707-.707z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 549 B |
4
icon/gear.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="white" class="bi bi-gear" viewBox="0 0 16 16">
|
||||
<path d="M8 4.754a3.246 3.246 0 1 0 0 6.492 3.246 3.246 0 0 0 0-6.492zM5.754 8a2.246 2.246 0 1 1 4.492 0 2.246 2.246 0 0 1-4.492 0z"/>
|
||||
<path d="M9.796 1.343c-.527-1.79-3.065-1.79-3.592 0l-.094.319a.873.873 0 0 1-1.255.52l-.292-.16c-1.64-.892-3.433.902-2.54 2.541l.159.292a.873.873 0 0 1-.52 1.255l-.319.094c-1.79.527-1.79 3.065 0 3.592l.319.094a.873.873 0 0 1 .52 1.255l-.16.292c-.892 1.64.901 3.434 2.541 2.54l.292-.159a.873.873 0 0 1 1.255.52l.094.319c.527 1.79 3.065 1.79 3.592 0l.094-.319a.873.873 0 0 1 1.255-.52l.292.16c1.64.893 3.434-.902 2.54-2.541l-.159-.292a.873.873 0 0 1 .52-1.255l.319-.094c1.79-.527 1.79-3.065 0-3.592l-.319-.094a.873.873 0 0 1-.52-1.255l.16-.292c.893-1.64-.902-3.433-2.541-2.54l-.292.159a.873.873 0 0 1-1.255-.52l-.094-.319zm-2.633.283c.246-.835 1.428-.835 1.674 0l.094.319a1.873 1.873 0 0 0 2.693 1.115l.291-.16c.764-.415 1.6.42 1.184 1.185l-.159.292a1.873 1.873 0 0 0 1.116 2.692l.318.094c.835.246.835 1.428 0 1.674l-.319.094a1.873 1.873 0 0 0-1.115 2.693l.16.291c.415.764-.42 1.6-1.185 1.184l-.291-.159a1.873 1.873 0 0 0-2.693 1.116l-.094.318c-.246.835-1.428.835-1.674 0l-.094-.319a1.873 1.873 0 0 0-2.692-1.115l-.292.16c-.764.415-1.6-.42-1.184-1.185l.159-.291A1.873 1.873 0 0 0 1.945 8.93l-.319-.094c-.835-.246-.835-1.428 0-1.674l.319-.094A1.873 1.873 0 0 0 3.06 4.377l-.16-.292c-.415-.764.42-1.6 1.185-1.184l.292.159a1.873 1.873 0 0 0 2.692-1.115l.094-.319z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
4
icon/geo-alt.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="white" class="bi bi-geo-alt" viewBox="0 0 16 16">
|
||||
<path d="M12.166 8.94c-.524 1.062-1.234 2.12-1.96 3.07A31.493 31.493 0 0 1 8 14.58a31.481 31.481 0 0 1-2.206-2.57c-.726-.95-1.436-2.008-1.96-3.07C3.304 7.867 3 6.862 3 6a5 5 0 0 1 10 0c0 .862-.305 1.867-.834 2.94zM8 16s6-5.686 6-10A6 6 0 0 0 2 6c0 4.314 6 10 6 10z"/>
|
||||
<path d="M8 8a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0 1a3 3 0 1 0 0-6 3 3 0 0 0 0 6z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 476 B |
3
icon/github.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="white" class="bi bi-github" viewBox="0 0 16 16">
|
||||
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 709 B |
4
icon/hdd.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="white" class="bi bi-hdd" viewBox="0 0 16 16">
|
||||
<path d="M4.5 11a.5.5 0 1 0 0-1 .5.5 0 0 0 0 1zM3 10.5a.5.5 0 1 1-1 0 .5.5 0 0 1 1 0z"/>
|
||||
<path d="M16 11a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V9.51c0-.418.105-.83.305-1.197l2.472-4.531A1.5 1.5 0 0 1 4.094 3h7.812a1.5 1.5 0 0 1 1.317.782l2.472 4.53c.2.368.305.78.305 1.198V11zM3.655 4.26 1.592 8.043C1.724 8.014 1.86 8 2 8h12c.14 0 .276.014.408.042L12.345 4.26a.5.5 0 0 0-.439-.26H4.094a.5.5 0 0 0-.44.26zM1 10v1a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-1a1 1 0 0 0-1-1H2a1 1 0 0 0-1 1z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 597 B |
3
icon/heart-fill.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="white" class="bi bi-heart-fill" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M8 1.314C12.438-3.248 23.534 4.735 8 15-7.534 4.736 3.562-3.248 8 1.314z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 235 B |
3
icon/house.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="white" class="bi bi-house" viewBox="0 0 16 16">
|
||||
<path d="M8.707 1.5a1 1 0 0 0-1.414 0L.646 8.146a.5.5 0 0 0 .708.708L2 8.207V13.5A1.5 1.5 0 0 0 3.5 15h9a1.5 1.5 0 0 0 1.5-1.5V8.207l.646.647a.5.5 0 0 0 .708-.708L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.707 1.5ZM13 7.207V13.5a.5.5 0 0 1-.5.5h-9a.5.5 0 0 1-.5-.5V7.207l5-5 5 5Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 422 B |
12
icon/import.svg
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--Generator: Apple Native CoreSVG 232.5-->
|
||||
<!DOCTYPE svg
|
||||
PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="443.75" height="648.75">
|
||||
<g>
|
||||
<rect height="648.75" opacity="0" width="443.75" x="0" y="0"/>
|
||||
<path d="M443.75 261L443.75 510.5C443.75 562 417.5 588 365.25 588L78.5 588C26.25 588 0 562 0 510.5L0 261C0 209.5 26.25 183.5 78.5 183.5L153.75 183.5L153.75 223.75L79 223.75C54 223.75 40.25 237.25 40.25 263.25L40.25 508.25C40.25 534.25 54 547.75 79 547.75L364.5 547.75C389.25 547.75 403.5 534.25 403.5 508.25L403.5 263.25C403.5 237.25 389.25 223.75 364.5 223.75L290 223.75L290 183.5L365.25 183.5C417.5 183.5 443.75 209.5 443.75 261Z" fill="#ffffff" fill-opacity="0.85"/>
|
||||
<path d="M221.75 423.5C227 423.5 231.25 422 236.25 417L320.75 335.25C324.5 331.5 326.75 327.5 326.75 322.25C326.75 312 318.75 304.75 308.5 304.75C303.5 304.75 298.5 306.75 295 310.75L257 351L240.25 368.75L241.75 331.25L241.75 67.75C241.75 57.25 232.5 48.25 221.75 48.25C211 48.25 202 57.25 202 67.75L202 331.25L203.5 368.75L186.5 351L148.75 310.75C145.25 306.75 139.75 304.75 135 304.75C124.5 304.75 117 312 117 322.25C117 327.5 119 331.5 122.75 335.25L207.25 417C212.5 422 216.75 423.5 221.75 423.5Z" fill="#ffffff" fill-opacity="0.85"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
12
icon/iphone-island.svg
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--Generator: Apple Native CoreSVG 326-->
|
||||
<!DOCTYPE svg
|
||||
PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 10.5176 16.4941">
|
||||
<g>
|
||||
<rect height="16.4941" opacity="0" width="10.5176" x="0" y="0"/>
|
||||
<path d="M0 14.4531C0 15.6445 0.878906 16.4746 2.13867 16.4746L8.00781 16.4746C9.26758 16.4746 10.1562 15.6445 10.1562 14.4531L10.1562 2.02148C10.1562 0.830078 9.26758 0 8.00781 0L2.13867 0C0.878906 0 0 0.830078 0 2.02148ZM1.40625 14.209L1.40625 2.26562C1.40625 1.72852 1.75781 1.40625 2.32422 1.40625L7.82227 1.40625C8.39844 1.40625 8.74023 1.72852 8.74023 2.26562L8.74023 14.209C8.74023 14.7461 8.39844 15.0684 7.82227 15.0684L2.32422 15.0684C1.75781 15.0684 1.40625 14.7461 1.40625 14.209Z" fill="white" fill-opacity="0.85"/>
|
||||
<path d="M2.91016 7.4707C2.91016 7.74414 3.10547 7.91992 3.36914 7.91992C3.49609 7.92969 3.60352 7.87109 3.69141 7.7832L4.22852 7.25586L4.64844 6.70898L4.60938 7.80273L4.60938 9.70703C4.60938 9.9707 4.80469 10.166 5.07812 10.166C5.35156 10.166 5.53711 9.9707 5.53711 9.70703L5.53711 7.80273L5.49805 6.70898L5.91797 7.25586L6.46484 7.7832C6.55273 7.87109 6.65039 7.92969 6.78711 7.91992C7.05078 7.91992 7.23633 7.74414 7.23633 7.4707C7.23633 7.35352 7.20703 7.22656 7.12891 7.14844L5.42969 5.46875C5.33203 5.37109 5.19531 5.3125 5.07812 5.3125C4.95117 5.3125 4.82422 5.37109 4.7168 5.46875L3.02734 7.14844C2.93945 7.22656 2.91016 7.35352 2.91016 7.4707ZM3.05664 4.7168L7.08984 4.7168C7.49023 4.7168 7.76367 4.44336 7.76367 4.02344L7.76367 3.07617C7.76367 2.65625 7.49023 2.38281 7.08984 2.38281L3.05664 2.38281C2.65625 2.38281 2.38281 2.65625 2.38281 3.07617L2.38281 4.02344C2.38281 4.44336 2.65625 4.7168 3.05664 4.7168Z" fill="white" fill-opacity="0.85"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
12
icon/pencil.svg
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--Generator: Apple Native CoreSVG 232.5-->
|
||||
<!DOCTYPE svg
|
||||
PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="542.688" height="588.094">
|
||||
<g>
|
||||
<rect height="588.094" opacity="0" width="542.688" x="0" y="0"/>
|
||||
<path d="M381.248 69.6425L354.726 96.1646C314.093 72.149 268.287 58.1718 222.969 58.1718C114.219 58.1718 48.9689 135.172 48.9689 234.672C48.9689 353.422 139.719 490.672 318.219 516.422C329.719 517.922 335.219 526.172 335.219 534.422C335.219 543.422 327.969 552.922 313.969 551.172C154.469 535.672 13.7189 400.172 13.7189 235.672C13.7189 115.172 94.9689 22.9218 223.969 22.9218C278.179 22.9218 333.212 39.9495 381.248 69.6425ZM529.219 316.172C529.219 404.172 468.719 463.672 382.969 463.672C325.292 463.672 262.293 436.846 213.456 393.37L251.829 376.385C291.015 409.294 338.584 430.172 382.469 430.172C443.719 430.172 494.719 386.922 494.719 315.922C494.719 268.474 477.922 222.762 450.116 183.495L476.201 157.508C509.115 202.931 529.219 257.341 529.219 316.172Z" fill="#ffffff" fill-opacity="0.85"/>
|
||||
<path d="M234.219 344.922L458.719 120.672L421.469 83.6718L197.219 307.672L176.469 355.422C174.469 360.672 179.719 366.172 185.219 364.172ZM476.969 102.672L496.969 82.6718C506.469 73.1718 506.969 59.1718 497.219 50.1718L490.719 44.1718C482.219 36.1718 468.719 36.1718 459.719 45.1718L439.969 65.4218Z" fill="#ffffff" fill-opacity="0.85"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
4
icon/phone.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="white" class="bi bi-phone" viewBox="0 0 16 16">
|
||||
<path d="M11 1a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1h6zM5 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H5z"/>
|
||||
<path d="M8 14a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 328 B |
11
icon/plus.svg
Normal file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--Generator: Apple Native CoreSVG 232.5-->
|
||||
<!DOCTYPE svg
|
||||
PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="412.5" height="412.75">
|
||||
<g>
|
||||
<rect height="412.75" opacity="0" width="412.5" x="0" y="0"/>
|
||||
<path d="M0 206.25C0 218.5 10.25 228.5 22.25 228.5L184 228.5L184 390.25C184 402.25 194 412.5 206.25 412.5C218.5 412.5 228.75 402.25 228.75 390.25L228.75 228.5L390.25 228.5C402.25 228.5 412.5 218.5 412.5 206.25C412.5 194 402.25 183.75 390.25 183.75L228.75 183.75L228.75 22.25C228.75 10.25 218.5 0 206.25 0C194 0 184 10.25 184 22.25L184 183.75L22.25 183.75C10.25 183.75 0 194 0 206.25Z" fill="#ffffff" fill-opacity="0.85"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 824 B |
3
icon/star.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="white" class="bi bi-star" viewBox="0 0 16 16">
|
||||
<path d="M2.866 14.85c-.078.444.36.791.746.593l4.39-2.256 4.389 2.256c.386.198.824-.149.746-.592l-.83-4.73 3.522-3.356c.33-.314.16-.888-.282-.95l-4.898-.696L8.465.792a.513.513 0 0 0-.927 0L5.354 5.12l-4.898.696c-.441.062-.612.636-.283.95l3.523 3.356-.83 4.73zm4.905-2.767-3.686 1.894.694-3.957a.565.565 0 0 0-.163-.505L1.71 6.745l4.052-.576a.525.525 0 0 0 .393-.288L8 2.223l1.847 3.658a.525.525 0 0 0 .393.288l4.052.575-2.906 2.77a.565.565 0 0 0-.163.506l.694 3.957-3.686-1.894a.503.503 0 0 0-.461 0z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 628 B |
3
icon/toggles.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="white" class="bi bi-toggles" viewBox="0 0 16 16">
|
||||
<path d="M4.5 9a3.5 3.5 0 1 0 0 7h7a3.5 3.5 0 1 0 0-7h-7zm7 6a2.5 2.5 0 1 1 0-5 2.5 2.5 0 0 1 0 5zm-7-14a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5zm2.45 0A3.49 3.49 0 0 1 8 3.5 3.49 3.49 0 0 1 6.95 6h4.55a2.5 2.5 0 0 0 0-5H6.95zM4.5 0h7a3.5 3.5 0 1 1 0 7h-7a3.5 3.5 0 1 1 0-7z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 401 B |
4
icon/trash.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#eb5545" class="bi bi-trash" viewBox="0 0 16 16">
|
||||
<path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5Zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5Zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6Z"/>
|
||||
<path d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1ZM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118ZM2.5 3h11V2h-11v1Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 548 B |
3
icon/twitter.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="white" class="bi bi-twitter" viewBox="0 0 16 16">
|
||||
<path d="M5.026 15c6.038 0 9.341-5.003 9.341-9.334 0-.14 0-.282-.006-.422A6.685 6.685 0 0 0 16 3.542a6.658 6.658 0 0 1-1.889.518 3.301 3.301 0 0 0 1.447-1.817 6.533 6.533 0 0 1-2.087.793A3.286 3.286 0 0 0 7.875 6.03a9.325 9.325 0 0 1-6.767-3.429 3.289 3.289 0 0 0 1.018 4.382A3.323 3.323 0 0 1 .64 6.575v.045a3.288 3.288 0 0 0 2.632 3.218 3.203 3.203 0 0 1-.865.115 3.23 3.23 0 0 1-.614-.057 3.283 3.283 0 0 0 3.067 2.277A6.588 6.588 0 0 1 .78 13.58a6.32 6.32 0 0 1-.78-.045A9.344 9.344 0 0 0 5.026 15z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 633 B |
4
icon/wifi.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="white" class="bi bi-wifi" viewBox="0 0 16 16">
|
||||
<path d="M15.384 6.115a.485.485 0 0 0-.047-.736A12.444 12.444 0 0 0 8 3C5.259 3 2.723 3.882.663 5.379a.485.485 0 0 0-.048.736.518.518 0 0 0 .668.05A11.448 11.448 0 0 1 8 4c2.507 0 4.827.802 6.716 2.164.205.148.49.13.668-.049z"/>
|
||||
<path d="M13.229 8.271a.482.482 0 0 0-.063-.745A9.455 9.455 0 0 0 8 6c-1.905 0-3.68.56-5.166 1.526a.48.48 0 0 0-.063.745.525.525 0 0 0 .652.065A8.46 8.46 0 0 1 8 7a8.46 8.46 0 0 1 4.576 1.336c.206.132.48.108.653-.065zm-2.183 2.183c.226-.226.185-.605-.1-.75A6.473 6.473 0 0 0 8 9c-1.06 0-2.062.254-2.946.704-.285.145-.326.524-.1.75l.015.015c.16.16.407.19.611.09A5.478 5.478 0 0 1 8 10c.868 0 1.69.201 2.42.56.203.1.45.07.61-.091l.016-.015zM9.06 12.44c.196-.196.198-.52-.04-.66A1.99 1.99 0 0 0 8 11.5a1.99 1.99 0 0 0-1.02.28c-.238.14-.236.464-.04.66l.706.706a.5.5 0 0 0 .707 0l.707-.707z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 944 B |
3
icon/x-lg.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="white" class="bi bi-x-lg" viewBox="0 0 16 16">
|
||||
<path d="M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8 8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8 2.146 2.854Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 313 B |
155
main_app.py
@ -1,147 +1,16 @@
|
||||
from exploit.restore import restore_file
|
||||
from pathlib import Path
|
||||
import plistlib
|
||||
import traceback
|
||||
import sys
|
||||
from PySide6 import QtCore, QtWidgets
|
||||
|
||||
running = True
|
||||
passed_check = False
|
||||
# tweaks
|
||||
dynamic_island_enabled = False
|
||||
current_model_name = ""
|
||||
boot_chime_enabled = False
|
||||
charge_limit_enabled = False
|
||||
stage_manager_enabled = False
|
||||
shutter_sound_enabled = False
|
||||
always_on_display_enabled = False
|
||||
apple_pencil_enabled = False
|
||||
action_button_enabled = False
|
||||
internal_storage_enabled = False
|
||||
from gui.main_window import MainWindow
|
||||
from devicemanagement.device_manager import DeviceManager
|
||||
|
||||
gestalt_path = Path.joinpath(Path.cwd(), "com.apple.MobileGestalt.plist")
|
||||
if __name__ == "__main__":
|
||||
app = QtWidgets.QApplication([])
|
||||
dm = DeviceManager()
|
||||
dm.get_devices()
|
||||
|
||||
def print_option(num: int, active: bool, message: str):
|
||||
txt = str(num) + ". "
|
||||
if active:
|
||||
txt = txt + "[Y] "
|
||||
txt = txt + message
|
||||
print(txt)
|
||||
|
||||
while running:
|
||||
print("""\n\n\n\n
|
||||
|
||||
,--.
|
||||
,--.'| ___
|
||||
,--,: : | ,--.'|_
|
||||
,`--.'`| ' : ,--, | | :,'
|
||||
| : : | | ,'_ /| ,----._,. ,----._,. : : ' :
|
||||
: | \\ | : .--. | | : / / ' / / / ' / ,---. .;__,' /
|
||||
| : ' '; |,'_ /| : . || : || : | / \\ | | |
|
||||
' ' ;. ;| ' | | . .| | .\\ .| | .\\ . / / |:__,'| :
|
||||
| | | \\ || | ' | | |. ; '; |. ; '; |. ' / | ' : |__
|
||||
' : | ; .': | : ; ; |' . . |' . . |' ; /| | | '.'|
|
||||
| | '`--' ' : `--' \\`---`-'| | `---`-'| |' | / | ; : ;
|
||||
' : | : , .-./.'__/\\_: | .'__/\\_: || : | | , /
|
||||
; |.' `--`----' | : : | : : \\ \\ / ---`-'
|
||||
'---' \\ \\ / \\ \\ / `----'
|
||||
`--`-' `--`-'
|
||||
""")
|
||||
print("by LeminLimez")
|
||||
print("v1.2\n\n")
|
||||
print("\nPlease back up your device before using!")
|
||||
widget = MainWindow(device_manager=dm)
|
||||
widget.resize(800, 600)
|
||||
widget.show()
|
||||
|
||||
if not passed_check and Path.exists(gestalt_path) and Path.is_file(gestalt_path):
|
||||
passed_check = True
|
||||
|
||||
if passed_check:
|
||||
print_option(1, dynamic_island_enabled, "Toggle Dynamic Island")
|
||||
print_option(2, current_model_name != "", "Set Device Model Name")
|
||||
print_option(3, boot_chime_enabled, "Toggle Boot Chime")
|
||||
print_option(4, charge_limit_enabled, "Toggle Charge Limit")
|
||||
print_option(5, stage_manager_enabled, "Toggle Stage Manager Supported")
|
||||
print_option(6, shutter_sound_enabled, "Disable Region Restrictions (ie. Shutter Sound)")
|
||||
print_option(7, always_on_display_enabled, "Always On Display (iOS 18+ only)")
|
||||
print_option(8, apple_pencil_enabled, "Toggle Apple Pencil")
|
||||
print_option(9, action_button_enabled, "Toggle Action Button")
|
||||
print_option(10, internal_storage_enabled, "Toggle Internal Storage")
|
||||
print("\n11. Apply")
|
||||
print("0. Exit\n")
|
||||
page = int(input("Enter a number: "))
|
||||
if page == 1:
|
||||
dynamic_island_enabled = not dynamic_island_enabled
|
||||
elif page == 2:
|
||||
print("\n\nSet Model Name")
|
||||
print("Leave blank to turn off custom name.\n")
|
||||
name = input("Enter Model Name: ")
|
||||
current_model_name = name
|
||||
elif page == 3:
|
||||
boot_chime_enabled = not boot_chime_enabled
|
||||
elif page == 4:
|
||||
charge_limit_enabled = not charge_limit_enabled
|
||||
elif page == 5:
|
||||
stage_manager_enabled = not stage_manager_enabled
|
||||
elif page == 6:
|
||||
shutter_sound_enabled = not shutter_sound_enabled
|
||||
elif page == 7:
|
||||
always_on_display_enabled = not always_on_display_enabled
|
||||
elif page == 8:
|
||||
apple_pencil_enabled = not apple_pencil_enabled
|
||||
elif page == 9:
|
||||
action_button_enabled = not action_button_enabled
|
||||
elif page == 10:
|
||||
internal_storage_enabled = not internal_storage_enabled
|
||||
elif page == 11:
|
||||
print()
|
||||
# set the tweaks and apply
|
||||
# first open the file in read mode
|
||||
with open(gestalt_path, 'rb') as in_fp:
|
||||
plist = plistlib.load(in_fp)
|
||||
|
||||
# set the plist keys
|
||||
if dynamic_island_enabled:
|
||||
plist["CacheExtra"]["oPeik/9e8lQWMszEjbPzng"]["ArtworkDeviceSubType"] = 2556
|
||||
if current_model_name != "":
|
||||
plist["CacheExtra"]["oPeik/9e8lQWMszEjbPzng"]["ArtworkDeviceProductDescription"] = current_model_name
|
||||
if boot_chime_enabled:
|
||||
plist["CacheExtra"]["QHxt+hGLaBPbQJbXiUJX3w"] = True
|
||||
if charge_limit_enabled:
|
||||
plist["CacheExtra"]["37NVydb//GP/GrhuTN+exg"] = True
|
||||
if stage_manager_enabled:
|
||||
plist["CacheExtra"]["qeaj75wk3HF4DwQ8qbIi7g"] = 1
|
||||
if shutter_sound_enabled:
|
||||
plist["CacheExtra"]["h63QSdBCiT/z0WU6rdQv6Q"] = "US"
|
||||
plist["CacheExtra"]["zHeENZu+wbg7PUprwNwBWg"] = "LL/A"
|
||||
if always_on_display_enabled:
|
||||
plist["CacheExtra"]["2OOJf1VhaM7NxfRok3HbWQ"] = True
|
||||
plist["CacheExtra"]["j8/Omm6s1lsmTDFsXjsBfA"] = True
|
||||
if apple_pencil_enabled:
|
||||
plist["CacheExtra"]["yhHcB0iH0d1XzPO/CFd3ow"] = True
|
||||
if action_button_enabled:
|
||||
plist["CacheExtra"]["cT44WE1EohiwRzhsZ8xEsw"] = True
|
||||
if internal_storage_enabled:
|
||||
plist["CacheExtra"]["LBJfwOEzExRxzlAnSuI7eg"] = True
|
||||
|
||||
# write back to the file
|
||||
with open(gestalt_path, 'wb') as out_fp:
|
||||
plistlib.dump(plist, out_fp)
|
||||
# restore to the device
|
||||
try:
|
||||
restore_file(fp=gestalt_path, restore_path="/var/containers/Shared/SystemGroup/systemgroup.com.apple.mobilegestaltcache/Library/Caches/", restore_name="com.apple.MobileGestalt.plist")
|
||||
input("Success! Reboot your device to see the changes.")
|
||||
except Exception as e:
|
||||
print(traceback.format_exc())
|
||||
input("Press Enter to continue...")
|
||||
running = False
|
||||
elif page == 0:
|
||||
# exit the panel
|
||||
print("Goodbye!")
|
||||
running = False
|
||||
else:
|
||||
print("No MobileGestalt file found!")
|
||||
print(f"Please place the file in \'{Path.cwd()}\' with the name \'com.apple.MobileGestalt.plist\'")
|
||||
print("Remember to make a backup of the file!!\n")
|
||||
print("1. Retry")
|
||||
print("2. Enter path\n")
|
||||
choice = int(input("Enter number: "))
|
||||
if choice == 2:
|
||||
new_path = input("Enter new path to file: ")
|
||||
gestalt_path = Path(new_path)
|
||||
sys.exit(app.exec())
|
||||
BIN
nugget.ico
Normal file
|
After Width: | Height: | Size: 84 KiB |
BIN
qt/__pycache__/ui_mainwindow.cpython-312.pyc
Normal file
3975
qt/mainwindow.ui
Normal file
2231
qt/mainwindow_ui.py
Normal file
34
qt/resources.qrc
Normal file
@ -0,0 +1,34 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>credits/LeminLimez.png</file>
|
||||
<file>icon/app-indicator.svg</file>
|
||||
<file>icon/arrow-clockwise.svg</file>
|
||||
<file>icon/brush.svg</file>
|
||||
<file>icon/caret-down-fill.svg</file>
|
||||
<file>icon/check-circle.svg</file>
|
||||
<file>icon/compass.svg</file>
|
||||
<file>icon/gear.svg</file>
|
||||
<file>icon/hdd.svg</file>
|
||||
<file>icon/house.svg</file>
|
||||
<file>icon/phone.svg</file>
|
||||
<file>icon/toggles.svg</file>
|
||||
<file>icon/wifi.svg</file>
|
||||
<file>icon/x-lg.svg</file>
|
||||
<file>icon/discord.svg</file>
|
||||
<file>icon/heart-fill.svg</file>
|
||||
<file>icon/github.svg</file>
|
||||
<file>icon/twitter.svg</file>
|
||||
<file>icon/file-earmark-zip.svg</file>
|
||||
<file>icon/folder.svg</file>
|
||||
<file>icon/trash.svg</file>
|
||||
<file>icon/currency-dollar.svg</file>
|
||||
<file>icon/geo-alt.svg</file>
|
||||
<file>icon/star.svg</file>
|
||||
<file>icon/pencil.svg</file>
|
||||
<file>icon/import.svg</file>
|
||||
<file>icon/plus.svg</file>
|
||||
<file>icon/iphone-island.svg</file>
|
||||
<file>icon/flag.svg</file>
|
||||
<file>credits/big_nugget.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
2231
qt/ui_mainwindow.py
Normal file
@ -1 +1,2 @@
|
||||
pymobiledevice3
|
||||
pymobiledevice3
|
||||
pyside6
|
||||
9830
resources_rc.py
Normal file
0
tweaks/__init__.py
Normal file
BIN
tweaks/__pycache__/__init__.cpython-312.pyc
Normal file
BIN
tweaks/__pycache__/eligibility_tweak.cpython-312.pyc
Normal file
BIN
tweaks/__pycache__/tweak_classes.cpython-312.pyc
Normal file
BIN
tweaks/__pycache__/tweaks.cpython-312.pyc
Normal file
92
tweaks/eligibility_tweak.py
Normal file
@ -0,0 +1,92 @@
|
||||
from .tweak_classes import Tweak, TweakModifyType
|
||||
from exploit.restore import FileToRestore
|
||||
from devicemanagement.constants import Version
|
||||
|
||||
import plistlib
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from os import path, getcwd
|
||||
|
||||
class InvalidRegionCodeException(Exception):
|
||||
"Region code must be exactly 2 characters long!"
|
||||
pass
|
||||
|
||||
def replace_region_code(plist_path: str, original_code: str = "US", new_code: str = "US"):
|
||||
with open(plist_path, 'rb') as f:
|
||||
plist_data = plistlib.load(f)
|
||||
|
||||
plist_str = str(plist_data)
|
||||
updated_plist_str = plist_str.replace(original_code, new_code)
|
||||
updated_plist_data = eval(updated_plist_str) # Convert string back to dictionary
|
||||
|
||||
return plistlib.dumps(updated_plist_data)
|
||||
|
||||
class EligibilityTweak(Tweak):
|
||||
def __init__(
|
||||
self, label: str,
|
||||
min_version: Version = Version("1.0"),
|
||||
divider_below: bool = False
|
||||
):
|
||||
super().__init__(label=label, key=None, value=["Method 1", "Method 2"], edit_type=TweakModifyType.PICKER, min_version=min_version, divider_below=divider_below)
|
||||
self.code = "US"
|
||||
self.method = 0 # between 0 and 1
|
||||
|
||||
def set_region_code(self, new_code: str):
|
||||
if new_code == '':
|
||||
self.code = "US"
|
||||
else:
|
||||
self.code = new_code
|
||||
|
||||
def set_selected_option(self, new_method: int):
|
||||
self.method = new_method % 2 # force it to be either 0 or 1
|
||||
self.set_enabled(True)
|
||||
|
||||
def get_selected_option(self) -> int:
|
||||
return self.method
|
||||
|
||||
def apply_tweak(self) -> list[FileToRestore]:
|
||||
# credit to lrdsnow for EU Enabler
|
||||
# https://github.com/Lrdsnow/EUEnabler/blob/main/app.py
|
||||
if not self.enabled:
|
||||
return None
|
||||
print(f"Applying EU Enabler for region \'{self.code}\'...")
|
||||
# get the plists directory
|
||||
try:
|
||||
source_dir = path.join(sys._MEIPASS, "files/eligibility")
|
||||
except:
|
||||
source_dir = path.join(getcwd(), "files/eligibility")
|
||||
|
||||
# start with eligibility.plist
|
||||
file_path = path.join(source_dir, 'eligibility.plist')
|
||||
eligibility_data = replace_region_code(file_path, original_code="US", new_code=self.code)
|
||||
files_to_restore = [
|
||||
FileToRestore(
|
||||
contents=eligibility_data,
|
||||
restore_path="/var/db/os_eligibility/",
|
||||
restore_name="eligibility.plist"
|
||||
)
|
||||
]
|
||||
|
||||
# next modify config.plist
|
||||
file_path = path.join(source_dir, 'Config.plist')
|
||||
config_data = replace_region_code(file_path, original_code="US", new_code=self.code)
|
||||
if self.method == 0:
|
||||
files_to_restore.append(
|
||||
FileToRestore(
|
||||
contents=config_data,
|
||||
restore_path="/var/MobileAsset/AssetsV2/com_apple_MobileAsset_OSEligibility/purpose_auto/c55a421c053e10233e5bfc15c42fa6230e5639a9.asset/AssetData/",
|
||||
restore_name="Config.plist"
|
||||
)
|
||||
)
|
||||
elif self.method == 1:
|
||||
files_to_restore.append(
|
||||
FileToRestore(
|
||||
contents=config_data,
|
||||
restore_path="/var/MobileAsset/AssetsV2/com_apple_MobileAsset_OSEligibility/purpose_auto/247556c634fc4cc4fd742f1b33af9abf194a986e.asset/AssetData/",
|
||||
restore_name="Config.plist"
|
||||
)
|
||||
)
|
||||
|
||||
# return the new files to restore
|
||||
return files_to_restore
|
||||
|
||||
123
tweaks/tweak_classes.py
Normal file
@ -0,0 +1,123 @@
|
||||
from enum import Enum
|
||||
from devicemanagement.constants import Version
|
||||
|
||||
class TweakModifyType(Enum):
|
||||
TOGGLE = 1
|
||||
TEXT = 2
|
||||
PICKER = 3
|
||||
|
||||
class Tweak:
|
||||
def __init__(
|
||||
self, label: str,
|
||||
key: str, subkey: str = None,
|
||||
value: any = 1,
|
||||
edit_type: TweakModifyType = TweakModifyType.TOGGLE,
|
||||
min_version: Version = Version("1.0"),
|
||||
divider_below: bool = False
|
||||
):
|
||||
self.label = label
|
||||
self.key = key
|
||||
self.subkey = subkey
|
||||
self.value = value
|
||||
self.min_version = min_version
|
||||
self.edit_type = edit_type
|
||||
self.divider_below = divider_below
|
||||
self.enabled = False
|
||||
|
||||
def set_enabled(self, value: bool):
|
||||
self.enabled = value
|
||||
def toggle_enabled(self):
|
||||
self.enabled = not self.enabled
|
||||
def set_value(self, new_value: any, toggle_enabled: bool = True):
|
||||
self.value = new_value
|
||||
if toggle_enabled:
|
||||
self.enabled = True
|
||||
|
||||
def is_compatible(self, device_ver: str):
|
||||
return Version(device_ver) >= self.min_version
|
||||
|
||||
def apply_tweak(self):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class MobileGestaltTweak(Tweak):
|
||||
def apply_tweak(self, plist: dict):
|
||||
if not self.enabled:
|
||||
return plist
|
||||
new_value = self.value
|
||||
if self.subkey == None:
|
||||
plist["CacheExtra"][self.key] = new_value
|
||||
else:
|
||||
plist["CacheExtra"][self.key][self.subkey] = new_value
|
||||
return plist
|
||||
|
||||
class MobileGestaltPickerTweak(Tweak):
|
||||
def __init__(
|
||||
self, label: str,
|
||||
key: str, subkey: str = None,
|
||||
values: list = [1],
|
||||
min_version: Version = Version("1.0"),
|
||||
divider_below: bool = False
|
||||
):
|
||||
super().__init__(label=label, key=key, subkey=subkey, value=values, edit_type=TweakModifyType.PICKER, min_version=min_version, divider_below=divider_below)
|
||||
self.selected_option = 0 # index of the selected option
|
||||
|
||||
def apply_tweak(self, plist: dict):
|
||||
if not self.enabled:
|
||||
return plist
|
||||
new_value = self.value[self.selected_option]
|
||||
if self.subkey == None:
|
||||
plist["CacheExtra"][self.key] = new_value
|
||||
else:
|
||||
plist["CacheExtra"][self.key][self.subkey] = new_value
|
||||
return plist
|
||||
|
||||
def set_selected_option(self, new_option: int):
|
||||
self.selected_option = new_option
|
||||
self.enabled = True
|
||||
|
||||
def get_selected_option(self) -> int:
|
||||
return self.selected_option
|
||||
|
||||
class MobileGestaltMultiTweak(Tweak):
|
||||
def __init__(self, label: str, keyValues: dict, min_version: Version = Version("1.0"), divider_below: bool = False):
|
||||
super().__init__(label=label, key=None, min_version=min_version, divider_below=divider_below)
|
||||
self.keyValues = keyValues
|
||||
# key values looks like ["key name" = value]
|
||||
|
||||
def apply_tweak(self, plist: dict):
|
||||
if not self.enabled:
|
||||
return plist
|
||||
for key in self.keyValues:
|
||||
plist["CacheExtra"][key] = self.keyValues[key]
|
||||
return plist
|
||||
|
||||
class FeatureFlagTweak(Tweak):
|
||||
def __init__(
|
||||
self, label: str,
|
||||
flag_category: str, flag_names: list,
|
||||
is_list: bool=True, inverted: bool=False,
|
||||
min_version: Version = Version("1.0"),
|
||||
divider_below: bool = False
|
||||
):
|
||||
super().__init__(label=label, key=None, min_version=min_version, divider_below=divider_below)
|
||||
self.flag_category = flag_category
|
||||
self.flag_names = flag_names
|
||||
self.is_list = is_list
|
||||
self.inverted = inverted
|
||||
|
||||
def apply_tweak(self, plist: dict):
|
||||
to_enable = self.enabled
|
||||
if self.inverted:
|
||||
to_enable = not self.enabled
|
||||
# create the category list if it doesn't exist
|
||||
if not self.flag_category in plist:
|
||||
plist[self.flag_category] = {}
|
||||
for flag in self.flag_names:
|
||||
if self.is_list:
|
||||
plist[self.flag_category][flag] = {
|
||||
'Enabled': to_enable
|
||||
}
|
||||
else:
|
||||
plist[self.flag_category][flag] = to_enable
|
||||
return plist
|
||||
37
tweaks/tweaks.py
Normal file
@ -0,0 +1,37 @@
|
||||
from devicemanagement.constants import Version
|
||||
from .tweak_classes import MobileGestaltTweak, MobileGestaltMultiTweak, MobileGestaltPickerTweak, FeatureFlagTweak, TweakModifyType
|
||||
from .eligibility_tweak import EligibilityTweak
|
||||
|
||||
|
||||
tweaks = {
|
||||
"DynamicIsland": MobileGestaltPickerTweak("Toggle Dynamic Island", "oPeik/9e8lQWMszEjbPzng", "ArtworkDeviceSubType", [2436, 2556, 2796, 2976, 2622, 2868]),
|
||||
"ModelName": MobileGestaltTweak("Set Device Model Name", "oPeik/9e8lQWMszEjbPzng", "ArtworkDeviceProductDescription", "", TweakModifyType.TEXT),
|
||||
# MobileGestaltTweak("Fix Dynamic Island", "YlEtTtHlNesRBMal1CqRaA"),
|
||||
# MobileGestaltTweak("Set Dynamic Island Location", "Zg7DduDoSCy6vY6mhy3n2w", value="{ x: 390.000000, y: 205.848432, width: 50.000000, height: 105.651573 }"), # not sure what value this is supposed to be but it removes the island currently
|
||||
"BootChime": MobileGestaltTweak("Toggle Boot Chime", "QHxt+hGLaBPbQJbXiUJX3w"),
|
||||
"ChargeLimit": MobileGestaltTweak("Toggle Charge Limit", "37NVydb//GP/GrhuTN+exg"),
|
||||
"CollisionSOS": MobileGestaltTweak("Toggle Collision SOS", "HCzWusHQwZDea6nNhaKndw"),
|
||||
"TapToWake": MobileGestaltTweak("Toggle Tap To Wake (iPhone SE)", "yZf3GTRMGTuwSV/lD7Cagw"),
|
||||
"CameraButton": MobileGestaltMultiTweak("Toggle iPhone 16 Settings", {"CwvKxM2cEogD3p+HYgaW0Q": 1, "oOV1jhJbdV3AddkcCg0AEA": 1}, min_version=Version("18.0")),
|
||||
"Parallax": MobileGestaltTweak("Disable Wallpaper Parallax", "UIParallaxCapability", value=0),
|
||||
"StageManager": MobileGestaltTweak("Toggle Stage Manager Supported (WARNING: risky on some devices, mainly phones)", "qeaj75wk3HF4DwQ8qbIi7g", value=1),
|
||||
"iPadApps": MobileGestaltTweak("Allow iPad Apps on iPhone", "9MZ5AdH43csAUajl/dU+IQ", value=[1, 2]),
|
||||
"Shutter": MobileGestaltMultiTweak("Disable Region Restrictions (ie. Shutter Sound)", {"h63QSdBCiT/z0WU6rdQv6Q": "US", "zHeENZu+wbg7PUprwNwBWg": "LL/A"}),
|
||||
"Pencil": MobileGestaltTweak("Toggle Apple Pencil", "yhHcB0iH0d1XzPO/CFd3ow"),
|
||||
"ActionButton": MobileGestaltTweak("Toggle Action Button", "cT44WE1EohiwRzhsZ8xEsw"),
|
||||
"InternalStorage": MobileGestaltTweak("Toggle Internal Storage (WARNING: risky for some devices, mainly iPads)", "LBJfwOEzExRxzlAnSuI7eg"),
|
||||
"InternalInstall": MobileGestaltTweak("Set as Apple Internal Install (ie Metal HUD in any app)", "EqrsVvjcYDdxHBiQmGhAWw", divider_below=True),
|
||||
"EUEnabler": EligibilityTweak("EU Enabler", divider_below=True),
|
||||
"AOD": MobileGestaltMultiTweak("Always On Display",
|
||||
{"2OOJf1VhaM7NxfRok3HbWQ": 1, "j8/Omm6s1lsmTDFsXjsBfA": 1},
|
||||
min_version=Version("18.0")),
|
||||
"SleepApnea": MobileGestaltTweak("Toggle Sleep Apnea (real) [for apple watches]", "e0HV2blYUDBk/MsMEQACNA", min_version=Version("18.0"), divider_below=True),
|
||||
"ClockAnim": FeatureFlagTweak("Toggle Lockscreen Clock Animation", flag_category='SpringBoard',
|
||||
flag_names=['SwiftUITimeAnimation'],
|
||||
min_version=Version("18.0")),
|
||||
"Lockscreen": FeatureFlagTweak("Toggle Duplicate Lockscreen Button and Lockscreen Quickswitch", flag_category="SpringBoard",
|
||||
flag_names=['AutobahnQuickSwitchTransition', 'SlipSwitch', 'PosterEditorKashida'],
|
||||
min_version=Version("18.0")),
|
||||
"PhotoUI": FeatureFlagTweak("Enable Old Photo UI", flag_category='Photos', flag_names=['Lemonade'], is_list=False, inverted=True, min_version=Version("18.0")),
|
||||
"AI": FeatureFlagTweak("Enable Apple Intelligence", flag_category='SpringBoard', flag_names=['Domino', 'SuperDomino'], min_version=Version("18.1"))
|
||||
}
|
||||