all of nugget's features

This commit is contained in:
leminlimez 2024-09-18 12:41:47 -04:00
parent 347a34e40f
commit df5dd5dc02
73 changed files with 20376 additions and 176 deletions

View File

@ -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
View 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)

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
credits/big_nugget.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

View File

Binary file not shown.

Binary file not shown.

View 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"

View 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

View 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__)

View File

@ -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!")

View File

@ -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)

Binary file not shown.

View 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
View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

27
gui/gestalt_dialog.py Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

3975
qt/mainwindow.ui Normal file

File diff suppressed because it is too large Load Diff

2231
qt/mainwindow_ui.py Normal file

File diff suppressed because it is too large Load Diff

34
qt/resources.qrc Normal file
View 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

File diff suppressed because it is too large Load Diff

View File

@ -1 +1,2 @@
pymobiledevice3
pymobiledevice3
pyside6

9830
resources_rc.py Normal file

File diff suppressed because it is too large Load Diff

0
tweaks/__init__.py Normal file
View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View 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
View 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
View 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"))
}