diff --git a/AKPlugin.swift b/AKPlugin.swift index b0ebfece..f286006a 100644 --- a/AKPlugin.swift +++ b/AKPlugin.swift @@ -18,6 +18,23 @@ private struct AKAppSettingsData: Codable { var resizableAspectRatioHeight: Int? } +private func akCurrentUserHomeDirectoryPath() -> String { + let userName = NSUserName() + if let homeDirectory = NSHomeDirectoryForUser(userName) { + return homeDirectory + } + return NSString(string: "~\(userName)").expandingTildeInPath +} + +private func akSettingsURLForBundleIdentifier(_ bundleIdentifier: String) -> URL { + return URL(fileURLWithPath: akCurrentUserHomeDirectoryPath(), isDirectory: true) + .appendingPathComponent("Library", isDirectory: true) + .appendingPathComponent("Containers", isDirectory: true) + .appendingPathComponent("io.playcover.PlayCover", isDirectory: true) + .appendingPathComponent("App Settings") + .appendingPathComponent("\(bundleIdentifier).plist") +} + class AKPlugin: NSObject, Plugin { required override init() { super.init() @@ -304,9 +321,7 @@ class AKPlugin: NSObject, Plugin { fileprivate static var akAppSettingsData: AKAppSettingsData? = { let bundleIdentifier = Bundle.main.bundleIdentifier ?? "" - let settingsURL = URL(fileURLWithPath: "/Users/\(NSUserName())/Library/Containers/io.playcover.PlayCover") - .appendingPathComponent("App Settings") - .appendingPathComponent("\(bundleIdentifier).plist") + let settingsURL = akSettingsURLForBundleIdentifier(bundleIdentifier) guard let data = try? Data(contentsOf: settingsURL), let decoded = try? PropertyListDecoder().decode(AKAppSettingsData.self, from: data) else { return nil diff --git a/PlayTools/MysticRunes/PlayedAppleDB.swift b/PlayTools/MysticRunes/PlayedAppleDB.swift index 06103b65..09148768 100644 --- a/PlayTools/MysticRunes/PlayedAppleDB.swift +++ b/PlayTools/MysticRunes/PlayedAppleDB.swift @@ -238,7 +238,7 @@ class PlayKeychainDB: NSObject { private func connectToDB() -> OpaquePointer? { var sqlite3DB: OpaquePointer? let bundleID = Bundle.main.infoDictionary?["CFBundleIdentifier"] as? String ?? "Shared" - let keychainDB = URL(fileURLWithPath: "/Users/\(NSUserName())/Library/Containers/io.playcover.PlayCover") + let keychainDB = playCoverContainerBaseURL() .appendingPathComponent("PlayChain") .appendingPathComponent("\(bundleID).db") diff --git a/PlayTools/PlayLoader.m b/PlayTools/PlayLoader.m index 42881cef..c320245d 100644 --- a/PlayTools/PlayLoader.m +++ b/PlayTools/PlayLoader.m @@ -5,6 +5,8 @@ #include #include +#include +#include #include #import "PlayLoader.h" @@ -216,14 +218,75 @@ static OSStatus pt_SecKeyGeneratePair(CFDictionaryRef parameters, SecKeyRef *pub static uint8_t ue_status = 0; +static void pt_copyCurrentUserHomeDirectory(char *buffer, size_t bufferSize) { + if (bufferSize == 0) { + return; + } + + buffer[0] = '\0'; + + char executablePath[PATH_MAX]; + uint32_t executablePathSize = sizeof(executablePath); + if (_NSGetExecutablePath(executablePath, &executablePathSize) == 0) { + static char const containerMarker[] = "/Library/Containers/io.playcover.PlayCover/"; + char *marker = strstr(executablePath, containerMarker); + if (marker != NULL) { + size_t homePathLength = (size_t)(marker - executablePath); + if (homePathLength > 0 && homePathLength < bufferSize) { + memcpy(buffer, executablePath, homePathLength); + buffer[homePathLength] = '\0'; + return; + } + } + } + + char const *homeDirectory = getenv("HOME"); + if (homeDirectory != NULL && homeDirectory[0] != '\0') { + static char const containerSuffix[] = "/Library/Containers/io.playcover.PlayCover"; + char const *suffix = strstr(homeDirectory, containerSuffix); + if (suffix != NULL) { + size_t homePathLength = (size_t)(suffix - homeDirectory); + if (homePathLength > 0 && homePathLength < bufferSize) { + memcpy(buffer, homeDirectory, homePathLength); + buffer[homePathLength] = '\0'; + return; + } + } + + snprintf(buffer, bufferSize, "%s", homeDirectory); + return; + } + + char userName[256]; + if (getlogin_r(userName, sizeof(userName)) == 0) { + snprintf(buffer, bufferSize, "/Users/%s", userName); + } +} + static char const* ue_fix_filename(char const* filename) { - static char UE_PATTERN[1024] = "//Users/"; - getlogin_r(UE_PATTERN + 8, sizeof(UE_PATTERN) - 8); + static char uePattern[PATH_MAX]; + static atomic_int uePatternState = 0; // 0 = uninitialized, 1 = initializing, 2 = ready, 3 = unavailable + int expectedState = 0; + if (atomic_compare_exchange_strong(&uePatternState, &expectedState, 1)) { + char homePath[PATH_MAX]; + pt_copyCurrentUserHomeDirectory(homePath, sizeof(homePath)); + if (homePath[0] != '\0') { + snprintf(uePattern, sizeof(uePattern), "/%s", homePath); + atomic_store(&uePatternState, 2); + } else { + atomic_store(&uePatternState, 3); + } + } + + int patternState = atomic_load(&uePatternState); + if (patternState != 2 || uePattern[0] == '\0') { + return filename; + } char const* p = filename; if (ue_status == 2) { char const* last_p = p; - while ((p = strstr(p, UE_PATTERN))) { + while ((p = strstr(p, uePattern))) { last_p = ++p; } diff --git a/PlayTools/PlaySettings.swift b/PlayTools/PlaySettings.swift index b034a1f4..3711e4ac 100644 --- a/PlayTools/PlaySettings.swift +++ b/PlayTools/PlaySettings.swift @@ -3,6 +3,21 @@ import UIKit let settings = PlaySettings.shared +func playCoverUserHomeDirectoryPath() -> String { + let userName = NSUserName() + if let homeDirectory = NSHomeDirectoryForUser(userName) { + return homeDirectory + } + return NSString(string: "~\(userName)").expandingTildeInPath +} + +func playCoverContainerBaseURL() -> URL { + URL(fileURLWithPath: playCoverUserHomeDirectoryPath(), isDirectory: true) + .appendingPathComponent("Library", isDirectory: true) + .appendingPathComponent("Containers", isDirectory: true) + .appendingPathComponent("io.playcover.PlayCover", isDirectory: true) +} + @objc public final class PlaySettings: NSObject { @objc public static let shared = PlaySettings() @@ -11,7 +26,7 @@ let settings = PlaySettings.shared var settingsData: AppSettingsData override init() { - settingsUrl = URL(fileURLWithPath: "/Users/\(NSUserName())/Library/Containers/io.playcover.PlayCover") + settingsUrl = playCoverContainerBaseURL() .appendingPathComponent("App Settings") .appendingPathComponent("\(bundleIdentifier).plist") do {