@@ -60,6 +60,9 @@ Note: I am not responsible if your device bootloops. Please back up your data be
|
||||
- PassBook
|
||||
- Spotlight
|
||||
- Voice Control
|
||||
- PosterBoard: Animated wallpapers and descriptors.
|
||||
- Community wallpapers can be found [here](https://cowabun.ga/wallpapers)
|
||||
- See documentation on the structure of tendies files in `documentation.md`
|
||||
- Risky (Hidden) Options:
|
||||
- Disable thermalmonitord
|
||||
- OTA Killer
|
||||
@@ -118,6 +121,8 @@ If you would like to read more about the inner workings of the exploit and iOS r
|
||||
|
||||
## Credits
|
||||
- [JJTech](https://github.com/JJTech0130) for Sparserestore/[TrollRestore](https://github.com/JJTech0130/TrollRestore)
|
||||
- [PosterRestore](https://discord.gg/gWtzTVhMvh) for their help with PosterBoard
|
||||
- Special thanks to dootskyre, [Middo](https://twitter.com/MWRevamped), [dulark](https://github.com/dularkian), forcequitOS, and pingubow for their work on this. It would not have been possible without them!
|
||||
- [disfordottie](https://x.com/disfordottie) for some global flag features
|
||||
- [Mikasa-san](https://github.com/Mikasa-san) for [Quiet Daemon](https://github.com/Mikasa-san/QuietDaemon)
|
||||
- [sneakyf1shy](https://github.com/f1shy-dev) for [AI Eligibility](https://gist.github.com/f1shy-dev/23b4a78dc283edd30ae2b2e6429129b5) (iOS 18.1 beta 4 and below)
|
||||
|
||||
@@ -23,19 +23,35 @@ class BackupFile:
|
||||
@dataclass
|
||||
class ConcreteFile(BackupFile):
|
||||
contents: bytes
|
||||
src_path: Optional[str] = None
|
||||
owner: int = 0
|
||||
group: int = 0
|
||||
inode: Optional[int] = None
|
||||
mode: _FileMode = DEFAULT
|
||||
|
||||
hash: bytes = None
|
||||
size: int = None
|
||||
|
||||
def read_contents(self) -> bytes:
|
||||
contents = self.contents
|
||||
if self.contents == None:
|
||||
with open(self.src_path, "rb") as in_file:
|
||||
contents = in_file.read()
|
||||
# prepopulate hash and size
|
||||
self.hash = sha1(contents).digest()
|
||||
self.size = len(contents)
|
||||
return contents
|
||||
|
||||
def to_record(self) -> mbdb.MbdbRecord:
|
||||
if self.inode is None:
|
||||
self.inode = int.from_bytes(randbytes(8), "big")
|
||||
if self.hash == None or self.size == None:
|
||||
self.read_contents()
|
||||
return mbdb.MbdbRecord(
|
||||
domain=self.domain,
|
||||
filename=self.path,
|
||||
link="",
|
||||
hash=sha1(self.contents).digest(),
|
||||
hash=self.hash,
|
||||
key=b"",
|
||||
mode=self.mode | _FileMode.S_IFREG,
|
||||
#unknown2=0,
|
||||
@@ -46,7 +62,7 @@ class ConcreteFile(BackupFile):
|
||||
mtime=int(datetime.now().timestamp()),
|
||||
atime=int(datetime.now().timestamp()),
|
||||
ctime=int(datetime.now().timestamp()),
|
||||
size=len(self.contents),
|
||||
size=self.size,
|
||||
flags=4,
|
||||
properties=[]
|
||||
)
|
||||
@@ -108,17 +124,25 @@ class SymbolicLink(BackupFile):
|
||||
flags=4,
|
||||
properties=[]
|
||||
)
|
||||
|
||||
@dataclass
|
||||
class AppBundle:
|
||||
identifier: str
|
||||
path: str
|
||||
container_content_class: str
|
||||
version: str = 804
|
||||
|
||||
@dataclass
|
||||
class Backup:
|
||||
files: list[BackupFile]
|
||||
apps: list[AppBundle]
|
||||
|
||||
def write_to_directory(self, directory: Path):
|
||||
for file in self.files:
|
||||
if isinstance(file, ConcreteFile):
|
||||
#print("Writing", file.path, "to", directory / sha1((file.domain + "-" + file.path).encode()).digest().hex())
|
||||
with open(directory / sha1((file.domain + "-" + file.path).encode()).digest().hex(), "wb") as f:
|
||||
f.write(file.contents)
|
||||
f.write(file.read_contents())
|
||||
|
||||
with open(directory / "Manifest.mbdb", "wb") as f:
|
||||
f.write(self.generate_manifest_db().to_bytes())
|
||||
@@ -150,7 +174,7 @@ class Backup:
|
||||
})
|
||||
|
||||
def generate_manifest(self) -> bytes: # Manifest.plist
|
||||
return plistlib.dumps({
|
||||
plist = {
|
||||
"BackupKeyBag": b64decode("""
|
||||
VkVSUwAAAAQAAAAFVFlQRQAAAAQAAAABVVVJRAAAABDud41d1b9NBICR1BH9JfVtSE1D
|
||||
SwAAACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAV1JBUAAA
|
||||
@@ -182,4 +206,16 @@ class Backup:
|
||||
"Lockdown": {},
|
||||
"SystemDomainsVersion": "20.0",
|
||||
"Version": "9.1"
|
||||
})
|
||||
}
|
||||
# add the apps
|
||||
if len(self.apps) > 0:
|
||||
plist["Applications"] = {}
|
||||
for app in self.apps:
|
||||
appInfo = {
|
||||
"CFBundleIdentifier": app.identifier,
|
||||
"CFBundleVersion": app.version,
|
||||
"ContainerContentClass": app.container_content_class,
|
||||
"Path": app.path
|
||||
}
|
||||
plist["Applications"][app.identifier] = appInfo
|
||||
return plistlib.dumps(plist)
|
||||
@@ -1,10 +1,12 @@
|
||||
from . import backup, perform_restore
|
||||
from pymobiledevice3.lockdown import LockdownClient
|
||||
from pymobiledevice3.services.installation_proxy import InstallationProxyService
|
||||
import os
|
||||
|
||||
class FileToRestore:
|
||||
def __init__(self, contents: str, restore_path: str, domain: str = None, owner: int = 501, group: int = 501):
|
||||
def __init__(self, contents: str, restore_path: str, contents_path: str = None, domain: str = None, owner: int = 501, group: int = 501):
|
||||
self.contents = contents
|
||||
self.contents_path = contents_path
|
||||
self.restore_path = restore_path
|
||||
self.domain = domain
|
||||
self.owner = owner
|
||||
@@ -74,7 +76,8 @@ def concat_regular_file(file: FileToRestore, files_list: list[FileToRestore], la
|
||||
file.domain,
|
||||
owner=file.owner,
|
||||
group=file.group,
|
||||
contents=file.contents
|
||||
contents=file.contents,
|
||||
src_path=file.contents_path
|
||||
))
|
||||
return new_last_domain, full_path
|
||||
|
||||
@@ -83,6 +86,9 @@ def restore_files(files: list, reboot: bool = False, lockdown_client: LockdownCl
|
||||
# create the files to be backed up
|
||||
files_list = [
|
||||
]
|
||||
apps_list = []
|
||||
active_bundle_ids = []
|
||||
apps = None
|
||||
sorted_files = sorted(files, key=lambda x: x.restore_path, reverse=False)
|
||||
# add the file paths
|
||||
last_domain = ""
|
||||
@@ -94,13 +100,27 @@ def restore_files(files: list, reboot: bool = False, lockdown_client: LockdownCl
|
||||
else:
|
||||
last_domain, last_path = concat_regular_file(file, files_list, last_domain, last_path)
|
||||
exploit_only = False
|
||||
# add the app bundle to the list
|
||||
if last_domain.startswith("AppDomain"):
|
||||
bundle_id = last_domain.removeprefix("AppDomain-")
|
||||
if not bundle_id in active_bundle_ids:
|
||||
if apps == None:
|
||||
apps = InstallationProxyService(lockdown=lockdown_client).get_apps(application_type="Any", calculate_sizes=False)
|
||||
app_info = apps[bundle_id]
|
||||
active_bundle_ids.append(bundle_id)
|
||||
apps_list.append(backup.AppBundle(
|
||||
identifier=bundle_id,
|
||||
path=app_info["Container"],
|
||||
version=app_info["CFBundleVersion"],
|
||||
container_content_class="Data/Application"
|
||||
))
|
||||
|
||||
# crash the restore to skip the setup (only works for exploit files)
|
||||
if exploit_only:
|
||||
files_list.append(backup.ConcreteFile("", "SysContainerDomain-../../../../../../../.." + "/crash_on_purpose", contents=b""))
|
||||
|
||||
# create the backup
|
||||
back = backup.Backup(files=files_list)
|
||||
back = backup.Backup(files=files_list, apps=apps_list)
|
||||
|
||||
perform_restore(backup=back, reboot=reboot, lockdown_client=lockdown_client)
|
||||
|
||||
|
||||
16
controllers/plist_handler.py
Normal file
@@ -0,0 +1,16 @@
|
||||
import plistlib
|
||||
|
||||
def recursive_set(plist: dict, key: str, value: any):
|
||||
new_plist: dict = plist
|
||||
for k, v in plist.items():
|
||||
if k == key:
|
||||
new_plist[k] = value
|
||||
elif isinstance(v, dict):
|
||||
new_plist[k] = recursive_set(v, key, value)
|
||||
return new_plist
|
||||
|
||||
def set_plist_value(file: str, key: str, value: any):
|
||||
with open(file, 'rb') as in_fp:
|
||||
plist = plistlib.load(in_fp)
|
||||
new_plist = recursive_set(plist, key, value)
|
||||
return plistlib.dumps(new_plist)
|
||||
@@ -2,8 +2,9 @@ from enum import Enum
|
||||
from pymobiledevice3.lockdown import LockdownClient
|
||||
|
||||
class Device:
|
||||
def __init__(self, uuid: int, name: str, version: str, build: str, model: str, hardware: str, cpu: str, locale: str, ld: LockdownClient):
|
||||
def __init__(self, uuid: int, usb: bool, name: str, version: str, build: str, model: str, hardware: str, cpu: str, locale: str, ld: LockdownClient):
|
||||
self.uuid = uuid
|
||||
self.connected_via_usb = usb
|
||||
self.name = name
|
||||
self.version = version
|
||||
self.build = build
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import traceback
|
||||
import plistlib
|
||||
from pathlib import Path
|
||||
from tempfile import TemporaryDirectory
|
||||
|
||||
from PySide6.QtWidgets import QMessageBox
|
||||
from PySide6.QtCore import QSettings
|
||||
@@ -14,6 +14,7 @@ from devicemanagement.data_singleton import DataSingleton
|
||||
|
||||
from tweaks.tweaks import tweaks, FeatureFlagTweak, EligibilityTweak, AITweak, BasicPlistTweak, AdvancedPlistTweak, RdarFixTweak, NullifyFileTweak
|
||||
from tweaks.custom_gestalt_tweaks import CustomGestaltTweaks
|
||||
from tweaks.posterboard_tweak import PosterboardTweak
|
||||
from tweaks.basic_plist_locations import FileLocationsList, RiskyFileLocationsList
|
||||
from Sparserestore.restore import restore_files, FileToRestore
|
||||
|
||||
@@ -108,6 +109,7 @@ class DeviceManager:
|
||||
pass
|
||||
dev = Device(
|
||||
uuid=device.serial,
|
||||
usb=device.is_usb,
|
||||
name=vals['DeviceName'],
|
||||
version=vals['ProductVersion'],
|
||||
build=vals['BuildVersion'],
|
||||
@@ -285,137 +287,145 @@ class DeviceManager:
|
||||
|
||||
## 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
|
||||
ai_file = None
|
||||
basic_plists: dict = {}
|
||||
basic_plists_ownership: dict = {}
|
||||
files_data: dict = {}
|
||||
uses_domains: bool = False
|
||||
try:
|
||||
# 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
|
||||
ai_file = None
|
||||
basic_plists: dict = {}
|
||||
basic_plists_ownership: dict = {}
|
||||
files_data: dict = {}
|
||||
uses_domains: bool = False
|
||||
# create the restore file list
|
||||
files_to_restore: dict[FileToRestore] = [
|
||||
]
|
||||
tmp_pb_dir = None # temporary directory for unzipping pb files
|
||||
|
||||
# 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()
|
||||
elif isinstance(tweak, AITweak):
|
||||
ai_file = tweak.apply_tweak()
|
||||
elif isinstance(tweak, BasicPlistTweak) or isinstance(tweak, RdarFixTweak) or isinstance(tweak, AdvancedPlistTweak):
|
||||
basic_plists = tweak.apply_tweak(basic_plists, self.allow_risky_tweaks)
|
||||
basic_plists_ownership[tweak.file_location] = tweak.owner
|
||||
if tweak.enabled and tweak.owner == 0:
|
||||
uses_domains = True
|
||||
elif isinstance(tweak, NullifyFileTweak):
|
||||
tweak.apply_tweak(files_data)
|
||||
if tweak.enabled and tweak.file_location.value.startswith("/var/mobile/"):
|
||||
uses_domains = True
|
||||
else:
|
||||
if gestalt_plist != None:
|
||||
gestalt_plist = tweak.apply_tweak(gestalt_plist)
|
||||
elif tweak.enabled:
|
||||
# no mobilegestalt file provided but applying mga tweaks, give warning
|
||||
show_error_msg("No mobilegestalt file provided! Please select your file to apply mobilegestalt tweaks.")
|
||||
update_label("Failed.")
|
||||
return
|
||||
# set the custom gestalt keys
|
||||
if gestalt_plist != None:
|
||||
gestalt_plist = CustomGestaltTweaks.apply_tweaks(gestalt_plist)
|
||||
|
||||
gestalt_data = None
|
||||
if resetting:
|
||||
gestalt_data = b""
|
||||
elif gestalt_plist != None:
|
||||
gestalt_data = plistlib.dumps(gestalt_plist)
|
||||
|
||||
# Generate backup
|
||||
update_label("Generating backup...")
|
||||
# create the restore file list
|
||||
files_to_restore: dict[FileToRestore] = [
|
||||
]
|
||||
self.concat_file(
|
||||
contents=plistlib.dumps(flag_plist),
|
||||
path="/var/preferences/FeatureFlags/Global.plist",
|
||||
files_to_restore=files_to_restore
|
||||
)
|
||||
self.add_skip_setup(files_to_restore, uses_domains)
|
||||
if gestalt_data != 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()
|
||||
elif isinstance(tweak, AITweak):
|
||||
ai_file = tweak.apply_tweak()
|
||||
elif isinstance(tweak, BasicPlistTweak) or isinstance(tweak, RdarFixTweak) or isinstance(tweak, AdvancedPlistTweak):
|
||||
basic_plists = tweak.apply_tweak(basic_plists, self.allow_risky_tweaks)
|
||||
basic_plists_ownership[tweak.file_location] = tweak.owner
|
||||
if tweak.enabled and tweak.owner == 0:
|
||||
uses_domains = True
|
||||
elif isinstance(tweak, NullifyFileTweak):
|
||||
tweak.apply_tweak(files_data)
|
||||
if tweak.enabled and tweak.file_location.value.startswith("/var/mobile/"):
|
||||
uses_domains = True
|
||||
elif isinstance(tweak, PosterboardTweak):
|
||||
tmp_pb_dir = TemporaryDirectory()
|
||||
tweak.apply_tweak(files_to_restore=files_to_restore, output_dir=tmp_pb_dir.name)
|
||||
else:
|
||||
if gestalt_plist != None:
|
||||
gestalt_plist = tweak.apply_tweak(gestalt_plist)
|
||||
elif tweak.enabled:
|
||||
# no mobilegestalt file provided but applying mga tweaks, give warning
|
||||
show_error_msg("No mobilegestalt file provided! Please select your file to apply mobilegestalt tweaks.")
|
||||
update_label("Failed.")
|
||||
return
|
||||
# set the custom gestalt keys
|
||||
if gestalt_plist != None:
|
||||
gestalt_plist = CustomGestaltTweaks.apply_tweaks(gestalt_plist)
|
||||
|
||||
gestalt_data = None
|
||||
if resetting:
|
||||
gestalt_data = b""
|
||||
elif gestalt_plist != None:
|
||||
gestalt_data = plistlib.dumps(gestalt_plist)
|
||||
|
||||
# Generate backup
|
||||
update_label("Generating backup...")
|
||||
self.concat_file(
|
||||
contents=gestalt_data,
|
||||
path="/var/containers/Shared/SystemGroup/systemgroup.com.apple.mobilegestaltcache/Library/Caches/com.apple.MobileGestalt.plist",
|
||||
contents=plistlib.dumps(flag_plist),
|
||||
path="/var/preferences/FeatureFlags/Global.plist",
|
||||
files_to_restore=files_to_restore
|
||||
)
|
||||
if eligibility_files:
|
||||
new_eligibility_files: dict[FileToRestore] = []
|
||||
if not self.get_current_device_supported():
|
||||
# update the files
|
||||
for file in eligibility_files:
|
||||
self.concat_file(
|
||||
contents=file.contents,
|
||||
path=file.restore_path,
|
||||
files_to_restore=new_eligibility_files
|
||||
)
|
||||
else:
|
||||
new_eligibility_files = eligibility_files
|
||||
files_to_restore += new_eligibility_files
|
||||
if ai_file != None:
|
||||
self.concat_file(
|
||||
contents=ai_file.contents,
|
||||
path=ai_file.restore_path,
|
||||
files_to_restore=files_to_restore
|
||||
)
|
||||
for location, plist in basic_plists.items():
|
||||
ownership = basic_plists_ownership[location]
|
||||
self.concat_file(
|
||||
contents=plistlib.dumps(plist),
|
||||
path=location.value,
|
||||
files_to_restore=files_to_restore,
|
||||
owner=ownership, group=ownership
|
||||
)
|
||||
for location, data in files_data.items():
|
||||
self.concat_file(
|
||||
contents=data,
|
||||
path=location.value,
|
||||
files_to_restore=files_to_restore,
|
||||
owner=ownership, group=ownership
|
||||
)
|
||||
# reset basic tweaks
|
||||
if resetting:
|
||||
empty_data = plistlib.dumps({})
|
||||
for location in FileLocationsList:
|
||||
self.add_skip_setup(files_to_restore, uses_domains)
|
||||
if gestalt_data != None:
|
||||
self.concat_file(
|
||||
contents=empty_data,
|
||||
path=location.value,
|
||||
contents=gestalt_data,
|
||||
path="/var/containers/Shared/SystemGroup/systemgroup.com.apple.mobilegestaltcache/Library/Caches/com.apple.MobileGestalt.plist",
|
||||
files_to_restore=files_to_restore
|
||||
)
|
||||
if self.allow_risky_tweaks:
|
||||
for location in RiskyFileLocationsList:
|
||||
if eligibility_files:
|
||||
new_eligibility_files: dict[FileToRestore] = []
|
||||
if not self.get_current_device_supported():
|
||||
# update the files
|
||||
for file in eligibility_files:
|
||||
self.concat_file(
|
||||
contents=file.contents,
|
||||
path=file.restore_path,
|
||||
files_to_restore=new_eligibility_files
|
||||
)
|
||||
else:
|
||||
new_eligibility_files = eligibility_files
|
||||
files_to_restore += new_eligibility_files
|
||||
if ai_file != None:
|
||||
self.concat_file(
|
||||
contents=ai_file.contents,
|
||||
path=ai_file.restore_path,
|
||||
files_to_restore=files_to_restore
|
||||
)
|
||||
for location, plist in basic_plists.items():
|
||||
ownership = basic_plists_ownership[location]
|
||||
self.concat_file(
|
||||
contents=plistlib.dumps(plist),
|
||||
path=location.value,
|
||||
files_to_restore=files_to_restore,
|
||||
owner=ownership, group=ownership
|
||||
)
|
||||
for location, data in files_data.items():
|
||||
self.concat_file(
|
||||
contents=data,
|
||||
path=location.value,
|
||||
files_to_restore=files_to_restore,
|
||||
owner=ownership, group=ownership
|
||||
)
|
||||
# reset basic tweaks
|
||||
if resetting:
|
||||
empty_data = plistlib.dumps({})
|
||||
for location in FileLocationsList:
|
||||
self.concat_file(
|
||||
contents=empty_data,
|
||||
path=location.value,
|
||||
files_to_restore=files_to_restore
|
||||
)
|
||||
if self.allow_risky_tweaks:
|
||||
for location in RiskyFileLocationsList:
|
||||
self.concat_file(
|
||||
contents=empty_data,
|
||||
path=location.value,
|
||||
files_to_restore=files_to_restore
|
||||
)
|
||||
|
||||
# restore to the device
|
||||
update_label("Restoring to device...")
|
||||
try:
|
||||
# restore to the device
|
||||
update_label("Restoring to device...")
|
||||
restore_files(files=files_to_restore, reboot=self.auto_reboot, lockdown_client=self.data_singleton.current_device.ld)
|
||||
if tmp_pb_dir != None:
|
||||
tmp_pb_dir.cleanup()
|
||||
msg = "Your device will now restart."
|
||||
if not self.auto_reboot:
|
||||
msg = "Please restart your device to see changes."
|
||||
QMessageBox.information(None, "Success!", "All done! " + msg)
|
||||
update_label("Success!")
|
||||
except Exception as e:
|
||||
if tmp_pb_dir != None:
|
||||
tmp_pb_dir.cleanup()
|
||||
show_apply_error(e, update_label)
|
||||
|
||||
## RESETTING MOBILE GESTALT
|
||||
|
||||
12
documentation.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# Documentation
|
||||
## Tendies Files (PosterBoard wallpapers)
|
||||
Tendies files store the file structure to be restored to PosterBoard.
|
||||
|
||||
There are 2 formats for these:
|
||||
1. Container format: a containing folder has the name "container"
|
||||
- This restores directly to the app container inside of /var/mobile/Containers/Applications/PosterBoard.app and will keep that file structure.
|
||||
- Descriptor UUIDs and wallpaper IDs will not be randomized using this format.
|
||||
2. Descriptor format: a containing folder has the name "descriptor" or "descriptors"
|
||||
- This restores to descriptors inside the container. Currently, it restores to the 61 folder, but in future versions it may be handled by iOS version if needed in future versions. If the structure also changes, this may be automatically handled by Nugget in future versions.
|
||||
- Descriptor UUIDs and wallpaper IDs will be randomized, preventing overlapping.
|
||||
- It is recommended to use these if you are restoring descriptors to collections since this will be more future proof. Randomization of IDs is also safer.
|
||||
@@ -1,5 +1,6 @@
|
||||
from PySide6.QtWidgets import QDialog, QDialogButtonBox, QLabel, QVBoxLayout
|
||||
from PySide6.QtGui import QFont
|
||||
from PySide6.QtWidgets import QDialog, QDialogButtonBox, QLabel, QVBoxLayout, QHBoxLayout, QWidget, QToolButton, QSizePolicy
|
||||
from PySide6.QtGui import QFont, QIcon, QPixmap
|
||||
from PySide6.QtCore import QSize
|
||||
|
||||
from webbrowser import open_new_tab
|
||||
|
||||
@@ -32,6 +33,41 @@ class GestaltDialog(QDialog):
|
||||
super().accept()
|
||||
|
||||
|
||||
class PBHelpDialog(QDialog):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
QBtn = (
|
||||
QDialogButtonBox.Ok
|
||||
)
|
||||
self.buttonBox = QDialogButtonBox(QBtn)
|
||||
self.buttonBox.accepted.connect(self.accept)
|
||||
self.setWindowTitle("PosterBoard Info")
|
||||
|
||||
layout = QVBoxLayout()
|
||||
message = QLabel("Descriptors will be under the Collections section when adding a new wallpaper.\n\nIf the wallpapers don't appear in the menu, you either have to wait a bit for them to load,\nor you've reached the maximum amount of wallpapers (15) and have to wipe them.")
|
||||
layout.addWidget(message)
|
||||
|
||||
imgBox = QWidget()
|
||||
imgBox.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
||||
imgBox.setStyleSheet("QWidget { background: none; padding: 0px; border: none; }")
|
||||
hlayout = QHBoxLayout()
|
||||
tut1 = QToolButton()
|
||||
tut1.setIconSize(QSize(138, 300))
|
||||
tut1.setStyleSheet("QToolButton { background: none; padding: 0px; border: none; }")
|
||||
tut1.setIcon(QIcon(QPixmap(":/gui/pb_tutorial1.png")))
|
||||
hlayout.addWidget(tut1)
|
||||
tut2 = QToolButton()
|
||||
tut2.setIconSize(QSize(138, 300))
|
||||
tut2.setStyleSheet("QToolButton { background: none; padding: 0px; border: none; }")
|
||||
tut2.setIcon(QIcon(QPixmap(":/gui/pb_tutorial2.png")))
|
||||
hlayout.addWidget(tut2)
|
||||
imgBox.setLayout(hlayout)
|
||||
|
||||
layout.addWidget(imgBox)
|
||||
layout.addWidget(self.buttonBox)
|
||||
self.setLayout(layout)
|
||||
|
||||
|
||||
class UpdateAppDialog(QDialog):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
|
||||
@@ -12,13 +12,13 @@ from controllers.web_request_handler import is_update_available
|
||||
from devicemanagement.constants import Version
|
||||
from devicemanagement.device_manager import DeviceManager
|
||||
|
||||
from gui.dialogs import GestaltDialog, UpdateAppDialog
|
||||
from gui.dialogs import GestaltDialog, UpdateAppDialog, PBHelpDialog
|
||||
|
||||
from tweaks.tweaks import tweaks
|
||||
from tweaks.custom_gestalt_tweaks import CustomGestaltTweaks, ValueTypeStrings
|
||||
from tweaks.daemons_tweak import Daemon
|
||||
|
||||
App_Version = "4.2.3"
|
||||
App_Version = "5.0"
|
||||
App_Build = 0
|
||||
|
||||
class Page(Enum):
|
||||
@@ -29,9 +29,10 @@ class Page(Enum):
|
||||
Springboard = 4
|
||||
InternalOptions = 5
|
||||
Daemons = 6
|
||||
RiskyTweaks = 7
|
||||
Apply = 8
|
||||
Settings = 9
|
||||
Posterboard = 7
|
||||
RiskyTweaks = 8
|
||||
Apply = 9
|
||||
Settings = 10
|
||||
|
||||
class MainWindow(QtWidgets.QMainWindow):
|
||||
def __init__(self, device_manager: DeviceManager):
|
||||
@@ -40,6 +41,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||
self.ui = Ui_Nugget()
|
||||
self.ui.setupUi(self)
|
||||
self.show_uuid = False
|
||||
self.pb_mainLayout = None
|
||||
self.loadSettings()
|
||||
|
||||
# Check for an update
|
||||
@@ -63,6 +65,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||
self.ui.springboardOptionsPageBtn.clicked.connect(self.on_springboardOptionsPageBtn_clicked)
|
||||
self.ui.internalOptionsPageBtn.clicked.connect(self.on_internalOptionsPageBtn_clicked)
|
||||
self.ui.daemonsPageBtn.clicked.connect(self.on_daemonsPageBtn_clicked)
|
||||
self.ui.posterboardPageBtn.clicked.connect(self.on_posterboardPageBtn_clicked)
|
||||
self.ui.advancedPageBtn.clicked.connect(self.on_advancedPageBtn_clicked)
|
||||
self.ui.applyPageBtn.clicked.connect(self.on_applyPageBtn_clicked)
|
||||
self.ui.settingsPageBtn.clicked.connect(self.on_settingsPageBtn_clicked)
|
||||
@@ -78,11 +81,12 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||
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.posterRestoreBtn.clicked.connect(self.on_posterRestoreBtn_clicked)
|
||||
self.ui.disfordottieBtn.clicked.connect(self.on_disfordottieBtn_clicked)
|
||||
self.ui.mikasaBtn.clicked.connect(self.on_mikasaBtn_clicked)
|
||||
|
||||
self.ui.libiBtn.clicked.connect(self.on_libiBtn_clicked)
|
||||
self.ui.jjtechBtn.clicked.connect(self.on_jjtechBtn_clicked)
|
||||
self.ui.qtBtn.clicked.connect(self.on_qtBtn_clicked)
|
||||
|
||||
self.ui.discordBtn.clicked.connect(self.on_discordBtn_clicked)
|
||||
@@ -155,6 +159,14 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||
self.ui.spotlightChk.toggled.connect(self.on_spotlightChk_clicked)
|
||||
self.ui.voiceControlChk.toggled.connect(self.on_voiceControlChk_clicked)
|
||||
|
||||
## POSTERBOARD PAGE ACTIONS
|
||||
self.ui.modifyPosterboardsChk.toggled.connect(self.on_modifyPosterboardsChk_clicked)
|
||||
self.ui.importTendiesBtn.clicked.connect(self.on_importTendiesBtn_clicked)
|
||||
self.ui.resetPRBExtBtn.clicked.connect(self.on_resetPRBExtBtn_clicked)
|
||||
self.ui.deleteAllDescriptorsBtn.clicked.connect(self.on_deleteAllDescriptorsBtn_clicked)
|
||||
self.ui.findPBBtn.clicked.connect(self.on_findPBBtn_clicked)
|
||||
self.ui.pbHelpBtn.clicked.connect(self.on_pbHelpBtn_clicked)
|
||||
|
||||
## RISKY OPTIONS PAGE ACTIONS
|
||||
self.ui.disableOTAChk.toggled.connect(self.on_disableOTAChk_clicked)
|
||||
self.ui.enableResolutionChk.toggled.connect(self.on_enableResolutionChk_clicked)
|
||||
@@ -210,6 +222,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||
self.ui.resChangerContent.hide()
|
||||
self.ui.resHeightWarningLbl.hide()
|
||||
self.ui.resWidthWarningLbl.hide()
|
||||
self.ui.pbActionLbl.hide()
|
||||
|
||||
|
||||
## GENERAL INTERFACE FUNCTIONS
|
||||
@@ -250,6 +263,7 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||
self.ui.springboardOptionsPageBtn.hide()
|
||||
self.ui.internalOptionsPageBtn.hide()
|
||||
self.ui.daemonsPageBtn.hide()
|
||||
self.ui.posterboardPageBtn.hide()
|
||||
self.ui.advancedPageBtn.hide()
|
||||
|
||||
self.ui.sidebarDiv2.hide()
|
||||
@@ -261,18 +275,27 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||
self.ui.devicePicker.setEnabled(True)
|
||||
# populate the ComboBox with device names
|
||||
for device in self.device_manager.devices:
|
||||
self.ui.devicePicker.addItem(device.name)
|
||||
tag = ""
|
||||
if self.device_manager.apply_over_wifi:
|
||||
if device.connected_via_usb:
|
||||
tag = " (@ USB)"
|
||||
else:
|
||||
tag = " (@ WiFi)"
|
||||
self.ui.devicePicker.addItem(f"{device.name}{tag}")
|
||||
|
||||
# show all pages
|
||||
self.ui.sidebarDiv1.show()
|
||||
self.ui.springboardOptionsPageBtn.show()
|
||||
self.ui.internalOptionsPageBtn.show()
|
||||
self.ui.daemonsPageBtn.show()
|
||||
self.ui.posterboardPageBtn.show()
|
||||
|
||||
if self.device_manager.allow_risky_tweaks:
|
||||
self.ui.advancedPageBtn.show()
|
||||
self.ui.resetPRBExtBtn.show()
|
||||
else:
|
||||
self.ui.advancedPageBtn.hide()
|
||||
self.ui.resetPRBExtBtn.hide()
|
||||
|
||||
self.ui.sidebarDiv2.show()
|
||||
self.ui.applyPageBtn.show()
|
||||
@@ -451,6 +474,9 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||
def on_daemonsPageBtn_clicked(self):
|
||||
self.ui.pages.setCurrentIndex(Page.Daemons.value)
|
||||
|
||||
def on_posterboardPageBtn_clicked(self):
|
||||
self.ui.pages.setCurrentIndex(Page.Posterboard.value)
|
||||
|
||||
def on_advancedPageBtn_clicked(self):
|
||||
self.ui.pages.setCurrentIndex(Page.RiskyTweaks.value)
|
||||
|
||||
@@ -513,8 +539,8 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||
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_posterRestoreBtn_clicked(self):
|
||||
webbrowser.open_new_tab("https://discord.gg/gWtzTVhMvh")
|
||||
def on_disfordottieBtn_clicked(self):
|
||||
webbrowser.open_new_tab("https://twitter.com/disfordottie")
|
||||
def on_mikasaBtn_clicked(self):
|
||||
@@ -522,6 +548,8 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||
|
||||
def on_libiBtn_clicked(self):
|
||||
webbrowser.open_new_tab("https://github.com/doronz88/pymobiledevice3")
|
||||
def on_jjtechBtn_clicked(self):
|
||||
webbrowser.open_new_tab("https://github.com/JJTech0130/TrollRestore")
|
||||
def on_qtBtn_clicked(self):
|
||||
webbrowser.open_new_tab("https://www.qt.io/product/development-tools")
|
||||
|
||||
@@ -815,6 +843,123 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||
def on_voiceControlChk_clicked(self, checked: bool):
|
||||
tweaks["Daemons"].set_multiple_values(Daemon.VoiceControl.value, value=checked)
|
||||
|
||||
## PosterBoard Page
|
||||
def delete_pb_file(self, file, widget):
|
||||
if file in tweaks["PosterBoard"].tendies:
|
||||
tweaks["PosterBoard"].tendies.remove(file)
|
||||
widget.deleteLater()
|
||||
|
||||
def load_posterboard(self):
|
||||
if len(tweaks["PosterBoard"].tendies) == 0:
|
||||
return
|
||||
|
||||
if self.pb_mainLayout == None:
|
||||
# Create scroll layout
|
||||
self.pb_mainLayout = QtWidgets.QVBoxLayout()
|
||||
self.pb_mainLayout.setContentsMargins(0, 0, 0, 0)
|
||||
self.pb_mainLayout.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop)
|
||||
# Create a QWidget to act as the container for the scroll area
|
||||
scrollWidget = QtWidgets.QWidget()
|
||||
|
||||
# Set the main layout (containing all the widgets) on the scroll widget
|
||||
scrollWidget.setLayout(self.pb_mainLayout)
|
||||
|
||||
# Create a QScrollArea to hold the content widget (scrollWidget)
|
||||
scrollArea = QtWidgets.QScrollArea()
|
||||
scrollArea.setWidgetResizable(True) # Allow the content widget to resize within the scroll area
|
||||
scrollArea.setFrameStyle(QtWidgets.QScrollArea.NoFrame) # Remove the outline from the scroll area
|
||||
|
||||
# Set the scrollWidget as the content widget of the scroll area
|
||||
scrollArea.setWidget(scrollWidget)
|
||||
|
||||
# Set the size policy of the scroll area to expand in both directions
|
||||
scrollArea.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
|
||||
|
||||
# Set the scroll area as the central widget of the main window
|
||||
scrollLayout = QtWidgets.QVBoxLayout()
|
||||
scrollLayout.setContentsMargins(0, 0, 0, 0)
|
||||
scrollLayout.addWidget(scrollArea)
|
||||
self.ui.pbFilesList.setLayout(scrollLayout)
|
||||
|
||||
widgets = {}
|
||||
# Iterate through the files
|
||||
for tendie in tweaks["PosterBoard"].tendies:
|
||||
if tendie.loaded:
|
||||
continue
|
||||
widget = QtWidgets.QWidget()
|
||||
widgets[tendie] = widget
|
||||
|
||||
# create the icon/label
|
||||
titleBtn = QtWidgets.QToolButton(widget)
|
||||
titleBtn.setIcon(QtGui.QIcon(tendie.get_icon()))
|
||||
titleBtn.setIconSize(QtCore.QSize(20, 20))
|
||||
titleBtn.setText(f" {tendie.name}")
|
||||
titleBtn.setStyleSheet("QToolButton {\n background-color: transparent;\n icon-size: 20px;\n}")
|
||||
titleBtn.setToolButtonStyle(QtCore.Qt.ToolButtonStyle.ToolButtonTextBesideIcon)
|
||||
titleBtn.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
|
||||
|
||||
delBtn = QtWidgets.QToolButton(widget)
|
||||
delBtn.setIcon(QtGui.QIcon(":/icon/trash.svg"))
|
||||
delBtn.clicked.connect(lambda _, file=tendie: (widgets[file].deleteLater(), tweaks["PosterBoard"].tendies.remove(file)))
|
||||
|
||||
spacer = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
|
||||
# main layout
|
||||
layout = QtWidgets.QHBoxLayout(widget)
|
||||
layout.setContentsMargins(0, 0, 0, 9)
|
||||
layout.addWidget(titleBtn)
|
||||
layout.addItem(spacer)
|
||||
layout.addWidget(delBtn)
|
||||
# Add the widget to the mainLayout
|
||||
widget.setLayout(layout)
|
||||
self.pb_mainLayout.addWidget(widget)
|
||||
tendie.loaded = True
|
||||
|
||||
def on_modifyPosterboardsChk_clicked(self, checked: bool):
|
||||
tweaks["PosterBoard"].set_enabled(checked)
|
||||
self.ui.posterboardPageContent.setDisabled(not checked)
|
||||
def on_importTendiesBtn_clicked(self):
|
||||
selected_files, _ = QtWidgets.QFileDialog.getOpenFileNames(self, "Select PosterBoard Files", "", "Zip Files (*.tendies)", options=QtWidgets.QFileDialog.ReadOnly)
|
||||
tweaks["PosterBoard"].resetting = False
|
||||
self.ui.pbActionLbl.hide()
|
||||
if selected_files != None and len(selected_files) > 0:
|
||||
# user selected files, add them
|
||||
for file in selected_files:
|
||||
if not tweaks["PosterBoard"].add_tendie(file):
|
||||
# alert that there are too many
|
||||
detailsBox = QtWidgets.QMessageBox()
|
||||
detailsBox.setIcon(QtWidgets.QMessageBox.Critical)
|
||||
detailsBox.setWindowTitle("Error!")
|
||||
detailsBox.setText("You selected too many descriptors! The limit is 10.")
|
||||
detailsBox.exec()
|
||||
self.load_posterboard()
|
||||
|
||||
def on_deleteAllDescriptorsBtn_clicked(self):
|
||||
if tweaks["PosterBoard"].resetting and tweaks["PosterBoard"].resetType == 0:
|
||||
tweaks["PosterBoard"].resetting = False
|
||||
self.ui.pbActionLbl.hide()
|
||||
else:
|
||||
tweaks["PosterBoard"].resetting = True
|
||||
tweaks["PosterBoard"].resetType = 0
|
||||
self.ui.pbActionLbl.setText("! Clearing Collections Wallpapers")
|
||||
self.ui.pbActionLbl.show()
|
||||
def on_resetPRBExtBtn_clicked(self):
|
||||
if tweaks["PosterBoard"].resetting and tweaks["PosterBoard"].resetType == 1:
|
||||
tweaks["PosterBoard"].resetting = False
|
||||
self.ui.pbActionLbl.hide()
|
||||
else:
|
||||
tweaks["PosterBoard"].resetting = True
|
||||
tweaks["PosterBoard"].resetType = 1
|
||||
self.ui.pbActionLbl.setText("! Resetting PRB Extension")
|
||||
self.ui.pbActionLbl.show()
|
||||
|
||||
def on_findPBBtn_clicked(self):
|
||||
webbrowser.open_new_tab("https://cowabun.ga/wallpapers")
|
||||
|
||||
def on_pbHelpBtn_clicked(self):
|
||||
dialog = PBHelpDialog()
|
||||
dialog.exec()
|
||||
|
||||
|
||||
## Risky Options Page
|
||||
def on_disableOTAChk_clicked(self, checked: bool):
|
||||
tweaks["DisableOTAFile"].set_enabled(checked)
|
||||
@@ -864,8 +1009,10 @@ class MainWindow(QtWidgets.QMainWindow):
|
||||
# toggle the button visibility
|
||||
if checked:
|
||||
self.ui.advancedPageBtn.show()
|
||||
self.ui.resetPRBExtBtn.show()
|
||||
else:
|
||||
self.ui.advancedPageBtn.hide()
|
||||
self.ui.resetPRBExtBtn.hide()
|
||||
def on_showAllSpoofableChk_toggled(self, checked: bool):
|
||||
self.device_manager.show_all_spoofable_models = checked
|
||||
# save the setting
|
||||
|
||||
BIN
gui/pb_tutorial1.png
Normal file
|
After Width: | Height: | Size: 420 KiB |
BIN
gui/pb_tutorial2.png
Normal file
|
After Width: | Height: | Size: 3.0 MiB |
11
icon/arrow.clockwise.svg
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--Generator: Apple Native CoreSVG 326-->
|
||||
<!DOCTYPE svg
|
||||
PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 174.5 233.875">
|
||||
<g>
|
||||
<rect height="233.875" opacity="0" width="174.5" x="0" y="0"/>
|
||||
<path d="M0 124.375C0 172.5 39 211.625 87.25 211.625C135.5 211.625 174.5 172.5 174.5 124.375C174.5 118.75 170.625 114.875 165.125 114.875C159.875 114.875 156.5 118.75 156.5 124.25C156.5 162.5 125.5 193.375 87.25 193.375C49 193.375 18 162.5 18 124.25C18 86 49 55 87.25 55C93.75 55 99.625 55.25 104.875 56.375L78.375 82.375C76.625 84 75.875 86.375 75.875 88.625C75.875 93.75 79.75 97.625 84.625 97.625C87.5 97.625 89.5 96.625 91.125 95.125L130.625 55.625C132.375 53.875 133.25 51.75 133.25 49.125C133.25 46.75 132.25 44.375 130.625 42.75L91.125 2.75C89.625 1 87.375 0 84.625 0C79.75 0 75.875 4.125 75.875 9.25C75.875 11.625 76.75 13.875 78.25 15.625L101.25 38.375C96.75 37.5 92 37.125 87.25 37.125C39 37.125 0 76.125 0 124.375Z" fill="#ff453a"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
11
icon/globe.svg
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--Generator: Apple Native CoreSVG 326-->
|
||||
<!DOCTYPE svg
|
||||
PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 201.875 201.875">
|
||||
<g>
|
||||
<rect height="201.875" opacity="0" width="201.875" x="0" y="0"/>
|
||||
<path d="M100.875 195.375C128.875 195.375 151 154.25 151 101.125C151 47.625 129 6.375 100.875 6.375C72.75 6.375 50.875 47.625 50.875 101.125C50.875 154.25 72.875 195.375 100.875 195.375ZM100.875 20C119.5 20 136.25 58 136.25 101.125C136.25 143.75 119.5 181.75 100.875 181.75C82.375 181.75 65.625 143.75 65.625 101.125C65.625 58 82.375 20 100.875 20ZM93.625 7.75L93.625 193.5L108.125 193.5L108.125 7.75ZM100.875 136.25C70.5 136.25 43.25 144.375 29.625 157.5L40.75 166.625C53.5 156.375 74.5 150.75 100.875 150.75C127.375 150.75 148.25 156.375 161.125 166.625L172.25 157.5C158.5 144.375 131.375 136.25 100.875 136.25ZM190.875 93.625L10.875 93.625L10.875 108.125L190.875 108.125ZM100.875 66C131.375 66 158.5 57.875 172.25 44.75L161.125 35.625C148.25 45.875 127.375 51.5 100.875 51.5C74.5 51.5 53.5 45.875 40.75 35.625L29.625 44.75C43.25 57.875 70.5 66 100.875 66ZM100.875 201.75C156.625 201.75 201.875 156.625 201.875 100.875C201.875 45.125 156.625 0 100.875 0C45.25 0 0 45.125 0 100.875C0 156.625 45.25 201.75 100.875 201.75ZM100.875 186.75C53.5 186.75 15.125 148.25 15.125 100.875C15.125 53.5 53.5 15 100.875 15C148.25 15 186.75 53.5 186.75 100.875C186.75 148.25 148.25 186.75 100.875 186.75Z" fill="white" fill-opacity="0.85"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
14
icon/photo-stack.svg
Normal file
@@ -0,0 +1,14 @@
|
||||
<?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 235.75 250.625">
|
||||
<g>
|
||||
<rect height="250.625" opacity="0" width="235.75" x="0" y="0"/>
|
||||
<path d="M179.597 29L56.1528 29C56.4256 19.9631 61.8854 14.875 71.25 14.875L164.5 14.875C173.865 14.875 179.324 19.9631 179.597 29Z" fill="white" fill-opacity="0.85"/>
|
||||
<path d="M200.083 58.4578C196.553 57.8144 192.764 57.5 188.75 57.5L47.125 57.5C43.0623 57.5 39.2305 57.8194 35.6653 58.4746C36.7346 47.6228 43.8122 41.625 55.625 41.625L180.125 41.625C191.932 41.625 199.008 47.6165 200.083 58.4578Z" fill="white" fill-opacity="0.85"/>
|
||||
<path d="M48.5 232.75L187.625 232.75C206.875 232.75 217 223 217 203.875L217 197.625L167.625 150.75C163.75 147 158.625 145.25 153.75 145.25C148.625 145.25 144 146.875 139.75 150.625L97.125 188.5L80 173.25C76 169.625 71.625 167.875 67.25 167.875C63.125 167.875 59.125 169.75 55.125 173.125L19.125 204C19.125 223 29.25 232.75 48.5 232.75ZM47.125 235.375L188.75 235.375C210 235.375 220.625 225 220.625 204L220.625 104C220.625 83.125 210 72.625 188.75 72.625L47.125 72.625C25.75 72.625 15.25 83 15.25 104L15.25 204C15.25 225 25.75 235.375 47.125 235.375ZM47.375 217.375C38.375 217.375 33.25 212.5 33.25 203L33.25 105C33.25 95.5 38.375 90.625 47.375 90.625L188.5 90.625C197.5 90.625 202.625 95.5 202.625 105L202.625 203C202.625 212.5 197.5 217.375 188.5 217.375Z" fill="white" fill-opacity="0.85"/>
|
||||
<path d="M89 153.375C101.375 153.375 111.375 143.125 111.375 130.75C111.375 118.625 101.375 108.5 89 108.5C76.625 108.5 66.625 118.625 66.625 130.75C66.625 143.125 76.625 153.375 89 153.375Z" fill="white" fill-opacity="0.85"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
12
icon/photo.svg
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--Generator: Apple Native CoreSVG 326-->
|
||||
<!DOCTYPE svg
|
||||
PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 231.875 182.375">
|
||||
<g>
|
||||
<rect height="182.375" opacity="0" width="231.875" x="0" y="0"/>
|
||||
<path d="M220.25 143.875L162.875 89.625C158.625 85.75 153.375 83.75 148.375 83.75C143.125 83.75 138.25 85.625 133.75 89.5L89.75 128.875L71.75 112.75C67.625 109.125 63.125 107.25 58.5 107.25C54.375 107.25 50 109 46 112.625L8.5 146.25C8.625 164.75 16.5 174.625 31.5 174.625L192.375 174.625C210.625 174.625 220.25 164.5 220.25 143.875ZM31.875 182.375L200 182.375C221.375 182.375 231.875 171.875 231.875 150.875L231.875 31.625C231.875 10.625 221.375 0.125 200 0.125L31.875 0.125C10.625 0.125 0 10.625 0 31.625L0 150.875C0 171.875 10.625 182.375 31.875 182.375ZM32.125 164.25C23.125 164.25 18 159.375 18 150L18 32.5C18 23 23.125 18.25 32.125 18.25L199.75 18.25C208.75 18.25 213.875 23 213.875 32.5L213.875 150C213.875 159.375 208.75 164.25 199.75 164.25Z" fill="white" fill-opacity="0.85"/>
|
||||
<path d="M73.5 91.875C86.125 91.875 96.5 81.5 96.5 68.75C96.5 56.125 86.125 45.625 73.5 45.625C60.75 45.625 50.375 56.125 50.375 68.75C50.375 81.5 60.75 91.875 73.5 91.875Z" fill="white" fill-opacity="0.85"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
12
icon/questionmark.circle.svg
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--Generator: Apple Native CoreSVG 326-->
|
||||
<!DOCTYPE svg
|
||||
PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 201.875 201.875">
|
||||
<g>
|
||||
<rect height="201.875" opacity="0" width="201.875" x="0" y="0"/>
|
||||
<path d="M100.875 201.75C156.625 201.75 201.875 156.625 201.875 100.875C201.875 45.125 156.625 0 100.875 0C45.25 0 0 45.125 0 100.875C0 156.625 45.25 201.75 100.875 201.75ZM100.875 182.75C55.625 182.75 19.125 146.125 19.125 100.875C19.125 55.625 55.625 19 100.875 19C146.125 19 182.75 55.625 182.75 100.875C182.75 146.125 146.125 182.75 100.875 182.75Z" fill="white" fill-opacity="0.85"/>
|
||||
<path d="M98.875 121.375C104.5 121.375 107.875 117.875 107.875 113.5C107.875 113.25 107.875 113 107.875 112.75C107.875 107.5 111.125 104.125 117.75 99.75C127 93.75 134.5 88 134.5 75.625C134.5 58.375 119.125 49.625 102.125 49.625C84.875 49.625 73.5 57.375 70.5 66C69.875 67.75 69.5 69.5 69.5 71.25C69.5 76.125 73.5 78.75 77.125 78.75C80.875 78.75 83.125 77.125 85.25 74.5L87.375 72C91.625 66.75 96 64.625 101.375 64.625C109.625 64.625 115 69.375 115 76.5C115 83.125 110.75 86.125 102.375 91.875C95.625 96.625 89.875 101.875 89.875 111.875C89.875 112 89.875 112.375 89.875 112.5C89.875 118.375 93 121.375 98.875 121.375ZM98.625 151.375C104.625 151.375 110 146.5 110 140.375C110 134.125 104.75 129.375 98.625 129.375C92.375 129.375 87.125 134.25 87.125 140.375C87.125 146.375 92.5 151.375 98.625 151.375Z" fill="white" fill-opacity="0.85"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
11
icon/shippingbox.svg
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--Generator: Apple Native CoreSVG 326-->
|
||||
<!DOCTYPE svg
|
||||
PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 199.125 218.688">
|
||||
<g>
|
||||
<rect height="218.688" opacity="0" width="199.125" x="0" y="0"/>
|
||||
<path d="M14.75 172.531L91 216.156C96.875 219.531 102.25 219.531 108 216.156L184.25 172.531C193.75 167.156 199.125 161.781 199.125 146.156L199.125 70.6562C199.125 59.2812 195 52.1562 185.75 46.7812L118.875 8.40625C105.75 0.90625 93.375 0.90625 80.25 8.40625L13.375 46.7812C4 52.1562 0 59.2812 0 70.6562L0 146.156C0 161.781 5.375 167.156 14.75 172.531ZM24.375 157.656C19 154.531 16.875 151.156 16.875 145.656L16.875 74.1562L90.75 116.906L90.75 195.906ZM174.75 157.656L108.375 195.906L108.375 116.906L182.25 74.1562L182.25 145.656C182.25 151.156 180 154.531 174.75 157.656ZM99.5 101.281L26.75 59.6562L54.5 43.5312L127.125 85.4062ZM145.125 75.1562L72 33.5312L87.25 24.7812C95.75 19.9062 103.375 19.7812 111.75 24.7812L172.375 59.6562Z" fill="white" fill-opacity="0.85"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
9
icon/wallpaper.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 11.6098 11.6035" width="11.6098px" height="11.6035px">
|
||||
<defs>
|
||||
<style>.defaults{-sfsymbols-rotates-clockwise:true;}.hierarchical-0:primary{-sfsymbols-motion-group:0;-sfsymbols-layer-tags:apple.photos;}.monochrome-0{-sfsymbols-motion-group:0;-sfsymbols-layer-tags:apple.photos;}.multicolor-0:tintColor{-sfsymbols-motion-group:0;-sfsymbols-layer-tags:apple.photos;}</style>
|
||||
</defs>
|
||||
<g id="s" transform="matrix(0.00634764926508069, 0, 0, 0.00634764926508069, -0.8886709809303284, 10.441900253295898)">
|
||||
<path class="monochrome-0 multicolor-0:tintColor hierarchical-0:primary" d="m435-1015c-177 0-295 115-295 284 0 170 118 284 295 284h231c177 0 294-114 294-284 0-169-117-284-294-284Zm0 115h231c112 0 178 63 178 169 0 107-65 169-178 169h-231c-113 0-179-63-179-169s66-169 179-169Zm382-470c-125-125-289-127-410-7-120 120-117 284 8 409l163 163c126 125 289 127 409 7 121-119 119-284-7-409Zm-82 82 164 163c79 80 81 170 6 246-74 75-165 73-245-7l-163-164c-79-80-82-171-8-245 75-76 167-73 246 7Zm35 167c0 177 115 295 285 295s284-118 284-295v-231c0-176-114-293-284-293s-285 117-285 293Zm116 0v-231c0-112 63-178 169-178s168 65 168 178v231c0 114-63 180-168 180-106 0-169-66-169-180Zm242-87c-125 125-127 291-7 410 121 120 285 118 410-7l163-164c126-125 128-288 7-408-120-120-283-119-409 6Zm82 82 164-163c79-79 171-82 245-6 75 75 73 164-7 244l-163 164c-80 80-171 82-246 8-74-76-72-166 7-247Zm234 111c-177 0-295 115-295 284 0 170 118 284 295 284h231c177 0 294-114 294-284 0-169-117-284-294-284Zm0 115h231c112 0 178 63 178 169 0 107-65 169-178 169h-231c-113 0-179-63-179-169s66-169 179-169Zm87 243c-125-125-290-127-410-7s-118 285 7 410l164 163c126 125 288 127 408 7 121-120 120-284-6-409Zm-82 82 163 164c79 79 82 170 6 245-74 75-164 73-244-7l-164-163c-79-81-81-171-8-246 76-75 167-73 247 7Zm-679 463c0 177 115 295 285 295s284-118 284-295v-231c0-176-114-293-284-293s-285 117-285 293Zm116 0v-231c0-112 63-178 169-178s168 65 168 178v231c0 114-63 180-168 180-106 0-169-66-169-180Zm-471-382c-125 125-127 290-7 410 121 120 284 117 409-8l163-163c126-125 128-289 7-409-119-120-283-118-409 7Zm82 82 163-164c80-79 171-81 246-6s73 165-7 245l-164 163c-79 80-171 83-245 8-75-75-72-166 7-246Z" style="fill: rgb(255, 255, 255);"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
879
mainwindow_ui.py
488
qt/mainwindow.ui
@@ -243,7 +243,7 @@ QSlider::tick:horizontal {
|
||||
}</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icon/phone.svg</normaloff>:/icon/phone.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
@@ -308,7 +308,7 @@ QSlider::tick:horizontal {
|
||||
}</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icon/arrow-clockwise.svg</normaloff>:/icon/arrow-clockwise.svg</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
@@ -415,7 +415,7 @@ QSlider::tick:horizontal {
|
||||
<string> Home</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icon/house.svg</normaloff>:/icon/house.svg</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
@@ -462,7 +462,7 @@ QSlider::tick:horizontal {
|
||||
<string> Mobile Gestalt</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icon/iphone-island.svg</normaloff>:/icon/iphone-island.svg</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
@@ -506,7 +506,7 @@ QSlider::tick:horizontal {
|
||||
<string> Feature Flags</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icon/flag.svg</normaloff>:/icon/flag.svg</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
@@ -535,7 +535,7 @@ QSlider::tick:horizontal {
|
||||
<string> Eligibility</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icon/geo-alt.svg</normaloff>:/icon/geo-alt.svg</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
@@ -564,7 +564,7 @@ QSlider::tick:horizontal {
|
||||
<string> Springboard Options</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icon/app-indicator.svg</normaloff>:/icon/app-indicator.svg</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
@@ -593,7 +593,7 @@ QSlider::tick:horizontal {
|
||||
<string> Internal Options</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icon/hdd.svg</normaloff>:/icon/hdd.svg</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
@@ -625,7 +625,7 @@ QSlider::tick:horizontal {
|
||||
<string> Daemons</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icon/toggles.svg</normaloff>:/icon/toggles.svg</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
@@ -642,6 +642,38 @@ QSlider::tick:horizontal {
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="posterboardPageBtn">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string> Posterboard</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icon/wallpaper.svg</normaloff>:/icon/wallpaper.svg</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="autoExclusive">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||
</property>
|
||||
<property name="cls" stdset="0">
|
||||
<string>sidebarBtn</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="advancedPageBtn">
|
||||
<property name="sizePolicy">
|
||||
@@ -654,7 +686,7 @@ QSlider::tick:horizontal {
|
||||
<string> Risky Options</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icon/star.svg</normaloff>:/icon/star.svg</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
@@ -698,7 +730,7 @@ QSlider::tick:horizontal {
|
||||
<string> Apply</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icon/check-circle.svg</normaloff>:/icon/check-circle.svg</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
@@ -727,7 +759,7 @@ QSlider::tick:horizontal {
|
||||
<string> Settings</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icon/gear.svg</normaloff>:/icon/gear.svg</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
@@ -841,7 +873,7 @@ QSlider::tick:horizontal {
|
||||
}</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icon/phone.svg</normaloff>:/icon/phone.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
@@ -970,7 +1002,7 @@ QSlider::tick:horizontal {
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/credits/big_nugget.png</normaloff>:/credits/big_nugget.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
@@ -1057,7 +1089,7 @@ QSlider::tick:horizontal {
|
||||
<string> Join the Discord</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icon/discord.svg</normaloff>:/icon/discord.svg</iconset>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
@@ -1071,7 +1103,7 @@ QSlider::tick:horizontal {
|
||||
<string> Star on Github</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icon/star.svg</normaloff>:/icon/star.svg</iconset>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
@@ -1175,7 +1207,7 @@ QSlider::tick:horizontal {
|
||||
<string> LeminLimez</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/credits/LeminLimez.png</normaloff>:/credits/LeminLimez.png</iconset>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
@@ -1202,7 +1234,7 @@ QToolButton:pressed {
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icon/twitter.svg</normaloff>:/icon/twitter.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
@@ -1226,7 +1258,7 @@ QToolButton:pressed {
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icon/github.svg</normaloff>:/icon/github.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
@@ -1251,7 +1283,7 @@ QToolButton:pressed {
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icon/currency-dollar.svg</normaloff>:/icon/currency-dollar.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
@@ -1371,7 +1403,7 @@ QToolButton:pressed {
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="jjtechBtn">
|
||||
<widget class="QToolButton" name="posterRestoreBtn">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
@@ -1398,8 +1430,8 @@ QToolButton:pressed {
|
||||
}</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>JJTech
|
||||
Sparserestore</string>
|
||||
<string>PosterRestore Team
|
||||
Posterboard</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -1538,6 +1570,33 @@ QToolButton:pressed {
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="jjtechBtn">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QToolButton {
|
||||
border-radius: 0px;
|
||||
background: none;
|
||||
border: 1px solid #3b3b3b;
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
QToolButton:pressed {
|
||||
background-color: #535353;
|
||||
color: #FFFFFF;
|
||||
}</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>JJTech
|
||||
Sparserestore</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="qtBtn">
|
||||
<property name="sizePolicy">
|
||||
@@ -1624,7 +1683,7 @@ QToolButton:pressed {
|
||||
}</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icon/iphone-island.svg</normaloff>:/icon/iphone-island.svg</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
@@ -2162,7 +2221,7 @@ Note: OTA updates will be broken until this is disabled.</string>
|
||||
<string> Add Key</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icon/plus.svg</normaloff>:/icon/plus.svg</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
@@ -2279,7 +2338,7 @@ what you are doing.</string>
|
||||
}</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icon/flag.svg</normaloff>:/icon/flag.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
@@ -2511,7 +2570,7 @@ Only works on iOS 18.0 beta 1-2.</string>
|
||||
}</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icon/geo-alt.svg</normaloff>:/icon/geo-alt.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
@@ -3151,7 +3210,7 @@ QComboBox QAbstractItemView::item:hover {
|
||||
}</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icon/app-indicator.svg</normaloff>:/icon/app-indicator.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
@@ -3403,7 +3462,7 @@ QComboBox QAbstractItemView::item:hover {
|
||||
}</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icon/hdd.svg</normaloff>:/icon/hdd.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
@@ -3726,7 +3785,7 @@ QComboBox QAbstractItemView::item:hover {
|
||||
}</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icon/toggles.svg</normaloff>:/icon/toggles.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
@@ -4038,6 +4097,349 @@ To work properly, also disable the daemon using the toggle above.</string>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="posterboardPage">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_14">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QWidget" name="horizontalWidget_5" native="true">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_20">
|
||||
<property name="spacing">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>9</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QToolButton" name="toolButton_10">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QToolButton {
|
||||
icon-size: 24px;
|
||||
background-color: transparent;
|
||||
padding-left: 0px;
|
||||
padding-right: 5px;
|
||||
border-radius: 0px;
|
||||
}</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icon/wallpaper.svg</normaloff>:/icon/wallpaper.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="verticalWidget_4" native="true">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_12">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="posterboardLbl">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>-1</pointsize>
|
||||
<bold>false</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Posterboard</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="modifyPosterboardsChk">
|
||||
<property name="text">
|
||||
<string>Modify</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_7">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="findPBBtn">
|
||||
<property name="text">
|
||||
<string> Find Wallpapers</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icon/globe.svg</normaloff>:/icon/globe.svg</iconset>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="pbHelpBtn">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>35</width>
|
||||
<height>35</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>35</width>
|
||||
<height>35</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icon/questionmark.circle.svg</normaloff>:/icon/questionmark.circle.svg</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_12">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QFrame {
|
||||
color: #414141;
|
||||
}</string>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Plain</enum>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="posterboardPageContent" native="true">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_13">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_12">
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_18">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="importTendiesBtn">
|
||||
<property name="toolTip">
|
||||
<string>Select a wallpaper file with the .tendies extension.</string>
|
||||
</property>
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::RightToLeft</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string> Import Files (.tendies)</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icon/import.svg</normaloff>:/icon/import.svg</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="pbActionLbl">
|
||||
<property name="text">
|
||||
<string>None</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_27">
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QFrame {
|
||||
color: #414141;
|
||||
}</string>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Plain</enum>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="pbFilesList" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>35</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_14">
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_19">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="deleteAllDescriptorsBtn">
|
||||
<property name="toolTip">
|
||||
<string>Clears all the wallpapers in the Collections section so that you can import more.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string> Clear Collections Wallpapers</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icon/arrow.clockwise.svg</normaloff>:/icon/arrow.clockwise.svg</iconset>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="resetPRBExtBtn">
|
||||
<property name="toolTip">
|
||||
<string>Wipes the PRB Extension folder.
|
||||
|
||||
Warning: This will remove all of your wallpapers and will restrict you from adding new ones until you restore again.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string> Remove All Wallpapers</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icon/arrow.clockwise.svg</normaloff>:/icon/arrow.clockwise.svg</iconset>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextBesideIcon</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_20">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="advancedOptionsPage">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_14">
|
||||
<property name="leftMargin">
|
||||
@@ -4085,7 +4487,7 @@ To work properly, also disable the daemon using the toggle above.</string>
|
||||
}</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icon/star.svg</normaloff>:/icon/star.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
@@ -4468,7 +4870,7 @@ Warning: Disabling will cause the battery to show "Unknown Part" or &q
|
||||
}</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icon/check-circle.svg</normaloff>:/icon/check-circle.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
@@ -4592,7 +4994,7 @@ Warning: Disabling will cause the battery to show "Unknown Part" or &q
|
||||
<string> Choose Gestalt File</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icon/folder.svg</normaloff>:/icon/folder.svg</iconset>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
@@ -4623,7 +5025,7 @@ Warning: Disabling will cause the battery to show "Unknown Part" or &q
|
||||
<string> Apply Changes</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icon/check-circle.svg</normaloff>:/icon/check-circle.svg</iconset>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
@@ -4805,7 +5207,7 @@ Warning: Disabling will cause the battery to show "Unknown Part" or &q
|
||||
}</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icon/gear.svg</normaloff>:/icon/gear.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
@@ -5142,7 +5544,7 @@ Warning: Disabling will cause the battery to show "Unknown Part" or &q
|
||||
}</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icon/geo-alt.svg</normaloff>:/icon/geo-alt.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
@@ -5427,7 +5829,7 @@ Warning: Disabling will cause the battery to show "Unknown Part" or &q
|
||||
}</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icon/pencil.svg</normaloff>:/icon/pencil.svg</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
@@ -5558,7 +5960,7 @@ Warning: Disabling will cause the battery to show "Unknown Part" or &q
|
||||
<string> Import .cowperation</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icon/import.svg</normaloff>:/icon/import.svg</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
@@ -5593,7 +5995,7 @@ Warning: Disabling will cause the battery to show "Unknown Part" or &q
|
||||
<string> New Operation</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icon/plus.svg</normaloff>:/icon/plus.svg</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
@@ -5674,7 +6076,7 @@ Warning: Disabling will cause the battery to show "Unknown Part" or &q
|
||||
}</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icon/compass.svg</normaloff>:/icon/compass.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
@@ -5810,7 +6212,7 @@ Warning: Disabling will cause the battery to show "Unknown Part" or &q
|
||||
}</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icon/iphone-island.svg</normaloff>:/icon/iphone-island.svg</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
@@ -5904,7 +6306,7 @@ Warning: Disabling will cause the battery to show "Unknown Part" or &q
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icon/folder.svg</normaloff>:/icon/folder.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
@@ -5915,7 +6317,7 @@ Warning: Disabling will cause the battery to show "Unknown Part" or &q
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<iconset resource="resources.qrc">
|
||||
<normaloff>:/icon/file-earmark-zip.svg</normaloff>:/icon/file-earmark-zip.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
|
||||
@@ -30,5 +30,14 @@
|
||||
<file>icon/iphone-island.svg</file>
|
||||
<file>icon/flag.svg</file>
|
||||
<file>credits/big_nugget.png</file>
|
||||
<file>icon/photo-stack.svg</file>
|
||||
<file>icon/photo.svg</file>
|
||||
<file>icon/shippingbox.svg</file>
|
||||
<file>icon/wallpaper.svg</file>
|
||||
<file>icon/questionmark.circle.svg</file>
|
||||
<file>icon/globe.svg</file>
|
||||
<file>gui/pb_tutorial1.png</file>
|
||||
<file>gui/pb_tutorial2.png</file>
|
||||
<file>icon/arrow.clockwise.svg</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
225954
resources_rc.py
181
tweaks/posterboard_tweak.py
Normal file
@@ -0,0 +1,181 @@
|
||||
import os
|
||||
import zipfile
|
||||
import uuid
|
||||
import re
|
||||
from random import randint
|
||||
from PySide6 import QtWidgets, QtCore, QtGui
|
||||
|
||||
from .tweak_classes import Tweak
|
||||
from Sparserestore.restore import FileToRestore
|
||||
from controllers.plist_handler import set_plist_value
|
||||
from qt.ui_mainwindow import Ui_Nugget
|
||||
|
||||
class TendieFile:
|
||||
path: str
|
||||
name: str
|
||||
descriptor_cnt: int
|
||||
is_container: bool
|
||||
unsafe_container: bool
|
||||
loaded: bool
|
||||
|
||||
def __init__(self, path: str):
|
||||
self.path = path
|
||||
self.name = os.path.basename(path)
|
||||
self.descriptor_cnt = 0
|
||||
self.is_container = False
|
||||
self.unsafe_container = False
|
||||
self.loaded = False
|
||||
|
||||
# read the contents
|
||||
with zipfile.ZipFile(path, mode="r") as archive:
|
||||
for option in archive.namelist():
|
||||
if "__macosx/" in option.lower():
|
||||
continue
|
||||
if "container" in option.lower():
|
||||
self.is_container = True
|
||||
# check for the unsafe file that requires prb reset
|
||||
if "PBFPosterExtensionDataStoreSQLiteDatabase.sqlite3" in option:
|
||||
self.unsafe_container = True
|
||||
if "descriptor/" in option.lower():
|
||||
item = option.lower().split("descriptor/")[1]
|
||||
if item.count('/') == 1 and item.endswith('/'):
|
||||
self.descriptor_cnt += 1
|
||||
elif "descriptors/" in option.lower():
|
||||
item = option.lower().split("descriptors/")[1]
|
||||
if item.count('/') == 1 and item.endswith('/'):
|
||||
self.descriptor_cnt += 1
|
||||
|
||||
def get_icon(self):
|
||||
if self.is_container:
|
||||
# container
|
||||
return ":/icon/shippingbox.svg"
|
||||
elif self.descriptor_cnt == 1:
|
||||
# single descriptor
|
||||
return ":/icon/photo.svg"
|
||||
else:
|
||||
# multiple descriptors
|
||||
return ":/icon/photo-stack.svg"
|
||||
|
||||
class PosterboardTweak(Tweak):
|
||||
def __init__(self):
|
||||
super().__init__(key=None)
|
||||
self.tendies: list[TendieFile] = []
|
||||
self.bundle_id = "com.apple.PosterBoard"
|
||||
self.resetting = False
|
||||
self.resetType = 0 # 0 for descriptor 1 for prb
|
||||
|
||||
def add_tendie(self, file: str):
|
||||
new_tendie = TendieFile(path=file)
|
||||
if new_tendie.descriptor_cnt + self.get_descriptor_count() <= 10:
|
||||
self.tendies.append(new_tendie)
|
||||
# alert if prb reset is needed
|
||||
if new_tendie.unsafe_container:
|
||||
detailsBox = QtWidgets.QMessageBox()
|
||||
detailsBox.setIcon(QtWidgets.QMessageBox.Critical)
|
||||
detailsBox.setWindowTitle("Warning")
|
||||
detailsBox.setText("NOTE: You may need to reset all wallpapers (enable Risky Options in settings) and then re-apply for this file to work.")
|
||||
detailsBox.exec()
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_descriptor_count(self):
|
||||
cnt = 0
|
||||
for tendie in self.tendies:
|
||||
cnt += tendie.descriptor_cnt
|
||||
return cnt
|
||||
|
||||
def update_plist_id(self, file_path: str, file_name: str, randomizedID: int):
|
||||
if file_name == "com.apple.posterkit.provider.descriptor.identifier":
|
||||
return str(randomizedID).encode()
|
||||
elif file_name == "com.apple.posterkit.provider.contents.userInfo":
|
||||
return set_plist_value(file=os.path.join(file_path, file_name), key="wallpaperRepresentingIdentifier", value=randomizedID)
|
||||
elif file_name == "Wallpaper.plist":
|
||||
return set_plist_value(file=os.path.join(file_path, file_name), key="identifier", value=randomizedID)
|
||||
return None
|
||||
|
||||
|
||||
def clean_path_name(self, path: str):
|
||||
return path# re.sub('[^a-zA-Z0-9\.\/\-_ ]', '', path)
|
||||
|
||||
|
||||
def recursive_add(self,
|
||||
files_to_restore: list[FileToRestore],
|
||||
curr_path: str, restore_path: str = "",
|
||||
isAdding: bool = False,
|
||||
randomizeUUID: bool = False, randomizedID: int = None
|
||||
):
|
||||
for folder in sorted(os.listdir(curr_path)):
|
||||
if folder.startswith('.') or folder == "__MACOSX":
|
||||
continue
|
||||
if isAdding:
|
||||
# randomize uuid
|
||||
folder_name = folder
|
||||
curr_randomized_id = randomizedID
|
||||
if randomizeUUID:
|
||||
folder_name = str(uuid.uuid4()).upper()
|
||||
curr_randomized_id = randint(9999, 99999)
|
||||
# if file then add it, otherwise recursively call again
|
||||
if os.path.isfile(os.path.join(curr_path, folder)):
|
||||
try:
|
||||
# update plist ids if needed
|
||||
new_contents = None
|
||||
contents_path = os.path.join(curr_path, folder)
|
||||
if curr_randomized_id != None:
|
||||
new_contents = self.update_plist_id(curr_path, folder, curr_randomized_id)
|
||||
if new_contents != None:
|
||||
contents_path = None
|
||||
files_to_restore.append(FileToRestore(
|
||||
contents=new_contents,
|
||||
contents_path=contents_path,
|
||||
restore_path=self.clean_path_name(f"{restore_path}/{folder_name}"),
|
||||
domain=f"AppDomain-{self.bundle_id}"
|
||||
))
|
||||
except IOError:
|
||||
print(f"Failed to open file: {folder}") # TODO: Add QDebug equivalent
|
||||
else:
|
||||
self.recursive_add(files_to_restore, os.path.join(curr_path, folder), f"{restore_path}/{folder_name}", isAdding, randomizedID=curr_randomized_id)
|
||||
else:
|
||||
# look for container folder
|
||||
name = folder.lower()
|
||||
if name == "container":
|
||||
self.recursive_add(files_to_restore, os.path.join(curr_path, folder), restore_path="/", isAdding=True)
|
||||
return
|
||||
elif name == "descriptor" or name == "descriptors":
|
||||
self.recursive_add(
|
||||
files_to_restore,
|
||||
os.path.join(curr_path, folder),
|
||||
restore_path="/Library/Application Support/PRBPosterExtensionDataStore/61/Extensions/com.apple.WallpaperKit.CollectionsPoster/descriptors",
|
||||
isAdding=True,
|
||||
randomizeUUID=True
|
||||
)
|
||||
else:
|
||||
self.recursive_add(files_to_restore, os.path.join(curr_path, folder), isAdding=False)
|
||||
|
||||
def apply_tweak(self, files_to_restore: list[FileToRestore], output_dir: str):
|
||||
# unzip the file
|
||||
if not self.enabled:
|
||||
return
|
||||
if self.resetting:
|
||||
# null out the folder
|
||||
file_path = ""
|
||||
if self.resetType == 0:
|
||||
# resetting descriptors
|
||||
file_path = "/61/Extensions/com.apple.WallpaperKit.CollectionsPoster/descriptors"
|
||||
files_to_restore.append(FileToRestore(
|
||||
contents=b"",
|
||||
restore_path=f"/Library/Application Support/PRBPosterExtensionDataStore{file_path}",
|
||||
domain=f"AppDomain-{self.bundle_id}"
|
||||
))
|
||||
return
|
||||
elif self.tendies == None or len(self.tendies) == 0:
|
||||
return
|
||||
if os.name == "nt":
|
||||
# try to get past directory name limit on windows
|
||||
output_dir = "\\\\?\\" + output_dir
|
||||
for tendie in self.tendies:
|
||||
zip_output = os.path.join(output_dir, str(uuid.uuid4()))
|
||||
os.makedirs(zip_output)
|
||||
with zipfile.ZipFile(tendie.path, 'r') as zip_ref:
|
||||
zip_ref.extractall(zip_output)
|
||||
# add the files
|
||||
self.recursive_add(files_to_restore, curr_path=output_dir)
|
||||
@@ -1,6 +1,7 @@
|
||||
from devicemanagement.constants import Version
|
||||
from .tweak_classes import MobileGestaltTweak, MobileGestaltMultiTweak, MobileGestaltPickerTweak, FeatureFlagTweak, BasicPlistTweak, AdvancedPlistTweak, RdarFixTweak, NullifyFileTweak
|
||||
from .eligibility_tweak import EligibilityTweak, AITweak
|
||||
from .posterboard_tweak import PosterboardTweak
|
||||
from .basic_plist_locations import FileLocation
|
||||
|
||||
tweaks = {
|
||||
@@ -259,6 +260,9 @@ tweaks = {
|
||||
),
|
||||
"ClearScreenTimeAgentPlist": NullifyFileTweak(FileLocation.screentime),
|
||||
|
||||
## PosterBoard
|
||||
"PosterBoard": PosterboardTweak(),
|
||||
|
||||
## Risky Options
|
||||
"DisableOTAFile": AdvancedPlistTweak(
|
||||
FileLocation.ota,
|
||||
|
||||