diff --git a/.swiftlint.yml b/.swiftlint.yml index fb88bd64a..52db4316a 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -1,5 +1,5 @@ -disabled_rules: - - inclusive_language +opt_in_rules: + - force_unwrapping excluded: - Carthage diff --git a/PlayCover.xcodeproj/project.pbxproj b/PlayCover.xcodeproj/project.pbxproj index b2be39ffc..a3bd37373 100644 --- a/PlayCover.xcodeproj/project.pbxproj +++ b/PlayCover.xcodeproj/project.pbxproj @@ -18,7 +18,6 @@ 532644DE26E79E56002EA34D /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 532644E026E79E56002EA34D /* Localizable.strings */; }; 53D9DAF326C1849D0071959E /* PlayCoverError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53D9DAF226C1849D0071959E /* PlayCoverError.swift */; }; 53F3802826EB6F6B00D6B525 /* NotifyService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53F3802726EB6F6B00D6B525 /* NotifyService.swift */; }; - 53F4D29E26C43C040020167C /* UserIntentFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53F4D29D26C43C040020167C /* UserIntentFlow.swift */; }; 53F4D2A026C43C690020167C /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53F4D29F26C43C690020167C /* Log.swift */; }; 53F50C4926E3CA42007AD2D3 /* AppLibraryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53F50C4826E3CA42007AD2D3 /* AppLibraryView.swift */; }; 68E48B952957046D00C39879 /* DownloadManager in Frameworks */ = {isa = PBXBuildFile; productRef = 68E48B942957046D00C39879 /* DownloadManager */; }; @@ -108,7 +107,6 @@ 53E3311126F574B600217197 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/Localizable.strings; sourceTree = ""; }; 53F3802526EB6CEA00D6B525 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = ""; }; 53F3802726EB6F6B00D6B525 /* NotifyService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotifyService.swift; sourceTree = ""; }; - 53F4D29D26C43C040020167C /* UserIntentFlow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserIntentFlow.swift; sourceTree = ""; }; 53F4D29F26C43C690020167C /* Log.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Log.swift; sourceTree = ""; }; 53F50C4826E3CA42007AD2D3 /* AppLibraryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLibraryView.swift; sourceTree = ""; }; 6854C5E528D53C9500CE28A0 /* fa */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fa; path = fa.lproj/Localizable.strings; sourceTree = ""; }; @@ -133,7 +131,7 @@ 6E692F1D290B349F0090D9EC /* ro */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ro; path = ro.lproj/Localizable.strings; sourceTree = ""; }; 6E7CA16428B4D02900216CD8 /* ITunesResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ITunesResponse.swift; sourceTree = ""; }; 6E7CA16628B4EEAE00216CD8 /* Keymapping.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Keymapping.swift; sourceTree = ""; }; - 6E8001382974FE3400E53461 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/Localizable.strings; sourceTree = ""; }; + 6EA4A34B29B61CE9005F4679 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Localizable.strings; sourceTree = ""; }; 6EB4B57328C93E0600630890 /* LegacySettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacySettings.swift; sourceTree = ""; }; 6EC228A028B14A0600D7D73A /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Localizable.strings"; sourceTree = ""; }; 6ECB1D0D29798DFA00CD92AA /* DataExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataExtensions.swift; sourceTree = ""; }; @@ -275,14 +273,6 @@ path = Model; sourceTree = ""; }; - 53F4D29C26C43BED0020167C /* IntentFlow */ = { - isa = PBXGroup; - children = ( - 53F4D29D26C43C040020167C /* UserIntentFlow.swift */, - ); - path = IntentFlow; - sourceTree = ""; - }; 53F5E73B26C1C566005AED1D /* AppInstaller */ = { isa = PBXGroup; children = ( @@ -403,7 +393,6 @@ AA970FD228793A310099A5D0 /* PlayCoverRelease.entitlements */, 53D9DAF226C1849D0071959E /* PlayCoverError.swift */, 53F3802626EB6F5600D6B525 /* Services */, - 53F4D29C26C43BED0020167C /* IntentFlow */, 53F4D29926C43A390020167C /* Model */, 53F5E74126C1D369005AED1D /* Utils */, 53F5E73E26C1C654005AED1D /* ViewModel */, @@ -508,7 +497,7 @@ tr, ro, hi, - ca, + da, ); mainGroup = 8783CFEE26B8C52D00171041; packageReferences = ( @@ -659,7 +648,6 @@ 6E5C68BA289865C8008EC11B /* MainView.swift in Sources */, 28361D6028927CAC00B35EDB /* SaveGenshinUserData.swift in Sources */, B1419FB628BA82EE000CB69F /* DiscordActivity.swift in Sources */, - 53F4D29E26C43C040020167C /* UserIntentFlow.swift in Sources */, 6E66B0BF289DE6240099B907 /* StoreVM.swift in Sources */, B6603E1328E2257800DEFA3F /* Uninstaller.swift in Sources */, B6ABDA2A2971EEF700A46E80 /* ProgressVM.swift in Sources */, @@ -718,7 +706,7 @@ 6E692F1C290B34890090D9EC /* tr */, 6E692F1D290B349F0090D9EC /* ro */, 68C79E67296741580041DBC9 /* hi */, - 6E8001382974FE3400E53461 /* ca */, + 6EA4A34B29B61CE9005F4679 /* da */, ); name = Localizable.strings; sourceTree = ""; diff --git a/PlayCover/AppInstaller/Downloader.swift b/PlayCover/AppInstaller/Downloader.swift index d5fe2eaef..648f2248a 100644 --- a/PlayCover/AppInstaller/Downloader.swift +++ b/PlayCover/AppInstaller/Downloader.swift @@ -36,16 +36,15 @@ class DownloadApp { let downloader = DownloadManager.shared func start() { - if !NetworkVM.isConnectedToNetwork() { return } if installVM.inProgress { Log.shared.error(PlayCoverError.waitInstallation) } else { - if let warningMessage = warning { + if let warningMessage = warning, let app = app { let alert = NSAlert() alert.messageText = NSLocalizedString(warningMessage, comment: "") alert.informativeText = String( format: NSLocalizedString("ipaLibrary.alert.download", comment: ""), - arguments: [app!.name] + arguments: [app.name] ) alert.alertStyle = .warning alert.addButton(withTitle: NSLocalizedString("button.Yes", comment: "")) @@ -58,7 +57,7 @@ class DownloadApp { if let url = url, url.isFileURL { proceedInstall(url, deleteIPA: false) - } else { + } else if NetworkVM.urlAccessible(url: url, popup: true) { proceedDownload() } } @@ -110,29 +109,32 @@ class DownloadApp { in: .userDomainMask, appropriateFor: URL(fileURLWithPath: "/Users"), create: true) - downloader.addDownload(url: url!, - destinationURL: tmpDir!, - onProgress: { progress in - // progress is a Float - self.downloadVM.progress = Double(progress) - }, onCompletion: { error, fileURL in - self.downloadVM.next(.integrity, 0.7, 0.95) - - guard error == nil else { - self.downloadVM.next(.failed, 0.95, 1.0) - self.downloadVM.storeAppData = nil - return Log.shared.error(error!) - } - self.verifyChecksum(checksum: self.downloadVM.storeAppData?.checksum, file: fileURL) { completing in - self.downloadVM.next(completing ? .finish : .failed, 0.95, 1.0) - if completing { - Task { @MainActor in - self.proceedInstall(fileURL) + if let tmpDir = tmpDir, let url = url { + downloader.addDownload(url: url, + destinationURL: tmpDir, + onProgress: { progress in + // progress is a Float + self.downloadVM.progress = Double(progress) + }, onCompletion: { error, fileURL in + self.downloadVM.next(.integrity, 0.7, 0.95) + + if let error = error { + self.downloadVM.next(.failed, 0.95, 1.0) + self.downloadVM.storeAppData = nil + return Log.shared.error(error) + } + + self.verifyChecksum(checksum: self.downloadVM.storeAppData?.checksum, file: fileURL) { completing in + self.downloadVM.next(completing ? .finish : .failed, 0.95, 1.0) + if completing { + Task { @MainActor in + self.proceedInstall(fileURL) + } } } - } - }) + }) + } } catch { self.downloadVM.next(.failed, 0.95, 1.0) @@ -145,8 +147,7 @@ class DownloadApp { private func proceedInstall(_ url: URL?, deleteIPA: Bool = true) { if let url = url { - uif.ipaUrl = url - Installer.install(ipaUrl: uif.ipaUrl!, export: false, returnCompletion: { _ in + Installer.install(ipaUrl: url, export: false, returnCompletion: { _ in Task { @MainActor in if deleteIPA { FileManager.default.delete(at: url) diff --git a/PlayCover/IntentFlow/UserIntentFlow.swift b/PlayCover/IntentFlow/UserIntentFlow.swift deleted file mode 100644 index 0031140e4..000000000 --- a/PlayCover/IntentFlow/UserIntentFlow.swift +++ /dev/null @@ -1,15 +0,0 @@ -// -// UserIntentFlow.swift -// PlayCover -// - -import Foundation - -let uif = UserIntentFlow.shared - -class UserIntentFlow: ObservableObject { - static let shared = UserIntentFlow() - var ipaUrl: URL? - var searchText = "" - required init() { } -} diff --git a/PlayCover/Model/AppInfo.swift b/PlayCover/Model/AppInfo.swift index 947d5b82a..d95ba2b86 100644 --- a/PlayCover/Model/AppInfo.swift +++ b/PlayCover/Model/AppInfo.swift @@ -124,7 +124,7 @@ public class AppInfo { var minimumOSVersion: String { get { - self[string: "MinimumOSVersion"]! + self[string: "MinimumOSVersion"] ?? "" } set { self[string: "MinimumOSVersion"] = newValue @@ -133,30 +133,30 @@ public class AppInfo { var bundleName: String { if self[string: "CFBundleName"] == nil || self[string: "CFBundleName"] == "" { - return self[string: "CFBundleDisplayName"]! + return self[string: "CFBundleDisplayName"] ?? "" } else { - return self[string: "CFBundleName"]! + return self[string: "CFBundleName"] ?? "" } } var displayName: String { if self[string: "CFBundleDisplayName"] == nil || self[string: "CFBundleDisplayName"] == "" { - return self[string: "CFBundleName"]! + return self[string: "CFBundleName"] ?? "" } else { - return self[string: "CFBundleDisplayName"]! + return self[string: "CFBundleDisplayName"] ?? "" } } var bundleIdentifier: String { - self[string: "CFBundleIdentifier"]! + self[string: "CFBundleIdentifier"] ?? "" } var executableName: String { - self[string: "CFBundleExecutable"]! + self[string: "CFBundleExecutable"] ?? "" } var bundleVersion: String { - self[string: "CFBundleShortVersionString"]! + self[string: "CFBundleShortVersionString"] ?? "" } var primaryIconName: String { @@ -186,18 +186,11 @@ public class AppInfo { return "AppIcon" } - var supportsTrueScreenSizeOnMac: Bool { - get { - self[bool: "UISupportsTrueScreenSizeOnMac"]! - } - set { - self[bool: "UISupportsTrueScreenSizeOnMac"] = newValue - } - } - func assert(minimumVersion: Double) { - if Double(minimumOSVersion)! > 11.0 { - minimumOSVersion = Int(minimumVersion).description + if let double = Double(minimumOSVersion) { + if double > 11.0 { + minimumOSVersion = Int(minimumVersion).description + } } } } diff --git a/PlayCover/Model/AppSettings.swift b/PlayCover/Model/AppSettings.swift index 974c4b23c..8243ef2d9 100644 --- a/PlayCover/Model/AppSettings.swift +++ b/PlayCover/Model/AppSettings.swift @@ -26,6 +26,8 @@ struct AppSettingsData: Codable { var playChainDebugging = false var inverseScreenValues = false var metalHUD = false + var windowFixMethod = 0 + var injectIntrospection = false init() {} @@ -50,6 +52,8 @@ struct AppSettingsData: Codable { playChainDebugging = try container.decodeIfPresent(Bool.self, forKey: .playChainDebugging) ?? false inverseScreenValues = try container.decodeIfPresent(Bool.self, forKey: .inverseScreenValues) ?? false metalHUD = try container.decodeIfPresent(Bool.self, forKey: .metalHUD) ?? false + windowFixMethod = try container.decodeIfPresent(Int.self, forKey: .windowFixMethod) ?? 0 + injectIntrospection = try container.decodeIfPresent(Bool.self, forKey: .injectIntrospection) ?? false } } diff --git a/PlayCover/Model/ITunesResponse.swift b/PlayCover/Model/ITunesResponse.swift index f69051ddd..35dffa19a 100644 --- a/PlayCover/Model/ITunesResponse.swift +++ b/PlayCover/Model/ITunesResponse.swift @@ -59,19 +59,24 @@ struct ITunesResponse: Codable { } func getITunesData(_ itunesLookup: String) async -> ITunesResponse? { - if !NetworkVM.isConnectedToNetwork() { return nil } - guard let url = URL(string: itunesLookup) else { return nil } - - do { - let (data, _) = try await URLSession.shared.data(for: URLRequest(url: url)) - let decoder = JSONDecoder() - let jsonResult: ITunesResponse = try decoder.decode(ITunesResponse.self, from: data) - if jsonResult.resultCount > 0 { - return jsonResult - } - } catch { - print("Error getting iTunes data from URL: \(itunesLookup): \(error)") + guard NetworkVM.isConnectedToNetwork(), let url = URL(string: itunesLookup) else { + return nil } - return nil + return await withCheckedContinuation { continuation in + URLSession.shared.dataTask(with: URLRequest(url: url)) { data, _, error in + do { + if error == nil, let data = data { + let decoder = JSONDecoder() + let jsonResult: ITunesResponse = try decoder.decode(ITunesResponse.self, from: data) + continuation.resume(returning: jsonResult.resultCount > 0 ? jsonResult : nil) + return + } + } catch { + print("Error getting iTunes data from URL: \(itunesLookup): \(error)") + } + + continuation.resume(returning: nil) + }.resume() + } } diff --git a/PlayCover/Model/PlayApp.swift b/PlayCover/Model/PlayApp.swift index 2c1aa9676..21e04fa33 100644 --- a/PlayCover/Model/PlayApp.swift +++ b/PlayCover/Model/PlayApp.swift @@ -9,13 +9,16 @@ import IOKit.pwr_mgt class PlayApp: BaseApp { private static let library = FileManager.default.homeDirectoryForCurrentUser.appendingPathComponent("Library") + var displaySleepAssertionID: IOPMAssertionID? + public var isStarting = false var searchText: String { info.displayName.lowercased().appending(" ").appending(info.bundleName).lowercased() } - func launch() { + func launch() async { do { + isStarting = true if prohibitedToPlay { clearAllCache() throw PlayCoverError.appProhibited @@ -45,11 +48,12 @@ class PlayApp: BaseApp { Log.shared.error("The app is not codesigned! Please open Xcode and accept license agreement.") } else { if settings.openWithLLDB { - Shell.lldb(executable, withTerminalWindow: settings.openLLDBWithTerminal) + try Shell.lldb(executable, withTerminalWindow: settings.openLLDBWithTerminal) } else { runAppExec() // Splitting to reduce complexity } } + isStarting = false } catch { Log.shared.error(error) } @@ -64,37 +68,57 @@ class PlayApp: BaseApp { config.environment = ["MTL_HUD_ENABLED": "0"] } + if settings.settings.injectIntrospection { + config.environment["DYLD_LIBRARY_PATH"] = "/usr/lib/system/introspection" + } + NSWorkspace.shared.openApplication( at: url, configuration: config, completionHandler: { runningApp, error in guard error == nil else { return } - if self.settings.settings.disableTimeout { - // Yeet into a thread - Task { - debugPrint("Disabling timeout...") - let reason = "PlayCover: " + self.name + " disabled screen timeout" as CFString - var assertionID: IOPMAssertionID = 0 - var success = IOPMAssertionCreateWithName( - kIOPMAssertionTypeNoDisplaySleep as CFString, - IOPMAssertionLevel(kIOPMAssertionLevelOn), - reason, - &assertionID) - if success == kIOReturnSuccess { - while true { // Run a loop until the app closes - try await Task.sleep(nanoseconds: 10000000000) // Sleep for 10 seconds - guard - let isFinish = runningApp?.isTerminated, - !isFinish else { break } - } - success = IOPMAssertionRelease(assertionID) - debugPrint("Enabling timeout...") + // Run a thread loop in the background to handle background tasks + Task(priority: .background) { + while !(runningApp?.isTerminated ?? true) { + // Check if the app is in the foreground + if runningApp!.isActive { + // If the app is in the foreground, disable the display sleep + self.disableTimeOut() + } else { + // If the app is not in the foreground, enable the display sleep + self.enableTimeOut() } + sleep(1) } } }) } + func disableTimeOut() { + if displaySleepAssertionID != nil { + return + } + // Disable display sleep + let reason = "PlayCover: \(info.bundleIdentifier) is disabling sleep" as CFString + var assertionID: IOPMAssertionID = 0 + let result = IOPMAssertionCreateWithName( + kIOPMAssertionTypeNoDisplaySleep as CFString, + IOPMAssertionLevel(kIOPMAssertionLevelOn), + reason, + &assertionID) + if result == kIOReturnSuccess { + displaySleepAssertionID = assertionID + } + } + + func enableTimeOut() { + // Enable display sleep + if let assertionID = displaySleepAssertionID { + IOPMAssertionRelease(assertionID) + displaySleepAssertionID = nil + } + } + var name: String { if info.displayName.isEmpty { return info.bundleName @@ -133,7 +157,7 @@ class PlayApp: BaseApp { } func isCodesigned() throws -> Bool { - try shell.shello("/usr/bin/codesign", "-dv", executable.path).contains("adhoc") + try Shell.run("/usr/bin/codesign", "-dv", executable.path).contains("adhoc") } func showInFinder() { @@ -185,7 +209,7 @@ class PlayApp: BaseApp { .appendingPathExtension("plist") let conf = try Entitlements.composeEntitlements(self) try conf.store(tmpEnts) - shell.signAppWith(executable, entitlements: tmpEnts) + try Shell.signAppWith(executable, entitlements: tmpEnts) try FileManager.default.removeItem(at: tmpDir) } catch { print(error) @@ -193,13 +217,6 @@ class PlayApp: BaseApp { } } - func largerImage(image imageA: NSImage, compareTo imageB: NSImage?) -> NSImage { - if imageA.size.height > imageB?.size.height ?? -1 { - return imageA - } - return imageB! - } - var prohibitedToPlay: Bool { PlayApp.PROHIBITED_APPS.contains(info.bundleIdentifier) } diff --git a/PlayCover/Model/PlayRules.swift b/PlayCover/Model/PlayRules.swift index 1edcafe4e..1adcba042 100644 --- a/PlayCover/Model/PlayRules.swift +++ b/PlayCover/Model/PlayRules.swift @@ -8,8 +8,8 @@ import Foundation struct PlayRules: Decodable { - var blacklist: [String]? - var whitelist: [String]? + var blocklist: [String]? + var greenlist: [String]? var allow: [String]? var bypass: [String]? diff --git a/PlayCover/Utils/AppIntegrity.swift b/PlayCover/Utils/AppIntegrity.swift index c0b340893..3da702fbc 100644 --- a/PlayCover/Utils/AppIntegrity.swift +++ b/PlayCover/Utils/AppIntegrity.swift @@ -15,13 +15,15 @@ class AppIntegrity: ObservableObject { func moveToApps() { do { - FileManager.default.delete(at: AppIntegrity.expectedUrl) - try FileManager.default.copyItem(at: AppIntegrity.appUrl!, to: AppIntegrity.expectedUrl) - URL(fileURLWithPath: AppIntegrity.expectedUrl.path).openInFinder() - FileManager.default.delete(at: AppIntegrity.appUrl!) - exit(0) - } catch { - Log.shared.error(error) + if let url = AppIntegrity.appUrl { + FileManager.default.delete(at: AppIntegrity.expectedUrl) + try FileManager.default.copyItem(at: url, to: AppIntegrity.expectedUrl) + URL(fileURLWithPath: AppIntegrity.expectedUrl.path).openInFinder() + FileManager.default.delete(at: url) + exit(0) + } + } catch { + Log.shared.error(error) } } diff --git a/PlayCover/Utils/Entitlements.swift b/PlayCover/Utils/Entitlements.swift index 9ee23b080..3eb89c21c 100644 --- a/PlayCover/Utils/Entitlements.swift +++ b/PlayCover/Utils/Entitlements.swift @@ -91,14 +91,14 @@ class Entitlements { sandboxProfile.append(contentsOf: PlayRules.buildRules(rules: rules.allow ?? [], bundleID: bundleID)) if app.settings.settings.bypass { - for file in PlayRules.buildRules(rules: rules.blacklist ?? [], bundleID: bundleID) { + for file in PlayRules.buildRules(rules: rules.blocklist ?? [], bundleID: bundleID) { sandboxProfile.append( """ (deny file* file-read* file-read-metadata file-ioctl (literal "\(file)")) """) } - for file in PlayRules.buildRules(rules: rules.whitelist ?? [], bundleID: bundleID) { + for file in PlayRules.buildRules(rules: rules.greenlist ?? [], bundleID: bundleID) { sandboxProfile.append( """ (allow file* file-read* file-read-metadata file-ioctl (literal "\(file)")) diff --git a/PlayCover/Utils/Extensions/DataExtensions.swift b/PlayCover/Utils/Extensions/DataExtensions.swift index 534128093..15b23d1df 100644 --- a/PlayCover/Utils/Extensions/DataExtensions.swift +++ b/PlayCover/Utils/Extensions/DataExtensions.swift @@ -3,6 +3,8 @@ // PlayCover // +// swiftlint:disable force_unwrapping + import Foundation extension String { diff --git a/PlayCover/Utils/Extensions/FileExtensions.swift b/PlayCover/Utils/Extensions/FileExtensions.swift index 298eb6cf9..7fdd77586 100644 --- a/PlayCover/Utils/Extensions/FileExtensions.swift +++ b/PlayCover/Utils/Extensions/FileExtensions.swift @@ -28,8 +28,9 @@ extension NSOpenPanel { panel.canChooseFiles = true panel.begin { result in if result == .OK { - let url = panel.urls.first - completion(.success(url!)) + if let url = panel.urls.first { + completion(.success(url)) + } } } } diff --git a/PlayCover/Utils/IPA.swift b/PlayCover/Utils/IPA.swift index 4933ca2ef..e50764bd1 100644 --- a/PlayCover/Utils/IPA.swift +++ b/PlayCover/Utils/IPA.swift @@ -31,12 +31,13 @@ public class IPA { } func removeQuarantine(_ execUrl: URL) throws { - try shell.shello("/usr/bin/xattr", "-r", "-d", "com.apple.quarantine", execUrl.relativePath) + try Shell.run("/usr/bin/xattr", "-r", "-d", "com.apple.quarantine", execUrl.relativePath) } public func unzip() throws -> BaseApp { if let workDir = tmpDir { - if Shell.quietUnzip(url, toUrl: workDir) == "" { + if try Shell.run("/usr/bin/unzip", + "-oq", url.path, "-d", workDir.path) == "" { try removeQuarantine(workDir) return try Installer.fromIPA(detectingAppNameInFolder: workDir.appendingPathComponent("Payload")) } else { @@ -48,12 +49,15 @@ public class IPA { } func packIPABack(app: URL) throws -> URL { + let payload = app.deletingPathExtension().deletingLastPathComponent() + let name = app.deletingPathExtension().lastPathComponent + let newIpa = getDocumentsDirectory() - .appendingEscapedPathComponent(app.deletingPathExtension().lastPathComponent).appendingPathExtension("ipa") - try Shell.zip( - ipa: newIpa, - name: app.deletingPathExtension().lastPathComponent, - payload: app.deletingLastPathComponent().deletingLastPathComponent()) + .appendingEscapedPathComponent(name) + .appendingPathExtension("ipa") + + try Shell.run("usr/bin/zip", "-r", newIpa.path, payload.path) + return newIpa } diff --git a/PlayCover/Utils/Shell.swift b/PlayCover/Utils/Shell.swift index d7c8745e4..eca076d60 100644 --- a/PlayCover/Utils/Shell.swift +++ b/PlayCover/Utils/Shell.swift @@ -5,52 +5,9 @@ import Foundation -let shell = Shell.self - class Shell: ObservableObject { - static let shared = Shell() - - @discardableResult - static func sh(_ command: String, print: Bool = true, pipeStdErr: Bool = true) throws -> String { - let task = Process() - let pipe = Pipe() - let errPipe = Pipe() - - task.standardOutput = pipe - if pipeStdErr { task.standardError = pipe } else {task.standardError = errPipe} - task.executableURL = URL(fileURLWithPath: "/bin/zsh") - task.arguments = ["-c", command] - try task.run() - - let data = try pipe.fileHandleForReading.readToEnd() ?? Data() - let output = String(data: data, encoding: .utf8)! - - if print { - Log.shared.log(output) - } - - task.waitUntilExit() - - let status = task.terminationStatus - if status != 0 { - if pipeStdErr { - throw output - } else { - let errOutput: String - do { - let errData = try errPipe.fileHandleForReading.readToEnd() ?? Data() - errOutput = String(data: errData, encoding: .utf8)! - } catch { - errOutput = "Command '\(command)' failed to execute." - } - throw errOutput - } - } - return output - } - @discardableResult - internal static func shello(print: Bool = true, _ binary: String, _ args: String...) throws -> String { + static func run(print: Bool = true, _ binary: String, _ args: String...) throws -> String { let process = Process() let pipe = Pipe() @@ -74,54 +31,7 @@ class Shell: ObservableObject { return String(decoding: output, as: UTF8.self) } - static func codesign(_ binary: URL) { - shell("/usr/bin/codesign -fs- \(binary.esc)") - } - - static func quietUnzip(_ zip: URL, toUrl: URL) -> String { - return shell("unzip -oq \(zip.esc) -d \(toUrl.esc)") - } - - static func unzip(_ zip: URL, toUrl: URL) { - shell("unzip \(zip.esc) -d \(toUrl.esc)") - } - - static func zip(ipa: URL, name: String, payload: URL) throws { - shell("cd \(payload.esc) && zip -r \(name.esc).ipa Payload") - try FileManager.default - .moveItem(at: payload.appendingEscapedPathComponent(name).appendingPathExtension("ipa"), to: ipa) - } - - static func signAppWith(_ exec: URL, entitlements: URL) { - shell( - "/usr/bin/codesign -fs- \(exec.deletingLastPathComponent().esc) --deep --entitlements \(entitlements.esc)") - } - - static func signApp(_ exec: URL) { - shell("/usr/bin/codesign -fs- \(exec.deletingLastPathComponent().esc) --deep --preserve-metadata=entitlements") - } - - static func lldb(_ url: URL, withTerminalWindow: Bool = false) { - Task(priority: .utility) { - var command = "/usr/bin/lldb -o run \(url.esc) -o exit" - - if withTerminalWindow { - command = command.replacingOccurrences(of: "\\", with: "\\\\") - let osascript = """ - tell app "Terminal" - reopen - activate - do script "\(command)" - end tell - """ - shell("/usr/bin/osascript -e '\(osascript)'", print: true) - } else { - shell(command, print: true) - } - } - } - - static func sudosh(_ args: [String], _ argc: String) -> Bool { + static func runSu(_ args: [String], _ argc: String) -> Bool { let password = argc let passwordWithNewline = password + "\n" let sudo = Process() @@ -148,37 +58,61 @@ class Shell: ObservableObject { } } } - // Write the password - sudoIn.fileHandleForWriting.write(passwordWithNewline.data(using: .utf8)!) + if let data = passwordWithNewline.data(using: .utf8) { + // Write the password + sudoIn.fileHandleForWriting.write(data) - // Close the file handle after writing the password; avoids a - // hang for incorrect password. - try? sudoIn.fileHandleForWriting.close() + // Close the file handle after writing the password; avoids a + // hang for incorrect password. + try? sudoIn.fileHandleForWriting.close() + } // Make sure we don't disappear while output is still being produced. sudo.waitUntilExit() return result } - @discardableResult - static func shell(_ command: String, print: Bool = false) -> String { - let task = Process() - let pipe = Pipe() + static func signMacho(_ binary: URL) throws { + try run("/usr/bin/codesign", "-fs-", binary.path) + } - task.standardOutput = pipe - task.standardError = pipe - task.arguments = ["-c", command] - task.launchPath = "/bin/zsh" - task.launch() + static func signAppWith(_ exec: URL, entitlements: URL) throws { + try run("/usr/bin/codesign", "-fs-", exec.deletingLastPathComponent().path, + "--deep", "--entitlements", entitlements.path) + } - let data = pipe.fileHandleForReading.readDataToEndOfFile() - let output = String(data: data, encoding: .utf8)! + static func signApp(_ exec: URL) throws { + try run("/usr/bin/codesign", "-fs-", exec.deletingLastPathComponent().path, + "--deep", "--preserve-metadata=entitlements") + } - if print { - Log.shared.log(output) + static func lldb(_ url: URL, withTerminalWindow: Bool = false) throws { + Task(priority: .utility) { + if withTerminalWindow { + let command = "/usr/bin/lldb -o run \(url.esc) -o exit" + .replacingOccurrences(of: "\\", with: "\\\\") + let osascript = """ + tell app "Terminal" + reopen + activate + do script "\(command)" + end tell + """ + let appleScript = NSAppleScript(source: osascript) + var possibleError: NSDictionary? + appleScript?.executeAndReturnError(&possibleError) + + if let error = possibleError { + for key in error.allKeys { + if let key = key as? String { + throw error.value(forKey: key).debugDescription + } + } + } + } else { + try run("/usr/bin/lldb", "-o", "run", url.path, "-o", "exit") + } } - - return output } } diff --git a/PlayCover/Utils/SystemConfig.swift b/PlayCover/Utils/SystemConfig.swift index dd7552082..487b230d6 100644 --- a/PlayCover/Utils/SystemConfig.swift +++ b/PlayCover/Utils/SystemConfig.swift @@ -6,13 +6,12 @@ import Foundation class SystemConfig { - static var isFirstTimePlaySign = false static let isPlaySignActive: Bool = isSIPDisabled() && isRunningAMFIEnabled() static func enablePlaySign(_ argc: String) -> Bool { - shell.sudosh([ + Shell.runSu([ "-S", "/usr/sbin/nvram", "boot-args=amfi_get_out_of_my_way=0x1 ipc_control_port_options=0" @@ -20,24 +19,36 @@ class SystemConfig { } static func isSIPDisabled() -> Bool { - let check = shell.shell("csrutil status") - return check.contains("unknown") || check.contains("disabled") + do { + let check = try Shell.run("/usr/bin/csrutil", "status") + return check.contains("unknown") || check.contains("disabled") + } catch { + return false + } } static func isPRAMValid() -> Bool { - let check = shell.shell("nvram boot-args") - for option in NVRAM_OPTIONS where check.contains(option) { - return true + do { + let check = try Shell.run("/usr/sbin/nvram", "boot-args") + for option in NVRAM_OPTIONS where check.contains(option) { + return true + } + return false + } catch { + return false } - return false } static func isRunningAMFIEnabled() -> Bool { - let check = shell.shell("sysctl kern.bootargs") - for option in NVRAM_OPTIONS where check.contains(option) { - return true + do { + let check = try Shell.run("/usr/sbin/sysctl", "kern.bootargs") + for option in NVRAM_OPTIONS where check.contains(option) { + return true + } + return false + } catch { + return false } - return false } private static let NVRAM_OPTIONS = [ @@ -46,5 +57,4 @@ class SystemConfig { "amfi_get_out_of_my_way=1 ipc_control_port_options=0", "amfi_get_out_of_my_way=0x1 ipc_control_port_options=0" ] - } diff --git a/PlayCover/ViewModel/AppsVM.swift b/PlayCover/ViewModel/AppsVM.swift index ecb816e13..b1043b328 100644 --- a/PlayCover/ViewModel/AppsVM.swift +++ b/PlayCover/ViewModel/AppsVM.swift @@ -16,7 +16,7 @@ class AppsVM: ObservableObject { @Published var filteredApps: [PlayApp] = [] @Published var apps: [PlayApp] = [] - + @Published var searchText: String = "" @Published var updatingApps = true func fetchApps() { @@ -44,7 +44,7 @@ class AppsVM: ObservableObject { print("Application installed under:", sub.path) } apps.append(app) - if uif.searchText.isEmpty || app.searchText.contains(uif.searchText.lowercased()) { + if searchText.isEmpty || app.searchText.contains(searchText.lowercased()) { filteredApps.append(app) } } diff --git a/PlayCover/ViewModel/NetworkVM.swift b/PlayCover/ViewModel/NetworkVM.swift index 4225a612c..b34da0456 100644 --- a/PlayCover/ViewModel/NetworkVM.swift +++ b/PlayCover/ViewModel/NetworkVM.swift @@ -15,14 +15,11 @@ class NetworkVM { let needsConnection = flags.contains(.connectionRequired) let result = (isReachable && !needsConnection) - if !result { - let networkToastExists = ToastVM.shared.toasts.contains { $0.toastType == .network } - if !networkToastExists { - ToastVM.shared.showToast( - toastType: .network, - toastDetails: NSLocalizedString("ipaLibrary.noNetworkConnection.toast", comment: "") - ) - } + if !result && !ToastVM.shared.toasts.contains(where: { $0.toastType == .network }) { + ToastVM.shared.showToast( + toastType: .network, + toastDetails: NSLocalizedString("ipaLibrary.noNetworkConnection.toast", comment: "") + ) } return result @@ -60,4 +57,38 @@ class NetworkVM { } }) } + + static func urlAccessible(url: URL?, popup: Bool = false) -> Bool { + if let url = url { + guard isConnectedToNetwork() else { + return false + } + + let semaphore = DispatchSemaphore(value: 0) + + var avaliable = false + + var request = URLRequest(url: url) + request.httpMethod = "HEAD" + + URLSession.shared.dataTask(with: request) { _, response, error in + defer { semaphore.signal() } + + if let statusCode = (response as? HTTPURLResponse)?.statusCode, statusCode != 200 { + if popup { + Log.shared.error("Unable to download: \(statusCode) " + + "\(HTTPURLResponse.localizedString(forStatusCode: statusCode))") + } + } else if error == nil { + avaliable = true + } + }.resume() + + semaphore.wait() + + return avaliable + } else { + return false + } + } } diff --git a/PlayCover/ViewModel/StoreVM.swift b/PlayCover/ViewModel/StoreVM.swift index 9120f77a9..f6b8e7de6 100644 --- a/PlayCover/ViewModel/StoreVM.swift +++ b/PlayCover/ViewModel/StoreVM.swift @@ -7,7 +7,7 @@ import Foundation -class StoreVM: ObservableObject { +class StoreVM: ObservableObject, @unchecked Sendable { static let shared = StoreVM() @@ -23,6 +23,7 @@ class StoreVM: ObservableObject { } @Published var apps: [StoreAppData] = [] + @Published var searchText: String = "" @Published var filteredApps: [StoreAppData] = [] @Published var sources: [SourceData] { didSet { @@ -76,58 +77,66 @@ class StoreVM: ObservableObject { func fetchApps() { filteredApps.removeAll() filteredApps = apps - if !uif.searchText.isEmpty { + if !searchText.isEmpty { filteredApps = filteredApps.filter({ - $0.name.lowercased().contains(uif.searchText.lowercased()) + $0.name.lowercased().contains(searchText.lowercased()) }) } - filteredApps.sort(by: { $0.name.lowercased() < $1.name.lowercased() }) } func resolveSources() { - if !NetworkVM.isConnectedToNetwork() { return } + guard NetworkVM.isConnectedToNetwork() else { + return + } apps.removeAll() for index in 0.. 0 { - Task { @MainActor in - self.sources[index].status = - sources[0.. 0 { Task { @MainActor in - self.sources[index].status = .badjson + self.sources[index].status = self.sources[0..) { let selectedData = self.sources.filter({ selected.contains($0.id) }) - var index = self.sources.firstIndex(of: selectedData.first!)! - 1 - self.sources.removeAll(where: { selected.contains($0.id) }) - if index < 0 { - index = 0 + + if let first = selectedData.first { + if var index = self.sources.firstIndex(of: first) { + index -= 1 + self.sources.removeAll(where: { selected.contains($0.id) }) + if index < 0 { + index = 0 + } + self.sources.insert(contentsOf: selectedData, at: index) + } } - self.sources.insert(contentsOf: selectedData, at: index) } func moveSourceDown(_ selected: inout Set) { let selectedData = self.sources.filter({ selected.contains($0.id) }) - var index = self.sources.firstIndex(of: selectedData.first!)! + 1 - self.sources.removeAll(where: { selected.contains($0.id) }) - if index > self.sources.endIndex { - index = self.sources.endIndex + + if let first = selectedData.first { + if var index = self.sources.firstIndex(of: first) { + index += 1 + self.sources.removeAll(where: { selected.contains($0.id) }) + if index > self.sources.endIndex { + index = self.sources.endIndex + } + self.sources.insert(contentsOf: selectedData, at: index) + } } - self.sources.insert(contentsOf: selectedData, at: index) } func appendSourceData(_ data: SourceData) { self.sources.append(data) self.resolveSources() } - - static func checkAvaliability(url: URL) -> Bool { - var avaliable = true - var request = URLRequest(url: url) - request.httpMethod = "HEAD" - URLSession(configuration: .default) - .dataTask(with: request) { _, response, error in - guard error == nil else { - print("Error:", error ?? "") - avaliable = false - return - } - - guard (response as? HTTPURLResponse)?.statusCode == 200 else { - print("down") - avaliable = false - return - } - } - .resume() - return avaliable - } } struct StoreAppData: Codable, Equatable { diff --git a/PlayCover/Views/App Views/PlayAppView.swift b/PlayCover/Views/App Views/PlayAppView.swift index cff77a302..3f1d2b74e 100644 --- a/PlayCover/Views/App Views/PlayAppView.swift +++ b/PlayCover/Views/App Views/PlayAppView.swift @@ -37,7 +37,9 @@ struct PlayAppView: View { if app.info.bundleIdentifier == "com.miHoYo.GenshinImpact" { removeTwitterSessionCookie() } - app.launch() + Task(priority: .userInitiated) { + if !app.isStarting { await app.launch() } + } }) .simultaneousGesture(TapGesture().onEnded { selected = app diff --git a/PlayCover/Views/App Views/StoreAppView.swift b/PlayCover/Views/App Views/StoreAppView.swift index 04ef7c3a2..c0e88d07f 100644 --- a/PlayCover/Views/App Views/StoreAppView.swift +++ b/PlayCover/Views/App Views/StoreAppView.swift @@ -73,7 +73,7 @@ struct StoreAppConditionalView: View { @Binding var selected: StoreAppData? @State var app: StoreAppData - @State var itunesResponce: ITunesResponse? + @State var itunesResponse: ITunesResponse? @State var onlineIcon: URL? @State var localIcon: NSImage? @State var isList: Bool @@ -186,9 +186,9 @@ struct StoreAppConditionalView: View { if !cache.hasData(forKey: app.itunesLookup) { await Cacher().resolveITunesData(app.itunesLookup) } - itunesResponce = try? cache.readCodable(forKey: app.itunesLookup) - if itunesResponce != nil { - onlineIcon = URL(string: itunesResponce!.results[0].artworkUrl512) + itunesResponse = try? cache.readCodable(forKey: app.itunesLookup) + if let response = itunesResponse { + onlineIcon = URL(string: response.results[0].artworkUrl512) } else { localIcon = Cacher().getLocalIcon(bundleId: app.bundleID) } diff --git a/PlayCover/Views/AppSettingsView.swift b/PlayCover/Views/AppSettingsView.swift index ef5a048a1..cbcce9b3b 100644 --- a/PlayCover/Views/AppSettingsView.swift +++ b/PlayCover/Views/AppSettingsView.swift @@ -285,10 +285,25 @@ struct GraphicsView: View { Spacer() } } - HStack { + VStack(alignment: .leading) { if #available(macOS 13.2, *) { - Toggle("settings.toggle.windowExperimentalFix", isOn: $settings.settings.inverseScreenValues) - .help("settings.toggle.windowExperimentalFix.help") + HStack { + Toggle("settings.picker.windowFix", isOn: $settings.settings.inverseScreenValues) + .help("settings.picker.windowFix.help") + .onChange(of: settings.settings.inverseScreenValues) { _ in + settings.settings.windowFixMethod = 0 + } + Spacer() + // Dropdown to choose fix method + Picker("", selection: $settings.settings.windowFixMethod) { + Text("settings.picker.windowFixMethod.0").tag(0) + Text("settings.picker.windowFixMethod.1").tag(1) + } + .frame(alignment: .leading) + .help("settings.picker.windowFixMethod.help") + .disabled(!settings.settings.inverseScreenValues) + .disabled(settings.settings.resolution != 0) + } Spacer() } Toggle("settings.toggle.disableDisplaySleep", isOn: $settings.settings.disableTimeout) @@ -407,6 +422,12 @@ struct BypassesView: View { .help("settings.toggle.jbBypass.help") Spacer() } + Spacer() + HStack { + Toggle("settings.toggle.introspection", isOn: $settings.settings.injectIntrospection) + .help("settings.toggle.introspection.help") + Spacer() + } } .padding() } diff --git a/PlayCover/Views/MainView.swift b/PlayCover/Views/MainView.swift index e1ea97339..ac77b317a 100644 --- a/PlayCover/Views/MainView.swift +++ b/PlayCover/Views/MainView.swift @@ -156,6 +156,7 @@ struct SplitViewAccessor: NSViewRepresentable { var sview = superview // Find split view through hierarchy + // swiftlint:disable:next force_unwrapping while sview != nil, !sview!.isKind(of: NSSplitView.self) { sview = sview?.superview } diff --git a/PlayCover/Views/MenuBarView.swift b/PlayCover/Views/MenuBarView.swift index 84f4db1dc..b2386d232 100644 --- a/PlayCover/Views/MenuBarView.swift +++ b/PlayCover/Views/MenuBarView.swift @@ -32,17 +32,25 @@ struct PlayCoverHelpMenuView: Commands { CommandGroup(replacing: .help) { Button("menubar.documentation") { - NSWorkspace.shared.open(URL(string: "https://docs.playcover.io")!) + if let url = URL(string: "https://docs.playcover.io") { + NSWorkspace.shared.open(url) + } } Divider() Button("menubar.website") { - NSWorkspace.shared.open(URL(string: "https://playcover.io")!) + if let url = URL(string: "https://playcover.io") { + NSWorkspace.shared.open(url) + } } Button("menubar.github") { - NSWorkspace.shared.open(URL(string: "https://github.com/PlayCover/PlayCover/")!) + if let url = URL(string: "https://github.com/PlayCover/PlayCover/") { + NSWorkspace.shared.open(url) + } } Button("menubar.discord") { - NSWorkspace.shared.open(URL(string: "https://discord.gg/PlayCover")!) + if let url = URL(string: "https://discord.gg/PlayCover") { + NSWorkspace.shared.open(url) + } } #if DEBUG Divider() @@ -67,9 +75,8 @@ struct PlayCoverViewMenuView: Commands { } else { await NSOpenPanel.selectIPA { result in if case .success(let url) = result { - uif.ipaUrl = url Task { - Installer.install(ipaUrl: uif.ipaUrl!, + Installer.install(ipaUrl: url, export: true, returnCompletion: { ipa in Task { @MainActor in diff --git a/PlayCover/Views/Settings/IPASourceSettings.swift b/PlayCover/Views/Settings/IPASourceSettings.swift index 2b05285c7..b6e1b5b26 100644 --- a/PlayCover/Views/Settings/IPASourceSettings.swift +++ b/PlayCover/Views/Settings/IPASourceSettings.swift @@ -128,12 +128,17 @@ struct SourceView: View { popoverText: "preferences.popover.badurl", showingPopover: $showingPopover) case .checking: - EmptyView() + StatusBadgeView(imageName: "exclamationmark.circle.fill", + imageColor: .yellow, + popoverText: "preferences.popover.checking", + showingPopover: $showingPopover) case .duplicate: StatusBadgeView(imageName: "exclamationmark.circle.fill", imageColor: .yellow, popoverText: "preferences.popover.duplicate", showingPopover: $showingPopover) + case .empty: + EmptyView() case .valid: StatusBadgeView(imageName: "checkmark.circle.fill", imageColor: .green, @@ -166,13 +171,13 @@ struct StatusBadgeView: View { } enum SourceValidation { - case badjson, badurl, checking, duplicate, valid + case badjson, badurl, checking, duplicate, valid, empty } struct AddSourceView: View { @State var newSource = "" @State var newSourceURL: URL? - @State var sourceValidationState = SourceValidation.checking + @State var sourceValidationState = SourceValidation.empty @Binding var addSourceSheet: Bool @EnvironmentObject var storeVM: StoreVM @@ -194,12 +199,17 @@ struct AddSourceView: View { Text("preferences.popover.badurl") .font(.system(.subheadline)) case .checking: - EmptyView() + Image(systemName: "exclamationmark.circle.fill") + .foregroundColor(.yellow) + Text("preferences.popover.checking") + .font(.system(.subheadline)) case .duplicate: Image(systemName: "exclamationmark.circle.fill") .foregroundColor(.yellow) Text("preferences.popover.duplicate") .font(.system(.subheadline)) + case .empty: + EmptyView() case .valid: Image(systemName: "checkmark.circle.fill") .foregroundColor(.green) @@ -213,8 +223,8 @@ struct AddSourceView: View { Text("button.Cancel") }) Button(action: { - if newSourceURL != nil { - storeVM.appendSourceData(SourceData(source: newSourceURL!.absoluteString)) + if let sourceURL = newSourceURL { + storeVM.appendSourceData(SourceData(source: sourceURL.absoluteString)) addSourceSheet.toggle() } }, label: { @@ -243,36 +253,53 @@ struct AddSourceView: View { } func validateSource(_ source: String) { - sourceValidationState = .checking + guard NetworkVM.isConnectedToNetwork() else { + return + } + + sourceValidationState = .empty + Task { - if let url = URL(string: source) { - newSourceURL = url - if StoreVM.checkAvaliability(url: newSourceURL!) { - do { - if newSourceURL!.scheme == nil { - newSourceURL = URL(string: "https://" + newSourceURL!.absoluteString)! + if var url = URL(string: source) { + if url.scheme == nil { + url = URL(string: "https://" + url.absoluteString) ?? url + } + + URLSession.shared.dataTask(with: URLRequest(url: url)) { jsonData, response, error in + guard error == nil, + ((response as? HTTPURLResponse)?.statusCode ?? 200) == 200, + let jsonData = jsonData else { + Task { @MainActor in + self.sourceValidationState = .badurl } - let (jsonData, _) = try await URLSession.shared.data(for: URLRequest(url: newSourceURL!)) - do { - let data: [StoreAppData] = try JSONDecoder().decode([StoreAppData].self, from: jsonData) - if data.count > 0 { + return + } + + do { + let data: [StoreAppData] = try JSONDecoder().decode([StoreAppData].self, + from: jsonData) + if data.count > 0 { + Task { @MainActor in sourceValidationState = storeVM.sources.filter({ $0.source == source }).isEmpty ? .valid : .duplicate - return } - } catch { - sourceValidationState = .badjson - return } } catch { - sourceValidationState = .badurl - return + Task { @MainActor in + self.sourceValidationState = .badjson + } } - } + }.resume() + + sourceValidationState = .checking + + return + } + + Task { @MainActor in + self.sourceValidationState = .badurl } - sourceValidationState = .badurl - return } } } diff --git a/PlayCover/Views/Sidebar Views/AppLibraryView.swift b/PlayCover/Views/Sidebar Views/AppLibraryView.swift index f933fb686..9a985c11a 100644 --- a/PlayCover/Views/Sidebar Views/AppLibraryView.swift +++ b/PlayCover/Views/Sidebar Views/AppLibraryView.swift @@ -112,14 +112,20 @@ struct AppLibraryView: View { } .searchable(text: $searchString, placement: .toolbar) .onChange(of: searchString, perform: { value in - uif.searchText = value + appsVM.searchText = value appsVM.fetchApps() }) + .onAppear { + appsVM.searchText = "" + appsVM.fetchApps() + } .onChange(of: isList, perform: { value in UserDefaults.standard.set(value, forKey: "AppLibraryView") }) .sheet(isPresented: $showSettings) { - AppSettingsView(viewModel: AppSettingsVM(app: selected!)) + if let selected = selected { + AppSettingsView(viewModel: AppSettingsVM(app: selected)) + } } .onAppear { showLegacyConvertAlert = LegacySettings.doesMonolithExist @@ -139,8 +145,7 @@ struct AppLibraryView: View { if let urlData = urlData as? Data { let url = NSURL(absoluteURLWithDataRepresentation: urlData, relativeTo: nil) as URL if url.pathExtension == "ipa" { - uif.ipaUrl = url - installApp() + installApp(url) } else { showWrongfileTypeAlert = true } @@ -176,8 +181,8 @@ struct AppLibraryView: View { }) } - private func installApp() { - Installer.install(ipaUrl: uif.ipaUrl!, export: false, returnCompletion: { _ in + private func installApp(_ url: URL) { + Installer.install(ipaUrl: url, export: false, returnCompletion: { _ in Task { @MainActor in appsVM.fetchApps() NotifyService.shared.notify( @@ -190,8 +195,7 @@ struct AppLibraryView: View { private func selectFile() { NSOpenPanel.selectIPA { result in if case .success(let url) = result { - uif.ipaUrl = url - installApp() + installApp(url) } } } diff --git a/PlayCover/Views/Sidebar Views/IPALibraryView.swift b/PlayCover/Views/Sidebar Views/IPALibraryView.swift index be93216a1..90502a979 100644 --- a/PlayCover/Views/Sidebar Views/IPALibraryView.swift +++ b/PlayCover/Views/Sidebar Views/IPALibraryView.swift @@ -120,7 +120,11 @@ struct IPALibraryView: View { } .searchable(text: $searchString, placement: .toolbar) .onChange(of: searchString) { value in - uif.searchText = value + storeVM.searchText = value + storeVM.fetchApps() + } + .onAppear { + storeVM.searchText = "" storeVM.fetchApps() } .onChange(of: isList, perform: { value in diff --git a/PlayCover/Views/SigningSetupView.swift b/PlayCover/Views/SigningSetupView.swift index 1d2a00e84..225bc4a9c 100644 --- a/PlayCover/Views/SigningSetupView.swift +++ b/PlayCover/Views/SigningSetupView.swift @@ -118,8 +118,9 @@ struct SignSetupView: View { Divider() HStack { Button("button.Help") { - NSWorkspace.shared.open( - URL(string: "https://docs.playcover.io/getting_started/troubleshoot_login")!) + if let url = URL(string: "https://docs.playcover.io/getting_started/troubleshoot_login") { + NSWorkspace.shared.open(url) + } } Button("button.Dismiss", role: .cancel) { isSigningSetupShown = false diff --git a/PlayCover/Views/Uninstaller.swift b/PlayCover/Views/Uninstaller.swift index 9e115e2b9..3ee5c4ff2 100644 --- a/PlayCover/Views/Uninstaller.swift +++ b/PlayCover/Views/Uninstaller.swift @@ -69,7 +69,7 @@ class Uninstaller { let viewWidth = checkboxes.max(by: { $0.view.frame.width < $1.view.frame.width })?.view.frame.width - let settingsView = NSStackView(frame: NSRect(x: 0, y: 0, width: viewWidth!, height: viewY)) + let settingsView = NSStackView(frame: NSRect(x: 0, y: 0, width: viewWidth ?? 0, height: viewY)) for checkboxhelper in checkboxes { settingsView.addSubview(checkboxhelper.view) diff --git a/PlayCover/ca.lproj/Localizable.strings b/PlayCover/da.lproj/Localizable.strings similarity index 50% rename from PlayCover/ca.lproj/Localizable.strings rename to PlayCover/da.lproj/Localizable.strings index 7dbf487fb..706a12aef 100644 --- a/PlayCover/ca.lproj/Localizable.strings +++ b/PlayCover/da.lproj/Localizable.strings @@ -1,96 +1,111 @@ -"playapp.add" = "Add app"; -"playapp.addSource" = "Add source"; -"playapp.settings" = "Settings"; -"playapp.showInFinder" = "Show in Finder"; -"playapp.openCache" = "Open App Data"; -"playapp.clearCache" = "Clear App Data"; -"playapp.importKm" = "Import Keymapping"; -"playapp.exportKm" = "Export Keymapping"; -"playapp.exportKmPanel.fieldLabel" = "PlayMap Name:"; -"playapp.delete" = "Uninstall App"; -"playapp.deleteConfirm" = "Uninstall"; -"playapp.deleteMessage" = "Are you sure you want to uninstall %@?"; -"playapp.storeCurrentAccount" = "Store Account"; -"playapp.activateAccount" = "Switch Account"; -"playapp.deleteAccount" = "Delete Account"; -"playapp.clearPreferences" = "Clear App Preferences"; -"playapp.refreshSources" = "Refresh Sources"; -"ipaLibrary.noSources.title" = "No IPA Sources Added"; -"ipaLibrary.noSources.subtitle" = "You currently have no IPA Sources added. Click the button below to add one."; -"ipaLibrary.noSources.button" = "Add Source"; -"ipaLibrary.noNetworkConnection.toast" = "No internet connection!"; -"ipaLibrary.version.older" = "This version is older than the one you have installed"; -"ipaLibrary.version.same" = "App already installed"; -"ipaLibrary.version.newer" = "This version is newer than the version you have installed"; -"ipaLibrary.alert.download" = "Are you sure you want to download this version of %@?"; +"playapp.add" = "Tilføj app"; +"playapp.addSource" = "Tilføj kilde"; +"playapp.settings" = "Indstillinger"; +"playapp.showInFinder" = "Vis i Finder"; +"playapp.openCache" = "Åben app data"; +"playapp.clearCache" = "Fjern app data"; +"playapp.clearPlayChain" = "Fjern PlayChain data"; +"playapp.importIPA" = "Importer IPA"; +"playapp.importKm" = "Importer keymapping"; +"playapp.exportKm" = "Exporter keymapping"; +"playapp.exportKmPanel.fieldLabel" = "PlayMap navn:"; +"playapp.delete" = "Afinstaller app"; +"playapp.deleteConfirm" = "Afinstaller"; +"playapp.deleteMessage" = "Er du sikker på du vil afinstallere %@?"; +"playapp.storeCurrentAccount" = "Gem konto"; +"playapp.activateAccount" = "Skift konto"; +"playapp.deleteAccount" = "Slet konto"; +"playapp.clearPreferences" = "Fjern app instillinger"; +"playapp.refreshSources" = "Ajourfør kilder"; +"playapp.noSources.title" = "Ingen IPAer installeret"; +"playapp.noSources.subtitle" = "Du har ingen IPAer installeret. Klik på knappen under, for at importere én."; +"ipaLibrary.noSources.title" = "Ingen IPA kilder tilføjet"; +"ipaLibrary.noSources.subtitle" = "Der er ingen IPA kilder tilføjet. Klik på knappen under for at tilføje én."; +"ipaLibrary.noSources.button" = "Tilføj kilde"; +"ipaLibrary.noNetworkConnection.toast" = "Ingen internet forbindelse!"; +"ipaLibrary.noNetworkConnection.required" = "IPA bibliotek kræver internet forbindelse"; +"ipaLibrary.version.older" = "Denne version er ældre end den installeret"; +"ipaLibrary.version.same" = "App allerede installeret"; +"ipaLibrary.version.newer" = "Denne version er nyere end den installeret"; +"ipaLibrary.alert.download" = "Er du sikker på du vil downloade denne version af %@?"; "ipaLibrary.download" = "Download"; -"preferences.button.checkForUpdates" = "Check for Updates"; -"preferences.tab.updates" = "Updates"; -"preferences.toggle.automaticUpdates" = "Automatically check for updates"; -"preferences.tab.uninstall" = "Uninstall"; -"preferences.whenUninstalling" = "When uninstalling:"; -"preferences.toggle.showUninstall" = "Show warning popup"; -"preferences.toggle.clearAppData" = "Clear data"; -"preferences.toggle.removeKeymap" = "Remove keymap"; -"preferences.toggle.removeSetting" = "Remove settings"; -"preferences.toggle.removeEntitlements" = "Remove entitlements"; -"preferences.button.pruneFiles" = "Prune Files"; -"preferences.prune.alert" = "Are you sure you want to prune all files?"; -"preferences.prune.message" = "This will remove all unused files including settings, keymappings, entitlements, and app data"; -"preferences.tab.ipasource" = "IPA Sources"; -"preferences.button.addSource" = "Add Source"; -"preferences.button.deleteSource" = "Delete Source"; -"preferences.button.moveSourceUp" = "Move Source Up"; -"preferences.button.moveSourceDown" = "Move Source Down"; -"preferences.popover.valid" = "Link Valid"; -"preferences.popover.badurl" = "URL Invalid!"; -"preferences.popover.badjson" = "JSON Invalid or Not Found!"; -"preferences.textfield.url" = "Source URL..."; -"preferences.tab.install" = "Install"; -"preferences.toggle.showInstallPopup" = "Show install PlayTools popup"; -"preferences.toggle.alwaysInstallPlayTools" = "Always install PlayTools"; -"sidebar.appLibrary" = "App Library"; -"sidebar.ipaLibrary" = "IPA Library"; -"storeAccount.selectAccRegion" = "Select account region"; -"storeAccount.selectAccRegion.usa" = "America"; -"storeAccount.selectAccRegion.euro" = "Europe"; -"storeAccount.selectAccRegion.asia" = "Asia"; +"preferences.button.checkForUpdates" = "Søg efter opdateringer"; +"preferences.tab.updates" = "Updateringer"; +"preferences.toggle.automaticUpdates" = "Tjek for opdateringer automatisk"; +"preferences.tab.uninstall" = "Afinstaller"; +"preferences.whenUninstalling" = "Når afinstallere:"; +"preferences.toggle.showUninstall" = "Vis advarselspopup"; +"preferences.toggle.clearAppData" = "Fjern data"; +"preferences.toggle.removeKeymap" = "Fjern keymap"; +"preferences.toggle.removeSetting" = "Fjern indstillinger"; +"preferences.toggle.removeEntitlements" = "Fjern rettigheder"; +"preferences.toggle.removePlayChain" = "Fjern PlayChain"; +"preferences.button.pruneFiles" = "Fjern filer"; +"preferences.prune.alert" = "Er du sikker på du vil fjerne alle filer?"; +"preferences.prune.message" = "Dette vil fjerne alle filer ikke i brug som indstillinger, keymappinger, rettigheder, og app data"; +"preferences.tab.ipasource" = "IPA kilder"; +"preferences.button.addSource" = "Tilføj kilde"; +"preferences.button.deleteSource" = "Fjern kilde"; +"preferences.button.moveSourceUp" = "Flyt kilde op"; +"preferences.button.moveSourceDown" = "Flyt kilde ned"; +"preferences.popover.valid" = "Link valid"; +"preferences.popover.badurl" = "URL ikke valid!"; +"preferences.popover.badjson" = "JSON enten ikke valid eller ikke fundet!"; +"preferences.popover.duplicate" = "Dobbelt link"; +"preferences.textfield.url" = "URL kilde…"; +"preferences.tab.install" = "Installer"; +"preferences.toggle.showInstallPopup" = "Vis installering af PlayTools popup"; +"preferences.toggle.alwaysInstallPlayTools" = "Altid installer PlayTools"; +"sidebar.appLibrary" = "App bibliotek"; +"sidebar.ipaLibrary" = "IPA bibliotek"; +"storeAccount.selectAccRegion" = "Vælg konto region"; +"storeAccount.selectAccRegion.usa" = "Amerika"; +"storeAccount.selectAccRegion.euro" = "Europa"; +"storeAccount.selectAccRegion.asia" = "Asien"; "storeAccount.selectAccRegion.cht" = "TW, HK, MO"; -"storeAccount.nameOfAcc" = "Name of your account"; -"storeAccount.nameOfAcc.textfieldPlaceholder" = "Name of account..."; -"storeAccount.store" = "Store account"; -"storeAccount.selectAcc" = "Select an account"; -"storeAccount.storeAcc" = "Store an account"; -"storeAccount.deleteAcc" = "Delete an account"; -"storeAccount.alert.restoreAccount" = "Really Restore Account?"; -"storeAccount.alert.restoreAccount.button" = "Restore Account"; -"storeAccount.alert.restoreAccount.msg" = "This will override your currently signed-in account."; -"storeAccount.alert.deleteAccount" = "Really Delete Account?"; -"storeAccount.alert.deleteAccount.button" = "Delete Account"; -"playapp.install.unzip" = "Extracting app from .ipa file..."; -"playapp.install.createWrapper" = "Creating app wrapper..."; -"playapp.install.installPlayTools" = "Installing PlayTools..."; -"playapp.install.signing" = "Signing app..."; -"playapp.install.addToLib" = "Adding app to library..."; -"playapp.install.copy" = "Copying app..."; -"alert.appCacheCleared" = "App data has been cleared!"; -"alert.kmImported" = "Keymapping imported!"; -"alert.errorImportKm" = "Error occured when importing!"; -"alert.app.delete" = "All app data will be erased. You may need to download app files again. Do you wish to continue?"; -"alert.app.preferences" = "All in-app preferences will be deleted. Do you wish to continue?"; -"alert.restart" = "Please restart PlayCover."; -"alert.error" = "An error occurred!"; +"storeAccount.nameOfAcc" = "Konto navn"; +"storeAccount.nameOfAcc.textfieldPlaceholder" = "Konto navn…"; +"storeAccount.store" = "Gem konto"; +"storeAccount.selectAcc" = "Vælg en konto"; +"storeAccount.storeAcc" = "Gem en konto"; +"storeAccount.deleteAcc" = "Slet en konto"; +"storeAccount.alert.restoreAccount" = "Sikker på du vil genskabe konto?"; +"storeAccount.alert.restoreAccount.button" = "Genskab konto"; +"storeAccount.alert.restoreAccount.msg" = "Dette vil overskrive din nuværende logged-in konto."; +"storeAccount.alert.deleteAccount" = "Sikker på du vil slette konto?"; +"storeAccount.alert.deleteAccount.button" = "Slet konto"; +"playapp.download.downloading" = "Downloader"; +"playapp.download.integrityCheck" = "Valider integritet af filer:"; +"playapp.download.differentChecksum" = "Forskellige checksum! Er du sikker på du vil fortsætte med at installere?"; +"playapp.download.differentChecksumDesc" = "Forventede \"%@\", fik \"%@\""; +"playapp.install.unzip" = "Henter app fra .ipa fil…"; +"playapp.install.createWrapper" = "Skaber app wrapper…"; +"playapp.install.installPlayTools" = "Installere PlayTools…"; +"playapp.install.signing" = "Signere app..."; +"playapp.install.addToLib" = "Tilføjer app til bibliotek…"; +"playapp.install.copy" = "Kopiere app..."; +"playapp.progress.finished" = "Færdig"; +"playapp.progress.failed" = "Der skete en fejl"; +"playapp.progress.canceled" = "Annulleret"; +"alert.appCacheCleared" = "App data er blevet slettet!"; +"alert.kmImported" = "Keymapping importeret!"; +"alert.errorImportKm" = "Der opstod en fejl under importering!"; +"alert.app.delete" = "Al app data vil slettes. Du skal muligvis downloade app-filer igen. Ønsker du at fortsætte?"; +"alert.app.preferences" = "Alle præferencer i appen slettes. Ønsker du at fortsætte?"; +"alert.app.clearPlayChain" = "Al PlayChain data slettes. Du skal muligvis logge ind i appen igen. Ønsker du at fortsætte?"; +"alert.restart" = "Venligst genstart PlayCover."; +"alert.error" = "Der opstod en fejl!"; "alert.success" = "Success"; -"alert.moveAppToApplications.title" = "Move to Applications folder?"; -"alert.moveAppToApplications.subtitle" = "PlayCover must be in the Applications folder to run correctly. Press the button below to move PlayCover automatically."; -"alert.moveAppToApplications.move" = "Move to Applications folder"; -"alert.storeAccount.regionIsNotValid" = "The current account is set to a different Region. Launch the game, Change the region, and pass through the gates to save. Then try again"; -"alert.legacyImport.title" = "Legacy App Settings Detected!"; -"alert.legacyImport.subtitle" = "Legacy settings and keymapping files were detected. Would you like to convert these files to the new format?"; -"alert.wrongFileType.title" = "Wrong file type!"; -"alert.wrongFileType.subtitle" = "PlayCover can only install valid .ipa files."; -"alert.install.injectPlayTools" = "Inject PlayTools"; -"alert.install.injectPlayToolsQuestion" = "Do you want to inject PlayTools into this application?"; +"alert.moveAppToApplications.title" = "Flyt til applicationsmappen?"; +"alert.moveAppToApplications.subtitle" = "PlayCover skal være i applicationmappen for at køre ordentligt. Klik på nedenstående knap for at flytte PlayCover automatisk."; +"alert.moveAppToApplications.move" = "Flyt til applicationsmappen"; +"alert.storeAccount.regionIsNotValid" = "Den aktuelle konto er indstillet til en anden region. Start spillet, skift region, og gå gennem portene for at gemme. Prøv derefter igen"; +"alert.legacyImport.title" = "Legacy appindstillinger fundet!"; +"alert.legacyImport.subtitle" = "Der blev fundet legacy indstillinger og keymapping filer. Vil du gerne konvertere disse filer til det nye format?"; +"alert.wrongFileType.title" = "Forkert filtype!"; +"alert.wrongFileType.subtitle" = "PlayCover kan kun installere gyldige .ipa filer."; +"alert.install.injectPlayTools" = "Sprøjt PlayTools ind"; +"alert.install.injectPlayToolsQuestion" = "Ønsker du at sprøjte PlayTools ind i denne applikation?"; "alert.install.playToolsInformative" = "PlayTools is a tool that allows you to bind keys to screen touches, custom display resolutions, and more, but it may break certain apps"; "alert.supression" = "You can change this in the PlayCover preferences"; "alert.differentBundleIdKeymap.message" = "The keymap may have been created for another application."; @@ -108,6 +123,7 @@ "button.No" = "No"; "button.Help" = "Help"; "button.Dismiss" = "Dismiss"; +"button.Reload" = "Reload"; "state.enabled" = "Enabled"; "state.disabled" = "Disabled"; "state.pendingReboot" = "Pending Reboot"; @@ -119,6 +135,7 @@ "error.appProhibited" = "Using this app through PlayCover will likely result in a ban!"; "error.appMaliciousProhibited" = "This App behaves maliciously on Macs, and using it can leave your device in an unbootable state, therefore it is blocked from PlayCover. (Uninstalling...)"; "error.noGenshinAccount" = "We couldn't find a Genshin account! Are you signed into an account in-app?"; +"error.failedToStripBinary" = "Could not find ARM64 arch in fat binary!"; "menubar.exportToSideloady" = "Export to Sideloadly"; "settings.title" = "%@ Settings"; "settings.tab.info" = "Info"; @@ -142,9 +159,16 @@ "settings.toggle.disableDisplaySleep" = "Disable display sleep"; "settings.toggle.disableDisplaySleep.help" = "Prevent display from turning off while this app is running"; "settings.noPlayTools" = "PlayTools is not installed in this app"; -"settings.toggle.jbBypass" = "Enable Jailbreak Bypass (Alpha)"; +"settings.highResolution" = "High resolution may cause crashing"; +"settings.tab.bypasses" = "Bypasses"; +"settings.toggle.jbBypass" = "Enable Jailbreak Bypass (Experimental)"; "settings.toggle.jbBypass.help" = "Attempts to bypass Jailbreak Detection in some games (Not guaranteed to work all the time)"; +"settings.playChain.enable" = "Enable PlayChain (Experimental)"; +"settings.playChain.help" = "Enable PlayChain support for this application, allowing it to (partially) use Apple Keychain Services"; +"settings.playChain.debugging" = "PlayChain debugging"; "settings.removePlayTools" = "Remove PlayTools"; +"settings.addToLaunchpad" = "Add to Launchpad"; +"settings.removeFromLaunchpad" = "Remove from Launchpad"; "settings.info.displayName" = "Display name:"; "settings.info.bundleName" = "Bundle name:"; "settings.info.bundleIdentifier" = "Bundle identifier:"; @@ -168,9 +192,9 @@ "menubar.clearCache" = "Clear Cache"; "menubar.configSigning" = "Configure Signing"; "soundAlert.messageText" = "Incorrect Audio Settings Detected!"; -"soundAlert.informativeText" = "Your current output device does not have a sample rate of 48 or 44.1 KHz! Crashes may occur. Would you like to change your current output device's sample rate to 48 KHz?"; -"soundAlert.successText" = "Current output device sample rate set to 48 KHz"; -"soundAlert.failureText" = "Failed to set current output device's sample rate to 48 KHz. You can manually set sample rate in the Audio MIDI Setup app"; +"soundAlert.informativeText" = "Your current output device does not have a sample rate of 48 or 44.1 kHz! Crashes may occur. Would you like to change your current output device's sample rate to 48 kHz?"; +"soundAlert.successText" = "Current output device sample rate set to 48 kHz"; +"soundAlert.failureText" = "Failed to set current output device's sample rate to 48 kHz. You can manually set sample rate in the Audio MIDI Setup app"; "settings.tab.misc" = "Misc"; "settings.button.discord" = "Custom Discord activity"; "settings.button.clearActivity" = "Clear custom activity"; @@ -183,6 +207,7 @@ "settings.text.state.help" = "Second row below title"; "settings.toggle.discord" = "Enable Discord activity"; "settings.toggle.hud" = "Metal HUD"; +"settings.unavailable.hud" = "Metal HUD unavailable"; "settings.text.debugger" = "Debugger:"; "settings.toggle.lldb" = "Open with LLDB"; "settings.toggle.lldbWithTerminal" = "Open in terminal"; @@ -201,30 +226,10 @@ "configSigning.step.complete" = "Package Signing has been configured successfully"; "configSigning.alert.copied" = "Command Copied!"; "configSigning.alert.info" = "Please paste and run the command in Terminal.app. Your Mac will reboot after"; -"settings.unavailable.hud" = "Metal HUD unavailable"; -"settings.playChain.enable" = "Enable PlayChain (experimental)"; -"error.failedToStripBinary" = "Could not find ARM64 arch in fat binary!"; -"settings.playChain.help" = "Enable PlayChain support for this application, allowing it to (partially) use Apple Keychain Services"; -"settings.playChain.debugging" = "PlayChain debugging"; -"playapp.noSources.title" = "No IPAs Installed"; -"playapp.noSources.subtitle" = "You currently have no IPAs installed. Click the button below to import one."; -"settings.tab.bypasses" = "Bypasses"; -"settings.addToLaunchpad" = "Add to Launchpad"; -"settings.removeFromLaunchpad" = "Remove from Launchpad"; -"playapp.download.downloading" = "Downloading"; -"playapp.download.integrityCheck" = "Verifying file integrity:"; -"playapp.download.differentChecksum" = "Different checksums! Do you want to continue to install?"; -"playapp.download.differentChecksumDesc" = "Expected \"%@\", got \"%@\""; -"playapp.progress.finished" = "Finished"; -"playapp.progress.failed" = "Failed"; -"playapp.progress.canceled" = "Canceled"; -"playapp.clearPlayChain" = "Clear PlayChain Data"; -"playapp.importIPA" = "Import IPA"; -"preferences.toggle.removePlayChain" = "Remove PlayChain"; -"button.Reload" = "Reload"; -"ipaLibrary.noNetworkConnection.required" = "IPA Library requires internet connection"; -"preferences.popover.duplicate" = "Duplicate Link"; -"alert.app.clearPlayChain" = "All PlayChain data will be erased. You may need to login to the app again. Do you wish to continue?"; -"settings.toggle.windowExperimentalFix.help" = "Attempts to fix your app scene if you have problems with it"; -"settings.toggle.windowExperimentalFix" = "Fix window issues"; -"settings.highResolution" = "High resolution may cause crashing"; +"settings.picker.windowFix" = "Fix window display issues"; +"settings.picker.windowFix.help" = "Apply window fixes that may helps your apps display normally"; +"settings.picker.windowFixMethod.0" = "Normal"; +"settings.picker.windowFixMethod.1" = "Alternate"; +"preferences.popover.checking" = "Checking URL"; +"settings.toggle.introspection" = "Insert Introspection libraries"; +"settings.toggle.introspection.help" = "Add the system introspection libraries to the app. Known to fix issues with some apps not starting correctly"; diff --git a/PlayCover/de.lproj/Localizable.strings b/PlayCover/de.lproj/Localizable.strings index 01afe22a8..f322e558f 100644 --- a/PlayCover/de.lproj/Localizable.strings +++ b/PlayCover/de.lproj/Localizable.strings @@ -382,6 +382,11 @@ "preferences.toggle.removePlayChain" = "PlayChain entfernen"; "preferences.popover.duplicate" = "Link duplizieren"; "alert.app.clearPlayChain" = "Alle PlayChain-Daten werden gelöscht. Möglicherweise müssen Sie sich erneut bei der App anmelden. Möchten Sie fortfahren?"; -"settings.toggle.windowExperimentalFix.help" = "Versucht, deine App-Szene zu reparieren, wenn du Probleme damit hast"; -"settings.toggle.windowExperimentalFix" = "Fensterprobleme beheben"; "settings.highResolution" = "Hohe Auflösung kann zu Abstürzen führen"; +"settings.picker.windowFix" = "Probleme mit der Fensteranzeige beheben"; +"settings.picker.windowFix.help" = "Wende Fensterkorrekturen an, die dazu beitragen können, dass deine Apps normal angezeigt werden"; +"settings.picker.windowFixMethod.0" = "Normal"; +"settings.picker.windowFixMethod.1" = "Alternative"; +"settings.toggle.introspection" = "Introspektionsbibliotheken einfügen"; +"preferences.popover.checking" = "URL prüfen"; +"settings.toggle.introspection.help" = "Füge der App die Systemintrospektionbibliotheken hinzu. Behebt bekanntermaßen Probleme mit einigen Apps, die nicht richtig starten"; diff --git a/PlayCover/en.lproj/Localizable.strings b/PlayCover/en.lproj/Localizable.strings index 6bf423684..d6279c0c7 100644 --- a/PlayCover/en.lproj/Localizable.strings +++ b/PlayCover/en.lproj/Localizable.strings @@ -54,6 +54,7 @@ "preferences.popover.badurl" = "URL Invalid!"; "preferences.popover.badjson" = "JSON Invalid or Not Found!"; "preferences.popover.duplicate" = "Duplicate Link"; +"preferences.popover.checking" = "Checking URL"; "preferences.textfield.url" = "Source URL..."; "preferences.tab.install" = "Install"; "preferences.toggle.showInstallPopup" = "Show install PlayTools popup"; @@ -169,8 +170,10 @@ "settings.text.customWidth" = "Width"; "settings.text.detectedResolution" = "Detected Resolution:"; "settings.picker.aspectRatio" = "Aspect ratio:"; -"settings.toggle.windowExperimentalFix.help" = "Attempts to fix your app scene if you have problems with it"; -"settings.toggle.windowExperimentalFix" = "Fix window issues"; +"settings.picker.windowFix" = "Fix window display issues"; +"settings.picker.windowFix.help" = "Apply window fixes that may helps your apps display normally"; +"settings.picker.windowFixMethod.0" = "Normal"; +"settings.picker.windowFixMethod.1" = "Alternate"; "settings.toggle.disableDisplaySleep" = "Disable display sleep"; "settings.toggle.disableDisplaySleep.help" = "Prevent display from turning off while this app is running"; "settings.noPlayTools" = "PlayTools is not installed in this app"; @@ -182,6 +185,8 @@ "settings.playChain.enable" = "Enable PlayChain (Experimental)"; "settings.playChain.help" = "Enable PlayChain support for this application, allowing it to (partially) use Apple Keychain Services"; "settings.playChain.debugging" = "PlayChain debugging"; +"settings.toggle.introspection" = "Insert Introspection libraries"; +"settings.toggle.introspection.help" = "Add the system introspection libraries to the app. Known to fix issues with some apps not starting correctly"; "settings.removePlayTools" = "Remove PlayTools"; "settings.addToLaunchpad" = "Add to Launchpad"; diff --git a/PlayCover/es.lproj/Localizable.strings b/PlayCover/es.lproj/Localizable.strings index b8b238c7e..417f25aac 100644 --- a/PlayCover/es.lproj/Localizable.strings +++ b/PlayCover/es.lproj/Localizable.strings @@ -380,8 +380,13 @@ "playapp.importIPA" = "Importar IPA"; "ipaLibrary.noNetworkConnection.required" = "La Biblioteca IPA requiere conexión a Internet"; "preferences.toggle.removePlayChain" = "Eliminar PlayChain"; -"preferences.popover.duplicate" = "Duplicar enlace"; -"alert.app.clearPlayChain" = "Se borrarán todos los datos de PlayChain. Es posible que tengas que volver a iniciar la sesión en la aplicación. ¿Deseas continuar?"; -"settings.toggle.windowExperimentalFix.help" = "Intenta arreglar la escena de tu aplicación si tienes problemas con ella"; -"settings.toggle.windowExperimentalFix" = "Solucionado los problemas donde la ventana"; +"preferences.popover.duplicate" = "Enlace duplicado"; +"alert.app.clearPlayChain" = "Se borrarán todos los datos de PlayChain. Es posible que tenga que volver a iniciar sesión en la aplicación. ¿Desea continuar?"; "settings.highResolution" = "La alta resolución puede provocar fallos"; +"settings.picker.windowFixMethod.0" = "Normal"; +"settings.picker.windowFix" = "Solucionar problemas de visualización en ventana"; +"settings.picker.windowFix.help" = "Aplicar correcciones de ventanas que pueden ayudar a que tus aplicaciones se muestren con normalidad"; +"settings.picker.windowFixMethod.1" = "Alternativo"; +"preferences.popover.checking" = "Comprobando la URL"; +"settings.toggle.introspection" = "Insertar bibliotecas de introspección"; +"settings.toggle.introspection.help" = "Añade las bibliotecas de introspección del sistema a la aplicación. Se sabe que soluciona problemas con algunas aplicaciones que no se inician correctamente"; diff --git a/PlayCover/fa.lproj/Localizable.strings b/PlayCover/fa.lproj/Localizable.strings index 45e77321f..c9c9f72e2 100644 --- a/PlayCover/fa.lproj/Localizable.strings +++ b/PlayCover/fa.lproj/Localizable.strings @@ -233,6 +233,11 @@ "preferences.popover.duplicate" = "Duplicate Link"; "alert.app.clearPlayChain" = "All PlayChain data will be erased. You may need to login to the app again. Do you wish to continue?"; "button.Reload" = "Reload"; -"settings.toggle.windowExperimentalFix.help" = "Attempts to fix your app scene if you have problems with it"; -"settings.toggle.windowExperimentalFix" = "Fix window issues"; "settings.highResolution" = "High resolution may cause crashing"; +"settings.picker.windowFix" = "Fix window display issues"; +"settings.picker.windowFix.help" = "Apply window fixes that may helps your apps display normally"; +"settings.picker.windowFixMethod.0" = "Normal"; +"settings.picker.windowFixMethod.1" = "Alternate"; +"preferences.popover.checking" = "Checking URL"; +"settings.toggle.introspection" = "Insert Introspection libraries"; +"settings.toggle.introspection.help" = "Add the system introspection libraries to the app. Known to fix issues with some apps not starting correctly"; diff --git a/PlayCover/fr.lproj/Localizable.strings b/PlayCover/fr.lproj/Localizable.strings index 643db1514..bfddd0e93 100644 --- a/PlayCover/fr.lproj/Localizable.strings +++ b/PlayCover/fr.lproj/Localizable.strings @@ -382,6 +382,11 @@ "preferences.popover.duplicate" = "Duplicate Link"; "alert.app.clearPlayChain" = "All PlayChain data will be erased. You may need to login to the app again. Do you wish to continue?"; "button.Reload" = "Reload"; -"settings.toggle.windowExperimentalFix.help" = "Attempts to fix your app scene if you have problems with it"; -"settings.toggle.windowExperimentalFix" = "Fix window issues"; "settings.highResolution" = "High resolution may cause crashing"; +"settings.picker.windowFix" = "Fix window display issues"; +"settings.picker.windowFix.help" = "Apply window fixes that may helps your apps display normally"; +"settings.picker.windowFixMethod.0" = "Normal"; +"settings.picker.windowFixMethod.1" = "Alternate"; +"preferences.popover.checking" = "Checking URL"; +"settings.toggle.introspection" = "Insert Introspection libraries"; +"settings.toggle.introspection.help" = "Add the system introspection libraries to the app. Known to fix issues with some apps not starting correctly"; diff --git a/PlayCover/hi.lproj/Localizable.strings b/PlayCover/hi.lproj/Localizable.strings index 473833128..7a35116e1 100644 --- a/PlayCover/hi.lproj/Localizable.strings +++ b/PlayCover/hi.lproj/Localizable.strings @@ -225,6 +225,11 @@ "preferences.popover.duplicate" = "Duplicate Link"; "alert.app.clearPlayChain" = "All PlayChain data will be erased. You may need to login to the app again. Do you wish to continue?"; "button.Reload" = "Reload"; -"settings.toggle.windowExperimentalFix.help" = "Attempts to fix your app scene if you have problems with it"; -"settings.toggle.windowExperimentalFix" = "Fix window issues"; "settings.highResolution" = "High resolution may cause crashing"; +"settings.picker.windowFix" = "Fix window display issues"; +"settings.picker.windowFix.help" = "Apply window fixes that may helps your apps display normally"; +"settings.picker.windowFixMethod.0" = "Normal"; +"settings.picker.windowFixMethod.1" = "Alternate"; +"preferences.popover.checking" = "Checking URL"; +"settings.toggle.introspection" = "Insert Introspection libraries"; +"settings.toggle.introspection.help" = "Add the system introspection libraries to the app. Known to fix issues with some apps not starting correctly"; diff --git a/PlayCover/id.lproj/Localizable.strings b/PlayCover/id.lproj/Localizable.strings index 36918a15a..bb7217b9f 100644 --- a/PlayCover/id.lproj/Localizable.strings +++ b/PlayCover/id.lproj/Localizable.strings @@ -382,6 +382,11 @@ "playapp.clearPlayChain" = "Clear PlayChain Data"; "alert.app.clearPlayChain" = "All PlayChain data will be erased. You may need to login to the app again. Do you wish to continue?"; "button.Reload" = "Reload"; -"settings.toggle.windowExperimentalFix.help" = "Attempts to fix your app scene if you have problems with it"; -"settings.toggle.windowExperimentalFix" = "Fix window issues"; "settings.highResolution" = "High resolution may cause crashing"; +"settings.picker.windowFix" = "Fix window display issues"; +"settings.picker.windowFixMethod.0" = "Normal"; +"settings.picker.windowFixMethod.1" = "Alternate"; +"settings.picker.windowFix.help" = "Apply window fixes that may helps your apps display normally"; +"preferences.popover.checking" = "Checking URL"; +"settings.toggle.introspection" = "Insert Introspection libraries"; +"settings.toggle.introspection.help" = "Add the system introspection libraries to the app. Known to fix issues with some apps not starting correctly"; diff --git a/PlayCover/ja.lproj/Localizable.strings b/PlayCover/ja.lproj/Localizable.strings index 6ddc08444..d12a9f394 100644 --- a/PlayCover/ja.lproj/Localizable.strings +++ b/PlayCover/ja.lproj/Localizable.strings @@ -382,6 +382,11 @@ "ipaLibrary.noNetworkConnection.required" = "IPAライブラリにはインターネット接続が必要です"; "preferences.popover.duplicate" = "リンクを複製"; "alert.app.clearPlayChain" = "すべての PlayChain データが消去されます。 アプリへの再ログインが必要になる場合があります。 続けますか?"; -"settings.toggle.windowExperimentalFix.help" = "問題がある場合は、アプリのシーンを修正しようとします"; -"settings.toggle.windowExperimentalFix" = "Fix window issues"; "settings.highResolution" = "High resolution may cause crashing"; +"settings.picker.windowFix" = "Fix window display issues"; +"settings.picker.windowFix.help" = "Apply window fixes that may helps your apps display normally"; +"settings.picker.windowFixMethod.0" = "Normal"; +"settings.picker.windowFixMethod.1" = "Alternate"; +"preferences.popover.checking" = "Checking URL"; +"settings.toggle.introspection" = "Insert Introspection libraries"; +"settings.toggle.introspection.help" = "Add the system introspection libraries to the app. Known to fix issues with some apps not starting correctly"; diff --git a/PlayCover/ko.lproj/Localizable.strings b/PlayCover/ko.lproj/Localizable.strings index 750b8a78c..98215d93b 100644 --- a/PlayCover/ko.lproj/Localizable.strings +++ b/PlayCover/ko.lproj/Localizable.strings @@ -382,6 +382,11 @@ "preferences.popover.duplicate" = "Duplicate Link"; "alert.app.clearPlayChain" = "All PlayChain data will be erased. You may need to login to the app again. Do you wish to continue?"; "button.Reload" = "Reload"; -"settings.toggle.windowExperimentalFix.help" = "Attempts to fix your app scene if you have problems with it"; -"settings.toggle.windowExperimentalFix" = "Fix window issues"; "settings.highResolution" = "High resolution may cause crashing"; +"settings.picker.windowFix" = "Fix window display issues"; +"settings.picker.windowFix.help" = "Apply window fixes that may helps your apps display normally"; +"settings.picker.windowFixMethod.0" = "Normal"; +"settings.picker.windowFixMethod.1" = "Alternate"; +"settings.toggle.introspection" = "Insert Introspection libraries"; +"settings.toggle.introspection.help" = "Add the system introspection libraries to the app. Known to fix issues with some apps not starting correctly"; +"preferences.popover.checking" = "Checking URL"; diff --git a/PlayCover/pt-br.lproj/Localizable.strings b/PlayCover/pt-br.lproj/Localizable.strings index d16bab339..ac47d7f0b 100644 --- a/PlayCover/pt-br.lproj/Localizable.strings +++ b/PlayCover/pt-br.lproj/Localizable.strings @@ -225,6 +225,11 @@ "preferences.toggle.removePlayChain" = "Remove PlayChain"; "preferences.popover.duplicate" = "Duplicate Link"; "alert.app.clearPlayChain" = "All PlayChain data will be erased. You may need to login to the app again. Do you wish to continue?"; -"settings.toggle.windowExperimentalFix.help" = "Attempts to fix your app scene if you have problems with it"; -"settings.toggle.windowExperimentalFix" = "Fix window issues"; "settings.highResolution" = "High resolution may cause crashing"; +"settings.picker.windowFix" = "Fix window display issues"; +"settings.picker.windowFixMethod.0" = "Normal"; +"settings.picker.windowFix.help" = "Apply window fixes that may helps your apps display normally"; +"settings.picker.windowFixMethod.1" = "Alternate"; +"preferences.popover.checking" = "Checking URL"; +"settings.toggle.introspection" = "Insert Introspection libraries"; +"settings.toggle.introspection.help" = "Add the system introspection libraries to the app. Known to fix issues with some apps not starting correctly"; diff --git a/PlayCover/ro.lproj/Localizable.strings b/PlayCover/ro.lproj/Localizable.strings index 049687b28..d35399643 100644 --- a/PlayCover/ro.lproj/Localizable.strings +++ b/PlayCover/ro.lproj/Localizable.strings @@ -225,6 +225,11 @@ "preferences.toggle.removePlayChain" = "Remove PlayChain"; "preferences.popover.duplicate" = "Duplicate Link"; "button.Reload" = "Reload"; -"settings.toggle.windowExperimentalFix.help" = "Attempts to fix your app scene if you have problems with it"; -"settings.toggle.windowExperimentalFix" = "Fix window issues"; "settings.highResolution" = "High resolution may cause crashing"; +"settings.picker.windowFix" = "Fix window display issues"; +"settings.picker.windowFixMethod.1" = "Alternate"; +"settings.picker.windowFix.help" = "Apply window fixes that may helps your apps display normally"; +"settings.picker.windowFixMethod.0" = "Normal"; +"preferences.popover.checking" = "Checking URL"; +"settings.toggle.introspection" = "Insert Introspection libraries"; +"settings.toggle.introspection.help" = "Add the system introspection libraries to the app. Known to fix issues with some apps not starting correctly"; diff --git a/PlayCover/ru.lproj/Localizable.strings b/PlayCover/ru.lproj/Localizable.strings index 44dc7ad48..22eecfaf1 100644 --- a/PlayCover/ru.lproj/Localizable.strings +++ b/PlayCover/ru.lproj/Localizable.strings @@ -382,6 +382,11 @@ "preferences.popover.duplicate" = "Duplicate Link"; "alert.app.clearPlayChain" = "All PlayChain data will be erased. You may need to login to the app again. Do you wish to continue?"; "button.Reload" = "Reload"; -"settings.toggle.windowExperimentalFix.help" = "Attempts to fix your app scene if you have problems with it"; -"settings.toggle.windowExperimentalFix" = "Fix window issues"; "settings.highResolution" = "High resolution may cause crashing"; +"settings.picker.windowFix" = "Fix window display issues"; +"settings.picker.windowFix.help" = "Apply window fixes that may helps your apps display normally"; +"settings.picker.windowFixMethod.0" = "Normal"; +"settings.picker.windowFixMethod.1" = "Alternate"; +"preferences.popover.checking" = "Checking URL"; +"settings.toggle.introspection" = "Insert Introspection libraries"; +"settings.toggle.introspection.help" = "Add the system introspection libraries to the app. Known to fix issues with some apps not starting correctly"; diff --git a/PlayCover/tr.lproj/Localizable.strings b/PlayCover/tr.lproj/Localizable.strings index d288d96c2..86489d17b 100644 --- a/PlayCover/tr.lproj/Localizable.strings +++ b/PlayCover/tr.lproj/Localizable.strings @@ -225,6 +225,11 @@ "ipaLibrary.noNetworkConnection.required" = "IPA Library requires internet connection"; "preferences.popover.duplicate" = "Duplicate Link"; "button.Reload" = "Reload"; -"settings.toggle.windowExperimentalFix.help" = "Attempts to fix your app scene if you have problems with it"; -"settings.toggle.windowExperimentalFix" = "Fix window issues"; "settings.highResolution" = "High resolution may cause crashing"; +"settings.picker.windowFix" = "Fix window display issues"; +"settings.picker.windowFix.help" = "Apply window fixes that may helps your apps display normally"; +"settings.picker.windowFixMethod.0" = "Normal"; +"settings.picker.windowFixMethod.1" = "Alternate"; +"preferences.popover.checking" = "Checking URL"; +"settings.toggle.introspection" = "Insert Introspection libraries"; +"settings.toggle.introspection.help" = "Add the system introspection libraries to the app. Known to fix issues with some apps not starting correctly"; diff --git a/PlayCover/vi.lproj/Localizable.strings b/PlayCover/vi.lproj/Localizable.strings index b5924ac13..66ee66ac9 100644 --- a/PlayCover/vi.lproj/Localizable.strings +++ b/PlayCover/vi.lproj/Localizable.strings @@ -233,6 +233,11 @@ "preferences.toggle.removePlayChain" = "Remove PlayChain"; "preferences.popover.duplicate" = "Duplicate Link"; "alert.app.clearPlayChain" = "All PlayChain data will be erased. You may need to login to the app again. Do you wish to continue?"; -"settings.toggle.windowExperimentalFix.help" = "Attempts to fix your app scene if you have problems with it"; -"settings.toggle.windowExperimentalFix" = "Fix window issues"; "settings.highResolution" = "High resolution may cause crashing"; +"settings.picker.windowFix" = "Fix window display issues"; +"settings.picker.windowFix.help" = "Apply window fixes that may helps your apps display normally"; +"settings.picker.windowFixMethod.0" = "Normal"; +"settings.picker.windowFixMethod.1" = "Alternate"; +"preferences.popover.checking" = "Checking URL"; +"settings.toggle.introspection" = "Insert Introspection libraries"; +"settings.toggle.introspection.help" = "Add the system introspection libraries to the app. Known to fix issues with some apps not starting correctly"; diff --git a/PlayCover/zh-Hans.lproj/Localizable.strings b/PlayCover/zh-Hans.lproj/Localizable.strings index 9a1217e19..3823af892 100644 --- a/PlayCover/zh-Hans.lproj/Localizable.strings +++ b/PlayCover/zh-Hans.lproj/Localizable.strings @@ -381,7 +381,12 @@ "playapp.importIPA" = "导入 IPA"; "ipaLibrary.noNetworkConnection.required" = "连接网络以查看 IPA Library"; "preferences.popover.duplicate" = "复制链接"; -"alert.app.clearPlayChain" = "所有 PlayChain 数据将被清除。您可能需要重新登录应用程式。您还想继续吗?"; -"settings.toggle.windowExperimentalFix.help" = "尝试修复您的应用程式场景"; -"settings.toggle.windowExperimentalFix" = "修复窗口问题"; +"alert.app.clearPlayChain" = "所有 PlayChain 数据将被清除。您可能需要重新登录 APP。您想要继续吗?"; "settings.highResolution" = "高画质可能会导致闪退"; +"settings.picker.windowFix" = "修复窗口化显示问题"; +"settings.picker.windowFix.help" = "启用窗口化修复也许可以让你的程序正常显示"; +"settings.picker.windowFixMethod.0" = "正常"; +"settings.picker.windowFixMethod.1" = "备用"; +"preferences.popover.checking" = "检查 URL"; +"settings.toggle.introspection" = "插入内省库"; +"settings.toggle.introspection.help" = "添加系统内省库到 app。已知可修复某些应用程序无法正确启动的问题"; diff --git a/PlayCover/zh-Hant.lproj/Localizable.strings b/PlayCover/zh-Hant.lproj/Localizable.strings index de9a5be82..add282c1a 100644 --- a/PlayCover/zh-Hant.lproj/Localizable.strings +++ b/PlayCover/zh-Hant.lproj/Localizable.strings @@ -194,7 +194,7 @@ "storeAccount.selectAccRegion.cht" = "港澳臺服"; /* (No Comment) */ -"storeAccount.selectAccRegion.euro" = "歐福"; +"storeAccount.selectAccRegion.euro" = "歐服"; /* (No Comment) */ "storeAccount.selectAccRegion.usa" = "美服"; @@ -223,9 +223,9 @@ "settings.title" = "%@ 設定"; "settings.tab.info" = "詳細資料"; "settings.tab.km" = "鍵盤映射佈局"; -"settings.toggle.km.help" = "為僅可透過觸控屏幕操控的遊戲添加鍵盤支援。使用 Command + K 開啟編輯器選單。"; +"settings.toggle.km.help" = "為僅可透過觸控螢幕操控的遊戲添加鍵盤支援。使用 Command + K 開啟編輯器選單。"; "settings.toggle.mm" = "啟用滑鼠映射佈局"; -"settings.toggle.mm.help" = "添加模擬觸控屏幕操作的滑鼠映射控制項。使用 Command + K 開啟編輯器選單。"; +"settings.toggle.mm.help" = "添加模擬觸控螢幕操作的滑鼠映射控制項。使用 Command + K 開啟編輯器選單。"; "settings.tab.graphics" = "影像設置"; "settings.picker.adaptiveRes" = "解析度:"; "settings.picker.adaptiveRes.help" = "修改應用程式的視窗解析度"; @@ -307,15 +307,15 @@ "playapp.refreshSources" = "刷新來源"; "error.appMaliciousProhibited" = "此應用程式在 Mac 上存在惡意行為,使用它可能會使您的設備處於無法啟動的狀態,因此它會被 PlayCover 封鎖。 (正在解除安裝...)"; "alert.install.injectPlayToolsQuestion" = "你要在這個應用程序裡面植入 PlayTools 嗎?"; -"alert.install.playToolsInformative" = "PlayTools是一個讓您將按鍵綁定到屏幕觸摸,自定義顯示分辨率,和更多的工具,但它有可能會讓某些應用程序崩潰"; +"alert.install.playToolsInformative" = "PlayTools是一個讓您將按鍵綁定到螢幕觸控,自定義顯示分辨率,和更多的工具,但它有可能會讓某些應用程序崩潰"; "button.Help" = "幫助"; "button.Dismiss" = "關閉"; "state.enabled" = "啟用"; "state.disabled" = "禁用"; "state.pendingReboot" = "等待重啟"; "menubar.configSigning" = "配置登錄"; -"configSigning.header" = "配置應用程序包簽名"; -"configSigning.subtext" = "應用程序包簽名幫助修復需要依靠正確 團隊 ID\n來處理身分驗證等操作的應用程序。\n這個功能有可能會帶來風險。\n\n因此,我們建議在不必要的情況下禁用它"; +"configSigning.header" = "配置應用程式包簽名"; +"configSigning.subtext" = "應用程式包簽名幫助修復需要依靠正確 團隊 ID\n來處理身分驗證等操作的應用程序。\n這個功能有可能會帶來風險。\n\n因此,我們建議在不必要的情況下禁用它"; "configSigning.info.SIP" = "部分的 系統完整性保護(SIP)需要禁用,以便關閉 蘋果移動文件完整性(AMFI)."; "configSigning.info.SIPHeader" = "系統完整性保護"; "configSigning.info.AMFI" = "蘋果移動文件完整性(AMFI)需要關閉以允許應用程序假簽名,以便它們使用正確的 團隊ID 運行"; @@ -326,13 +326,13 @@ "configSigning.step.disableSIP" = "請從恢復模式禁用SIP"; "configSigning.step.disableAMFI" = "請禁用AMFI"; "configSigning.step.AMFIreboot" = "蘋果移動文件完整性 已禁用。請重啟電腦"; -"configSigning.step.complete" = "應用程序包簽名已成功配置"; +"configSigning.step.complete" = "應用程式包簽名已成功配置"; "configSigning.alert.copied" = "命令已複製!"; -"configSigning.alert.info" = "請在 終端 應用程序內粘貼並執行此命令。稍後你的 Mac 將重啟"; +"configSigning.alert.info" = "請在終端機內貼上並執行此命令。稍後你的 Mac 將重啟"; "alert.power.title" = "低電量模式已啟用!"; "alert.power.subtitle" = "有些應用程序在低電量模式開啟的情況下不能使用。建議把它禁用。"; "ipaLibrary.version.older" = "此版本比你已經安裝的版本更舊"; -"ipaLibrary.version.same" = "應用程序已經安裝了"; +"ipaLibrary.version.same" = "應用程式已經安裝了"; "ipaLibrary.version.newer" = "此版本比你已經安裝的更新"; "ipaLibrary.alert.download" = "你確定要下載這個版本的 %@ 嗎?"; "ipaLibrary.download" = "下載"; @@ -340,7 +340,7 @@ "alert.differentBundleIdKeymap.text" = "你確定你要導入這個鍵盤映射文件嗎?"; "settings.text.debugger" = "調試器:"; "settings.toggle.lldb" = "用 LLDB 高性能調試器打開"; -"settings.toggle.lldbWithTerminal" = "用終端打開"; +"settings.toggle.lldbWithTerminal" = "用終端機打開"; "settings.unavailable.hud" = "Metal HUD 不可用"; "settings.playChain.debugging" = "PlayChain 調試"; "error.failedToStripBinary" = "無法在通用二進制文件中檢索到 ARM64 架構!"; @@ -362,6 +362,11 @@ "ipaLibrary.noNetworkConnection.required" = "連接實際網路以查看 IPA Library"; "preferences.toggle.removePlayChain" = "移除 PlayChain"; "alert.app.clearPlayChain" = "所有 PlayChain 數據將被清除。您可能需要重新登錄應用程式。 您還希望繼續嗎?"; -"settings.toggle.windowExperimentalFix.help" = "嘗試修復您的應用程式場景"; -"settings.toggle.windowExperimentalFix" = "修復窗口問題"; "settings.highResolution" = "高畫質可能會導致閃退"; +"settings.picker.windowFix" = "修復視窗模式顯示問題"; +"settings.picker.windowFix.help" = "啟用視窗修復也許可以讓你的程序正常顯示"; +"settings.picker.windowFixMethod.0" = "正常"; +"settings.picker.windowFixMethod.1" = "備用"; +"preferences.popover.checking" = "檢查 URL"; +"settings.toggle.introspection" = "Insert Introspection libraries"; +"settings.toggle.introspection.help" = "Add the system introspection libraries to the app. Known to fix issues with some apps not starting correctly";