diff --git a/WDBFontOverwrite.xcodeproj/project.pbxproj b/WDBFontOverwrite.xcodeproj/project.pbxproj index a1c4229..811c441 100644 --- a/WDBFontOverwrite.xcodeproj/project.pbxproj +++ b/WDBFontOverwrite.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 4F085E392994EF2F004099C1 /* ActionButtons.ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F085E382994EF2F004099C1 /* ActionButtons.ViewModel.swift */; }; 4F4E64A7295F9AB600D4F04D /* CustomFontsScene.ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F4E64A6295F9AB600D4F04D /* CustomFontsScene.ViewModel.swift */; }; 4FD690952986367C00B751B2 /* grant_full_disk_access.m in Sources */ = {isa = PBXBuildFile; fileRef = 4FD690942986367C00B751B2 /* grant_full_disk_access.m */; }; 4FD690992986395B00B751B2 /* helpers.m in Sources */ = {isa = PBXBuildFile; fileRef = 4FD690982986395B00B751B2 /* helpers.m */; }; @@ -38,6 +39,7 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 4F085E382994EF2F004099C1 /* ActionButtons.ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionButtons.ViewModel.swift; sourceTree = ""; }; 4F4E64A6295F9AB600D4F04D /* CustomFontsScene.ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomFontsScene.ViewModel.swift; sourceTree = ""; }; 4FD690942986367C00B751B2 /* grant_full_disk_access.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = grant_full_disk_access.m; sourceTree = ""; }; 4FD69096298637D400B751B2 /* grant_full_disk_access.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = grant_full_disk_access.h; sourceTree = ""; }; @@ -116,6 +118,7 @@ 4FE5EF302963E460003384EC /* NoticeView.swift */, 4FF28A1129679EEC00143640 /* ExplanationView.swift */, 4FF28A132967AA2D00143640 /* ActionButtons.swift */, + 4F085E382994EF2F004099C1 /* ActionButtons.ViewModel.swift */, ); path = MainInterface; sourceTree = ""; @@ -283,6 +286,7 @@ 4FE5EF4129668C9C003384EC /* AlignedRowContentView.swift in Sources */, 4FE5EF492966AE1A003384EC /* FontDiscoveryCard.swift in Sources */, 4FE5EF3529653188003384EC /* FontMap.swift in Sources */, + 4F085E392994EF2F004099C1 /* ActionButtons.ViewModel.swift in Sources */, 4FF28A102967956300143640 /* PresetFontsScene.ViewModel.swift in Sources */, 4FE5EF312963E460003384EC /* NoticeView.swift in Sources */, 4FE5EF3329640075003384EC /* WDBImportCustomFontPickerViewControllerDelegate.swift in Sources */, @@ -421,6 +425,7 @@ ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = WDBFontOverwrite/Info.plist; + INFOPLIST_KEY_NSAppleMusicUsageDescription = WDBFontOverwrite; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; @@ -435,8 +440,8 @@ "$(inherited)", "$(PROJECT_DIR)/WDBFontOverwrite", ); - MARKETING_VERSION = "1.10.7-1"; - PRODUCT_BUNDLE_IDENTIFIER = com.ginsu.WDBFontOverwrite; + MARKETING_VERSION = 1.10.8; + PRODUCT_BUNDLE_IDENTIFIER = com.ginsudev.WDBFontOverwrite; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OBJC_BRIDGING_HEADER = "WDBFontOverwrite/WDBFontOverwrite-Bridging-Header.h"; @@ -459,6 +464,7 @@ ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = WDBFontOverwrite/Info.plist; + INFOPLIST_KEY_NSAppleMusicUsageDescription = WDBFontOverwrite; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; @@ -473,8 +479,8 @@ "$(inherited)", "$(PROJECT_DIR)/WDBFontOverwrite", ); - MARKETING_VERSION = "1.10.7-1"; - PRODUCT_BUNDLE_IDENTIFIER = com.ginsu.WDBFontOverwrite; + MARKETING_VERSION = 1.10.8; + PRODUCT_BUNDLE_IDENTIFIER = com.ginsudev.WDBFontOverwrite; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OBJC_BRIDGING_HEADER = "WDBFontOverwrite/WDBFontOverwrite-Bridging-Header.h"; diff --git a/WDBFontOverwrite/FontMap.swift b/WDBFontOverwrite/FontMap.swift index e1173b9..fdf8cb9 100644 --- a/WDBFontOverwrite/FontMap.swift +++ b/WDBFontOverwrite/FontMap.swift @@ -12,10 +12,10 @@ struct FontMap { static let emojiCustomFont = CustomFont( name: "Emoji", - targetPath: .many([ + targetPaths: [ "/System/Library/Fonts/CoreAddition/AppleColorEmoji-160px.ttc", "/System/Library/Fonts/Core/AppleColorEmoji.ttc", - ]), + ], localPath: "CustomAppleColorEmoji.ttc" ) @@ -32,7 +32,7 @@ struct FontMap { } fontMap[key(forFont: font)] = CustomFont( name: font, - targetPath: .single("\(fontDirPath)\(dir)/\(font)"), + targetPaths: ["\(fontDirPath)\(dir)/\(font)"], localPath: "Custom\(font)" ) } diff --git a/WDBFontOverwrite/MainInterface/ActionButtons.ViewModel.swift b/WDBFontOverwrite/MainInterface/ActionButtons.ViewModel.swift new file mode 100644 index 0000000..8a3520d --- /dev/null +++ b/WDBFontOverwrite/MainInterface/ActionButtons.ViewModel.swift @@ -0,0 +1,45 @@ +// +// ActionButtons.ViewModel.swift +// WDBFontOverwrite +// +// Created by Noah Little on 9/2/2023. +// + +import UIKit.UIApplication + +extension ActionButtons { + struct ViewModel { + func clearKBCache() { + grant_full_disk_access { error in + if error != nil { + print("can't get disk access") + } else { + _UIKeyboardCache.purge() + } + } + } + + @available(iOS 15, *) + func respringModern() { + grant_full_disk_access { error in + if error != nil { + print("can't get disk access, using backup respring") + respringLegacy() + } else { + xpc_crasher(UnsafeMutablePointer(mutating: "com.apple.frontboard.systemappservices")) + } + } + } + + @available(iOS, deprecated: 15) + func respringLegacy() { + let sharedApplication = UIApplication.shared + let windows = sharedApplication.windows + if let window = windows.first { + while true { + window.snapshotView(afterScreenUpdates: false) + } + } + } + } +} diff --git a/WDBFontOverwrite/MainInterface/ActionButtons.swift b/WDBFontOverwrite/MainInterface/ActionButtons.swift index 4159dca..f968fda 100644 --- a/WDBFontOverwrite/MainInterface/ActionButtons.swift +++ b/WDBFontOverwrite/MainInterface/ActionButtons.swift @@ -8,10 +8,12 @@ import SwiftUI struct ActionButtons: View { + private let viewModel = ViewModel() + var body: some View { if #available(iOS 15, *) { Button { - clearKBCache() + viewModel.clearKBCache() } label: { AlignedRowContentView( imageName: "trash", @@ -20,7 +22,11 @@ struct ActionButtons: View { } } Button { - respring() + if #available(iOS 15, *) { + viewModel.respringModern() + } else { + viewModel.respringLegacy() + } } label: { AlignedRowContentView( imageName: "arrow.triangle.2.circlepath", @@ -28,41 +34,6 @@ struct ActionButtons: View { ) } } - - private func clearKBCache() { - grant_full_disk_access { error in - if error != nil { - print("can't get disk access") - } else { - _UIKeyboardCache.purge() - } - } - } - - private func respring() { - if #available(iOS 15, *) { - grant_full_disk_access { error in - if error != nil { - print("can't get disk access, using backup respring") - respringBackup() - } else { - xpc_crasher(UnsafeMutablePointer(mutating: "com.apple.frontboard.systemappservices")) - } - } - } else { - respringBackup() - } - } - - private func respringBackup() { - let sharedApplication = UIApplication.shared - let windows = sharedApplication.windows - if let window = windows.first { - while true { - window.snapshotView(afterScreenUpdates: false) - } - } - } } struct ActionButtons_Previews: PreviewProvider { diff --git a/WDBFontOverwrite/MainInterface/CustomFontsScene.ViewModel.swift b/WDBFontOverwrite/MainInterface/CustomFontsScene.ViewModel.swift index ccd6317..63f7a56 100644 --- a/WDBFontOverwrite/MainInterface/CustomFontsScene.ViewModel.swift +++ b/WDBFontOverwrite/MainInterface/CustomFontsScene.ViewModel.swift @@ -7,14 +7,9 @@ import Foundation -enum PathType { - case single(String) - case many([String]) -} - struct CustomFont { var name: String - var targetPath: PathType? + var targetPaths: [String] var localPath: String var notice: Notice? } @@ -42,7 +37,7 @@ extension CustomFontsScene { let emojiFont = FontMap.emojiCustomFont await overwriteWithCustomFont( name: emojiFont.localPath, - targetPath: emojiFont.targetPath + targetPaths: emojiFont.targetPaths ) await MainActor.run { ProgressManager.shared.isBusy = false @@ -62,7 +57,7 @@ extension CustomFontsScene { if let customFont = FontMap.fontMap[key] { await overwriteWithCustomFont( name: customFont.localPath, - targetPath: customFont.targetPath + targetPaths: customFont.targetPaths ) } } diff --git a/WDBFontOverwrite/MainInterface/ExplanationView.swift b/WDBFontOverwrite/MainInterface/ExplanationView.swift index 88ba7fc..16b9e78 100644 --- a/WDBFontOverwrite/MainInterface/ExplanationView.swift +++ b/WDBFontOverwrite/MainInterface/ExplanationView.swift @@ -18,7 +18,12 @@ struct ExplanationView: View { var body: some View { ZStack { RoundedRectangle(cornerRadius: 5) - .fill(Color(UIColor(red: 0.44, green: 0.69, blue: 0.67, alpha: 1.00))) + .fill(Color(UIColor( + red: 0.44, + green: 0.69, + blue: 0.67, + alpha: 1.00 + ))) VStack(alignment: .center, spacing: 10) { imageView descriptionView diff --git a/WDBFontOverwrite/MainInterface/FileEditor/FileEditorView.ViewModel.swift b/WDBFontOverwrite/MainInterface/FileEditor/FileEditorView.ViewModel.swift index 460a6b2..9ed1c04 100644 --- a/WDBFontOverwrite/MainInterface/FileEditor/FileEditorView.ViewModel.swift +++ b/WDBFontOverwrite/MainInterface/FileEditor/FileEditorView.ViewModel.swift @@ -13,12 +13,19 @@ extension FileEditorView { @Published var files = [String]() @Published var isVisibleRemoveAllAlert = false + private var documentsDirectory: URL { + fileManager.urls( + for: .documentDirectory, + in: .userDomainMask + )[0] + } + func populateFiles() async { do { - let path = documentsDirectory().relativePath + let path = documentsDirectory.relativePath try await MainActor.run { [weak self] in - self?.files = try fileManager.contentsOfDirectory(atPath: path) + self?.files = try fileManager.contentsOfDirectory(atPath: path).filter({ !$0.hasSuffix("token.txt") }) } } catch { print(error) @@ -27,7 +34,7 @@ extension FileEditorView { func remove(file: String) { do { - try fileManager.removeItem(at: documentsDirectory().appendingPathComponent(file)) + try fileManager.removeItem(at: documentsDirectory.appendingPathComponent(file)) } catch { print(error) } @@ -38,12 +45,5 @@ extension FileEditorView { remove(file: file) } } - - private func documentsDirectory() -> URL { - return fileManager.urls( - for: .documentDirectory, - in: .userDomainMask - )[0] - } } } diff --git a/WDBFontOverwrite/MainInterface/PresetFontsScene.ViewModel.swift b/WDBFontOverwrite/MainInterface/PresetFontsScene.ViewModel.swift index c9003f7..bc83934 100644 --- a/WDBFontOverwrite/MainInterface/PresetFontsScene.ViewModel.swift +++ b/WDBFontOverwrite/MainInterface/PresetFontsScene.ViewModel.swift @@ -63,9 +63,9 @@ extension PresetFontsScene { ), ] - func overwrite(withName name: String) async { + nonisolated func overwrite(withName name: String) async { await overwriteWithFont(name: name) - Task { @MainActor in + await MainActor.run { ProgressManager.shared.isBusy = false } } diff --git a/WDBFontOverwrite/OverwriteFontImpl.swift b/WDBFontOverwrite/OverwriteFontImpl.swift index b7edaf7..031ebde 100644 --- a/WDBFontOverwrite/OverwriteFontImpl.swift +++ b/WDBFontOverwrite/OverwriteFontImpl.swift @@ -156,7 +156,7 @@ func dumpCurrentFont() { func overwriteWithCustomFont( name: String, - targetPath: PathType? + targetPaths: [String]? ) async { let documentDirectory = FileManager.default.urls( for: .documentDirectory, @@ -171,14 +171,8 @@ func overwriteWithCustomFont( return } - switch targetPath { - case .single(let path): - await overwriteWithFont( - fontURL: fontURL, - pathToTargetFont: path - ) - case .many(let paths): - for path in paths { + if let targetPaths { + for path in targetPaths { if (access(path, F_OK) == 0) { await overwriteWithFont( fontURL: fontURL, @@ -186,7 +180,7 @@ func overwriteWithCustomFont( ) } } - default: + } else { await MainActor.run { ProgressManager.shared.message = "Either targetName or targetNames must be provided" } diff --git a/WDBFontOverwrite/Progress/ProgressManager.swift b/WDBFontOverwrite/Progress/ProgressManager.swift index 295015a..e4a0ec1 100644 --- a/WDBFontOverwrite/Progress/ProgressManager.swift +++ b/WDBFontOverwrite/Progress/ProgressManager.swift @@ -23,10 +23,8 @@ final class ProgressManager: ObservableObject { @Published var isPresentedResultsAlert = false { didSet { if !isPresentedResultsAlert { - Task { @MainActor in - importResults = [] - message = "Done." - } + importResults = [] + message = "Done." } } } @@ -35,11 +33,9 @@ final class ProgressManager: ObservableObject { didSet { if !isBusy { // Reset values when done. - Task { @MainActor in - completedProgress = 0 - totalProgress = 0 - isPresentedResultsAlert = true - } + completedProgress = 0 + totalProgress = 0 + isPresentedResultsAlert = true } } } diff --git a/WDBFontOverwrite/WDBImportCustomFontPickerViewControllerDelegate.swift b/WDBFontOverwrite/WDBImportCustomFontPickerViewControllerDelegate.swift index 24fbf4c..99730b2 100644 --- a/WDBFontOverwrite/WDBImportCustomFontPickerViewControllerDelegate.swift +++ b/WDBFontOverwrite/WDBImportCustomFontPickerViewControllerDelegate.swift @@ -24,7 +24,9 @@ class WDBImportCustomFontPickerViewControllerDelegate: NSObject, UIDocumentPicke } func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) { - ProgressManager.shared.message = "Cancelled" + Task { @MainActor in + ProgressManager.shared.message = "Cancelled" + } } private func importSelectedFonts(atURLs urls: [URL]) async { @@ -66,11 +68,7 @@ class WDBImportCustomFontPickerViewControllerDelegate: NSObject, UIDocumentPicke targetURL: targetURL, ttcRepackMode: self.ttcRepackMode ) - if success == nil { - return 1 - } else { - return 0 - } + return success == nil ? 1 : 0 } } diff --git a/WDBFontOverwrite/grant_full_disk_access.m b/WDBFontOverwrite/grant_full_disk_access.m index 300f018..152325e 100644 --- a/WDBFontOverwrite/grant_full_disk_access.m +++ b/WDBFontOverwrite/grant_full_disk_access.m @@ -127,8 +127,8 @@ static uint64_t patchfind_got(void* executable_map, size_t executable_length, struct section_64* section_array = ((void*)data_const_segment) + sizeof(struct segment_command_64); struct section_64* first_section = §ion_array[0]; - if (strcmp(first_section->sectname, "__auth_got")) { - // TODO(zhuowei): arm64? + if (!(strcmp(first_section->sectname, "__auth_got") == 0 || + strcmp(first_section->sectname, "__got") == 0)) { return 0; } uint32_t* indirect_table = executable_map + dysymtab_command->indirectsymoff; @@ -189,8 +189,8 @@ static bool patchfind(void* executable_map, size_t executable_length, static void call_tccd(void (^completion)(NSString* _Nullable extension_token)) { // reimplmentation of TCCAccessRequest, as we need to grab and cache the sandbox token so we can - // re-use it until next reboot returns the sandbox token if there is one, or nil if there isn't - // one. + // re-use it until next reboot. + // Returns the sandbox token if there is one, or nil if there isn't one. xpc_connection_t connection = xpc_connection_create_mach_service( "com.apple.tccd", dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0), 0); xpc_connection_set_event_handler(connection, ^(xpc_object_t object) { @@ -267,7 +267,7 @@ static void call_tccd(void (^completion)(NSString* _Nullable extension_token)) { *(uint64_t*)(mutableBytes + offsets.offset_addr_s_kTCCServiceMediaLibrary + 8) = strlen("com.apple.app-sandbox.read-write"); } - { + if (offsets.is_arm64e) { // make sandbox_init call return 0; struct dyld_chained_ptr_arm64e_auth_rebase targetRebase = { .auth = 1, @@ -281,6 +281,15 @@ static void call_tccd(void (^completion)(NSString* _Nullable extension_token)) { *(struct dyld_chained_ptr_arm64e_auth_rebase*)(mutableBytes + offsets.offset_auth_got__sandbox_init) = targetRebase; + } else { + // make sandbox_init call return 0; + struct dyld_chained_ptr_64_rebase targetRebase = { + .bind = 0, + .next = 2, + .target = offsets.offset_just_return_0, + }; + *(struct dyld_chained_ptr_64_rebase*)(mutableBytes + offsets.offset_auth_got__sandbox_init) = + targetRebase; } return data; } @@ -307,6 +316,11 @@ static void grant_full_disk_access_impl(void (^completion)(NSString* extension_t NSError* _Nullable error)) { char* targetPath = "/System/Library/PrivateFrameworks/TCC.framework/Support/tccd"; int fd = open(targetPath, O_RDONLY | O_CLOEXEC); + if (fd == -1) { + // iOS 15.3 and below + targetPath = "/System/Library/PrivateFrameworks/TCC.framework/tccd"; + fd = open(targetPath, O_RDONLY | O_CLOEXEC); + } off_t targetLength = lseek(fd, 0, SEEK_END); lseek(fd, 0, SEEK_SET); void* targetMap = mmap(nil, targetLength, PROT_READ, MAP_SHARED, fd, 0); @@ -362,6 +376,19 @@ static void grant_full_disk_access_impl(void (^completion)(NSString* extension_t } void grant_full_disk_access(void (^completion)(NSError* _Nullable)) { + if (!NSClassFromString(@"NSPresentationIntent")) { + // class introduced in iOS 15.0. + // TODO(zhuowei): maybe check the actual OS version instead? + completion([NSError + errorWithDomain:@"com.worthdoingbadly.fulldiskaccess" + code:6 + userInfo:@{ + NSLocalizedDescriptionKey : + @"Not supported on iOS 14 and below: on iOS 14 the system partition is not " + @"reverted after reboot, so running this may permanently corrupt tccd." + }]); + return; + } NSURL* documentDirectory = [NSFileManager.defaultManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask][0]; NSURL* sourceURL = @@ -397,4 +424,4 @@ void grant_full_disk_access(void (^completion)(NSError* _Nullable)) { error:&error]; completion(nil); }); -} \ No newline at end of file +}