├── .gitignore ├── 3developer.plist ├── LICENSE ├── Makefile ├── README.md ├── control └── src ├── Additions.h ├── DTAppInfoViewController.h ├── DTAppInfoViewController.m ├── Tweak.h └── Tweak.mm /.gitignore: -------------------------------------------------------------------------------- 1 | packages/ 2 | .theos/ 3 | .DS_STORE 4 | compile_commands.json 5 | .cache 6 | -------------------------------------------------------------------------------- /3developer.plist: -------------------------------------------------------------------------------- 1 | { Filter = { Bundles = ( "com.apple.springboard"); }; } 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Hearse 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Manually specifying to use theos sdks, since we are using private frameworks. 2 | SDK_PATH = $(THEOS)/sdks/iPhoneOS17.2.sdk/ 3 | SYSROOT = $(SDK_PATH) 4 | 5 | TARGET := iphone:clang:latest:14.0 6 | INSTALL_TARGET_PROCESSES = SpringBoard 7 | 8 | include $(THEOS)/makefiles/common.mk 9 | 10 | TWEAK_NAME = 3developer 11 | $(TWEAK_NAME)_FILES = $(wildcard src/*.m) $(wildcard src/*.mm) $(wildcard src/*.x) $(wildcard src/*.xm) 12 | $(TWEAK_NAME)_CFLAGS += -fobjc-arc 13 | $(TWEAK_NAME)_FRAMEWORKS += Foundation UIKit 14 | 15 | include $(THEOS_MAKE_PATH)/tweak.mk 16 | include $(THEOS_MAKE_PATH)/aggregate.mk 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **Add https://repo.packix.com/ for flexdecrypt.** 2 | -------------------------------------------------------------------------------- /control: -------------------------------------------------------------------------------- 1 | Package: com.hearse.3developer 2 | Name: 3developer 3 | Version: 0.0.2 4 | Architecture: iphoneos-arm 5 | Description: 3D touch menu options for developers. 6 | Maintainer: Hearse 7 | Author: Hearse 8 | Section: Development 9 | Depends: mobilesubstrate (>= 0.9.5000) 10 | -------------------------------------------------------------------------------- /src/Additions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #import 3 | @interface FBApplicationInfo (devtools) 4 | @property(readonly, nonatomic) 5 | NSString *teamIdentifier; // ivar: _teamIdentifier 6 | @property(readonly, copy, nonatomic) 7 | NSString *shortVersionString; // ivar: _shortVersionString 8 | @end 9 | -------------------------------------------------------------------------------- /src/DTAppInfoViewController.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | @interface DTAppInfoViewController : UIViewController 3 | @property(nonatomic, strong) UITextView *textView; 4 | @property(nonatomic, strong) FBApplicationInfo *app; 5 | - (instancetype)initWithApp:(FBApplicationInfo *)app; 6 | @end 7 | -------------------------------------------------------------------------------- /src/DTAppInfoViewController.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | #import "Additions.h" 5 | #import "DTAppInfoViewController.h" 6 | 7 | @implementation DTAppInfoViewController 8 | - (instancetype)initWithApp:(FBApplicationInfo *)app { 9 | self = [super init]; 10 | if (self) { 11 | self.app = app; 12 | } 13 | return self; 14 | } 15 | - (void)addField:(NSString *)text { 16 | if (!self.textView.text) { 17 | self.textView.text = @""; 18 | } 19 | if (text) { 20 | self.textView.text = 21 | [self.textView.text stringByAppendingFormat:@"%@\n", text]; 22 | [self.textView sizeToFit]; 23 | } 24 | } 25 | 26 | - (void)viewDidLoad { 27 | [super viewDidLoad]; 28 | [self setupTextView]; 29 | [self setupConstraints]; 30 | } 31 | - (void)setupTextView { 32 | self.textView = [UITextView new]; 33 | self.textView.translatesAutoresizingMaskIntoConstraints = false; 34 | self.textView.editable = NO; 35 | self.textView.scrollEnabled = true; 36 | @try { 37 | [self addField:[NSString stringWithFormat:@"isBeta: %d", self.app.beta]]; 38 | [self addField:[NSString stringWithFormat:@"signerIdentity: %@", 39 | self.app.signerIdentity]]; 40 | [self addField:[NSString stringWithFormat:@"sdkVersion: %@", 41 | self.app.sdkVersion]]; 42 | [self addField:[NSString stringWithFormat:@"shortVersionString: %@", 43 | self.app.shortVersionString]]; 44 | [self addField:[NSString stringWithFormat:@"teamIdentifier: %@", 45 | self.app.teamIdentifier]]; 46 | [self 47 | addField:[NSString 48 | stringWithFormat:@"lastModifiedDate: %@", 49 | [NSDate dateWithTimeIntervalSinceNow: 50 | self.app.lastModifiedDate]]]; 51 | [self addField:[NSString stringWithFormat:@"requiredCapabilities: %@", 52 | self.app.requiredCapabilities]]; 53 | [self addField:[NSString stringWithFormat:@"environmentVariables: %@", 54 | self.app.environmentVariables]]; 55 | [self addField:[NSString stringWithFormat:@"entitlements: %@", 56 | self.app.entitlements]]; 57 | } @catch (NSException *exception) { 58 | [self addField:[NSString stringWithFormat:@"error occured: %@", exception]]; 59 | } 60 | [self.textView sizeToFit]; 61 | [self.view addSubview:self.textView]; 62 | self.textView.scrollEnabled = NO; 63 | self.textView.scrollEnabled = YES; 64 | } 65 | - (void)setupConstraints { 66 | [self.textView.topAnchor constraintEqualToAnchor:self.view.topAnchor].active = 67 | YES; 68 | [self.textView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor] 69 | .active = YES; 70 | [self.textView.leftAnchor constraintEqualToAnchor:self.view.leftAnchor] 71 | .active = YES; 72 | [self.textView.rightAnchor constraintEqualToAnchor:self.view.rightAnchor] 73 | .active = YES; 74 | } 75 | 76 | @end 77 | -------------------------------------------------------------------------------- /src/Tweak.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | //@import Foundation; 3 | 4 | //@import UIKit; 5 | #import 6 | #import 7 | 8 | #import 9 | #import 10 | #import 11 | 12 | #import "DTAppInfoViewController.h" 13 | #import 14 | #define FLEX_BUNDLE_ID @"com.hearse.3developer.3d.flex" 15 | #define COPY_BUNDLE_ID @"com.hearse.3developer.3d.copy" 16 | #define INFO_BUNDLE_ID @"com.hearse.3developer.3d.info" 17 | #define OPEN_BUNDLE_IN_FILZA_BUNDLE_ID \ 18 | @"com.hearse.3developer.3d.openBundleInFilza" 19 | #define OPEN_CONTAINER_IN_FILZA_BUNDLE_ID \ 20 | @"com.hearse.3developer.3d.openContainerInFilza" 21 | 22 | #define CHOICY_BUNDLE_ID @"com.hearse.3developer.3d.choicy" 23 | 24 | @interface SBSApplicationShortcutIcon : NSObject 25 | @end 26 | 27 | @interface SBApplication (devtools) 28 | @property(retain, nonatomic) SBApplicationInfo *info; // ivar: _appInfo 29 | @end 30 | 31 | @interface UIApplication (devtools) 32 | - (void)applicationOpenURL:(id)arg1; 33 | - (bool)launchApplicationWithIdentifier:(id)arg1 suspended:(bool)arg2; 34 | @end 35 | 36 | @interface SBSApplicationShortcutItem : NSObject 37 | @property(nonatomic, retain) NSString *type; 38 | @property(nonatomic, copy) NSString *localizedTitle; 39 | @property(copy, nonatomic) 40 | NSString *localizedSubtitle; // ivar: _localizedSubtitle 41 | @property(copy, nonatomic) 42 | NSString *bundleIdentifierToLaunch; // ivar: _bundleIdentifierToLaunch 43 | @property(nonatomic, copy) SBSApplicationShortcutIcon *icon; 44 | @property(copy, nonatomic) NSDictionary *userInfo; 45 | @end 46 | 47 | @interface SBIcon : NSObject 48 | @end 49 | 50 | @interface SBIconView : UIView 51 | @property(readonly, copy, nonatomic) 52 | NSString *applicationBundleIdentifierForShortcuts; 53 | @property(nonatomic, strong, readwrite) SBIcon *icon; 54 | - (BOOL)isFolderIcon; 55 | @end 56 | 57 | @interface SBSApplicationShortcutSystemPrivateIcon : SBSApplicationShortcutIcon 58 | - (id)initWithSystemImageName:(id)arg1; 59 | @end 60 | -------------------------------------------------------------------------------- /src/Tweak.mm: -------------------------------------------------------------------------------- 1 | #import "Tweak.h" 2 | #import 3 | #import 4 | 5 | SBSApplicationShortcutItem *createShortcutItem(NSString *localizedTitle, 6 | NSString *localizedSubtitle, 7 | NSString *iconName, 8 | NSString *type) { 9 | SBSApplicationShortcutItem *item = 10 | [objc_getClass("SBSApplicationShortcutItem") alloc]; 11 | item.localizedTitle = localizedTitle; 12 | item.localizedSubtitle = localizedSubtitle; 13 | SBSApplicationShortcutSystemPrivateIcon *icon = 14 | [[objc_getClass("SBSApplicationShortcutSystemPrivateIcon") alloc] 15 | initWithSystemImageName:iconName]; 16 | [item setIcon:icon]; 17 | item.type = type; 18 | return item; 19 | } 20 | 21 | // %hook SBIconView 22 | void (*orig_setApplicationShortcutItems)(SBIconView *, SEL, NSArray *); 23 | void setApplicationShortcutItems(SBIconView *self, SEL _cmd, NSArray *arg1) { 24 | if ([self.icon isMemberOfClass:objc_getClass("SBWidgetIcon")] || 25 | [self isFolderIcon]) { 26 | orig_setApplicationShortcutItems(self, _cmd, arg1); 27 | return; 28 | } 29 | NSMutableArray *modifiedItems = 30 | (arg1) ? [arg1 mutableCopy] : [NSMutableArray new]; 31 | SBSApplicationShortcutItem *flexItem = createShortcutItem( 32 | @"flexdecrypt", nil, @"chevron.left.slash.chevron.right", FLEX_BUNDLE_ID); 33 | SBSApplicationShortcutItem *copyBundleItem = createShortcutItem( 34 | @"Copy Bundle ID", self.applicationBundleIdentifierForShortcuts, 35 | @"app.badge", COPY_BUNDLE_ID); 36 | SBSApplicationShortcutItem *openBundleItem = 37 | createShortcutItem(@"Open Bundle In Filza", nil, @"doc.zipper", 38 | OPEN_BUNDLE_IN_FILZA_BUNDLE_ID); 39 | SBSApplicationShortcutItem *openContainerItem = 40 | createShortcutItem(@"Open Container In Filza", nil, @"doc.fill", 41 | OPEN_CONTAINER_IN_FILZA_BUNDLE_ID); 42 | SBSApplicationShortcutItem *infoItem = 43 | createShortcutItem(@"Info", nil, @"info", INFO_BUNDLE_ID); 44 | SBSApplicationShortcutItem *choicyItem = 45 | createShortcutItem(@"Open Choicy", nil, @"info", CHOICY_BUNDLE_ID); 46 | 47 | [modifiedItems addObject:flexItem]; 48 | [modifiedItems addObject:copyBundleItem]; 49 | [modifiedItems addObject:infoItem]; 50 | [modifiedItems addObject:openContainerItem]; 51 | [modifiedItems addObject:openBundleItem]; 52 | [modifiedItems addObject:choicyItem]; 53 | 54 | orig_setApplicationShortcutItems(self, _cmd, modifiedItems); 55 | } 56 | 57 | void (*orig_activateShortcut)(SBIconView *, SEL, SBSApplicationShortcutItem *, 58 | NSString *, SBIconView *); 59 | void activateShortcut(SBIconView *self, SEL _cmd, 60 | SBSApplicationShortcutItem *item, NSString *bundleID, 61 | SBIconView *iconView) { 62 | if ([[item type] isEqualToString:FLEX_BUNDLE_ID] && bundleID && 63 | [bundleID length] != 0) { 64 | 65 | FBApplicationInfo *app = 66 | (FBApplicationInfo *)[[objc_getClass("SBApplicationController") 67 | sharedInstance] 68 | applicationWithBundleIdentifier:bundleID] 69 | .info; 70 | if (access(ROOT_PATH("/usr/bin/flexdecrypt"), F_OK) != 0) { 71 | UIAlertController *alert = [UIAlertController 72 | alertControllerWithTitle:@"flexdecrypt" 73 | message:[NSString 74 | stringWithFormat: 75 | @"%s not found", 76 | ROOT_PATH("/usr/bin/flexdecrypt")] 77 | preferredStyle:UIAlertControllerStyleAlert]; 78 | 79 | UIAlertAction *dismissAction = 80 | [UIAlertAction actionWithTitle:@"Dismiss" 81 | style:UIAlertActionStyleDefault 82 | handler:^(UIAlertAction *action){ 83 | }]; 84 | [alert addAction:dismissAction]; 85 | 86 | [iconView.window.rootViewController presentViewController:alert 87 | animated:YES 88 | completion:nil]; 89 | return; 90 | } 91 | 92 | NSTask *task = [[NSTask alloc] init]; 93 | [task setLaunchPath:ROOT_PATH_NS(@"/usr/bin/flexdecrypt")]; 94 | [task setArguments:@[ app.executableURL.path ]]; 95 | NSPipe *out = [NSPipe pipe]; 96 | [task setStandardOutput:out]; 97 | 98 | [task setTerminationHandler:^(NSTask *task) { 99 | dispatch_async(dispatch_get_main_queue(), ^{ 100 | NSFileHandle *read = [out fileHandleForReading]; 101 | NSData *dataRead = [read readDataToEndOfFile]; 102 | NSString *stringRead = 103 | [[NSString alloc] initWithData:dataRead 104 | encoding:NSUTF8StringEncoding]; 105 | UIAlertController *alert = [UIAlertController 106 | alertControllerWithTitle:@"flexdecrypt" 107 | message:stringRead 108 | preferredStyle:UIAlertControllerStyleAlert]; 109 | 110 | UIAlertAction *dismissAction = 111 | [UIAlertAction actionWithTitle:@"Dismiss" 112 | style:UIAlertActionStyleDefault 113 | handler:^(UIAlertAction *action){ 114 | }]; 115 | UIAlertAction *filzaAction = [UIAlertAction 116 | actionWithTitle:@"Open in Filza" 117 | style:UIAlertActionStyleDefault 118 | handler:^(UIAlertAction *action) { 119 | NSString *decryptedPath = [[NSString alloc] 120 | initWithFormat: 121 | @"%@", 122 | [stringRead 123 | stringByReplacingOccurrencesOfString: 124 | @"Wrote decrypted image to " 125 | withString:@""]]; 126 | 127 | NSString *pathInFilza = [@"filza://view" 128 | stringByAppendingString:decryptedPath]; 129 | NSString *trimmedUrlString = [pathInFilza 130 | stringByTrimmingCharactersInSet: 131 | [NSCharacterSet 132 | whitespaceAndNewlineCharacterSet]]; 133 | NSURL *url = [NSURL 134 | URLWithString: 135 | [trimmedUrlString 136 | stringByAddingPercentEncodingWithAllowedCharacters: 137 | [NSCharacterSet 138 | URLQueryAllowedCharacterSet]]]; 139 | 140 | NSLog(@"NSLogify |%@|", url); 141 | [[objc_getClass("SpringBoard") sharedApplication] 142 | applicationOpenURL:url]; 143 | }]; 144 | [alert addAction:dismissAction]; 145 | [alert addAction:filzaAction]; 146 | 147 | [iconView.window.rootViewController presentViewController:alert 148 | animated:YES 149 | completion:nil]; 150 | }); 151 | }]; 152 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{ 153 | [task launch]; 154 | }); 155 | 156 | } else if ([[item type] isEqualToString:COPY_BUNDLE_ID] && bundleID && 157 | [bundleID length] != 0) { 158 | UIPasteboard *pasteboard = [UIPasteboard generalPasteboard]; 159 | pasteboard.string = bundleID; 160 | 161 | } else if ([[item type] isEqualToString:OPEN_BUNDLE_IN_FILZA_BUNDLE_ID] && 162 | bundleID && [bundleID length] != 0) { 163 | 164 | @try { 165 | FBApplicationInfo *app = 166 | (FBApplicationInfo *)[[objc_getClass("SBApplicationController") 167 | sharedInstance] 168 | applicationWithBundleIdentifier:bundleID] 169 | .info; 170 | NSString *pathInFilza = 171 | [@"filza://view" stringByAppendingString:app.bundleURL.path]; 172 | NSURL *url = [NSURL 173 | URLWithString:[pathInFilza 174 | stringByAddingPercentEncodingWithAllowedCharacters: 175 | [NSCharacterSet URLQueryAllowedCharacterSet]]]; 176 | if (app && url) { 177 | [[objc_getClass("SpringBoard") sharedApplication] 178 | applicationOpenURL:url]; 179 | } 180 | } @catch (NSException *exception) { 181 | } 182 | 183 | } else if ([[item type] isEqualToString:OPEN_CONTAINER_IN_FILZA_BUNDLE_ID] && 184 | bundleID && [bundleID length] != 0) { 185 | @try { 186 | FBApplicationInfo *app = 187 | (FBApplicationInfo *)[[objc_getClass("SBApplicationController") 188 | sharedInstance] 189 | applicationWithBundleIdentifier:bundleID] 190 | .info; 191 | NSString *pathInFilza = 192 | [@"filza://view" stringByAppendingString:app.dataContainerURL.path]; 193 | NSURL *url = [NSURL 194 | URLWithString:[pathInFilza 195 | stringByAddingPercentEncodingWithAllowedCharacters: 196 | [NSCharacterSet URLQueryAllowedCharacterSet]]]; 197 | if (app && url) { 198 | [[objc_getClass("SpringBoard") sharedApplication] 199 | applicationOpenURL:url]; 200 | } 201 | } @catch (NSException *exception) { 202 | } 203 | } else if ([[item type] isEqualToString:INFO_BUNDLE_ID] && bundleID && 204 | [bundleID length] != 0) { 205 | 206 | UIWindow *keyWindow = nil; 207 | NSArray *windows = [[UIApplication sharedApplication] windows]; 208 | for (UIWindow *window in windows) { 209 | if (window.isKeyWindow) { 210 | keyWindow = window; 211 | break; 212 | } 213 | } 214 | 215 | @try { 216 | if (keyWindow) { 217 | FBApplicationInfo *app = 218 | (FBApplicationInfo *)[[objc_getClass("SBApplicationController") 219 | sharedInstance] 220 | applicationWithBundleIdentifier:bundleID] 221 | .info; 222 | if (app) { 223 | DTAppInfoViewController *infoVC = 224 | [[DTAppInfoViewController alloc] initWithApp:app]; 225 | [keyWindow.rootViewController presentViewController:infoVC 226 | animated:YES 227 | completion:NULL]; 228 | } 229 | } 230 | } @catch (NSException *exception) { 231 | } 232 | } else if ([[item type] isEqualToString:CHOICY_BUNDLE_ID] && bundleID && 233 | [bundleID length] != 0) { 234 | // Open Choicy 235 | [[objc_getClass("SpringBoard") sharedApplication] 236 | applicationOpenURL: 237 | [NSURL 238 | URLWithString:[NSString 239 | stringWithFormat: 240 | @"prefs:root=Choicy&path=APPLICATIONS/%@", 241 | bundleID]]]; 242 | } else { 243 | orig_activateShortcut(self, _cmd, item, bundleID, iconView); 244 | } 245 | } 246 | 247 | void replaceMethod(Class cls, SEL sel, void *replacement, void *orig) { 248 | Method orig_met = class_getInstanceMethod(cls, sel); 249 | IMP orig_imp = method_getImplementation(orig_met); 250 | *(IMP *)orig = orig_imp; 251 | class_replaceMethod(cls, sel, (IMP)replacement, 252 | method_getTypeEncoding(orig_met)); 253 | } 254 | 255 | void __attribute((constructor)) load() { 256 | replaceMethod(objc_getClass("SBIconView"), 257 | @selector(setApplicationShortcutItems:), 258 | (void *)setApplicationShortcutItems, 259 | (void *)&orig_setApplicationShortcutItems); 260 | 261 | replaceMethod(objc_getMetaClass("SBIconView"), 262 | @selector(activateShortcut:withBundleIdentifier:forIconView:), 263 | (void *)activateShortcut, (void *)&orig_activateShortcut); 264 | } --------------------------------------------------------------------------------