[
  {
    "path": ".github/FUNDING.yml",
    "content": "github: insidegui\n"
  },
  {
    "path": ".gitignore",
    "content": ".DS_Store\n__MACOSX\n*.pbxuser\n!default.pbxuser\n*.mode1v3\n!default.mode1v3\n*.mode2v3\n!default.mode2v3\n*.perspectivev3\n!default.perspectivev3\nxcuserdata\nprofile\n*.moved-aside\nDerivedData\n.idea/\nCrashlytics.sh\ngeneratechangelog.sh\nPods/\nCarthage\nProvisioning\nCrashlytics.sh\nSharing.h\nTests/Private\nDesign/Icon\nBuild/\nMockServer/"
  },
  {
    "path": "DeepLinkSecurity/DeepLinkSecurity.h",
    "content": "//\n//  DeepLinkSecurity.h\n//  DeepLinkSecurity\n//\n//  Created by Guilherme Rambo on 12/10/23.\n//\n\n#import <Foundation/Foundation.h>\n\n//! Project version number for DeepLinkSecurity.\nFOUNDATION_EXPORT double DeepLinkSecurityVersionNumber;\n\n//! Project version string for DeepLinkSecurity.\nFOUNDATION_EXPORT const unsigned char DeepLinkSecurityVersionString[];\n\n// In this header, you should import all the public headers of your framework using statements like #import <DeepLinkSecurity/PublicHeader.h>\n\n\n"
  },
  {
    "path": "DeepLinkSecurity/Source/Base/DeepLinkSecurityDefines.swift",
    "content": "import Foundation\nimport OSLog\n\nstruct DeepLinkSecurityDefines {\n    static let subsystem = \"codes.rambo.DeepLinkSecurity\"\n}\n\nstruct DeepLinkError: LocalizedError {\n    var errorDescription: String?\n    init(_ errorDescription: String) {\n        self.errorDescription = errorDescription\n    }\n}\n\nextension Logger {\n    static func deepLinkLogger<T>(for type: T.Type) -> Logger {\n        Logger(subsystem: DeepLinkSecurityDefines.subsystem, category: String(describing: type))\n    }\n}\n"
  },
  {
    "path": "DeepLinkSecurity/Source/DeepLinkSentinel.swift",
    "content": "import Cocoa\nimport OSLog\n\npublic final class DeepLinkSentinel: ObservableObject {\n    private lazy var logger = Logger.deepLinkLogger(for: Self.self)\n\n    public let authUI: DeepLinkAuthUI\n    public let authStore: DeepLinkAuthStore\n    public let managementStore: DeepLinkManagementStore\n\n    public init(authUI: DeepLinkAuthUI, authStore: DeepLinkAuthStore, managementStore: DeepLinkManagementStore) {\n        self.authUI = authUI\n        self.authStore = authStore\n        self.managementStore = managementStore\n    }\n\n    /// Sets the sentinel as the handler for URL scheme Apple Events.\n    public func installAppleEventHandler() {\n        NSAppleEventManager.shared().setEventHandler(\n            self,\n            andSelector: #selector(DeepLinkSentinel.handleURLEvent(_:replyEvent:)),\n            forEventClass: AEEventClass(kInternetEventClass),\n            andEventID: AEEventID(kAEGetURL)\n        )\n    }\n\n    /// Delivers open URL events once they've been authenticated.\n    /// Open URL requests that fail authentication are not delivered.\n    public private(set) lazy var openURL: AsyncStream<URL> = {\n        AsyncStream { [weak self] continuation in\n            self?.onOpenURL = { url in\n                continuation.yield(url)\n            }\n        }\n    }()\n\n    private var onOpenURL: (URL) -> Void = { _ in }\n\n    @objc func handleURLEvent(_ event: NSAppleEventDescriptor, replyEvent: NSAppleEventDescriptor) {\n        logger.debug(#function)\n\n        guard let urlStr = event.paramDescriptor(forKeyword: AEKeyword(keyDirectObject))?.stringValue else {\n            logger.fault(\"Failed to get string from URL open event\")\n            return\n        }\n\n        logger.debug(\"Handling URL \\(urlStr)\")\n\n        guard let tokenDescriptor = event.attributeDescriptor(forKeyword: AEKeyword(keySenderAuditTokenAttr))?.copy() as? NSAppleEventDescriptor else {\n            logger.error(\"Missing Apple Event sender audit token in URL event\")\n            return\n        }\n\n        guard let url = URL(string: urlStr) else {\n            logger.error(\"Invalid deep link URL: \\\"\\(urlStr)\\\"\")\n            return\n        }\n\n        Task {\n            await authenticate(descriptor: tokenDescriptor, url: url)\n        }\n    }\n\n    func storeDescriptor(_ descriptor: DeepLinkClientDescriptor, with authorization: DeepLinkClientAuthorization = .undetermined) async {\n        do {\n            var updatedDescriptor = descriptor\n            updatedDescriptor.authorization = authorization\n\n            try await managementStore.insert(updatedDescriptor)\n        } catch {\n            logger.warning(\"Failed to store management descriptor: \\(error, privacy: .public)\")\n        }\n    }\n\n    func setAuthorization(_ result: DeepLinkClientAuthorization, for client: DeepLinkClient, descriptor: DeepLinkClientDescriptor) async {\n        do {\n            try await authStore.setAuthorization(result, for: client)\n\n            /// Update the client management descriptor with the new authorization.\n            await storeDescriptor(descriptor, with: result)\n        } catch {\n            logger.warning(\"Failed to store authorization decision: \\(error, privacy: .public)\")\n        }\n    }\n\n    public func setAuthorization(_ result: DeepLinkClientAuthorization, for descriptor: DeepLinkClientDescriptor) async throws {\n        let client = try DeepLinkClient(url: descriptor.url)\n\n        try await authStore.setAuthorization(result, for: client)\n\n        /// Update the client management descriptor with the new authorization.\n        await storeDescriptor(descriptor, with: result)\n    }\n\n    private func authenticate(descriptor: NSAppleEventDescriptor, url: URL) async {\n        do {\n            let client = try DeepLinkClient(auditTokenDescriptor: descriptor)\n\n            let clientDescriptor = DeepLinkClientDescriptor(client: client)\n\n            do {\n                /// Store an initial entry for the client descriptor if it doesn't exist yet.\n                let descriptorExists = await managementStore.hasDescriptor(with: clientDescriptor.id)\n                if !descriptorExists {\n                    try await managementStore.insert(clientDescriptor)\n                }\n            } catch {\n                logger.warning(\"Failed to store management descriptor before auth: \\(error, privacy: .public)\")\n            }\n\n            let request = OpenDeepLinkRequest(url: url, client: clientDescriptor)\n\n            do {\n                var authorization = await existingAuthorization(for: client)\n\n                switch authorization {\n                case .undetermined:\n                    logger.debug(\"No existing authorization for \\(client.designatedRequirement), prompting\")\n\n                    authorization = try await authUI.presentDeepLinkAuth(for: request)\n\n                    guard authorization != .undetermined else {\n                        logger.warning(\"Auth UI ended with undetermined authorization, denying current request without modifying auth store\")\n                        return\n                    }\n\n                    await setAuthorization(authorization, for: client, descriptor: clientDescriptor)\n                case .authorized:\n                    logger.debug(\"Got existing client authorization for \\(client.designatedRequirement)\")\n                case .denied:\n                    logger.warning(\"Denying open URL: client authorization denied for \\(client.designatedRequirement)\")\n                    return\n                }\n\n                /// Just being extra paranoid here.\n                guard authorization != .denied else { return }\n\n                logger.notice(\"Successfully authenticated deep link request for opening \\(url)\")\n\n                await MainActor.run {\n                    onOpenURL(url)\n                }\n            } catch {\n                logger.error(\"Deep link authentication failed: \\(error, privacy: .public)\")\n            }\n        } catch {\n            logger.error(\"Failed to get client information: \\(error, privacy: .public)\")\n        }\n    }\n\n    private func existingAuthorization(for client: DeepLinkClient) async -> DeepLinkClientAuthorization {\n        let auth = await authStore.authorization(for: client)\n\n        guard auth == .authorized else { return auth }\n\n        do {\n            try await client.validate()\n\n            return .authorized\n        } catch {\n            logger.warning(\"Client signature validated for \\(client.designatedRequirement), prompting again. Error: \\(error, privacy: .public)\")\n\n            return .undetermined\n        }\n    }\n}\n\nextension NSAppleEventDescriptor: @retroactive @unchecked Sendable { }\n"
  },
  {
    "path": "DeepLinkSecurity/Source/Models/DeepLinkClient.swift",
    "content": "import Foundation\nimport CryptoKit\n\n/// Represents an app/process that's attempting to open a deep link in the app.\npublic struct DeepLinkClient: Identifiable {\n    /// The client ID is a SHA256 hash of its designated CS requirement.\n    public var id: String\n    /// The bundle URL for the app or executable URL for the process.\n    public var url: URL\n    /// The designated code signing requirement for the client.\n    /// This is hashed and used as a key to store the user's decision,\n    /// and it's also used in order to verify that the client's code signature is valid.\n    public var designatedRequirement: String\n\n    public init(url: URL, designatedRequirement: String) {\n        self.url = url\n        self.designatedRequirement = designatedRequirement\n        self.id = SHA256.hash(data: Data(designatedRequirement.utf8))\n            .map { String(format: \"%02X\", $0) }\n            .joined()\n    }\n}\n"
  },
  {
    "path": "DeepLinkSecurity/Source/Models/DeepLinkClientDescriptor.swift",
    "content": "import Cocoa\n\n/// Describes metadata for a client that's previously requested deep link authorization.\n/// See ``DeepLinkManagementStore``.\npublic struct DeepLinkClientDescriptor: Identifiable, Hashable, Codable {\n    public struct Icon: Hashable, Codable {\n        public var image: NSImage\n    }\n\n    /// Unique identifier for the client.\n    public var id: String\n    /// The client's main bundle or executable URL.\n    public var url: URL\n    /// The client's bundle identifier, if available.\n    public var bundleIdentifier: String?\n    /// A user-friendly name for the client.\n    public var displayName: String\n    /// Icon image representing the client app or executable.\n    public var icon: Icon\n    /// The current authorization state for the client.\n    public var authorization: DeepLinkClientAuthorization\n    /// Will be `false` if the descriptor's client could no longer be found on the filesystem.\n    public var isValid: Bool\n}\n"
  },
  {
    "path": "DeepLinkSecurity/Source/Models/Extensions/DeepLinkClient+Crypto.swift",
    "content": "import Cocoa\n\npublic extension DeepLinkClient {\n    init(auditTokenDescriptor: NSAppleEventDescriptor) throws {\n        let attrs = [kSecGuestAttributeAudit: auditTokenDescriptor.data]\n        var client: SecCode!\n\n        try checkSecError(SecCodeCopyGuestWithAttributes(nil, attrs as CFDictionary, .default, &client), task: \"copy client cs attributes\")\n\n        var staticCode: SecStaticCode!\n        try checkSecError(SecCodeCopyStaticCode(client, .default, &staticCode), task: \"copy client static code\")\n\n        var url: CFURL!\n\n        try checkSecError(SecCodeCopyPath(staticCode, .default, &url), task: \"copy client path\")\n\n        var requirement: SecRequirement!\n        try checkSecError(SecCodeCopyDesignatedRequirement(staticCode, .default, &requirement), task: \"copy client designated requirement\")\n\n        var requirementText: CFString!\n        try checkSecError(SecRequirementCopyString(requirement, .default, &requirementText), task: \"copy client designated requirement text\")\n\n        self.init(url: url as URL, designatedRequirement: requirementText as String)\n    }\n\n    init(url: URL) throws {\n        var staticCode: SecStaticCode!\n        try checkSecError(SecStaticCodeCreateWithPath(url as CFURL, .default, &staticCode), task: \"load client static code\")\n\n        var requirement: SecRequirement!\n        try checkSecError(SecCodeCopyDesignatedRequirement(staticCode, .default, &requirement), task: \"copy client designated requirement\")\n\n        var requirementText: CFString!\n        try checkSecError(SecRequirementCopyString(requirement, .default, &requirementText), task: \"copy client designated requirement text\")\n\n        self.init(url: url, designatedRequirement: requirementText as String)\n    }\n\n    /// Validates the client's static code against the designated requirement,\n    /// throwing an error if the signature doesn't match.\n    func validate() async throws {\n        var requirement: SecRequirement!\n        try checkSecError(SecRequirementCreateWithString(designatedRequirement as CFString, .default, &requirement), task: \"create client designated requirement\")\n\n        var staticCode: SecStaticCode!\n        try checkSecError(SecStaticCodeCreateWithPath(url as CFURL, .default, &staticCode), task: \"load client static code\")\n\n        var errors: Unmanaged<CFError>?\n        guard SecStaticCodeCheckValidityWithErrors(staticCode, .default, requirement, &errors) == noErr else {\n            let errorMessage = errors?.takeUnretainedValue().localizedDescription ?? \"Unknown error\"\n            throw DeepLinkError(\"Client code signature validation failed with: \\(errorMessage)\")\n        }\n    }\n}\n\nprivate func checkSecError(_ closure: @autoclosure () -> OSStatus, task: String) throws {\n    let err = closure()\n\n    guard err != noErr else { return }\n\n    let msg = SecCopyErrorMessageString(err, nil) as String?\n\n    throw DeepLinkError(\"Failed to \\(task). Error code \\(err): \\(msg ?? \"<unknown>\")\")\n}\n\nextension SecCSFlags {\n    static let `default` = SecCSFlags([])\n}\n"
  },
  {
    "path": "DeepLinkSecurity/Source/Models/Extensions/DeepLinkClientDescriptor+.swift",
    "content": "import Cocoa\n\npublic extension DeepLinkClientDescriptor {\n    init(client: DeepLinkClient, authorization: DeepLinkClientAuthorization = .undetermined) {\n        self.init(clientID: client.id, clientURL: client.url, authorization: authorization)\n    }\n\n    init(clientID: DeepLinkClient.ID, clientURL: URL, authorization: DeepLinkClientAuthorization = .undetermined) {\n        let bundle = Bundle(url: clientURL)\n\n        self.init(\n            id: clientID,\n            url: clientURL,\n            bundleIdentifier: bundle.flatMap(\\.bundleIdentifier),\n            displayName: bundle.flatMap(\\.bestEffortAppName) ?? clientURL.fileNameWithoutExtension,\n            icon: Icon(clientURL: clientURL),\n            authorization: authorization,\n            isValid: FileManager.default.fileExists(atPath: clientURL.path)\n        )\n    }\n\n    /// Checks if the descriptor's client is still present at its original filesystem location,\n    /// attempting to update the client if it's been moved or deleted.\n    func resolved() -> DeepLinkClientDescriptor {\n        guard let bundleIdentifier else { return self }\n        guard !FileManager.default.fileExists(atPath: url.path) else { return self }\n\n        guard let updatedURL = NSWorkspace.shared.urlForApplication(withBundleIdentifier: bundleIdentifier) else { return self.invalidated() }\n\n        return DeepLinkClientDescriptor(clientID: id, clientURL: updatedURL, authorization: authorization)\n    }\n\n    func invalidated() -> DeepLinkClientDescriptor {\n        var mSelf = self\n        mSelf.isValid = false\n        return mSelf\n    }\n\n    func withAuthorization(_ authorization: DeepLinkClientAuthorization) -> DeepLinkClientDescriptor {\n        var mSelf = self\n        mSelf.authorization = authorization\n        return mSelf\n    }\n}\n\npublic extension DeepLinkClientDescriptor.Icon {\n    init(from decoder: Decoder) throws {\n        let container = try decoder.singleValueContainer()\n        let data = try container.decode(Data.self)\n        let image = NSImage(data: data) ?? NSWorkspace.shared.icon(for: .application)\n\n        self.init(image: image)\n    }\n\n    func encode(to encoder: Encoder) throws {\n        var container = encoder.singleValueContainer()\n\n        guard let png = image.pngData() else {\n            throw EncodingError.invalidValue(0, .init(codingPath: [], debugDescription: \"Failed to get icon PNG data\"))\n        }\n\n        try container.encode(png)\n    }\n}\n\nprivate extension DeepLinkClientDescriptor.Icon {\n    init(clientURL: URL) {\n        let image: NSImage\n        if FileManager.default.fileExists(atPath: clientURL.path) {\n            image = NSWorkspace.shared.icon(forFile: clientURL.path)\n        } else {\n            image = NSWorkspace.shared.icon(for: .application)\n        }\n        image.size = NSSize(width: 64, height: 64)\n        self.init(image: image)\n    }\n}\n\nprivate extension NSImage {\n    func pngData() -> Data? {\n        guard let cgImage = cgImage(forProposedRect: nil, context: nil, hints: nil) else { return nil }\n\n        let rep = NSBitmapImageRep(cgImage: cgImage)\n\n        guard let png = rep.representation(using: .png, properties: [:]) else { return nil }\n\n        return png\n    }\n}\n\nprivate extension Bundle {\n    var bestEffortAppName: String? {\n        guard let info = infoDictionary else { return bundleURL.fileNameWithoutExtension }\n\n        return info[\"CFBundleDisplayName\"] as? String ?? info[\"CFBundleName\"] as? String ?? bundleURL.fileNameWithoutExtension\n    }\n}\n\nprivate extension URL {\n    var fileNameWithoutExtension: String { deletingPathExtension().lastPathComponent }\n}\n"
  },
  {
    "path": "DeepLinkSecurity/Source/Models/OpenDeepLinkRequest.swift",
    "content": "import Foundation\n\n/// Represents a client's request for opening a deep link in the app.\npublic struct OpenDeepLinkRequest {\n    /// The URL for the deep link the client is trying to open.\n    public var url: URL\n    /// The client model used for authentication.\n    public var client: DeepLinkClientDescriptor\n\n    public init(url: URL, client: DeepLinkClientDescriptor) {\n        self.url = url\n        self.client = client\n    }\n}\n"
  },
  {
    "path": "DeepLinkSecurity/Source/Storage/DeepLinkAuthStore.swift",
    "content": "import Cocoa\n\n/// Describes a user's decision about a client opening deep links in the app.\npublic enum DeepLinkClientAuthorization: Int, Codable, CustomStringConvertible {\n    /// The user has not granted/rejected the client yet.\n    /// Also used as a fallback when something goes wrong in the process of\n    /// authenticating a previously authorized/denied client.\n    case undetermined\n    /// The user has granted authorization to the client.\n    case authorized\n    /// The user has denied authorization for the client.\n    case denied\n}\n\npublic extension DeepLinkClientAuthorization {\n    var description: String {\n        switch self {\n        case .undetermined:\n            return \"undetermined\"\n        case .authorized:\n            return \"authorized\"\n        case .denied:\n            return \"denied\"\n        }\n    }\n}\n\n/// Implemented by types that can provide persistence for user's decision regarding the opening of deep links from other apps.\n/// See ``MemoryDeepLinkAuthStore`` and ``KeychainDeepLinkAuthStore``.\npublic protocol DeepLinkAuthStore {\n    func authorization(for client: DeepLinkClient) async -> DeepLinkClientAuthorization\n    func setAuthorization(_ authorization: DeepLinkClientAuthorization, for client: DeepLinkClient) async throws\n}\n"
  },
  {
    "path": "DeepLinkSecurity/Source/Storage/DeepLinkManagementStore.swift",
    "content": "import Foundation\n\n/// Implemented by types that can provide persistence for a list of authorized/denied deep link clients,\n/// so that a UI can be assembled showing the user their previous decisions and allowing users to change their mind.\npublic protocol DeepLinkManagementStore {\n    /// Returns deep link client descriptors previously added using ``insert(_:)``.\n    var currentClientDescriptors: [DeepLinkClientDescriptor] { get }\n\n    /// Streams deep link client descriptors previously added using ``insert(_:)`` as they're updated.\n    func clientDescriptors() -> AsyncStream<[DeepLinkClientDescriptor]>\n\n    /// Whether the store currently has a descriptor with the specified identifier.\n    func hasDescriptor(with id: DeepLinkClientDescriptor.ID) async -> Bool\n\n    /// Upserts a client descriptor.\n    func insert(_ descriptor: DeepLinkClientDescriptor) async throws\n\n    /// Deletes an existing descriptor.\n    func delete(_ descriptor: DeepLinkClientDescriptor) async throws\n}\n"
  },
  {
    "path": "DeepLinkSecurity/Source/Storage/KeychainDeepLinkAuthStore.swift",
    "content": "import Foundation\nimport Security\nimport OSLog\nimport CryptoKit\n\n/// A robust auth store that uses signed keychain items for storing the user's authorization decisions.\npublic final actor KeychainDeepLinkAuthStore: DeepLinkAuthStore {\n\n    private lazy var logger = Logger.deepLinkLogger(for: Self.self)\n\n    private let namespace: String\n    private let keyID: String\n\n    public init(namespace: String, keyID: String) {\n        self.namespace = namespace\n        self.keyID = keyID\n    }\n\n    private let encoder = PropertyListEncoder()\n    private let decoder = PropertyListDecoder()\n\n    public func authorization(for client: DeepLinkClient) async -> DeepLinkClientAuthorization {\n        do {\n            guard let authData = try readKeychainItem(id: client.id) else {\n                return .undetermined\n            }\n\n            do {\n                /// Fetch the existing response item from the keychain.\n                let response = try decoder.decode(KeychainAuthorizationResponse.self, from: authData)\n\n                do {\n                    /// The mere existence of the response in the keychain doesn't automatically mean the decision should be honored.\n                    /// When written to the keychain, responses are signed with a private key.\n                    /// When read from the keychain, the app verifies the stored signature against the corresponding public key,\n                    /// ensuring that this response was written by an app that has permission to access this app's private key item from the keychain.\n                    /// If another app attempts to write fake permissions to the keychain, they'll only be valid if the app can also sign the responses using our private key,\n                    /// but in order to do that, the app would need the user's permission to access our keychain item, which triggers a permission prompt on macOS.\n                    try validate(response: response)\n\n                    logger.debug(\"Validated authorization response\")\n\n                    return response.authorization\n                } catch {\n                    logger.error(\"Failed to validate authorization response: \\(error, privacy: .public)\")\n\n                    throw error\n                }\n            } catch {\n                logger.fault(\"Error decoding authorization from Keychain entry: \\(error, privacy: .public)\")\n                throw error\n            }\n        } catch {\n            return .undetermined\n        }\n    }\n\n    public func setAuthorization(_ authorization: DeepLinkClientAuthorization, for client: DeepLinkClient) async throws {\n        let clientID = client.id\n\n        let signingKey = try fetchSigningKey()\n        let payload = KeychainAuthorizationResponse.signingPayload(clientID: clientID, authorization: authorization)\n        let signature = try signingKey.sign(payload)\n\n        let response = KeychainAuthorizationResponse(\n            authorization: authorization,\n            clientID: clientID,\n            signingKeyID: keyID,\n            signature: signature\n        )\n\n        guard let authData = try? encoder.encode(response) else {\n            throw DeepLinkError(\"Failed to encode authorization\")\n        }\n\n        try writeKeychainItem(id: clientID, data: authData)\n\n        logger.debug(\"Stored authorization \\(authorization) for \\(client.designatedRequirement) (id = \\(clientID, privacy: .public))\")\n    }\n\n    private func validate(response: KeychainAuthorizationResponse) throws {\n        let key = try fetchSigningKey()\n\n        guard try key.verify(response.signature, for: response.signingPayload) else {\n            throw DeepLinkError(\"Invalid authorization signature for client \\(response.clientID)\")\n        }\n    }\n\n    private func fetchSigningKey() throws -> KeychainAuthorizationSigningKey {\n        func createNewKey() throws -> KeychainAuthorizationSigningKey {\n            logger.debug(\"Creating new authorization signing key\")\n\n            let newKey = KeychainAuthorizationSigningKey(id: keyID)\n\n            let keyData = try encoder.encode(newKey)\n\n            try writeKeychainItem(id: keyID, data: keyData)\n\n            return newKey\n        }\n\n        if let data = try readKeychainItem(id: keyID) {\n            logger.debug(\"Found existing authorization signing key\")\n\n            do {\n                let key = try decoder.decode(KeychainAuthorizationSigningKey.self, from: data)\n\n                return key\n            } catch {\n                logger.fault(\"Error reading existing authorization signing key, will generate new one. \\(error, privacy: .public)\")\n\n                return try createNewKey()\n            }\n        } else {\n            return try createNewKey()\n        }\n    }\n\n    /// Simple helper for reading an item's data from the keychain.\n    private func readKeychainItem(id: String) throws -> Data? {\n        let query = [\n            kSecClass: kSecClassGenericPassword,\n            kSecReturnData: true,\n            kSecMatchLimit: kSecMatchLimitOne,\n            kSecAttrService: namespace as CFString,\n            kSecAttrAccount: id as CFString\n        ] as [CFString: Any] as CFDictionary\n\n        var result: CFTypeRef?\n        let res = SecItemCopyMatching(query, &result)\n\n        guard res != errSecItemNotFound else { return nil }\n\n        try check(res)\n\n        guard let data = result as? Data else {\n            throw DeepLinkError(\"Failed to cast security query result to data\")\n        }\n\n        return data\n    }\n\n    /// Simple helper for writing an item to the keychain.\n    private func writeKeychainItem(id: String, data: Data) throws {\n        let query = [\n            kSecClass: kSecClassGenericPassword,\n            kSecAttrService: namespace as CFString,\n            kSecAttrAccount: id as CFString\n        ] as [CFString: Any]\n\n        var attrs = query\n        attrs[kSecValueData] = data as CFData\n\n        var result: CFTypeRef?\n        var res = SecItemAdd(attrs as CFDictionary, &result)\n\n        if res == errSecDuplicateItem {\n            logger.debug(\"Keychain item \\(id) already exists, updating\")\n            res = SecItemUpdate(query as CFDictionary, [kSecValueData: data as CFData] as CFDictionary)\n        }\n\n        try check(res)\n    }\n\n    /// Simple helper for removing an item from the keychain.\n    private func deleteKeychainItem(id: String) throws {\n        let query = [\n            kSecClass: kSecClassGenericPassword,\n            kSecMatchLimit: kSecMatchLimitOne,\n            kSecAttrService: namespace as CFString,\n            kSecAttrAccount: id as CFString\n        ] as [CFString: Any] as CFDictionary\n\n        let res = SecItemDelete(query)\n        \n        guard res != errSecItemNotFound else { return }\n\n        try check(res)\n    }\n\n}\n\n/// This is the payload that's stored as the value for a keychain item representing the user's decision\n/// regarding an app's permission to open deep links within this app.\nprivate struct KeychainAuthorizationResponse: Codable {\n    /// The user's decision (allow/deny).\n    var authorization: DeepLinkClientAuthorization\n    /// The SHA256 hash of the designated CS requirement for the app the decision is for.\n    var clientID: String\n    /// The identifier for the key used to generate the ECDSA signature.\n    var signingKeyID: String\n    /// The ECDSA signature for the payload, which is composed of the client ID and the user's decision.\n    var signature: Data\n}\n\nprivate extension KeychainAuthorizationResponse {\n    /// The payload that gets signed when storing the response on the keychain.\n    var signingPayload: Data { Self.signingPayload(clientID: clientID, authorization: authorization) }\n\n    static func signingPayload(clientID: String, authorization: DeepLinkClientAuthorization) -> Data {\n        Data(\"\\(clientID)-\\(authorization.rawValue)\".utf8)\n    }\n}\n\n// MARK: - Crypto\n\nprivate struct KeychainAuthorizationSigningKey: Codable {\n    var id: String\n    var keyData: Data\n}\n\nprivate extension KeychainAuthorizationSigningKey {\n    init(id: String = UUID().uuidString) {\n        let key = P521.Signing.PrivateKey()\n\n        self.init(id: id, keyData: key.rawRepresentation)\n    }\n}\n\nprivate extension KeychainAuthorizationSigningKey {\n    var privateKey: P521.Signing.PrivateKey {\n        get throws {\n            try P521.Signing.PrivateKey(rawRepresentation: keyData)\n        }\n    }\n\n    var publicKey: P521.Signing.PublicKey {\n        get throws {\n            try privateKey.publicKey\n        }\n    }\n}\n\nprivate extension KeychainAuthorizationSigningKey {\n    func sign(_ digest: some DataProtocol) throws -> Data {\n        try privateKey.signature(for: digest).rawRepresentation\n    }\n\n    func verify(_ signature: Data, for digest: some DataProtocol) throws -> Bool {\n        let signature = try P521.Signing.ECDSASignature(rawRepresentation: signature)\n        return try publicKey.isValidSignature(signature, for: digest)\n    }\n}\n\n// MARK: - Helpers\n\nprivate func check(_ res: OSStatus) throws {\n    guard res != errSecSuccess else { return }\n\n    if let str = SecCopyErrorMessageString(res, nil) {\n        throw DeepLinkError(\"Security error code \\(res): \\\"\\(str)\\\"\")\n    } else {\n        throw DeepLinkError(\"Security error code \\(res)\")\n    }\n}\n"
  },
  {
    "path": "DeepLinkSecurity/Source/Storage/MemoryDeepLinkAuthStore.swift",
    "content": "import Foundation\nimport OSLog\n\n/// A very basic store that uses an in-memory dictionary and is destroyed when the app terminates. Useful for testing.\npublic final actor MemoryDeepLinkAuthStore: DeepLinkAuthStore {\n\n    private lazy var logger = Logger.deepLinkLogger(for: Self.self)\n\n    private var authorizationByClientRequirement = [String: DeepLinkClientAuthorization]()\n\n    public init() { }\n\n    public func authorization(for client: DeepLinkClient) async -> DeepLinkClientAuthorization {\n        if let result = authorizationByClientRequirement[client.designatedRequirement] {\n            logger.debug(\"Found existing authorization \\(result) for \\(client.designatedRequirement)\")\n            return result\n        } else {\n            logger.debug(\"No authorization in store for \\(client.designatedRequirement), returning undetermined\")\n            return .undetermined\n        }\n    }\n\n    public func setAuthorization(_ authorization: DeepLinkClientAuthorization, for client: DeepLinkClient) async throws {\n        logger.debug(\"Setting authorization \\(authorization) for \\(client.designatedRequirement)\")\n        \n        authorizationByClientRequirement[client.designatedRequirement] = authorization\n    }\n\n}\n"
  },
  {
    "path": "DeepLinkSecurity/Source/Storage/UserDefaultsDeepLinkManagementStore.swift",
    "content": "import Foundation\nimport OSLog\n\n/// A management store that persists client descriptors in `UserDefaults`.\npublic final actor UserDefaultsDeepLinkManagementStore: DeepLinkManagementStore {\n    private let logger = Logger.deepLinkLogger(for: UserDefaultsDeepLinkManagementStore.self)\n\n    private let defaults: UserDefaults\n    private let storageKey: String\n\n    public init(namespace: String = \"DeepLinkSecurity\", suiteName: String? = nil, inMemory: Bool = false) {\n        self.storageKey = \"\\(namespace)-Management\"\n\n        if let suiteName {\n            if let instance = UserDefaults(suiteName: suiteName) {\n                self.defaults = instance\n            } else {\n                assertionFailure(\"Failed to initialize user defaults with suite name \\\"\\(suiteName)\\\"\")\n                self.defaults = .standard\n            }\n        } else if inMemory {\n            self.defaults = UserDefaults()\n        } else {\n            self.defaults = .standard\n        }\n\n        Task {\n            let existingDescriptors = readDescriptors()\n            await cacheDescriptors(existingDescriptors)\n        }\n    }\n\n    public func hasDescriptor(with id: DeepLinkClientDescriptor.ID) -> Bool { cachedDescriptors[id] != nil }\n\n    public nonisolated var currentClientDescriptors: [DeepLinkClientDescriptor] {\n        Self.descriptorsArray(from: readDescriptors())\n    }\n\n    public nonisolated func clientDescriptors() -> AsyncStream<[DeepLinkClientDescriptor]> {\n        let stream = AsyncStream { [weak self] continuation in\n            guard let self = self else { return }\n\n            Task {\n                await self.onStoreChanged { descriptorsByID in\n                    let descriptors = Self.descriptorsArray(from: descriptorsByID)\n\n                    continuation.yield(descriptors)\n                }\n            }\n        }\n\n        Task {\n            let snapshot = readDescriptors()\n            await storeChangeHandler(snapshot)\n        }\n\n        return stream\n    }\n\n    private static func descriptorsArray(from descriptorsByID: [DeepLinkClientDescriptor.ID: DeepLinkClientDescriptor]) -> [DeepLinkClientDescriptor] {\n        descriptorsByID\n            .values\n            .map { $0.resolved() }\n            .sorted(by: { $0.displayName.localizedStandardCompare($1.displayName) == .orderedAscending })\n    }\n\n    public func insert(_ descriptor: DeepLinkClientDescriptor) async throws {\n        await update(id: descriptor.id, with: descriptor)\n\n        cacheDescriptors(readDescriptors())\n    }\n    \n    public func delete(_ descriptor: DeepLinkClientDescriptor) async throws {\n        await update(id: descriptor.id, with: nil)\n\n        cacheDescriptors(readDescriptors())\n    }\n\n    private var cachedDescriptors = DescriptorStorage()\n\n    private var storeChangeHandler: (DescriptorStorage) async -> Void = { _ in }\n\n    private func onStoreChanged(perform block: @escaping (DescriptorStorage) async -> Void) {\n        storeChangeHandler = block\n    }\n\n    private func cacheDescriptors(_ descriptors: DescriptorStorage) {\n        self.cachedDescriptors = descriptors\n    }\n\n    private func update(id: DeepLinkClientDescriptor.ID, with descriptor: DeepLinkClientDescriptor?) async {\n        do {\n            var snapshot = readDescriptors()\n\n            snapshot[id] = descriptor\n\n            let data = try encoder.encode(snapshot)\n\n            defaults.set(data, forKey: storageKey)\n            defaults.synchronize()\n\n            if let descriptor {\n                logger.debug(\"Inserted descriptor \\(descriptor.id, privacy: .public)\")\n            } else {\n                logger.debug(\"Deleted descriptor \\(id, privacy: .public)\")\n            }\n\n            await storeChangeHandler(snapshot)\n        } catch {\n            logger.error(\"Failed to insert descriptor: \\(error, privacy: .public)\")\n        }\n    }\n\n    private typealias DescriptorStorage = [DeepLinkClientDescriptor.ID: DeepLinkClientDescriptor]\n\n    nonisolated private func readDescriptors() -> DescriptorStorage {\n        logger.debug(#function)\n        \n        guard let data = defaults.data(forKey: storageKey) else {\n            logger.debug(\"No data for \\(self.storageKey)\")\n            return [:]\n        }\n\n        do {\n            let descriptorsByID = try decoder.decode(DescriptorStorage.self, from: data)\n\n            logger.debug(\"Fetched \\(descriptorsByID.count, privacy: .public) client descriptor(s)\")\n\n            return descriptorsByID\n        } catch {\n            logger.error(\"Failed to decode management store data: \\(error, privacy: .public)\")\n\n            return [:]\n        }\n    }\n\n    private let encoder = PropertyListEncoder()\n    private let decoder = PropertyListDecoder()\n}\n\nextension UserDefaults: @retroactive @unchecked Sendable { }\n"
  },
  {
    "path": "DeepLinkSecurity/Source/UI/DeepLinkAuthUI.swift",
    "content": "import SwiftUI\n\npublic protocol DeepLinkAuthUI: AnyObject {\n    /// Return ``DeepLinkClientAuthorization/authorized`` if the user has allowed the client to open deep links in the app.\n    /// \n    /// If this method throws, then the auth store is not modified and the user will be prompted again the next time the same client\n    /// attempts to open a deep link in the app.\n    ///\n    /// If this method returns ``DeepLinkClientAuthorization/denied``, then the auth store will be modified and future\n    /// requests from the same client will be rejected without a prompt.\n    @MainActor\n    func presentDeepLinkAuth(for request: OpenDeepLinkRequest) async throws -> DeepLinkClientAuthorization\n}\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2022 Guilherme Rambo\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n- Redistributions of source code must retain the above copyright notice, this\n  list of conditions and the following disclaimer. \n\n- Redistributions in binary form must reproduce the above copyright notice,\n  this list of conditions and the following disclaimer in the documentation\n  and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
  },
  {
    "path": "README.md",
    "content": "<img src=\"./assets/VirtualBuddyIcon.png\" width=\"128\" />\n\n# VirtualBuddy\n\nVirtualBuddy can virtualize macOS 12 and later on Apple Silicon, with the goal of offering features that are useful to developers who need to test their apps on multiple versions of macOS, especially betas.\n\n![](./assets/Showcase.jpg)\n\n## System Requirements\n\n- **Apple Silicon Mac**\n- macOS 13 or later\n\n### Installing macOS\n\nTo install a macOS virtual machine, you can select from a list of macOS versions provided by VirtualBuddy, which will automatically download and install the selected version. You may also provide your own IPSW link or select from an IPSW you have already downloaded.\n\n### Installing macOS Betas\n\nIf you’d like to run a virtual machine with a macOS beta that’s more recent than the one you’re currently running, such as a macOS 15 virtual machine running in a macOS 14 host, then you’ll need the latest device support package from Apple. \n\nDevice support packages are sometimes made available by Apple directly, but they’re always included and installed automatically with the latest Xcode beta. These can be obtained from the [Apple Developer portal](https://developer.apple.com/download).\n\n![Screenshot of the Apple Developer device support download](./assets/DeviceSupport.png)\n\n## Download\n\nVirtualBuddy is free and open-source. If you’d like to support its development, [you may purchase it on Gumroad](https://insidegui.gumroad.com/l/virtualbuddy) or [sponsor my work on GitHub](https://github.com/sponsors/insidegui).\n\nIf you’d just like to download the latest version, [go to GitHub releases](https://github.com/insidegui/VirtualBuddy/releases/latest).\n\n### Feature Checklist\n\n- [x] Ability to boot any version of macOS 12 or macOS 13, including betas\n- [x] Ability to boot some ARM-based Linux distros (tested with Ubuntu Server and Ubuntu Desktop)\n- [x] Built-in installation wizard\n\t- [x] Select from a collection of restore images available on Apple’s servers\n\t- [x] Install the latest stable version of macOS\n\t- [x] Local restore image IPSW file\n\t- [x] Custom restore image URL\n\t- [x] Install a Linux distro from a local .iso file\n\t- [x] Select from a collection of Linux distros\n\t- [x] Install Linux from URL\n- [x] Boot into recovery mode (in order to disable SIP, for example)\n- [x] Networking and file sharing support\n- [x] Clipboard sharing\n- [x] Customize virtual machine hardware configuration\n- [x] Save and restore macOS virtual machine state\n\n### Tips and Tricks\n\n#### VirtualBuddyGuest app\n\nVirtualBuddy automatically mounts a disk image with the VirtualBuddyGuest app when you boot up a virtual machine running macOS. To install the VirtualBuddyGuest app, just select the “Guest” disk on Finder’s side bar then double-click the “VirtualBuddyGuest” app icon.\n\nVirtualBuddyGuest enables clipboard sharing between host and guest and automatic mounting of the shared folders configured for the virtual machine.\n\n![](./assets/GuestApp.jpg)\n\n#### Taking Advantage of APFS\n\nSometimes when trying things out in the OS installed in one of the virtual machines, things might break, requiring a full install of the guest operating system again, which is a pain.\n\nThanks to APFS cloning though, you can just duplicate a virtual machine within your library folder (using Command + D in Finder), and the copy will take almost no additional disk space. This way you can have a “clean” copy of your VM, do whatever you want with a duplicate of it, and then throw the copy away and re-duplicate the clean version if things break.\n\n#### Sharing Folders Between Host and Virtual Machine\n\nYou can share folders from your Mac to the Virtual Machine and vice-versa using regular macOS file sharing that can be configured in System Preferences/Settings.\n\nWhen both the Virtual Machine and the host are running macOS 13 or later, it’s possible to share folders directly by configuring them in the VM settings within VirtualBuddy before booting up the VM.\n\nTo mount shared folders in the VM, run the following command in the VM’s Terminal:\n\n```bash\nmkdir -p ~/Desktop/VirtualBuddyShared && mount -t virtiofs VirtualBuddyShared ~/Desktop/VirtualBuddyShared\n```\n\n## Building\n\n**Xcode 16** is required for building on `main`.\n\n- Open the `VirtualBuddy/Config/Signing.xcconfig` file\n- Set the `VB_BUNDLE_ID_PREFIX` variable to something unique like `com.yourname.`\n- Select the VirtualBuddy project in the Xcode sidebar\n- Under \"Targets\", select \"VirtualBuddy\"\n- Go to the Signing & Capabilities tab and select your development team under Signing > Team\n- Repeat the same process for the \"VirtualBuddyGuest\" target\n- Build the `VirtualBuddy` scheme (the one that **doesn't** have `(Managed)` in its name)"
  },
  {
    "path": "ReleaseNotes/VirtualBuddy 1.2 Release Nodes.md",
    "content": "# What's new in VirtualBuddy 1.2\n\n- Managing virtual machines can now be done entirely within the library view, the contextual menu offers options for renaming, deleting, duplicating, and showing the VM in Finder\n- The library now sorts virtual machines by creation date, in reverse chronological order\n- Virtual machines can now be configured with custom CPU, RAM, storage devices, network devices, displays, and many other options\n- The option to capture system keyboard shortcuts is now persisted for each virtual machine in the library\n- Adds support for shared folders to share specific directories from your Mac with the virtual machine\n- Adds support for bridged networking, allowing a physical network interface from your Mac to be exposed to the virtual machine\n- Additional storage can now be added to virtual machines by creating new disk images from within VirtualBuddy\n- A new debug console showing logs related to the installation process is now available while installing macOS in a new virtual machine\n- The default library directory for new installs is now ~/Library/Application Support/VirtualBuddy (this is where VirtualBuddy stores virtual machines and downloads)\n- Clicking a virtual machine that's already open in the library will now correctly focus the existing window for that virtual machine"
  },
  {
    "path": "ReleaseNotes/VirtualBuddy 1.2.1 Release Notes.md",
    "content": "# New in VirtualBuddy 1.2.1\n\n### General improvements to the installer user interface:\n\n- Addresses an issue that caused the installer to clip the configuration user interface, hiding the \"continue\" button\n- It is now possible to navigate using the arrow keys when selecting the installation method\n- Text fields now use the same consistent style\n- The virtual machine name button is automatically focused as expected\n- Command + R can be used to generate a new random name while editing the virtual machine's name during installation"
  },
  {
    "path": "ReleaseNotes/VirtualBuddy 1.2.2 Release Notes.md",
    "content": "# Fixed in VirtualBuddy 1.2.2\n\n- Makes custom IPSW URL validation less strict, allowing downloads from plain HTTP URLs and URLs that don't end in .ipsw\n- Addresses an issue that caused audio input to not work in virtual machines; VirtualBuddy will ask for microphone access the first time audio input is used within a virtual machine\n"
  },
  {
    "path": "VirtualBuddy/Assets.xcassets/AccentColor.colorset/Contents.json",
    "content": "{\n  \"colors\" : [\n    {\n      \"color\" : {\n        \"platform\" : \"universal\",\n        \"reference\" : \"systemOrangeColor\"\n      },\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "VirtualBuddy/Assets.xcassets/AppIcon-Default.appiconset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"icon_16x16.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"16x16\"\n    },\n    {\n      \"filename\" : \"icon_16x16@2x@2x.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"16x16\"\n    },\n    {\n      \"filename\" : \"icon_32x32.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"32x32\"\n    },\n    {\n      \"filename\" : \"icon_32x32@2x@2x.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"32x32\"\n    },\n    {\n      \"filename\" : \"icon_128x128.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"128x128\"\n    },\n    {\n      \"filename\" : \"icon_128x128@2x@2x.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"128x128\"\n    },\n    {\n      \"filename\" : \"icon_256x256.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"256x256\"\n    },\n    {\n      \"filename\" : \"icon_256x256@2x@2x.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"256x256\"\n    },\n    {\n      \"filename\" : \"icon_512x512.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"512x512\"\n    },\n    {\n      \"filename\" : \"icon_512x512@2x@2x.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"512x512\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "VirtualBuddy/Assets.xcassets/AppIcon-Dev.appiconset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"icon_16x16.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"16x16\"\n    },\n    {\n      \"filename\" : \"icon_16x16@2x@2x.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"16x16\"\n    },\n    {\n      \"filename\" : \"icon_32x32.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"32x32\"\n    },\n    {\n      \"filename\" : \"icon_32x32@2x@2x.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"32x32\"\n    },\n    {\n      \"filename\" : \"icon_128x128.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"128x128\"\n    },\n    {\n      \"filename\" : \"icon_128x128@2x@2x.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"128x128\"\n    },\n    {\n      \"filename\" : \"icon_256x256.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"256x256\"\n    },\n    {\n      \"filename\" : \"icon_256x256@2x@2x.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"256x256\"\n    },\n    {\n      \"filename\" : \"icon_512x512.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"512x512\"\n    },\n    {\n      \"filename\" : \"icon_512x512@2x@2x.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"512x512\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "VirtualBuddy/Assets.xcassets/AppIcon-zBeta.appiconset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"icon_16x16.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"16x16\"\n    },\n    {\n      \"filename\" : \"icon_16x16@2x@2x.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"16x16\"\n    },\n    {\n      \"filename\" : \"icon_32x32.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"32x32\"\n    },\n    {\n      \"filename\" : \"icon_32x32@2x@2x.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"32x32\"\n    },\n    {\n      \"filename\" : \"icon_128x128.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"128x128\"\n    },\n    {\n      \"filename\" : \"icon_128x128@2x@2x.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"128x128\"\n    },\n    {\n      \"filename\" : \"icon_256x256.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"256x256\"\n    },\n    {\n      \"filename\" : \"icon_256x256@2x@2x.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"256x256\"\n    },\n    {\n      \"filename\" : \"icon_512x512.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"512x512\"\n    },\n    {\n      \"filename\" : \"icon_512x512@2x@2x.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"512x512\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "VirtualBuddy/Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "VirtualBuddy/Automation/DeepLinkHandler.swift",
    "content": "import SwiftUI\nimport VirtualCore\nimport VirtualUI\nimport OSLog\nimport DeepLinkSecurity\n\ntypealias WindowUpdatingClosure = (_ perform: () -> Void) -> Void\n\nfinal class DeepLinkHandler {\n\n    private let settingsContainer: VBSettingsContainer\n    private let updateController: SoftwareUpdateController\n    private let library: VMLibraryController\n    private let sessionManager: VirtualMachineSessionUIManager\n\n    let runner: ActionRunner\n\n    static var shared: DeepLinkHandler {\n        guard let _shared else {\n            fatalError(\"Attempting to access DeepLinkHandler instance before calling bootstrap()\")\n        }\n        return _shared\n    }\n\n    private static var _shared: DeepLinkHandler!\n\n    @MainActor\n    static func bootstrap(library: VMLibraryController) {\n        DeepLinkHandler._shared = DeepLinkHandler(library: library)\n        DeepLinkHandler.shared.install()\n    }\n\n    @MainActor\n    private init(library: VMLibraryController) {\n        self.settingsContainer = VBSettingsContainer.current\n        self.updateController = SoftwareUpdateController.shared\n        self.library = library\n        self.sessionManager = VirtualMachineSessionUIManager.shared\n        self.runner = ActionRunner(\n            settingsContainer: settingsContainer,\n            updateController: updateController,\n            library: library,\n            sessionManager: sessionManager\n        )\n    }\n\n    private lazy var logger = Logger(subsystem: kShellAppSubsystem, category: String(describing: Self.self))\n\n    private let namespace = \"VirtualBuddy\"\n    private let keyID = \"c3bfea24ee1ca95700a4e56d73097aac\"\n\n    private(set) lazy var sentinel = DeepLinkSentinel(\n        authUI: DeepLinkAuthUIPresenter(),\n        authStore: KeychainDeepLinkAuthStore(namespace: namespace, keyID: keyID),\n        managementStore: UserDefaultsDeepLinkManagementStore()\n    )\n\n    func actions() -> AsyncCompactMapSequence<AsyncStream<URL>, DeepLinkAction> {\n        sentinel.openURL.compactMap { url in\n            do {\n                let action = try DeepLinkAction(url)\n\n                self.logger.debug(\"Action: \\(String(describing: action))\")\n\n                return action\n            } catch {\n                self.logger.error(\"Error processing deep link URL \\\"\\(url)\\\": \\(error, privacy: .public)\")\n                return nil\n            }\n        }\n    }\n\n    func install() {\n        sentinel.installAppleEventHandler()\n\n        Task {\n            for await action in DeepLinkHandler.shared.actions() {\n                await runner.run(action)\n            }\n        }\n    }\n\n    @MainActor\n    final class ActionRunner {\n        private let settingsContainer: VBSettingsContainer\n        private let updateController: SoftwareUpdateController\n        private let library: VMLibraryController\n        private let sessionManager: VirtualMachineSessionUIManager\n        private let openWindow = OpenCocoaWindowAction.default\n\n        init(settingsContainer: VBSettingsContainer,\n             updateController: SoftwareUpdateController,\n             library: VMLibraryController,\n             sessionManager: VirtualMachineSessionUIManager)\n        {\n            self.settingsContainer = settingsContainer\n            self.updateController = updateController\n            self.library = library\n            self.sessionManager = sessionManager\n        }\n\n        func run(_ action: DeepLinkAction) async {\n            do {\n                switch action {\n                case .open(let params):\n                    try openVM(named: params.name, options: nil)\n                case .boot(let params):\n                    var effectiveOptions = params.options ?? VMSessionOptions.default\n                    effectiveOptions.autoBoot = true\n                    try openVM(named: params.name, options: effectiveOptions)\n                case .stop(let params):\n                    try await stopVM(named: params.name)\n                }\n            } catch {\n                let alert = NSAlert(error: error)\n                alert.runModal()\n            }\n        }\n\n        func openVM(named name: String, options: VMSessionOptions?) throws {\n            let vm = try getVM(named: name)\n\n            sessionManager.launch(vm, library: library, options: options)\n        }\n\n        func stopVM(named name: String) async throws {\n            let controller = try getController(forVMNamed: name)\n\n            switch controller.state {\n            case .idle, .stopped:\n                throw Failure(\"Can't stop virtual machine \\(name.wrappedInSmartQuotes) because it's not running.\")\n            default:\n                try await controller.stop()\n            }\n        }\n\n        func getVM(named name: String) throws -> VBVirtualMachine {\n            guard let vm = library.virtualMachine(named: name) else {\n                throw Failure(\"Couldn't find a virtual machine with the name \\(name.wrappedInSmartQuotes).\")\n            }\n            return vm\n        }\n\n        func getController(forVMNamed name: String) throws -> VMController {\n            let vm = try getVM(named: name)\n            guard let controller = library.activeController(for: vm.id) else {\n                throw Failure(\"Couldn't find active instance of virtual machine with the name \\(name.wrappedInSmartQuotes).\")\n            }\n            return controller\n        }\n    }\n}\n\nprivate final class DeepLinkAuthUIPresenter: DeepLinkAuthUI {\n    func presentDeepLinkAuth(for request: OpenDeepLinkRequest) async throws -> DeepLinkClientAuthorization {\n        try await DeepLinkAuthPanel.run(for: request)\n    }\n}\n\nextension String {\n    var wrappedInSmartQuotes: String { \"“\\(self)”\" }\n}\n"
  },
  {
    "path": "VirtualBuddy/Automation/Support/DeepLinkAuthDialog.swift",
    "content": "import SwiftUI\nimport DeepLinkSecurity\n\nfinal class DeepLinkAuthPanel: NSPanel {\n\n    private static var panelInstances = NSHashTable<NSPanel>(options: [.strongMemory, .objectPointerPersonality])\n\n    @MainActor\n    static func run(for request: OpenDeepLinkRequest) async throws -> DeepLinkClientAuthorization {\n        try await withCheckedThrowingContinuation { continuation in\n            let panel = DeepLinkAuthPanel(request: request) { panelInstance, decision in\n                defer { panelInstances.remove(panelInstance) }\n\n                continuation.resume(returning: decision)\n\n                panelInstance.close()\n            }\n\n            panelInstances.add(panel)\n\n            panel.makeKeyAndOrderFront(nil)\n            panel.center()\n        }\n    }\n\n    private init(request: OpenDeepLinkRequest, completion: @escaping (DeepLinkAuthPanel, DeepLinkClientAuthorization) -> Void) {\n        super.init(contentRect: .zero, styleMask: [.borderless, .titled, .fullSizeContentView], backing: .buffered, defer: false)\n\n        titleVisibility = .hidden\n        titlebarAppearsTransparent = true\n        animationBehavior = .alertPanel\n        let dialog = DeepLinkAuthDialog(request: request) { [weak self] granted in\n            guard let self = self else { return }\n            completion(self, granted ? .authorized : .denied)\n        }\n        contentViewController = NSHostingController(rootView: dialog)\n    }\n\n    override var canBecomeKey: Bool { true }\n    override var canBecomeMain: Bool { true }\n\n}\n\nstruct DeepLinkAuthDialog: View {\n    var request: OpenDeepLinkRequest\n    var response: (Bool) -> Void\n\n    private var appName: String\n    private var appIcon: Image\n\n    init(request: OpenDeepLinkRequest, response: @escaping (Bool) -> Void) {\n        self.request = request\n        self.response = response\n        self.appName = request.client.displayName\n        self.appIcon = Image(nsImage: request.client.icon.image)\n    }\n\n    private var iconContainerSize: CGFloat { 58 }\n    private var handIconSizeMultiplier: CGFloat { 0.5 }\n    private var appIconSizeMultiplier: CGFloat { 0.432 }\n\n    var body: some View {\n        VStack(spacing: 22) {\n            ZStack {\n                RoundedRectangle(cornerRadius: 16, style: .continuous)\n                    .fill(LinearGradient(colors: [.cyan, .blue], startPoint: .top, endPoint: .bottom))\n                    .frame(width: iconContainerSize, height: iconContainerSize)\n                Image(systemName: \"hand.raised.fill\")\n                    .foregroundStyle(Color.white)\n                    .imageScale(.large)\n                    .font(.system(size: iconContainerSize * handIconSizeMultiplier, weight: .medium, design: .rounded))\n                    .shadow(color: .black.opacity(0.3), radius: 1, x: 0.5, y: 0.5)\n            }\n            .clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous))\n            .shadow(color: .black.opacity(0.3), radius: 1, x: 0, y: 1)\n            .overlay(alignment: .bottomTrailing) {\n                appIcon\n                    .resizable()\n                    .aspectRatio(contentMode: .fit)\n                    .frame(width: iconContainerSize * appIconSizeMultiplier, height: iconContainerSize * appIconSizeMultiplier)\n                    .offset(x: 8, y: 8)\n            }\n\n            Text(\"\\\"\\(appName)\\\" would like to access and control your virtual machines in VirtualBuddy.\")\n                .lineLimit(nil)\n                .font(.headline)\n                .multilineTextAlignment(.center)\n                .fixedSize(horizontal: false, vertical: true)\n\n            Spacer()\n\n            HStack {\n                Button(role: .cancel) {\n                    response(false)\n                } label: {\n                    Text(\"Don't Allow\")\n                        .frame(maxWidth: .infinity)\n                }\n                .keyboardShortcut(.cancelAction)\n\n                Button {\n                    response(true)\n                } label: {\n                    Text(\"Allow\")\n                        .frame(maxWidth: .infinity)\n                }\n                .keyboardShortcut(.defaultAction)\n            }\n            .controlSize(.large)\n            .frame(maxWidth: .infinity)\n        }\n        .padding()\n        .frame(minWidth: 260, minHeight: 230)\n    }\n}\n\n#if DEBUG\nextension DeepLinkClient {\n    static let preview = DeepLinkClient(\n        url: URL(fileURLWithPath: \"/System/Applications/Notes.app\"),\n        designatedRequirement: \"identifier \\\"com.apple.Notes\\\" and anchor apple\"\n    )\n}\n\nextension DeepLinkClientDescriptor {\n    static let preview = DeepLinkClientDescriptor(client: .preview)\n}\n\nextension OpenDeepLinkRequest {\n    static let preview = OpenDeepLinkRequest(url: URL(string: \"x-test-link-auth://test1\")!, client: .preview)\n}\n\n#Preview {\n    DeepLinkAuthDialog(request: .preview) { response in\n        print(\"Response: \\(response)\")\n    }\n    .frame(width: 320)\n}\n#endif\n"
  },
  {
    "path": "VirtualBuddy/Automation/VirtualBuddyDeepLinks.swift",
    "content": "import Foundation\nimport URLQueryItemCoder\nimport DeepLinkSecurity\nimport OSLog\nimport VirtualCore\n\nstruct OpenVMParameters: Codable {\n    var name: String\n}\n\nstruct BootVMParameters: Codable {\n    var name: String\n    var options: VMSessionOptions?\n}\n\nstruct StopVMParameters: Codable {\n    var name: String\n}\n\nenum DeepLinkAction  {\n    case open(OpenVMParameters)\n    case boot(BootVMParameters)\n    case stop(StopVMParameters)\n}\n\nextension DeepLinkAction {\n    init(_ url: URL) throws {\n        guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false) else {\n            throw Failure(\"Invalid URL: failed to construct URLComponents\")\n        }\n        guard let host = components.host else {\n            throw Failure(\"Invalid URL: missing host\")\n        }\n\n        switch host {\n        case \"open\":\n            let params = try Self.decodeParameters(OpenVMParameters.self, from: components)\n            self = .open(params)\n        case \"boot\":\n            let params = try Self.decodeParameters(BootVMParameters.self, from: components)\n            self = .boot(params)\n        case \"stop\":\n            let params = try Self.decodeParameters(StopVMParameters.self, from: components)\n            self = .stop(params)\n        default:\n            throw Failure(\"Unrecognized URL action \\\"\\(host)\\\"\")\n        }\n    }\n\n    private static func decodeParameters<T>(_ type: T.Type, from components: URLComponents) throws -> T where T: Decodable {\n        let items = components.queryItems ?? []\n        return try URLQueryItemDecoder.deepLink.decode(type, from: items)\n    }\n}\n\nprivate extension URLQueryItemDecoder {\n    static let deepLink = URLQueryItemDecoder()\n}\n"
  },
  {
    "path": "VirtualBuddy/Bootstrap/SoftwareUpdateController.swift",
    "content": "//\n//  SoftwareUpdateController.swift\n//  VirtualBuddy\n//\n//  Created by Guilherme Rambo on 25/06/22.\n//\n\nimport SwiftUI\nimport VirtualCore\nimport OSLog\n\n#if ENABLE_SPARKLE\nimport Sparkle\n#endif\n\nfinal class SoftwareUpdateController: NSObject, ObservableObject {\n\n    private let logger = Logger(subsystem: kShellAppSubsystem, category: \"SoftwareUpdateController\")\n\n    static let shared = SoftwareUpdateController()\n\n    private var settings: VBSettings { VBSettingsContainer.current.settings }\n\n    @Published var automaticUpdatesEnabled = true {\n        didSet {\n            #if ENABLE_SPARKLE\n            guard automaticUpdatesEnabled != oldValue else { return }\n\n            logger.debug(\"Setting \\(#function, privacy: .public) to \\(self.automaticUpdatesEnabled, privacy: .public)\")\n\n            updateController.updater.automaticallyChecksForUpdates = automaticUpdatesEnabled\n            #endif\n        }\n    }\n\n    var automaticUpdatesBinding: Binding<Bool> {\n        Binding { [self] in\n            automaticUpdatesEnabled\n        } set: { [self] newValue in\n            automaticUpdatesEnabled = newValue\n        }\n    }\n\n    #if ENABLE_SPARKLE\n    private lazy var updateController: SPUStandardUpdaterController = {\n        SPUStandardUpdaterController(\n            startingUpdater: false,\n            updaterDelegate: self,\n            userDriverDelegate: self\n        )\n    }()\n    #endif\n\n    func activate() {\n        #if ENABLE_SPARKLE\n        logger.debug(#function)\n\n        updateController.startUpdater()\n        automaticUpdatesEnabled = updateController.updater.automaticallyChecksForUpdates\n        registerForUpdateChannelChanges()\n        #endif\n    }\n\n    @objc func checkForUpdates(_ sender: Any?) {\n        #if ENABLE_SPARKLE\n        logger.debug(#function)\n        \n        updateController.checkForUpdates(sender)\n        #else\n        let alert = NSAlert()\n        alert.messageText = \"Updating Disabled\"\n        alert.informativeText = \"This build doesn't include Sparkle updates.\"\n        alert.runModal()\n        #endif\n    }\n\n    private func registerForUpdateChannelChanges() {\n        NotificationCenter.default.addObserver(\n            self,\n            selector: #selector(handleUpdateChannelChanged),\n            name: VBSettings.updateChannelDidChangeNotification,\n            object: nil\n        )\n    }\n\n    /// Check for updates when switching from release to beta channel.\n    @objc private func handleUpdateChannelChanged(_ note: Notification) {\n        guard let channel = note.object as? AppUpdateChannel else { return }\n        guard channel != .release else { return }\n\n        checkForUpdates(nil)\n    }\n\n}\n\n#if ENABLE_SPARKLE\nextension SoftwareUpdateController: SPUUpdaterDelegate, SPUStandardUserDriverDelegate {\n\n    func feedURLString(for updater: SPUUpdater) -> String? {\n        settings.updateChannel.appCastURL.absoluteString\n    }\n\n    func allowedChannels(for updater: SPUUpdater) -> Set<String> {\n        [settings.updateChannel.id]\n    }\n\n}\n#endif\n"
  },
  {
    "path": "VirtualBuddy/Bootstrap/VirtualBuddyApp.swift",
    "content": "//\n//  VirtualBuddyApp.swift\n//  VirtualBuddy\n//\n//  Created by Guilherme Rambo on 07/04/22.\n//\n\nimport SwiftUI\nimport VirtualCore\nimport VirtualUI\n\nlet kShellAppSubsystem = \"codes.rambo.VirtualBuddy\"\n\nstruct VirtualBuddyApp: App {\n    @NSApplicationDelegateAdaptor\n    var appDelegate: VirtualBuddyAppDelegate\n\n    private var settingsContainer: VBSettingsContainer { appDelegate.settingsContainer }\n    private var updateController: SoftwareUpdateController { appDelegate.updateController }\n    private var library: VMLibraryController { appDelegate.library }\n    private var sessionManager: VirtualMachineSessionUIManager { appDelegate.sessionManager }\n\n    @Environment(\\.openWindow)\n    private var openWindow\n\n    @StateObject private var updatesController = SoftwareUpdateController.shared\n\n    private let mainWindowTitle: String = Bundle.main.vbFullVersionDescription\n\n    var body: some Scene {\n        Window(Text(mainWindowTitle), id: .vb_libraryWindowID) {\n            LibraryView()\n                .onAppearOnce(perform: updateController.activate)\n                .environmentObject(library)\n                .environmentObject(sessionManager)\n                .handlesExternalEvents(preferring: [\"*\"], allowing: [\"*\"])\n                .onOpenURL { url in\n                    UILog(\"OPEN URL \\(url.path(percentEncoded: false))\")\n\n                    sessionManager.open(fileURL: url, library: library)\n                }\n                .environment(\\.openVirtualBuddySettings, appDelegate.openSettingsAction)\n                .background { TransparentWindowTitleBarView() }\n        }\n        .windowToolbarStyle(.unified)\n        .commands {\n            #if ENABLE_SPARKLE\n            CommandGroup(after: .appInfo) {\n                Button(\"Check for Updates…\") {\n                    updateController.checkForUpdates(nil)\n                }\n            }\n            #endif\n\n            CommandGroup(replacing: .appSettings) {\n                Button(\"Settings…\") {\n                    appDelegate.openSettingsAction()\n                }\n                .keyboardShortcut(\",\", modifiers: .command)\n            }\n\n            CommandGroup(before: .windowSize) {\n                VirtualMachineWindowCommands()\n                    .environmentObject(sessionManager)\n            }\n\n            CommandGroup(after: .windowArrangement) {\n                Button(\"Library\") {\n                    openWindow(id: .vb_libraryWindowID)\n                }\n                .keyboardShortcut(KeyEquivalent(\"0\"), modifiers: .command)\n            }\n        }\n        .handlesExternalEvents(matching: [\"*\"])\n    }\n}\n\n// TODO: Remove this after moving to AppKit lifecycle\nprivate struct TransparentWindowTitleBarView: NSViewRepresentable {\n    typealias NSViewType = _MakeWindowTitleBarTransparentView\n\n    func makeNSView(context: Context) -> _MakeWindowTitleBarTransparentView {\n        _MakeWindowTitleBarTransparentView(frame: .zero)\n    }\n\n    func updateNSView(_ nsView: _MakeWindowTitleBarTransparentView, context: Context) {\n\n    }\n\n    final class _MakeWindowTitleBarTransparentView: NSView {\n        override func viewDidMoveToWindow() {\n            super.viewDidMoveToWindow()\n            \n            window?.titlebarAppearsTransparent = true\n        }\n    }\n}\n"
  },
  {
    "path": "VirtualBuddy/Bootstrap/VirtualBuddyAppDelegate.swift",
    "content": "//\n//  VirtualBuddyNSApp.swift\n//  VirtualBuddy\n//\n//  Created by Guilherme Rambo on 07/04/22.\n//\n\nimport Cocoa\n@_exported import VirtualCore\n@_exported import VirtualUI\nimport VirtualWormhole\nimport DeepLinkSecurity\nimport OSLog\nimport Combine\nimport SwiftUI\n\n#if BUILDING_NON_MANAGED_RELEASE\n#error(\"Trying to build for release without using the managed scheme. This build won't include managed entitlements. This error is here for Rambo, you may safely comment it out and keep going.\")\n#endif\n\n@MainActor\n@objc final class VirtualBuddyAppDelegate: NSObject, NSApplicationDelegate {\n\n    private let logger = Logger(for: VirtualBuddyAppDelegate.self)\n\n    let settingsContainer = VBSettingsContainer.current\n    let updateController = SoftwareUpdateController.shared\n    let library = VMLibraryController()\n    let sessionManager = VirtualMachineSessionUIManager.shared\n\n    func applicationWillFinishLaunching(_ notification: Notification) {\n        DeepLinkHandler.bootstrap(library: library)\n\n        NSApp?.appearance = NSAppearance(named: .darkAqua)\n    }\n\n    private var cancellables = Set<AnyCancellable>()\n\n    func applicationDidFinishLaunching(_ notification: Notification) {\n        GuestAdditionsDiskImage.current.$state.sink { state in\n            switch state {\n            case .ready:\n                self.logger.debug(\"Guest disk image ready\")\n            case .installing:\n                self.logger.debug(\"Guest disk image installing\")\n            case .installFailed(let error):\n                self.logger.debug(\"Guest disk image installation failed - \\(error, privacy: .public)\")\n            }\n        }\n        .store(in: &cancellables)\n\n        Task {\n            try? await GuestAdditionsDiskImage.current.installIfNeeded()\n        }\n\n        #if DEBUG\n        runLaunchDebugTasks()\n        #endif\n    }\n\n    func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { false }\n\n    func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {\n        if let firstValidAssertion = sender.assertionsPreventingAppTermination.first {\n            logger.debug(\"Preventing app termination due to active assertions: \\(sender.assertionsPreventingAppTermination.map(\\.reason).formatted(.list(type: .and)), privacy: .public)\")\n\n            let reply: NSApplication.TerminateReply\n\n            if let assertionReply = firstValidAssertion.handleShouldTerminate() {\n                logger.debug(\"Assertion handles should terminate, returning its reply \\(assertionReply)\")\n\n                reply = assertionReply\n            } else {\n                logger.debug(\"Assertion doesn't handle should terminate, performing default handling\")\n\n                let alert = NSAlert()\n                alert.messageText = \"Quit VirtualBuddy?\"\n                alert.informativeText = \"VirtualBuddy is currently \\(firstValidAssertion.reason). This will be cancelled if you quit the app.\"\n\n                let button = alert.addButton(withTitle: \"Quit\")\n                button.hasDestructiveAction = true\n\n                let button2 = alert.addButton(withTitle: \"Quit When Done\")\n                button2.keyEquivalent = \"\\r\"\n\n                alert.addButton(withTitle: \"Cancel\")\n\n                let response = alert.runModal()\n\n                reply = switch response {\n                case .alertFirstButtonReturn: .terminateNow\n                case .alertSecondButtonReturn: .terminateLater\n                default: .terminateCancel\n                }\n            }\n\n            switch reply {\n            case .terminateCancel:\n                logger.info(\"User cancelled termination request. Good.\")\n            case .terminateNow:\n                logger.info(\"User decided to terminate now despite assertions :(\")\n            case .terminateLater:\n                logger.info(\"User wants app to terminate when assertions preventing termination are invalidated.\")\n\n                /// Note that there's  no point in resetting this to `false` in any other case because once `.terminateLater`\n                /// has been returned from this method, any attempt to terminate the app will no longer trigger it.\n                sender.shouldTerminateWhenLastAssertionInvalidated = true\n            @unknown default:\n                logger.fault(\"Unknown terminate reply \\(reply, privacy: .public)\")\n            }\n\n            return reply\n        } else {\n            return .terminateNow\n        }\n    }\n\n    private var settingsWindow: NSWindow?\n\n    private(set) lazy var openSettingsAction = OpenVirtualBuddySettingsAction { [weak self] in\n        self?.openSettingsWindow()\n    }\n\n    private func openSettingsWindow() {\n        if let settingsWindow {\n            logger.debug(\"Settings window already available, showing\")\n            settingsWindow.makeKeyAndOrderFront(self)\n            return\n        }\n\n        let rootView = SettingsScreen(\n            enableAutomaticUpdates: updateController.automaticUpdatesBinding,\n            deepLinkSentinel: DeepLinkHandler.shared.sentinel\n        )\n        .environmentObject(settingsContainer)\n\n        let window = NSWindow(\n            contentRect: NSRect(x: 0, y: 0, width: SettingsScreen.width, height: SettingsScreen.minHeight),\n            styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView, .unifiedTitleAndToolbar],\n            backing: .buffered,\n            defer: false\n        )\n        window.isReleasedWhenClosed = false\n        window.contentViewController = NSHostingController(rootView: rootView)\n\n        window.makeKeyAndOrderFront(self)\n        window.center()\n\n        self.settingsWindow = window\n    }\n\n}\n\nextension NSWindow {\n    /// At least as of macOS 14.4, a SwiftUI window's `identifier` matches the `id` that's set in SwiftUI.\n    var isVirtualBuddyLibraryWindow: Bool { identifier?.rawValue == .vb_libraryWindowID }\n}\n\n#if DEBUG\n// MARK: - Debugging Helpers\n\nprivate extension VirtualBuddyAppDelegate {\n    func runLaunchDebugTasks() {\n        RunLoop.main.perform { [self] in\n            MainActor.assumeIsolated {\n                VirtualMachineSessionUIManager.shared.testImportVMIfEnabled(library: library)\n            }\n        }\n    }\n}\n#endif\n"
  },
  {
    "path": "VirtualBuddy/Bootstrap/VirtualBuddyEntryPoint.swift",
    "content": "import SwiftUI\nimport VirtualCore\nimport VirtualUI\nimport BuddyFoundation\n\n/**\n Main entry point for the VirtualBuddy app and all supported command-line tools.\n\n Command-line tools are implemented using ArgumentParser and declared in ``VirtualBuddyCLI``.\n\n The app target symlinks the VirtualBuddy app executable with the names of each supported command-line tool\n as part of a run script build phase.\n\n This entry point uses ``VirtualBuddyCLI`` to check if the current executable name matches that of a supported command-line tool.\n If that's the case, ``VirtualBuddyCLI`` will invoke the tool's implementation and skip running the app itself.\n */\n@main\nstruct VirtualBuddyEntryPoint {\n    static func main() async throws {\n        let name: String\n\n        #if DEBUG\n        /**\n         `VB_TOOL` environment variable can be used to test command-line tools when running from within Xcode,\n         where it is inconvenient to deal with running the symlinked variants.\n\n         Each tool should have an aggregate target set up in the project that has this environment variable set.\n         */\n        if let overrideName = ProcessInfo.processInfo.environment[\"VB_TOOL\"] {\n            name = overrideName\n        } else {\n            name = ProcessInfo.processInfo.processName\n        }\n        #else\n        name = ProcessInfo.processInfo.processName\n        #endif\n\n        /// Only attempt to process commands if the executable name doesn't match the name in the app bundle.\n        guard name != Bundle.main.executableName else {\n            return VirtualBuddyApp.main()\n        }\n\n        await VirtualBuddyCLI.runCommand(named: name)\n\n        VirtualBuddyApp.main()\n    }\n}\n\nprivate extension Bundle {\n    /**\n     For the purposes of checking whether the user is running the VirtualBuddy app or a command-line tool,\n     the actual bundle executable declared in the `Info.plist` must be compared against the process name.\n\n     The `Bundle.executableURL` property will return the URL of the actual executable symlink instead of the one declared.\n     */\n    var executableName: String { infoDictionary?[\"CFBundleExecutable\"] as? String ?? \"VirtualBuddy\" }\n}\n"
  },
  {
    "path": "VirtualBuddy/CommandLine/VirtualBuddyCLI.swift",
    "content": "import Foundation\nimport ArgumentParser\n\n/**\n Declares supported `ParsableCommand`s that VirtualBuddy provides.\n\n Commands are implemented in the main app binary, but the app's entry point checks the process name\n in order to determine whether a command-line tool should be run instead of the app itself.\n */\nstruct VirtualBuddyCLI {\n    static let supportedEntryPoints: [ParsableCommand.Type] = [\n        VCTool.self,\n    ]\n\n    static func runCommand(named name: String) async {\n        guard let command = supportedEntryPoints.first(where: { $0.configuration.commandName == name }) else {\n            return\n        }\n\n        /// Remove any arguments injected by Xcode (such as `-NSDocumentRevisionsDebugMode`).\n        /// Also remove first argument, which is the name of the command itself.\n        let sanitizedArguments: [String] = CommandLine.arguments\n            .suffix(from: 1)\n            .filter { !$0.hasPrefix(\"-NS\") && $0 != \"YES\" && $0 != \"NO\" }\n\n        if let asyncCommand = command as? AsyncParsableCommand.Type {\n            await asyncCommand.main(sanitizedArguments)\n        } else {\n            command.main(sanitizedArguments)\n        }\n\n        /**\n         For non-async commands, we have to exit explicitly if the command didn't do it for us,\n         otherwise this function will return and the app could end up running from the command-line.\n\n         Placed at the end here just in case the behavior changes for async commands in the future.\n         **/\n        exit(0)\n    }\n}\n"
  },
  {
    "path": "VirtualBuddy/CommandLine/vctool/BlurHashCommand.swift",
    "content": "import Foundation\nimport ArgumentParser\nimport VirtualCore\nimport VirtualUI\nimport BuddyFoundation\n\nstruct BlurHashCommand: AsyncParsableCommand {\n    static let configuration = CommandConfiguration(\n        commandName: \"blurhash\",\n        abstract: \"Encodes or decodes blur hashes.\",\n        subcommands: [\n            BlurHashEncodeCommand.self,\n            BlurHashDecodeCommand.self,\n        ]\n    )\n}\n\nprivate struct BlurHashEncodeCommand: AsyncParsableCommand {\n    static let configuration = CommandConfiguration(\n        commandName: \"encode\",\n        abstract: \"Encodes an image into a blur hash.\"\n    )\n\n    @Argument(help: \"Path to input image.\")\n    var input: String\n\n    @Option(name: .shortAndLong, help: \"Number of blur hash components.\")\n    var components: Int = 4\n\n    @Option(name: .shortAndLong, help: \"Size of thumbnail used to generate the blur hash.\")\n    var size: Int = 128\n\n    @Option(name: .shortAndLong, help: \"HEIC compression quality of thumbnail used to generate the blur hash.\")\n    var quality: Double = 0.6\n\n    func run() async throws {\n        let inputURL = try URL(fileURLWithPath: (input as NSString).expandingTildeInPath)\n            .ensureExistingFile()\n\n        let blurHashComponents: (Int, Int) = (Int(CGSize.vbBlurHashSize.width), Int(CGSize.vbBlurHashSize.height))\n\n        let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent(\"\\(UUID()).heic\")\n\n        try inputURL.vctool_encodeHEIC(to: tempURL, maxSize: size, quality: quality)\n\n        let image = try NSImage(contentsOf: tempURL)\n            .require(\"Error loading input image.\")\n\n        let blurHash = try image.blurHash(numberOfComponents: blurHashComponents)\n            .require(\"Error generating blur hash.\")\n\n        try? FileManager.default.removeItem(at: tempURL)\n\n        print(blurHash)\n    }\n}\n\nprivate struct BlurHashDecodeCommand: AsyncParsableCommand {\n    static let configuration = CommandConfiguration(\n        commandName: \"decode\",\n        abstract: \"Decodes a blur hash into an image.\"\n    )\n\n    @Argument(help: \"Blur hash string.\")\n    var input: String\n\n    @Option(name: .shortAndLong, help: \"Path to output image file.\", completion: .file())\n    var output: String\n\n    @Option(name: .shortAndLong, help: \"Number of blur hash components.\")\n    var components: Int = 4\n\n    @Option(name: .shortAndLong, help: \"The punch parameter for the blur hash.\")\n    var punch: Float = 1.0\n\n    func run() async throws {\n        let image = try NSImage(blurHash: input, size: CGSize(width: components, height: components), punch: punch)\n            .require(\"Error decoding blur hash into image.\")\n\n        try image.vb_encodeHEIC(to: output.resolvedURL, options: [\n            kCGImageDestinationLossyCompressionQuality: 1,\n            kCGImageDestinationImageMaxPixelSize: components\n        ] as CFDictionary)\n\n        print(\"✅ Blur hash image saved to \\(output.quoted)\\n\")\n    }\n}\n"
  },
  {
    "path": "VirtualBuddy/CommandLine/vctool/CatalogCommand.swift",
    "content": "import Foundation\nimport ArgumentParser\nimport FragmentZip\n\nstruct CatalogCommand: AsyncParsableCommand {\n    static let configuration = CommandConfiguration(\n        commandName: \"catalog\",\n        abstract: \"Manipulates the VirtualBuddy software catalog\",\n        subcommands: [\n            ImageCommand.self,\n            GroupCommand.self,\n            MigrateCommand.self\n        ]\n    )\n}\n"
  },
  {
    "path": "VirtualBuddy/CommandLine/vctool/Core/BuildManifest+Fetch.swift",
    "content": "import Foundation\nimport FragmentZip\n\nextension BuildManifest {\n    init(remoteIPSWURL url: URL, build: String) async throws {\n        let manifestFileName = \"BuildManifest-\\(build).plist\"\n\n        let cachedManifestURL = try URL.vctoolBuildManifestCache.appending(path: manifestFileName)\n\n        let manifestURL: URL\n\n        if cachedManifestURL.exists {\n            fputs(\"Using cached build manifest for \\(build)\\n\", stderr)\n\n            manifestURL = cachedManifestURL\n        } else {\n            fputs(\"Retrieving build manifest for \\(build)\\n\", stderr)\n\n            let ipsw = FragmentZip(url: url)\n            let downloadedManifestURL = try await ipsw.download(filePath: \"BuildManifest.plist\", as: manifestFileName)\n\n            do {\n                try FileManager.default.copyItem(at: downloadedManifestURL, to: cachedManifestURL)\n            } catch {\n                fputs(\"WARN: Failed to cache build manifest: \\(error)\\n\", stderr)\n            }\n\n            manifestURL = downloadedManifestURL\n        }\n\n        try self.init(contentsOf: manifestURL)\n    }\n}\n\nextension URL {\n    static var vctoolBuildManifestCache: URL {\n        get throws {\n            try URL.vctoolCache\n                .appending(path: \"BuildManifests\", directoryHint: .isDirectory)\n                .ensureExistingDirectory(createIfNeeded: true)\n        }\n    }\n}\n"
  },
  {
    "path": "VirtualBuddy/CommandLine/vctool/Core/BuildManifest.swift",
    "content": "import Foundation\nimport BuddyFoundation\n\nstruct BuildManifest: Decodable, TreeStringConvertible {\n    var manifestVersion: Int\n    var productBuildVersion: String\n    var productVersion: SoftwareVersion\n    var supportedProductTypes: [String]\n    var buildIdentities: [BuildIdentity]\n    \n    enum CodingKeys: String, CodingKey {\n        case manifestVersion = \"ManifestVersion\"\n        case productBuildVersion = \"ProductBuildVersion\"\n        case productVersion = \"ProductVersion\"\n        case supportedProductTypes = \"SupportedProductTypes\"\n        case buildIdentities = \"BuildIdentities\"\n    }\n}\n\nstruct BuildIdentity: Decodable, TreeStringConvertible {\n    var productMarketingVersion: SoftwareVersion\n    var boardID: String\n    var chipID: String\n    var uniqueBuildID: Data\n    var info: BuildInfo\n\n    enum CodingKeys: String, CodingKey {\n        case productMarketingVersion = \"ProductMarketingVersion\"\n        case boardID = \"ApBoardID\"\n        case chipID = \"ApChipID\"\n        case uniqueBuildID = \"UniqueBuildID\"\n        case info = \"Info\"\n    }\n}\n\nstruct BuildInfo: Decodable, TreeStringConvertible {\n    var buildNumber: String\n    var buildTrain: String\n    var deviceClass: String\n    var restoreBehavior: String\n    var variant: String\n    var mobileDeviceMinVersion: SoftwareVersion\n    var virtualMachineMinCPUCount: Int?\n    var virtualMachineMinHostOS: SoftwareVersion?\n    var virtualMachineMinMemorySizeMB: Int?\n\n    enum CodingKeys: String, CodingKey {\n        case buildNumber = \"BuildNumber\"\n        case buildTrain = \"BuildTrain\"\n        case deviceClass = \"DeviceClass\"\n        case restoreBehavior = \"RestoreBehavior\"\n        case variant = \"Variant\"\n        case virtualMachineMinCPUCount = \"VirtualMachineMinCPUCount\"\n        case virtualMachineMinHostOS = \"VirtualMachineMinHostOS\"\n        case virtualMachineMinMemorySizeMB = \"VirtualMachineMinMemorySizeMB\"\n        case mobileDeviceMinVersion = \"MobileDeviceMinVersion\"\n    }\n\n}\n\n// MARK: - Parsing\n\nextension BuildManifest {\n    private static let decoder: PropertyListDecoder = {\n        let d = PropertyListDecoder()\n        return d\n    }()\n\n    init(contentsOf url: URL) throws {\n        let data = try Data(contentsOf: url, options: .mappedIfSafe)\n        try self.init(data: data)\n    }\n\n    init(data: Data) throws {\n        self = try Self.decoder.decode(Self.self, from: data)\n    }\n}\n\n// MARK: - Filtering\n\nextension BuildManifest {\n    mutating func filterBuildIdentities(using predicate: (BuildIdentity) -> Bool) {\n        buildIdentities.removeAll(where: { !predicate($0) })\n    }\n\n    func filteringBuildIdentities(using predicate: (BuildIdentity) -> Bool) -> BuildManifest {\n        var mSelf = self\n        mSelf.filterBuildIdentities(using: predicate)\n        return mSelf\n    }\n}\n\nextension BuildInfo {\n    /// `true` if the information indicates that the associated build identity is for a Mac VM.\n    var hasVMInformation: Bool {\n        virtualMachineMinHostOS != nil\n        || virtualMachineMinCPUCount != nil\n        || virtualMachineMinMemorySizeMB != nil\n        || deviceClass.caseInsensitiveCompare(\"vma2macosap\") == .orderedSame\n    }\n}\n\nextension BuildIdentity {\n    /// `true` if the information indicates that this build identity is for a Mac VM.\n    var hasVMInformation: Bool { info.hasVMInformation }\n}\n"
  },
  {
    "path": "VirtualBuddy/CommandLine/vctool/Core/Helpers.swift",
    "content": "import Foundation\nimport ArgumentParser\nimport BuddyFoundation\n\nextension URL {\n    init(validating string: String) throws {\n        guard let url = URL(string: string) else {\n            throw \"Invalid URL: \\\"\\(string)\\\"\"\n        }\n        self = url\n    }\n}\n\nextension ProcessInfo {\n    nonisolated(unsafe) private static var _sessionID: String?\n\n    var sessionID: String {\n        if let id = Self._sessionID { return id }\n\n        let newID = UUID().uuidString\n\n        Self._sessionID = newID\n\n        return newID\n    }\n}\n\nextension URL {\n    var exists: Bool { FileManager.default.fileExists(atPath: path) }\n\n    var isExistingDirectory: Bool {\n        var isDir = ObjCBool(false)\n        guard FileManager.default.fileExists(atPath: path, isDirectory: &isDir) else { return false }\n        return isDir.boolValue\n    }\n\n    func ensureExistingFile() throws -> URL {\n        try requireExistingFile()\n        return self\n    }\n\n    func ensureExistingDirectory(createIfNeeded: Bool = false) throws -> URL {\n        try requireExistingDirectory(createIfNeeded: createIfNeeded)\n        return self\n    }\n\n    func requireExistingFile() throws {\n        guard exists else {\n            throw \"File doesn't exist at \\(path)\"\n        }\n        guard !isExistingDirectory else {\n            throw \"Expected a file, but found a directory at \\(path)\"\n        }\n    }\n\n    func requireExistingDirectory(createIfNeeded: Bool = false) throws {\n        if exists {\n            guard isExistingDirectory else {\n                throw \"Expected a directory, but found a regular file at \\(path)\"\n            }\n        } else {\n            guard createIfNeeded else {\n                throw \"Directory doesn't exist at \\(path)\"\n            }\n\n            try FileManager.default.createDirectory(at: self, withIntermediateDirectories: true)\n        }\n    }\n\n    static var baseTempURL: URL {\n        get throws {\n            let tempBase = FileManager.default.temporaryDirectory\n            let sessionBase = tempBase.appendingPathComponent(ProcessInfo.processInfo.sessionID)\n            if !FileManager.default.fileExists(atPath: sessionBase.path) {\n                try FileManager.default.createDirectory(at: sessionBase, withIntermediateDirectories: true)\n            }\n            return sessionBase\n        }\n    }\n\n    static func tempFileURL(name: String, create: Bool = false) throws -> URL {\n        let fileURL = try baseTempURL.appendingPathComponent(name)\n        if create, !fileURL.exists {\n            FileManager.default.createFile(atPath: fileURL.path, contents: nil)\n        }\n        return fileURL\n    }\n\n    static func tempDirURL(name: String, create: Bool = false) throws -> URL {\n        let dirURL = try baseTempURL.appendingPathComponent(name)\n        if create, !dirURL.exists {\n            try FileManager.default.createDirectory(at: dirURL, withIntermediateDirectories: true)\n        }\n        return dirURL\n    }\n\n    static var vctoolCache: URL {\n        get throws {\n            try FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true)\n                .appending(path: \"vctool\", directoryHint: .isDirectory)\n        }\n    }        \n}\n\nextension String {\n    var resolvedPath: String { (self as NSString).expandingTildeInPath }\n    var resolvedURL: URL { URL(filePath: resolvedPath) }\n}\n\nextension CatalogGuestPlatform: @retroactive EnumerableFlag { }\n\nextension ResolvedFeatureStatus {\n    var cliDescription: String {\n        switch self {\n        case .supported:\n            return \"✅ Supported\"\n        case .warning(_, let message):\n            return \"⚠️ Warning: \\(message)\"\n        case .unsupported(_, let message):\n            return \"🛑 Not Supported: \\(message)\"\n        }\n    }\n}\n\nextension SoftwareVersion: @retroactive ExpressibleByArgument {\n    public init?(argument: String) {\n        self.init(string: argument)\n    }\n}\n\nextension RequirementSet {\n    func matches(info: BuildInfo) -> Bool {\n        guard let manifestMinVersionHost = info.virtualMachineMinHostOS else { return true }\n        guard manifestMinVersionHost == self.minVersionHost else { return false }\n\n        guard let manifestMinMemory = info.virtualMachineMinMemorySizeMB else { return true }\n        guard manifestMinMemory == self.minMemorySizeMB else { return false }\n\n        guard let manifestMinCPU = info.virtualMachineMinCPUCount else { return true }\n        return manifestMinCPU == self.minCPUCount\n    }\n}\n\nextension BuildInfo {\n    var requirementsDescription: String {\n        let minVer = virtualMachineMinHostOS?.description ?? \"?\"\n        let minMem = virtualMachineMinMemorySizeMB.flatMap({ String($0) }) ?? \"?\"\n        let minCPU = virtualMachineMinCPUCount.flatMap({ String($0) }) ?? \"?\"\n        return \"minHostVersion: \\(minVer) | minMemory: \\(minMem) | minCPU: \\(minCPU)\"\n    }\n}\n\nextension RestoreImage: TreeStringConvertible, @retroactive CustomStringConvertible {\n    public var description: String { description(level: 0) }\n}\n"
  },
  {
    "path": "VirtualBuddy/CommandLine/vctool/Core/TreeStringConvertible.swift",
    "content": "import Foundation\n\nprotocol TreeStringConvertible: CustomStringConvertible {\n    func description(level: Int) -> String\n}\n\nextension TreeStringConvertible {\n    func indentation(for level: Int) -> String { String(repeating: \" \", count: level * 2) }\n\n    func description(level: Int) -> String {\n        let prefix = indentation(for: level)\n\n        let mirror = Mirror(reflecting: self)\n\n        var output = [String]()\n\n        for child in mirror.children {\n            let name = \"- \" + (child.label ?? \"???\")\n            let item: String\n\n            if let convertibleChild = child.value as? TreeStringConvertible {\n                let childDescription = convertibleChild.description(level: level + 1)\n\n                if childDescription.contains(\"\\n\") {\n                    output.append(prefix + name)\n                    item = childDescription\n                } else {\n                    item = prefix + \"\\(name) = \\(childDescription)\"\n                }\n            } else {\n                let value = String(describing: child.value)\n                item = prefix + \"\\(name) = \\(value)\"\n            }\n\n            output.append(item)\n        }\n\n        return output.joined(separator: \"\\n\")\n    }\n\n    var description: String { description(level: 0) }\n}\n\nextension Array: TreeStringConvertible {\n    func description(level: Int) -> String {\n        let prefix = indentation(for: level)\n        return enumerated().map {\n            let elementPrefix = prefix + \"- [\\($0.offset)] \"\n\n            if let convertibleElement = $0.element as? TreeStringConvertible {\n                return elementPrefix + \"\\n\" + convertibleElement.description(level: level + 1)\n            } else {\n                return elementPrefix + String(describing: $0.element)\n            }\n        }.joined(separator: \"\\n\")\n    }\n}\n\nextension Optional: @retroactive CustomStringConvertible {}\n\nextension Optional: TreeStringConvertible {\n    func description(level: Int) -> String {\n        switch self {\n        case .none:\n            return \"<nil>\"\n        case .some(let value):\n            if let convertibleValue = value as? TreeStringConvertible {\n                return convertibleValue.description(level: level + 1)\n            } else {\n                return String(describing: value)\n            }\n        }\n    }\n\n    public var description: String { description(level: 0) }\n}\n"
  },
  {
    "path": "VirtualBuddy/CommandLine/vctool/Core/URL+ContentLength.swift",
    "content": "import Foundation\n\nextension URL {\n    func contentLength() async throws -> Int64 {\n        var request = URLRequest(url: self)\n        request.httpMethod = \"HEAD\"\n        request.timeoutInterval = 10\n\n        let (_, response) = try await URLSession.shared.data(for: request)\n\n        guard let httpResponse = response as? HTTPURLResponse else {\n            throw \"Invalid response\"\n        }\n\n        guard httpResponse.statusCode < 300 else {\n            throw \"HTTP \\(httpResponse.statusCode)\"\n        }\n\n        guard let stringValue = httpResponse.value(forHTTPHeaderField: \"content-length\"),\n              let length = Int64(stringValue)\n        else {\n            throw \"Missing or invalid Content-Length header\"\n        }\n\n        return length\n    }\n}\n"
  },
  {
    "path": "VirtualBuddy/CommandLine/vctool/GroupCommand.swift",
    "content": "import Foundation\nimport ArgumentParser\nimport AppKit\nimport BuddyFoundation\nimport VirtualUI\n\nextension CatalogCommand {\n    struct GroupCommand: AsyncParsableCommand {\n        static let configuration = CommandConfiguration(\n            commandName: \"group\",\n            abstract: \"View or modify groups.\",\n            subcommands: [\n                AddCommand.self\n            ]\n        )\n\n        struct AddCommand: AsyncParsableCommand {\n            static let configuration = CommandConfiguration(\n                commandName: \"add\",\n                abstract: \"Adds a new group to the catalog.\"\n            )\n\n            @Option(help: \"Unique identifier for the group (ex: \\\"sequoia\\\").\")\n            var id: String\n\n            @Option(help: \"The major version for releases in the group (ex: 15).\")\n            var version: SoftwareVersion\n\n            @Option(help: \"User-friendly name for the group (ex: \\\"macOS Sequoia\\\").\")\n            var name: String\n\n            @Option(help: \"Path to an image representing the group (usually that release's default wallpaper).\")\n            var image: String\n\n            @Option(name: [.short, .long], help: \"Path to an existing catalog JSON file that will be updated with the new group.\")\n            var output: String\n\n            @Option(help: \"Remote base URL where catalog will be served from.\")\n            var baseURL: String = \"https://api.virtualbuddy.app/v2\"\n\n            func run() async throws {\n                let catalogURL = try output.resolvedURL.ensureExistingFile()\n                var catalog = try SoftwareCatalog(contentsOf: catalogURL)\n\n                guard let remoteBaseURL = URL(string: baseURL) else {\n                    throw \"Invalid base URL: \\\"\\(baseURL)\\\"\"\n                }\n\n                guard !catalog.groups.contains(where: { $0.id.caseInsensitiveCompare(id) == .orderedSame }) else {\n                    throw \"A group already exists with id \\\"\\(id)\\\"\"\n                }\n\n                let imageURL = try image.resolvedURL.ensureExistingFile()\n\n                /// Dark image is expected to be named the same as the image but with the \"-dark\" suffix.\n                let darkImageURL = try imageURL\n                    .deletingLastPathComponent()\n                    .appendingPathComponent(imageURL.deletingPathExtension().lastPathComponent + \"-dark\")\n                    .appendingPathExtension(imageURL.pathExtension)\n                    .ensureExistingFile()\n\n                let localImagesBaseURL = try catalogURL\n                    .deletingLastPathComponent()\n                    .appending(path: \"images\", directoryHint: .isDirectory)\n                    .ensureExistingDirectory(createIfNeeded: true)\n\n                let localImageURL = localImagesBaseURL\n                    .appendingPathComponent(id, conformingTo: .heic)\n                let localThumbnailURL = localImagesBaseURL\n                    .appendingPathComponent(id + \"-thumbnail\", conformingTo: .heic)\n                let localDarkImageURL = localImagesBaseURL\n                    .appendingPathComponent(id + \"-dark\", conformingTo: .heic)\n                let localDarkThumbnailURL = localImagesBaseURL\n                    .appendingPathComponent(id + \"-dark-thumbnail\", conformingTo: .heic)\n\n                let remoteImageURL = remoteBaseURL.appendingPathComponent(\"images/\" + localImageURL.lastPathComponent)\n                let remoteThumbnailImageURL = remoteBaseURL.appendingPathComponent(\"images/\" + localThumbnailURL.lastPathComponent)\n                let remoteDarkImageURL = remoteBaseURL.appendingPathComponent(\"images/\" + localDarkImageURL.lastPathComponent)\n                let remoteDarkThumbnailImageURL = remoteBaseURL.appendingPathComponent(\"images/\" + localDarkThumbnailURL.lastPathComponent)\n\n                try imageURL.vctool_encodeHEIC(to: localImageURL, maxSize: 2048, quality: 0.9)\n                try imageURL.vctool_encodeHEIC(to: localThumbnailURL, maxSize: 720, quality: 0.8)\n                try darkImageURL.vctool_encodeHEIC(to: localDarkImageURL, maxSize: 2048, quality: 0.9)\n                try darkImageURL.vctool_encodeHEIC(to: localDarkThumbnailURL, maxSize: 720, quality: 0.8)\n\n                let blurHashComponents: (Int, Int) = (Int(CGSize.vbBlurHashSize.width), Int(CGSize.vbBlurHashSize.height))\n\n                guard let thumbnailImage = NSImage(contentsOf: localThumbnailURL) else {\n                    throw \"Failed to load generated thumbnail image from \\(localThumbnailURL.path)\"\n                }\n                guard let blurHash = thumbnailImage.blurHash(numberOfComponents: blurHashComponents) else {\n                    throw \"Failed to generate blur hash from generated thumbnail image at \\(localThumbnailURL.path)\"\n                }\n\n                guard let darkThumbnailImage = NSImage(contentsOf: localDarkThumbnailURL) else {\n                    throw \"Failed to load generated thumbnail dark image from \\(localDarkThumbnailURL.path)\"\n                }\n                guard let darkBlurHash = darkThumbnailImage.blurHash(numberOfComponents: blurHashComponents) else {\n                    throw \"Failed to generate blur hash from generated dark thumbnail image at \\(localDarkThumbnailURL.path)\"\n                }\n\n                let image = CatalogGraphic(\n                    id: id,\n                    url: remoteImageURL,\n                    thumbnail: CatalogGraphic.Thumbnail(\n                        url: remoteThumbnailImageURL,\n                        width: Int(thumbnailImage.size.width),\n                        height: Int(thumbnailImage.size.height),\n                        blurHash: blurHash\n                    )\n                )\n\n                let darkImage = CatalogGraphic(\n                    id: id,\n                    url: remoteDarkImageURL,\n                    thumbnail: CatalogGraphic.Thumbnail(\n                        url: remoteDarkThumbnailImageURL,\n                        width: Int(darkThumbnailImage.size.width),\n                        height: Int(darkThumbnailImage.size.height),\n                        blurHash: darkBlurHash\n                    )\n                )\n\n                let group = CatalogGroup(\n                    id: id,\n                    name: name,\n                    majorVersion: version,\n                    image: image,\n                    darkImage: darkImage\n                )\n\n                catalog.groups.insert(group, at: 0)\n\n                try catalog.write(to: catalogURL)\n            }\n        }\n    }\n}\n\n// MARK: - Helpers\n\nextension URL {\n    func vctool_encodeHEIC(to outputURL: URL, maxSize: Int, quality: Double) throws {\n        guard let image = NSImage(contentsOf: self) else {\n            throw \"Image couldn't be loaded from \\(self.path)\"\n        }\n\n        guard let cgImage = image.cgImage(forProposedRect: nil, context: nil, hints: nil) else {\n            throw \"Couldn't get CGImage from input image\"\n        }\n\n        guard let destination = CGImageDestinationCreateWithURL(outputURL as CFURL, \"public.heic\" as CFString, 1, nil) else {\n            throw \"Failed to create image destination\"\n        }\n\n        let imageOptions = [\n            kCGImageDestinationLossyCompressionQuality: quality,\n            kCGImageDestinationImageMaxPixelSize: maxSize\n        ] as CFDictionary\n\n        CGImageDestinationAddImage(destination, cgImage, imageOptions)\n        CGImageDestinationFinalize(destination)\n    }\n}\n"
  },
  {
    "path": "VirtualBuddy/CommandLine/vctool/IPSWCommand.swift",
    "content": "import Foundation\nimport ArgumentParser\nimport FragmentZip\n\nstruct IPSWCommand: AsyncParsableCommand {\n    static let configuration = CommandConfiguration(\n        commandName: \"ipsw\",\n        abstract: \"Tools for exploring IPSW packages.\",\n        subcommands: [\n            InspectCommand.self,\n            ManifestCommand.self\n        ]\n    )\n\n    struct ManifestOptions: ParsableArguments {\n        @Flag(help: \"List only the build identities containing virtual machine information.\")\n        var vm = false\n    }\n\n    // MARK: - Inspect Command\n\n    struct InspectCommand: AsyncParsableCommand {\n        static let configuration = CommandConfiguration(\n            commandName: \"inspect\",\n            abstract: \"Inspects an IPSW file, retrieving information that's relevant to VirtualBuddy.\"\n        )\n\n        @Argument(help: \"URL to the IPSW file.\")\n        var ipsw: String\n\n        @OptionGroup\n        var options: ManifestOptions\n\n        func run() async throws {\n            let url = try URL(validating: ipsw)\n\n            let zip = FragmentZip(url: url)\n\n            let fileURL = try await zip.download(filePath: \"BuildManifest.plist\")\n\n            let manifestCommand = ManifestCommand(manifestPath: fileURL.path, options: options)\n\n            try await manifestCommand.run()\n        }\n    }\n\n    // MARK: - Manifest Command\n\n    struct ManifestCommand: AsyncParsableCommand {\n        static let configuration = CommandConfiguration(\n            commandName: \"manifest\",\n            abstract: \"Parses properties from a local BuildManifest.plist file.\"\n        )\n\n        @Argument(help: \"Path to a BuildManifest.plist file.\")\n        var manifestPath: String\n\n        @OptionGroup\n        var options: ManifestOptions\n\n        init() { }\n\n        // This initializer is needed because this command is run by InspectCommand.\n        init(manifestPath: String, options: ManifestOptions) {\n            self.manifestPath = manifestPath\n            self.options = options\n        }\n\n        func run() async throws {\n            let url = try manifestPath.resolvedURL.ensureExistingFile()\n\n            var manifest = try BuildManifest(contentsOf: url)\n\n            manifest.filterBuildIdentities { identity in\n                options.vm ? identity.hasVMInformation : true\n            }\n\n            print(manifest)\n        }\n    }\n\n}\n"
  },
  {
    "path": "VirtualBuddy/CommandLine/vctool/ImageCommand.swift",
    "content": "import Foundation\nimport ArgumentParser\nimport FragmentZip\nimport BuddyFoundation\nimport VirtualCore\n\nextension CatalogCommand {\n    struct ImageCommand: AsyncParsableCommand {\n        static let configuration = CommandConfiguration(\n            commandName: \"image\",\n            abstract: \"Manipulates restore images in the VirtualBuddy catalog.\",\n            subcommands: [\n                AddCommand.self\n            ]\n        )\n\n        // MARK: - Add Command\n\n        struct AddCommand: AsyncParsableCommand {\n            static let configuration = CommandConfiguration(\n                commandName: \"add\",\n                abstract: \"Adds a new macOS release to a VirtualBuddy software catalog based on an IPSW URL.\"\n            )\n\n            @Option(name: [.short, .long], help: \"URL to the IPSW file.\")\n            var ipsw: String\n\n            @Option(name: [.short, .long], help: \"ID of the release channel (devbeta or regular).\")\n            var channel: String\n\n            @Option(name: [.short, .long], help: \"User-facing name for the release (ex: \\\"macOS 15.0 Developer Beta 4\\\").\")\n            var name: String\n\n            @Option(name: [.short, .long], help: \"Path to the software catalog JSON file that will be updated.\")\n            var output: String\n\n            @Flag(name: .shortAndLong, help: \"Replace existing build if it already exists in the catalog.\")\n            var force = false\n\n            func run() async throws {\n                let ipswURL = try URL(validating: ipsw)\n                let catalogURL = try output.resolvedURL.ensureExistingFile()\n\n                fputs(\"Detecting download size...\\n\", stderr)\n\n                let contentLength = try await ipswURL.contentLength()\n\n                var catalog = try SoftwareCatalog(contentsOf: catalogURL)\n\n                fputs(\"Reading build manifest from remote IPSW...\\n\", stderr)\n\n                let zip = FragmentZip(url: ipswURL)\n\n                let fileURL = try await zip.download(filePath: \"BuildManifest.plist\")\n\n                let manifest = try BuildManifest(contentsOf: fileURL)\n\n                guard let identity = manifest.buildIdentities.first(where: { $0.hasVMInformation }) else {\n                    throw \"Couldn't find a build identity with VM information in the specified IPSW. Are you sure this IPSW supports macOS VMs?\"\n                }\n\n                let majorVersion = SoftwareVersion(majorVersionFrom: manifest.productVersion)\n\n                guard let group = catalog.groups.first(where: { $0.majorVersion == majorVersion }) else {\n                    throw \"Couldn't find a group with majorVersion = \\(majorVersion). If this is a new major version of macOS, a new group must be added to the catalog manually before running this command.\"\n                }\n\n                guard catalog.channels.contains(where: { $0.id == channel }) else {\n                    throw \"Couldn't find \\\"\\(channel)\\\" channel in the catalog. Catalog channels: \\(catalog.channels.map(\\.id).joined(separator: \", \"))\"\n                }\n\n                fputs(\"Found group \\(group.name)\\n\", stderr)\n\n                let requirementSet: RequirementSet\n\n                if let existingSet = catalog.requirementSets.first(where: { $0.matches(info: identity.info) }) {\n                    fputs(\"Found existing requirement set \\(existingSet.id)\\n\", stderr)\n\n                    requirementSet = existingSet\n                } else {\n                    fputs(\"Found no existing requirement set matching properties from manifest, will create a new one: \\(identity.info.requirementsDescription)\\n\", stderr)\n\n                    requirementSet = RequirementSet(\n                        id: UUID().uuidString,\n                        minCPUCount: identity.info.virtualMachineMinCPUCount ?? 2,\n                        minMemorySizeMB: identity.info.virtualMachineMinMemorySizeMB ?? 4096,\n                        minVersionHost: identity.info.virtualMachineMinHostOS ?? SoftwareVersion(major: 12, minor: 0, patch: 0)\n                    )\n\n                    catalog.requirementSets.insert(requirementSet, at: 0)\n                }\n\n                let image = RestoreImage(\n                    id: manifest.productBuildVersion,\n                    group: group.id,\n                    channel: channel,\n                    requirements: requirementSet.id,\n                    name: name,\n                    build: manifest.productBuildVersion,\n                    version: manifest.productVersion,\n                    mobileDeviceMinVersion: identity.info.mobileDeviceMinVersion,\n                    url: ipswURL,\n                    downloadSize: UInt64(contentLength)\n                )\n\n                let index = catalog.index(forInserting: image)\n                let isUpdate = catalog.restoreImages[index].id == image.id\n\n                /// Replacing an existing image requires a flag.\n                if isUpdate {\n                    guard force else {\n                        fputs(\"\\n❌ Build \\(image.id) already exists in the catalog. Use --force flag to update it.\\n\\n\", stderr)\n                        Darwin.exit(1)\n                    }\n\n                    catalog.restoreImages.remove(at: index)\n                }\n\n                catalog.restoreImages.insert(image, at: index)\n\n                let successMessage = isUpdate ? \"Updated image in catalog\" : \"Added image to catalog\"\n                fputs(\"✅ \\(successMessage):\\n\\n\", stderr)\n\n                fputs(\"\\(image)\\n\\n\", stderr)\n\n                try catalog.write(to: catalogURL)\n\n                fputs(\"✅ Done!\\n\\n\", stderr)\n            }\n        }\n    }\n}\n\nprivate extension SoftwareCatalog {\n    func index(forInserting image: RestoreImage) -> Int {\n        if let existingIndex = restoreImages.firstIndex(where: { $0.id == image.id }) {\n            existingIndex /// Replace image at its current index (client must delete existing one before replacing)\n        } else if let placementIndex = restoreImages.firstIndex(where: { $0.group == image.group && $0.version <= image.version }) {\n            placementIndex /// Place image before the first image of the same release group and OS version\n        } else {\n            0 /// Place image in first slot\n        }\n    }\n}\n"
  },
  {
    "path": "VirtualBuddy/CommandLine/vctool/MigrateCommand.swift",
    "content": "import Foundation\nimport ArgumentParser\nimport FragmentZip\nimport BuddyFoundation\n\nextension CatalogCommand {\n    struct MigrateCommand: AsyncParsableCommand {\n        static let configuration = CommandConfiguration(\n            commandName: \"migrate\",\n            abstract: \"Migrates a version 1 restore image catalog to version 2.\",\n            discussion: \"This command will fetch metadata for each IPSW in the legacy catalog, so it requires an internet connection and may take a while to run.\"\n        )\n\n        @Option(name: [.short, .long], help: \"Path to version 1 catalog.\")\n        var inputPath: String?\n\n        @Flag(help: \"Migrate from live API catalog instead of a local file.\")\n        var live = false\n\n        @Option(name: [.short, .long], help: \"Path where to save the migrated version 2 catalog. If a catalog already exists at this path, all restore images will be removed from it and replaced by the migrated ones from the version 1 catalog, but groups and channels will be retained.\")\n        var outputPath: String\n\n        private func fetchLegacyCatalog() async throws -> LegacyCatalog {\n            if let inputPath {\n                return try LegacyCatalog(contentsOf: inputPath.resolvedURL.ensureExistingFile())\n            } else {\n                fputs(\"Downloading live v1 catalog...\\n\", stderr)\n\n                let (data, _) = try await URLSession.shared.data(from: URL(string: \"https://api.virtualbuddy.app/restore/mac?apiKey=15A25D48-4A34-4EE4-A293-C22B0DE1B54E\")!)\n\n                return try LegacyCatalog(data: data)\n            }\n        }\n\n        func run() async throws {\n            /// Any channels not listed here will be excluded from the output catalog.\n            let channelIdentifiers: Set<String> = [\"devbeta\", \"regular\"]\n\n            let outputURL = outputPath.resolvedURL\n\n            let legacyCatalog = try await fetchLegacyCatalog()\n\n            var catalog: SoftwareCatalog\n\n            /// If there's an existing version 2 catalog at the output path, then use that catalog as a template, migrating only the restore images from the v1 catalog.\n            if outputURL.exists {\n                fputs(\"Using existing version 2 catalog for migration\\n\", stderr)\n\n                catalog = try SoftwareCatalog(contentsOf: outputURL)\n\n                catalog.restoreImages.removeAll()\n            } else {\n                fputs(\"Creating empty version 2 catalog for migration\\n\", stderr)\n\n                catalog = SoftwareCatalog(apiVersion: 2, minAppVersion: .init(string: \"2.0.0\")!, channels: [], groups: [], restoreImages: [], features: [], requirementSets: [], deviceSupportVersions: [])\n            }\n\n            for legacyChannel in legacyCatalog.channels {\n                guard channelIdentifiers.contains(legacyChannel.id), !catalog.channels.contains(where: { $0.id == legacyChannel.id }) else { continue }\n\n                let channel = CatalogChannel(id: legacyChannel.id, name: legacyChannel.name, note: legacyChannel.note, icon: legacyChannel.icon)\n\n                catalog.channels.append(channel)\n            }\n\n            if catalog.groups.isEmpty {\n                for legacyGroup in legacyCatalog.groups {\n                    let group = CatalogGroup(id: legacyGroup.id, name: legacyGroup.name, majorVersion: legacyGroup.majorVersion, image: .placeholder, darkImage: .placeholder)\n\n                    catalog.groups.append(group)\n                }\n            }\n\n            let requirement_min_host_13 = catalog.requirementSets.first(where: { $0.id == \"min_host_13\" }) ?? RequirementSet(\n                id: \"min_host_13\",\n                minCPUCount: 2,\n                minMemorySizeMB: 4096,\n                minVersionHost: SoftwareVersion(string: \"13.0\")!\n            )\n            let requirement_min_host_12 = catalog.requirementSets.first(where: { $0.id == \"min_host_12\" }) ?? RequirementSet(\n                id: \"min_host_12\",\n                minCPUCount: 2,\n                minMemorySizeMB: 4096,\n                minVersionHost: SoftwareVersion(string: \"12.0\")!\n            )\n\n            if !catalog.requirementSets.contains(where: { $0.id == requirement_min_host_13.id }) {\n                catalog.requirementSets.append(requirement_min_host_13)\n            }\n            if !catalog.requirementSets.contains(where: { $0.id == requirement_min_host_12.id }) {\n                catalog.requirementSets.append(requirement_min_host_12)\n            }\n\n            for legacyImage in legacyCatalog.images {\n                do {\n                    let contentLength = try await legacyImage.url.contentLength()\n\n                    let manifest = try await BuildManifest(remoteIPSWURL: legacyImage.url, build: legacyImage.build)\n\n                    /// Version 13.3 started requiring macOS 13 host, all versions higher than that require macOS 13 host, all versions below that support macOS 12 host.\n                    let requirements: RequirementSet = manifest.productVersion >= SoftwareVersion(string: \"13.3\")! ? requirement_min_host_13 : requirement_min_host_12\n\n                    guard let vmIdentity = manifest.buildIdentities.first(where: { $0.hasVMInformation }) else {\n                        throw \"Couldn't find a build identity with VM properties\"\n                    }\n\n                    let image = RestoreImage(\n                        id: legacyImage.id,\n                        group: legacyImage.group.id,\n                        channel: legacyImage.channel.id,\n                        requirements: requirements.id,\n                        name: legacyImage.name,\n                        build: legacyImage.build,\n                        version: manifest.productVersion,\n                        mobileDeviceMinVersion: vmIdentity.info.mobileDeviceMinVersion,\n                        url: legacyImage.url,\n                        downloadSize: UInt64(contentLength)\n                    )\n\n                    catalog.restoreImages.append(image)\n\n                    try catalog.write(to: outputURL)\n                } catch {\n                    fputs(\"Error processing restore image \\(legacyImage.id): \\(error)\\n\", stderr)\n                }\n            }\n\n            print(\"Migrated catalog written to \\(outputURL.path)\")\n            print(\"\")\n        }\n    }\n}\n"
  },
  {
    "path": "VirtualBuddy/CommandLine/vctool/MobileDeviceCommand.swift",
    "content": "import Foundation\nimport ArgumentParser\n\nstruct MobileDeviceCommand: AsyncParsableCommand {\n    static let configuration = CommandConfiguration(\n        commandName: \"mobiledevice\",\n        abstract: \"Interacts with the MobileDevice framework on the host.\",\n        subcommands: [\n            VersionCommand.self\n        ]\n    )\n\n    struct VersionCommand: AsyncParsableCommand {\n        static let configuration = CommandConfiguration(\n            commandName: \"version\",\n            abstract: \"Retrieves the version of the MobileDevice framework that's currently installed.\"\n        )\n\n        func run() async throws {\n            guard let framework = MobileDeviceFramework.current else {\n                throw \"Couldn't find MobileDevice.framework\"\n            }\n\n            print(framework.version)\n        }\n    }\n}\n"
  },
  {
    "path": "VirtualBuddy/CommandLine/vctool/ResolveCommand.swift",
    "content": "import Foundation\nimport ArgumentParser\nimport BuddyFoundation\n\nextension CatalogCommand {\n\n    struct ResolveCommand: AsyncParsableCommand {\n        static let configuration = CommandConfiguration(\n            commandName: \"resolve\",\n            abstract: \"Resolves a VirtualBuddy software catalog for a given environment.\"\n        )\n\n        @Option(name: [.short, .long], help: \"Path to the catalog JSON file.\")\n        var input: String\n\n        @Option(help: \"Custom host version.\")\n        var host: SoftwareVersion?\n\n        @Option(help: \"Custom MobileDevice version.\")\n        var mobileDevice: SoftwareVersion?\n\n        @Flag(help: \"Specify guest platform.\")\n        var guestPlatform: CatalogGuestPlatform = .mac\n\n        @Option(help: \"Show results for a specific build.\")\n        var build: String?\n\n        func run() async throws {\n            let url = try input.resolvedURL.ensureExistingFile()\n\n            let catalog = try SoftwareCatalog(contentsOf: url)\n\n            var env = CatalogResolutionEnvironment.current\n            if let host {\n                env.hostVersion = host\n            }\n            if let mobileDevice {\n                env.mobileDeviceVersion = mobileDevice\n            }\n            env.guestPlatform = guestPlatform\n\n            let resolved = ResolvedCatalog(environment: env, catalog: catalog)\n\n            if let build {\n                guard let targetImage = resolved.groups.flatMap(\\.restoreImages).first(where: { $0.image.build == build }) else {\n                    throw \"Build not found: \\(build)\"\n                }\n                printResult(for: targetImage)\n            } else {\n                for group in resolved.groups {\n                    print(\"## \\(group.name)\")\n                    print()\n\n                    for resolvedImage in group.restoreImages {\n                        printResult(for: resolvedImage)\n                    }\n                }\n            }\n        }\n\n        func printResult(for resolvedImage: ResolvedRestoreImage) {\n            let image = resolvedImage.image\n\n            print(\"### \\(image.name) (\\(image.build))\")\n            print(\"  - Guest: \\(resolvedImage.status.cliDescription)\")\n            print(\"  - Host: \\(resolvedImage.requirements.status.cliDescription)\")\n            print(\"  - Features:\")\n            for feature in resolvedImage.features {\n                print(\"    - \\(feature.feature.name)\")\n                print(\"      - \\(feature.status.cliDescription)\")\n            }\n            print()\n        }\n    }\n\n}\n"
  },
  {
    "path": "VirtualBuddy/CommandLine/vctool/VCTool.swift",
    "content": "import Foundation\nimport ArgumentParser\nimport AppKit\n\nstruct VCTool: AsyncParsableCommand {\n    static let configuration = CommandConfiguration(\n        commandName: \"vctool\",\n        abstract: \"Tools for updating the VirtualBuddy software catalog.\",\n        subcommands: [\n            CatalogCommand.self,\n            IPSWCommand.self,\n            MobileDeviceCommand.self,\n            BlurHashCommand.self,\n        ]\n    )\n}\n"
  },
  {
    "path": "VirtualBuddy/Config/AppTarget.xcconfig",
    "content": "#include \"Paths.xcconfig\"\n\n// Settings in this file only apply to the main VirtualBuddy.app target\n\n// Entitlement Settings\n\n// Name of the provisioning profile used for all managed builds (debug, beta, release, dev release).\nMANAGED_PROFILE = VirtualBuddy Dev Mid 2025\n\nCODE_SIGN_ENTITLEMENTS[config=Debug][sdk=*][arch=*] = $(ENTITLEMENTS_DIR)/VirtualBuddy.entitlements\nCODE_SIGN_ENTITLEMENTS[config=Release][sdk=*][arch=*] = $(ENTITLEMENTS_DIR)/VirtualBuddy.entitlements\nCODE_SIGN_ENTITLEMENTS[config=Debug_Managed][sdk=*][arch=*] = $(ENTITLEMENTS_DIR)/VirtualBuddy_Managed.entitlements\nCODE_SIGN_ENTITLEMENTS[config=Release_Managed][sdk=*][arch=*] = $(ENTITLEMENTS_DIR)/VirtualBuddy_Managed.entitlements\nCODE_SIGN_ENTITLEMENTS[config=Dev_Release][sdk=*][arch=*] = $(ENTITLEMENTS_DIR)/VirtualBuddy_Managed.entitlements\nCODE_SIGN_ENTITLEMENTS[config=Beta_Debug][sdk=*][arch=*] = $(ENTITLEMENTS_DIR)/VirtualBuddy_Managed.entitlements\nCODE_SIGN_ENTITLEMENTS[config=Beta_Release][sdk=*][arch=*] = $(ENTITLEMENTS_DIR)/VirtualBuddy_Managed.entitlements\n\nPROVISIONING_PROFILE_SPECIFIER[config=Debug][sdk=*][arch=*] =\nPROVISIONING_PROFILE_SPECIFIER[config=Release][sdk=*][arch=*] =\nPROVISIONING_PROFILE_SPECIFIER[config=Debug_Managed][sdk=*][arch=*] = $(MANAGED_PROFILE)\nPROVISIONING_PROFILE_SPECIFIER[config=Release_Managed][sdk=*][arch=*] = $(MANAGED_PROFILE)\nPROVISIONING_PROFILE_SPECIFIER[config=Dev_Release][sdk=*][arch=*] = $(MANAGED_PROFILE)\nPROVISIONING_PROFILE_SPECIFIER[config=Beta_Debug][sdk=*][arch=*] = $(MANAGED_PROFILE)\nPROVISIONING_PROFILE_SPECIFIER[config=Beta_Release][sdk=*][arch=*] = $(MANAGED_PROFILE)\n\nCODE_SIGN_STYLE[config=Debug][sdk=*][arch=*] = Automatic\nCODE_SIGN_STYLE[config=Release][sdk=*][arch=*] = Automatic\nCODE_SIGN_STYLE[config=Debug_Managed][sdk=*][arch=*] = Manual\nCODE_SIGN_STYLE[config=Release_Managed][sdk=*][arch=*] = Manual\nCODE_SIGN_STYLE[config=Dev_Release][sdk=*][arch=*] = Manual\nCODE_SIGN_STYLE[config=Beta_Debug][sdk=*][arch=*] = Manual\nCODE_SIGN_STYLE[config=Beta_Release][sdk=*][arch=*] = Manual\n\nOTHER_SWIFT_FLAGS[config=Release][sdk=*][arch=*] = -D BUILDING_NON_MANAGED_RELEASE\nOTHER_SWIFT_FLAGS[config=Dev_Release][sdk=*][arch=*] = -D BUILDING_DEV_RELEASE\n\n// Release Train Settings\n\n// Special app icon for development releases. Note that Xcode uses the first icon when sorted alphabetically, hence why the default icon has the -Default suffix.\nASSETCATALOG_COMPILER_APPICON_NAME = AppIcon-Default\nASSETCATALOG_COMPILER_APPICON_NAME[config=Dev_Release][sdk=*][arch=*] = AppIcon-Dev\nASSETCATALOG_COMPILER_APPICON_NAME[config=Beta_Debug][sdk=*][arch=*] = AppIcon-zBeta\nASSETCATALOG_COMPILER_APPICON_NAME[config=Beta_Release][sdk=*][arch=*] = AppIcon-zBeta\n\nPRODUCT_NAME = $(TARGET_NAME)\n\n// Development releases named VirtualBuddy-Dev.app\nPRODUCT_NAME[config=Dev_Release][sdk=*][arch=*] = $(TARGET_NAME)-Dev\n\n"
  },
  {
    "path": "VirtualBuddy/Config/Entitlements/VirtualBuddy.entitlements",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>com.apple.security.device.audio-input</key>\n\t<true/>\n\t<key>com.apple.security.virtualization</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "VirtualBuddy/Config/Entitlements/VirtualBuddy_Managed.entitlements",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>com.apple.security.device.audio-input</key>\n\t<true/>\n\t<key>com.apple.security.virtualization</key>\n\t<true/>\n\t<key>com.apple.vm.networking</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "VirtualBuddy/Config/Features.xcconfig",
    "content": "// ENABLE_SPARKLE = Enables building with Sparkle for automatic updates\n// ENABLE_USERDEFAULTS_SYNC = Enables the user defaults sync feature\nOTHER_SWIFT_FLAGS = -D ENABLE_SPARKLE $(inherited)\n\n// The BETA flag must be present in all targets, hence why these are here instead of in AppTarget.xcconfig\nOTHER_SWIFT_FLAGS[config=Beta_Debug][sdk=*][arch=*] = -D BETA $(inherited)\nOTHER_SWIFT_FLAGS[config=Beta_Release][sdk=*][arch=*] = -D BETA $(inherited)\n\nVB_SPARKLE_PUBLIC_ED_KEY=dj8ljUPnwoLj/dLs6HyJg5Ayw+t8zWtgjQUfQsH56ww=\nVB_SPARKLE_CHECK_INTERVAL=86400\n"
  },
  {
    "path": "VirtualBuddy/Config/InfoPlist.xcconfig",
    "content": "INFOPLIST_KEY_NSHumanReadableCopyright = © 2024 Buddy Software LTD\nINFOPLIST_KEY_NSMicrophoneUsageDescription = Enable audio input for your virtual machines. Without this permission, virtual machines won't be able to record any audio.\n"
  },
  {
    "path": "VirtualBuddy/Config/Main.xcconfig",
    "content": "#include \"Paths.xcconfig\"\n#include \"Versions.xcconfig\"\n#include \"Signing.xcconfig\"\n#include \"Features.xcconfig\"\n#include \"InfoPlist.xcconfig\"\n\nARCHS=arm64\n"
  },
  {
    "path": "VirtualBuddy/Config/Paths.xcconfig",
    "content": "ENTITLEMENTS_DIR=VirtualBuddy/Config/Entitlements\n\nVB_APP_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/../Frameworks\nVB_FRAMEWORK_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/../Frameworks @loader_path/Frameworks\nVB_CLI_RUNPATH_SEARCH_PATHS = $(inherited) @executable_path/../Frameworks @executable_path/Frameworks @executable_path\n"
  },
  {
    "path": "VirtualBuddy/Config/Signing.xcconfig",
    "content": "CODE_SIGN_IDENTITY = Apple Development\nVB_BUNDLE_ID_PREFIX =\n\nGUEST_LAUNCH_AT_LOGIN_HELPER_BUNDLE_ID = $(VB_BUNDLE_ID_PREFIX)codes.rambo.VirtualBuddyGuestHelper\nGUEST_LAUNCH_AT_LOGIN_HELPER_BUNDLE_ID_STR=@\"$(GUEST_LAUNCH_AT_LOGIN_HELPER_BUNDLE_ID)\"\n\nGCC_PREPROCESSOR_DEFINITIONS=$(inherited) GUEST_LAUNCH_AT_LOGIN_HELPER_BUNDLE_ID='$(GUEST_LAUNCH_AT_LOGIN_HELPER_BUNDLE_ID_STR)'\n"
  },
  {
    "path": "VirtualBuddy/Config/Versions.xcconfig",
    "content": "MARKETING_VERSION = 2.1\nCURRENT_PROJECT_VERSION = 284\nDYLIB_CURRENT_VERSION = $(CURRENT_PROJECT_VERSION)\nVERSIONING_SYSTEM = apple-generic\nMACOSX_DEPLOYMENT_TARGET = 13.0\n"
  },
  {
    "path": "VirtualBuddy/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDocumentTypes</key>\n\t<array>\n\t\t<dict>\n\t\t\t<key>CFBundleTypeExtensions</key>\n\t\t\t<array>\n\t\t\t\t<string>vbvm</string>\n\t\t\t</array>\n\t\t\t<key>CFBundleTypeName</key>\n\t\t\t<string>VirtualBuddy VM</string>\n\t\t\t<key>CFBundleTypeRole</key>\n\t\t\t<string>Editor</string>\n\t\t\t<key>LSHandlerRank</key>\n\t\t\t<string>Owner</string>\n\t\t\t<key>LSItemContentTypes</key>\n\t\t\t<array>\n\t\t\t\t<string>codes.rambo.VirtualBuddy.VM</string>\n\t\t\t</array>\n\t\t\t<key>LSTypeIsPackage</key>\n\t\t\t<true/>\n\t\t</dict>\n\t\t<dict>\n\t\t\t<key>CFBundleTypeExtensions</key>\n\t\t\t<array>\n\t\t\t\t<string>vbst</string>\n\t\t\t</array>\n\t\t\t<key>CFBundleTypeName</key>\n\t\t\t<string>VirtualBuddy Saved State</string>\n\t\t\t<key>CFBundleTypeRole</key>\n\t\t\t<string>Editor</string>\n\t\t\t<key>LSHandlerRank</key>\n\t\t\t<string>Owner</string>\n\t\t\t<key>LSItemContentTypes</key>\n\t\t\t<array>\n\t\t\t\t<string>codes.rambo.VirtualBuddy.SavedState</string>\n\t\t\t</array>\n\t\t\t<key>LSTypeIsPackage</key>\n\t\t\t<true/>\n\t\t</dict>\n\t\t<dict>\n\t\t\t<key>CFBundleTypeName</key>\n\t\t\t<string>UTM virtual machine</string>\n\t\t\t<key>CFBundleTypeRole</key>\n\t\t\t<string>Viewer</string>\n\t\t\t<key>LSHandlerRank</key>\n\t\t\t<string>Alternate</string>\n\t\t\t<key>LSItemContentTypes</key>\n\t\t\t<array>\n\t\t\t\t<string>com.utmapp.utm</string>\n\t\t\t</array>\n\t\t\t<key>LSTypeIsPackageLSTypeIsPackage</key>\n\t\t\t<true/>\n\t\t</dict>\n\t</array>\n\t<key>CFBundleURLTypes</key>\n\t<array>\n\t\t<dict>\n\t\t\t<key>CFBundleTypeRole</key>\n\t\t\t<string>Editor</string>\n\t\t\t<key>CFBundleURLName</key>\n\t\t\t<string>codes.rambo.VirtualBuddy.Action</string>\n\t\t\t<key>CFBundleURLSchemes</key>\n\t\t\t<array>\n\t\t\t\t<string>virtualbuddy</string>\n\t\t\t</array>\n\t\t</dict>\n\t</array>\n\t<key>NSAppTransportSecurity</key>\n\t<dict>\n\t\t<key>NSAllowsArbitraryLoads</key>\n\t\t<true/>\n\t</dict>\n\t<key>SUPublicEDKey</key>\n\t<string>$(VB_SPARKLE_PUBLIC_ED_KEY)</string>\n\t<key>SUScheduledCheckInterval</key>\n\t<string>$(VB_SPARKLE_CHECK_INTERVAL)</string>\n\t<key>UTExportedTypeDeclarations</key>\n\t<array>\n\t\t<dict>\n\t\t\t<key>UTTypeConformsTo</key>\n\t\t\t<array>\n\t\t\t\t<string>public.composite-content</string>\n\t\t\t\t<string>com.apple.package</string>\n\t\t\t</array>\n\t\t\t<key>UTTypeDescription</key>\n\t\t\t<string>VirtualBuddy VM</string>\n\t\t\t<key>UTTypeIcons</key>\n\t\t\t<dict/>\n\t\t\t<key>UTTypeIdentifier</key>\n\t\t\t<string>codes.rambo.VirtualBuddy.VM</string>\n\t\t\t<key>UTTypeTagSpecification</key>\n\t\t\t<dict>\n\t\t\t\t<key>public.filename-extension</key>\n\t\t\t\t<array>\n\t\t\t\t\t<string>vbvm</string>\n\t\t\t\t</array>\n\t\t\t</dict>\n\t\t</dict>\n\t\t<dict>\n\t\t\t<key>UTTypeConformsTo</key>\n\t\t\t<array>\n\t\t\t\t<string>public.composite-content</string>\n\t\t\t\t<string>com.apple.package</string>\n\t\t\t</array>\n\t\t\t<key>UTTypeDescription</key>\n\t\t\t<string>VirtualBuddy Saved State</string>\n\t\t\t<key>UTTypeIcons</key>\n\t\t\t<dict/>\n\t\t\t<key>UTTypeIdentifier</key>\n\t\t\t<string>codes.rambo.VirtualBuddy.SavedState</string>\n\t\t\t<key>UTTypeTagSpecification</key>\n\t\t\t<dict>\n\t\t\t\t<key>public.filename-extension</key>\n\t\t\t\t<array>\n\t\t\t\t\t<string>vbst</string>\n\t\t\t\t</array>\n\t\t\t</dict>\n\t\t</dict>\n\t</array>\n\t<key>UTImportedTypeDeclarations</key>\n\t<array>\n\t\t<dict>\n\t\t\t<key>UTTypeConformsTo</key>\n\t\t\t<array>\n\t\t\t\t<string>com.apple.package</string>\n\t\t\t</array>\n\t\t\t<key>UTTypeDescription</key>\n\t\t\t<string>UTM virtual machine</string>\n\t\t\t<key>UTTypeIcons</key>\n\t\t\t<dict/>\n\t\t\t<key>UTTypeIdentifier</key>\n\t\t\t<string>com.utmapp.utm</string>\n\t\t\t<key>UTTypeTagSpecification</key>\n\t\t\t<dict>\n\t\t\t\t<key>public.filename-extension</key>\n\t\t\t\t<array>\n\t\t\t\t\t<string>utm</string>\n\t\t\t\t</array>\n\t\t\t</dict>\n\t\t</dict>\n\t</array>\n</dict>\n</plist>\n"
  },
  {
    "path": "VirtualBuddy/Preview Content/Preview Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "VirtualBuddy.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 55;\n\tobjects = {\n\n/* Begin PBXAggregateTarget section */\n\t\tF453C44F2DF0BCE3007EAD5F /* vctool */ = {\n\t\t\tisa = PBXAggregateTarget;\n\t\t\tbuildConfigurationList = F453C4502DF0BCE3007EAD5F /* Build configuration list for PBXAggregateTarget \"vctool\" */;\n\t\t\tbuildPhases = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\tF453C4592DF0BCEB007EAD5F /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = vctool;\n\t\t\tpackageProductDependencies = (\n\t\t\t);\n\t\t\tproductName = vctool;\n\t\t};\n/* End PBXAggregateTarget section */\n\n/* Begin PBXBuildFile section */\n\t\t0196B45329292B2A00614EF1 /* LinuxVirtualMachineConfigurationHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0196B45229292B2A00614EF1 /* LinuxVirtualMachineConfigurationHelper.swift */; };\n\t\t4BA6BE7D293D22E500F396AE /* VirtualMachineConfigurationHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BA6BE7C293D22E500F396AE /* VirtualMachineConfigurationHelper.swift */; };\n\t\tF40A1E9D2C1873CA0033E47D /* VBBuildType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F40A1E9C2C1873C60033E47D /* VBBuildType.swift */; };\n\t\tF413696229916F6E002CE8D3 /* StatusItemButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = F413695129916F6E002CE8D3 /* StatusItemButton.swift */; };\n\t\tF413696329916F6E002CE8D3 /* StatusBarPanelChrome.swift in Sources */ = {isa = PBXBuildFile; fileRef = F413695229916F6E002CE8D3 /* StatusBarPanelChrome.swift */; };\n\t\tF413696429916F6E002CE8D3 /* StatusItemProviderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F413695329916F6E002CE8D3 /* StatusItemProviderProtocol.swift */; };\n\t\tF413696529916F6E002CE8D3 /* StatusItemPanelContentController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F413695529916F6E002CE8D3 /* StatusItemPanelContentController.swift */; };\n\t\tF413696729916F6E002CE8D3 /* VUIAppKitViewControllerHost.swift in Sources */ = {isa = PBXBuildFile; fileRef = F413695729916F6E002CE8D3 /* VUIAppKitViewControllerHost.swift */; };\n\t\tF413696829916F6E002CE8D3 /* StatusItemMenuBarExtraView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F413695829916F6E002CE8D3 /* StatusItemMenuBarExtraView.swift */; };\n\t\tF413696929916F6E002CE8D3 /* StatusBarContentPanel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F413695929916F6E002CE8D3 /* StatusBarContentPanel.swift */; };\n\t\tF413696A29916F6E002CE8D3 /* StatusBarHighlightView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F413695A29916F6E002CE8D3 /* StatusBarHighlightView.swift */; };\n\t\tF413696B29916F6E002CE8D3 /* NSStatusItem+.h in Headers */ = {isa = PBXBuildFile; fileRef = F413695C29916F6E002CE8D3 /* NSStatusItem+.h */; settings = {ATTRIBUTES = (Public, ); }; };\n\t\tF413696C29916F6E002CE8D3 /* NSApplication+MenuBar.h in Headers */ = {isa = PBXBuildFile; fileRef = F413695D29916F6E002CE8D3 /* NSApplication+MenuBar.h */; settings = {ATTRIBUTES = (Public, ); }; };\n\t\tF413696D29916F6E002CE8D3 /* NSStatusBarPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = F413695E29916F6E002CE8D3 /* NSStatusBarPrivate.h */; };\n\t\tF413696E29916F6E002CE8D3 /* NSApplication+MenuBar.m in Sources */ = {isa = PBXBuildFile; fileRef = F413695F29916F6E002CE8D3 /* NSApplication+MenuBar.m */; };\n\t\tF413696F29916F6E002CE8D3 /* NSStatusItem+.m in Sources */ = {isa = PBXBuildFile; fileRef = F413696029916F6E002CE8D3 /* NSStatusItem+.m */; };\n\t\tF413697029916F6E002CE8D3 /* StatusItemManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F413696129916F6E002CE8D3 /* StatusItemManager.swift */; };\n\t\tF413697929917135002CE8D3 /* CGFloat+OnePixel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F413697829917135002CE8D3 /* CGFloat+OnePixel.swift */; };\n\t\tF4136999299179B1002CE8D3 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F4136998299179B1002CE8D3 /* Main.storyboard */; };\n\t\tF413699A299179F8002CE8D3 /* VirtualCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F4BE9C6527FF053A00B648F8 /* VirtualCore.framework */; };\n\t\tF413699B299179F8002CE8D3 /* VirtualCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = F4BE9C6527FF053A00B648F8 /* VirtualCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };\n\t\tF41369A329917FA0002CE8D3 /* ScreenChangeModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = F41369A229917FA0002CE8D3 /* ScreenChangeModifier.swift */; };\n\t\tF41369A6299183C8002CE8D3 /* GuestLaunchAtLoginManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F41369A5299183C8002CE8D3 /* GuestLaunchAtLoginManager.swift */; };\n\t\tF41369AE29918576002CE8D3 /* GuestHelperAppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F41369AD29918576002CE8D3 /* GuestHelperAppDelegate.swift */; };\n\t\tF41369B229918576002CE8D3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F41369B129918576002CE8D3 /* Assets.xcassets */; };\n\t\tF41369B529918576002CE8D3 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F41369B329918576002CE8D3 /* Main.storyboard */; };\n\t\tF41369BF2991863A002CE8D3 /* VirtualBuddyGuestHelper.app in Embed Login Item */ = {isa = PBXBuildFile; fileRef = F41369AB29918576002CE8D3 /* VirtualBuddyGuestHelper.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };\n\t\tF41369CA2991A492002CE8D3 /* HostConnectionStateProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = F41369C92991A492002CE8D3 /* HostConnectionStateProvider.swift */; };\n\t\tF41369CC2991A68F002CE8D3 /* GuestSharedFoldersManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F41369CB2991A68F002CE8D3 /* GuestSharedFoldersManager.swift */; };\n\t\tF417255D288604A8004FF8A7 /* SoundConfigurationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F417255C288604A8004FF8A7 /* SoundConfigurationView.swift */; };\n\t\tF417255F28861604004FF8A7 /* DecodableDefault.swift in Sources */ = {isa = PBXBuildFile; fileRef = F417255E28861604004FF8A7 /* DecodableDefault.swift */; };\n\t\tF417256128861A05004FF8A7 /* SharingConfigurationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F417256028861A05004FF8A7 /* SharingConfigurationView.swift */; };\n\t\tF41725632886DD37004FF8A7 /* SharedFolderListItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = F41725622886DD37004FF8A7 /* SharedFolderListItem.swift */; };\n\t\tF41725662886DF58004FF8A7 /* OpenSavePanelUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = F41725652886DF58004FF8A7 /* OpenSavePanelUtils.swift */; };\n\t\tF41725682886E5AD004FF8A7 /* MaterialView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F41725672886E5AD004FF8A7 /* MaterialView.swift */; };\n\t\tF417256C2887500F004FF8A7 /* StorageConfigurationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F417256B2887500F004FF8A7 /* StorageConfigurationView.swift */; };\n\t\tF417256F2887544A004FF8A7 /* StorageDeviceDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F417256E2887544A004FF8A7 /* StorageDeviceDetailView.swift */; };\n\t\tF417257128877121004FF8A7 /* DiskImageGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F417257028877121004FF8A7 /* DiskImageGenerator.swift */; };\n\t\tF417257428877478004FF8A7 /* VirtualCore.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F417257328877478004FF8A7 /* VirtualCore.xcassets */; };\n\t\tF41725762887758A004FF8A7 /* RandomNameGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F41725752887758A004FF8A7 /* RandomNameGenerator.swift */; };\n\t\tF417CB882E0EDECD0065B5D6 /* BackportedContentUnavailableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F417CB872E0EDECD0065B5D6 /* BackportedContentUnavailableView.swift */; };\n\t\tF417CB8A2E0EDF1A0065B5D6 /* EqualWidthHStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = F417CB892E0EDF1A0065B5D6 /* EqualWidthHStack.swift */; };\n\t\tF417CBB92E0F3D2E0065B5D6 /* FirstLaunchExperienceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F417CBB82E0F3D2E0065B5D6 /* FirstLaunchExperienceView.swift */; };\n\t\tF422586D2885CC9F009420AE /* SharedFocusEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = F422586C2885CC9F009420AE /* SharedFocusEnvironment.swift */; };\n\t\tF42258702885D537009420AE /* EphemeralTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = F422586F2885D537009420AE /* EphemeralTextField.swift */; };\n\t\tF42258722885E100009420AE /* VMConfigurationSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = F42258712885E100009420AE /* VMConfigurationSheet.swift */; };\n\t\tF42258742885E10B009420AE /* VMConfigurationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F42258732885E10B009420AE /* VMConfigurationViewModel.swift */; };\n\t\tF42258782885E14A009420AE /* DisplayConfigurationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F42258772885E14A009420AE /* DisplayConfigurationView.swift */; };\n\t\tF422587A2885E17D009420AE /* ConfigurationSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = F42258792885E17D009420AE /* ConfigurationSection.swift */; };\n\t\tF422587C2885E1CE009420AE /* NetworkConfigurationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F422587B2885E1CE009420AE /* NetworkConfigurationView.swift */; };\n\t\tF422587E2885E2ED009420AE /* HardwareConfigurationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F422587D2885E2ED009420AE /* HardwareConfigurationView.swift */; };\n\t\tF42258802885E71D009420AE /* PropertyControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = F422587F2885E71D009420AE /* PropertyControl.swift */; };\n\t\tF428622D2AE8726D0052F029 /* VirtualMachineControls.swift in Sources */ = {isa = PBXBuildFile; fileRef = F428622C2AE8726D0052F029 /* VirtualMachineControls.swift */; };\n\t\tF428622E2AE87D7E0052F029 /* DeepLinkSecurity.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F43B01152AD858FE00164CD1 /* DeepLinkSecurity.framework */; };\n\t\tF428622F2AE87D7E0052F029 /* DeepLinkSecurity.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = F43B01152AD858FE00164CD1 /* DeepLinkSecurity.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };\n\t\tF42862372AE947C90052F029 /* WHPayload.swift in Sources */ = {isa = PBXBuildFile; fileRef = F42862362AE947C90052F029 /* WHPayload.swift */; };\n\t\tF42C014A2888C2F800EB15CD /* InstallationConsole.swift in Sources */ = {isa = PBXBuildFile; fileRef = F42C01492888C2F800EB15CD /* InstallationConsole.swift */; };\n\t\tF42C014C2888C34B00EB15CD /* LogConsole.swift in Sources */ = {isa = PBXBuildFile; fileRef = F42C014B2888C34B00EB15CD /* LogConsole.swift */; };\n\t\tF42C014E2888CBCB00EB15CD /* InstallProgressStepView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F42C014D2888CBCB00EB15CD /* InstallProgressStepView.swift */; };\n\t\tF42C015A2888FC0C00EB15CD /* LibraryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F42C01512888FC0C00EB15CD /* LibraryView.swift */; };\n\t\tF42C015B2888FC0C00EB15CD /* VMSessionConfigurationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F42C01542888FC0C00EB15CD /* VMSessionConfigurationView.swift */; };\n\t\tF42C015C2888FC0C00EB15CD /* VirtualMachineSessionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F42C01552888FC0C00EB15CD /* VirtualMachineSessionView.swift */; };\n\t\tF42C015D2888FC0C00EB15CD /* SwiftUIVMView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F42C01572888FC0C00EB15CD /* SwiftUIVMView.swift */; };\n\t\tF42C015E2888FC0C00EB15CD /* SettingsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = F42C01592888FC0C00EB15CD /* SettingsScreen.swift */; };\n\t\tF42C01612888FC3500EB15CD /* LibraryItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F42C01602888FC3500EB15CD /* LibraryItemView.swift */; };\n\t\tF42CF4A82DF5FEC3001DE049 /* BlurHashToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = F42CF4A72DF5FEC3001DE049 /* BlurHashToken.swift */; };\n\t\tF43B01182AD858FE00164CD1 /* DeepLinkSecurity.h in Headers */ = {isa = PBXBuildFile; fileRef = F43B01172AD858FE00164CD1 /* DeepLinkSecurity.h */; settings = {ATTRIBUTES = (Public, ); }; };\n\t\tF43B011B2AD858FE00164CD1 /* DeepLinkSecurity.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F43B01152AD858FE00164CD1 /* DeepLinkSecurity.framework */; };\n\t\tF43B011C2AD858FE00164CD1 /* DeepLinkSecurity.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = F43B01152AD858FE00164CD1 /* DeepLinkSecurity.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };\n\t\tF43B01352AD8590F00164CD1 /* DeepLinkAuthUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = F43B01242AD8590F00164CD1 /* DeepLinkAuthUI.swift */; };\n\t\tF43B01362AD8590F00164CD1 /* DeepLinkSentinel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F43B01252AD8590F00164CD1 /* DeepLinkSentinel.swift */; };\n\t\tF43B01372AD8590F00164CD1 /* OpenDeepLinkRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F43B01272AD8590F00164CD1 /* OpenDeepLinkRequest.swift */; };\n\t\tF43B01382AD8590F00164CD1 /* DeepLinkClientDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = F43B01282AD8590F00164CD1 /* DeepLinkClientDescriptor.swift */; };\n\t\tF43B01392AD8590F00164CD1 /* DeepLinkClient+Crypto.swift in Sources */ = {isa = PBXBuildFile; fileRef = F43B012A2AD8590F00164CD1 /* DeepLinkClient+Crypto.swift */; };\n\t\tF43B013A2AD8590F00164CD1 /* DeepLinkClientDescriptor+.swift in Sources */ = {isa = PBXBuildFile; fileRef = F43B012B2AD8590F00164CD1 /* DeepLinkClientDescriptor+.swift */; };\n\t\tF43B013B2AD8590F00164CD1 /* DeepLinkClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = F43B012C2AD8590F00164CD1 /* DeepLinkClient.swift */; };\n\t\tF43B013C2AD8590F00164CD1 /* KeychainDeepLinkAuthStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F43B012E2AD8590F00164CD1 /* KeychainDeepLinkAuthStore.swift */; };\n\t\tF43B013D2AD8590F00164CD1 /* MemoryDeepLinkAuthStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F43B012F2AD8590F00164CD1 /* MemoryDeepLinkAuthStore.swift */; };\n\t\tF43B013E2AD8590F00164CD1 /* DeepLinkManagementStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F43B01302AD8590F00164CD1 /* DeepLinkManagementStore.swift */; };\n\t\tF43B013F2AD8590F00164CD1 /* UserDefaultsDeepLinkManagementStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F43B01312AD8590F00164CD1 /* UserDefaultsDeepLinkManagementStore.swift */; };\n\t\tF43B01402AD8590F00164CD1 /* DeepLinkAuthStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F43B01322AD8590F00164CD1 /* DeepLinkAuthStore.swift */; };\n\t\tF43B01412AD8590F00164CD1 /* DeepLinkSecurityDefines.swift in Sources */ = {isa = PBXBuildFile; fileRef = F43B01342AD8590F00164CD1 /* DeepLinkSecurityDefines.swift */; };\n\t\tF43B01442AD85A6500164CD1 /* VirtualBuddyDeepLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = F43B01432AD85A6500164CD1 /* VirtualBuddyDeepLinks.swift */; };\n\t\tF43B01472AD85A7D00164CD1 /* URLQueryItemCoder in Frameworks */ = {isa = PBXBuildFile; productRef = F43B01462AD85A7D00164CD1 /* URLQueryItemCoder */; };\n\t\tF43B014B2AD85ABB00164CD1 /* DeepLinkAuthDialog.swift in Sources */ = {isa = PBXBuildFile; fileRef = F43B01492AD85ABB00164CD1 /* DeepLinkAuthDialog.swift */; };\n\t\tF43B014E2AD86BFA00164CD1 /* DeepLinkHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = F43B014D2AD86BFA00164CD1 /* DeepLinkHandler.swift */; };\n\t\tF443620A29B7947A00745B43 /* GuestAdditionsDiskImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = F443620929B7947A00745B43 /* GuestAdditionsDiskImage.swift */; };\n\t\tF443620C29B79A6800745B43 /* VirtualBuddyGuest.app in Embed Guest App */ = {isa = PBXBuildFile; fileRef = F4C18A4228491B8500335EC7 /* VirtualBuddyGuest.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };\n\t\tF443620F29B7A0C600745B43 /* CreateGuestImage.sh in Resources */ = {isa = PBXBuildFile; fileRef = F443620E29B7A0C600745B43 /* CreateGuestImage.sh */; };\n\t\tF444D0CA2DF321CD0086537A /* CatalogGroupPlaceholder.heic in Resources */ = {isa = PBXBuildFile; fileRef = F444D0C92DF321CD0086537A /* CatalogGroupPlaceholder.heic */; };\n\t\tF444D0CC2DF322B90086537A /* SoftwareCatalog+Placeholder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F444D0CB2DF322B50086537A /* SoftwareCatalog+Placeholder.swift */; };\n\t\tF444D0CE2DF32E100086537A /* BlurHashFullBleedBackground.swift in Sources */ = {isa = PBXBuildFile; fileRef = F444D0CD2DF32E100086537A /* BlurHashFullBleedBackground.swift */; };\n\t\tF444D0F42DF34BE80086537A /* CALayer+Asset.swift in Sources */ = {isa = PBXBuildFile; fileRef = F444D0F32DF34BE40086537A /* CALayer+Asset.swift */; };\n\t\tF444D0F62DF37D170086537A /* VirtualBuddyMonoIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = F444D0F52DF37D170086537A /* VirtualBuddyMonoIcon.swift */; };\n\t\tF444D0F82DF37D410086537A /* VirtualDisplayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F444D0F72DF37D410086537A /* VirtualDisplayView.swift */; };\n\t\tF444D0FA2DF37DFB0086537A /* InstallProgressDisplayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F444D0F92DF37DFB0086537A /* InstallProgressDisplayView.swift */; };\n\t\tF444D0FC2DF37EF80086537A /* VirtualBuddyMonoProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F444D0FB2DF37EF80086537A /* VirtualBuddyMonoProgressView.swift */; };\n\t\tF444D1342BB478AD00AB786F /* VBMemoryLeakDebugAssertions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F444D1332BB478AD00AB786F /* VBMemoryLeakDebugAssertions.swift */; };\n\t\tF4450CCA2ACB0DB500092618 /* KeyboardDeviceConfigurationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4450CC92ACB0DB500092618 /* KeyboardDeviceConfigurationView.swift */; };\n\t\tF44C00FB2889CE1600640BF5 /* VBVirtualMachine+Virtualization.swift in Sources */ = {isa = PBXBuildFile; fileRef = F44C00FA2889CE1600640BF5 /* VBVirtualMachine+Virtualization.swift */; };\n\t\tF4510A782AE2A16F00E24DD9 /* WeakReference.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4510A772AE2A16F00E24DD9 /* WeakReference.swift */; };\n\t\tF4510A7B2AE2B3B300E24DD9 /* DeepLinkSecurity.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F43B01152AD858FE00164CD1 /* DeepLinkSecurity.framework */; };\n\t\tF453C4122DF0B1ED007EAD5F /* BuddyKit in Frameworks */ = {isa = PBXBuildFile; productRef = F453C4112DF0B1ED007EAD5F /* BuddyKit */; };\n\t\tF453C41D2DF0B43D007EAD5F /* ResolvedCatalog.swift in Sources */ = {isa = PBXBuildFile; fileRef = F453C41A2DF0B43D007EAD5F /* ResolvedCatalog.swift */; };\n\t\tF453C41E2DF0B43D007EAD5F /* LegacyCatalog.swift in Sources */ = {isa = PBXBuildFile; fileRef = F453C4182DF0B43D007EAD5F /* LegacyCatalog.swift */; };\n\t\tF453C41F2DF0B43D007EAD5F /* BlurHashEncode.swift in Sources */ = {isa = PBXBuildFile; fileRef = F453C4162DF0B43D007EAD5F /* BlurHashEncode.swift */; };\n\t\tF453C4202DF0B43D007EAD5F /* SoftwareCatalog.swift in Sources */ = {isa = PBXBuildFile; fileRef = F453C41B2DF0B43D007EAD5F /* SoftwareCatalog.swift */; };\n\t\tF453C4212DF0B43D007EAD5F /* MobileDeviceFramework.swift in Sources */ = {isa = PBXBuildFile; fileRef = F453C4192DF0B43D007EAD5F /* MobileDeviceFramework.swift */; };\n\t\tF453C4232DF0B5B1007EAD5F /* VirtualBuddyEntryPoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = F453C4222DF0B5B1007EAD5F /* VirtualBuddyEntryPoint.swift */; };\n\t\tF453C4242DF0B602007EAD5F /* VirtualUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F498ACFE2884BF13006F1C00 /* VirtualUI.framework */; };\n\t\tF453C4252DF0B602007EAD5F /* VirtualUI.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = F498ACFE2884BF13006F1C00 /* VirtualUI.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };\n\t\tF453C42B2DF0B792007EAD5F /* ArgumentParser in Frameworks */ = {isa = PBXBuildFile; productRef = F453C42A2DF0B792007EAD5F /* ArgumentParser */; };\n\t\tF453C43B2DF0B7A5007EAD5F /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = F453C42E2DF0B7A5007EAD5F /* Helpers.swift */; };\n\t\tF453C43C2DF0B7A5007EAD5F /* VCTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = F453C4392DF0B7A5007EAD5F /* VCTool.swift */; };\n\t\tF453C43D2DF0B7A5007EAD5F /* URL+ContentLength.swift in Sources */ = {isa = PBXBuildFile; fileRef = F453C4302DF0B7A5007EAD5F /* URL+ContentLength.swift */; };\n\t\tF453C43E2DF0B7A5007EAD5F /* IPSWCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = F453C4352DF0B7A5007EAD5F /* IPSWCommand.swift */; };\n\t\tF453C43F2DF0B7A5007EAD5F /* ImageCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = F453C4342DF0B7A5007EAD5F /* ImageCommand.swift */; };\n\t\tF453C4402DF0B7A5007EAD5F /* BuildManifest.swift in Sources */ = {isa = PBXBuildFile; fileRef = F453C42C2DF0B7A5007EAD5F /* BuildManifest.swift */; };\n\t\tF453C4412DF0B7A5007EAD5F /* GroupCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = F453C4332DF0B7A5007EAD5F /* GroupCommand.swift */; };\n\t\tF453C4422DF0B7A5007EAD5F /* MobileDeviceCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = F453C4372DF0B7A5007EAD5F /* MobileDeviceCommand.swift */; };\n\t\tF453C4432DF0B7A5007EAD5F /* BuildManifest+Fetch.swift in Sources */ = {isa = PBXBuildFile; fileRef = F453C42D2DF0B7A5007EAD5F /* BuildManifest+Fetch.swift */; };\n\t\tF453C4442DF0B7A5007EAD5F /* TreeStringConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = F453C42F2DF0B7A5007EAD5F /* TreeStringConvertible.swift */; };\n\t\tF453C4452DF0B7A5007EAD5F /* MigrateCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = F453C4362DF0B7A5007EAD5F /* MigrateCommand.swift */; };\n\t\tF453C4462DF0B7A5007EAD5F /* ResolveCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = F453C4382DF0B7A5007EAD5F /* ResolveCommand.swift */; };\n\t\tF453C4472DF0B7A5007EAD5F /* CatalogCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = F453C4322DF0B7A5007EAD5F /* CatalogCommand.swift */; };\n\t\tF453C44A2DF0B7F6007EAD5F /* FragmentZip in Frameworks */ = {isa = PBXBuildFile; productRef = F453C4492DF0B7F6007EAD5F /* FragmentZip */; };\n\t\tF453C44C2DF0B835007EAD5F /* libcurl.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = F453C44B2DF0B835007EAD5F /* libcurl.tbd */; };\n\t\tF453C44E2DF0B870007EAD5F /* VirtualBuddyCLI.swift in Sources */ = {isa = PBXBuildFile; fileRef = F453C44D2DF0B869007EAD5F /* VirtualBuddyCLI.swift */; };\n\t\tF453C45B2DF0C4BE007EAD5F /* BuddyKit in Frameworks */ = {isa = PBXBuildFile; productRef = F453C45A2DF0C4BE007EAD5F /* BuddyKit */; };\n\t\tF453C45D2DF0D28A007EAD5F /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = F453C45C2DF0D286007EAD5F /* README.md */; };\n\t\tF453C4632DF0E609007EAD5F /* BuddyKit in Frameworks */ = {isa = PBXBuildFile; productRef = F453C4622DF0E609007EAD5F /* BuddyKit */; };\n\t\tF453C4682DF10181007EAD5F /* BlurHashCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = F453C4672DF10181007EAD5F /* BlurHashCommand.swift */; };\n\t\tF453C4892DF1CDA0007EAD5F /* DownloadBackend.swift in Sources */ = {isa = PBXBuildFile; fileRef = F453C4882DF1CDA0007EAD5F /* DownloadBackend.swift */; };\n\t\tF453C4922DF1D213007EAD5F /* FakeRestoreImage.ipsw in Resources */ = {isa = PBXBuildFile; fileRef = F453C4912DF1D213007EAD5F /* FakeRestoreImage.ipsw */; };\n\t\tF453C49B2DF1D768007EAD5F /* SimulatedDownloadBackend.swift in Sources */ = {isa = PBXBuildFile; fileRef = F453C49A2DF1D768007EAD5F /* SimulatedDownloadBackend.swift */; };\n\t\tF453C4A02DF1D7C0007EAD5F /* RestoreBackend.swift in Sources */ = {isa = PBXBuildFile; fileRef = F453C49F2DF1D7C0007EAD5F /* RestoreBackend.swift */; };\n\t\tF453C4A22DF1D7F6007EAD5F /* SimulatedRestoreBackend.swift in Sources */ = {isa = PBXBuildFile; fileRef = F453C4A12DF1D7F6007EAD5F /* SimulatedRestoreBackend.swift */; };\n\t\tF453C4A42DF1D861007EAD5F /* VirtualizationRestoreBackend.swift in Sources */ = {isa = PBXBuildFile; fileRef = F453C4A32DF1D85C007EAD5F /* VirtualizationRestoreBackend.swift */; };\n\t\tF453C4B42DF20301007EAD5F /* RestoreImageURLInputView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F453C4B32DF20301007EAD5F /* RestoreImageURLInputView.swift */; };\n\t\tF453C4B92DF21985007EAD5F /* VMInstallData.swift in Sources */ = {isa = PBXBuildFile; fileRef = F453C4B82DF21985007EAD5F /* VMInstallData.swift */; };\n\t\tF453C4BB2DF231BB007EAD5F /* PreventTerminationAssertion.swift in Sources */ = {isa = PBXBuildFile; fileRef = F453C4BA2DF231B7007EAD5F /* PreventTerminationAssertion.swift */; };\n\t\tF45502142DF394DC005582A4 /* VirtualBuddyInstallerInputView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F45502132DF394DC005582A4 /* VirtualBuddyInstallerInputView.swift */; };\n\t\tF45502162DF45E53005582A4 /* VBSettings+CatalogDownload.swift in Sources */ = {isa = PBXBuildFile; fileRef = F45502152DF45E4D005582A4 /* VBSettings+CatalogDownload.swift */; };\n\t\tF45502232DF463A1005582A4 /* UniversalMac_13.3.1_22E261_Restore.ipsw in Copy Preview Library Downloads */ = {isa = PBXBuildFile; fileRef = F45502182DF46368005582A4 /* UniversalMac_13.3.1_22E261_Restore.ipsw */; };\n\t\tF45502242DF463A1005582A4 /* UniversalMac_14.0_23A344_Restore.ipsw in Copy Preview Library Downloads */ = {isa = PBXBuildFile; fileRef = F45502192DF46368005582A4 /* UniversalMac_14.0_23A344_Restore.ipsw */; };\n\t\tF45502252DF463A1005582A4 /* UniversalMac_14.5_23F79_Restore.ipsw in Copy Preview Library Downloads */ = {isa = PBXBuildFile; fileRef = F455021A2DF46368005582A4 /* UniversalMac_14.5_23F79_Restore.ipsw */; };\n\t\tF45502262DF463A1005582A4 /* UniversalMac_15.3_24D60_Restore.ipsw in Copy Preview Library Downloads */ = {isa = PBXBuildFile; fileRef = F455021B2DF46368005582A4 /* UniversalMac_15.3_24D60_Restore.ipsw */; };\n\t\tF45502272DF463A1005582A4 /* UniversalMac_15.5_24F74_Restore.ipsw in Copy Preview Library Downloads */ = {isa = PBXBuildFile; fileRef = F455021C2DF46368005582A4 /* UniversalMac_15.5_24F74_Restore.ipsw */; };\n\t\tF4561A6828981B4100055289 /* VirtualMachineNameInputView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4561A6728981B4100055289 /* VirtualMachineNameInputView.swift */; };\n\t\tF462C9422E0C96D300C172E2 /* FB18383725Window.swift in Sources */ = {isa = PBXBuildFile; fileRef = F462C9412E0C96D300C172E2 /* FB18383725Window.swift */; };\n\t\tF465C3AE284F93A5006E9ED4 /* VBAPIClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = F465C3AD284F93A5006E9ED4 /* VBAPIClient.swift */; };\n\t\tF465C3B0284F9660006E9ED4 /* VBRestoreImagesResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = F465C3AF284F9660006E9ED4 /* VBRestoreImagesResponse.swift */; };\n\t\tF465C3B2284F9666006E9ED4 /* VBRestoreImageInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = F465C3B1284F9666006E9ED4 /* VBRestoreImageInfo.swift */; };\n\t\tF465C3B8284FA252006E9ED4 /* URLSessionDownloadBackend.swift in Sources */ = {isa = PBXBuildFile; fileRef = F465C3B7284FA252006E9ED4 /* URLSessionDownloadBackend.swift */; };\n\t\tF46FFBA82804F07400D61023 /* VBNVRAMVariable.swift in Sources */ = {isa = PBXBuildFile; fileRef = F46FFBA72804F07400D61023 /* VBNVRAMVariable.swift */; };\n\t\tF46FFBAA2804F0A000D61023 /* VZVirtualMachineConfiguration+NVRAM.swift in Sources */ = {isa = PBXBuildFile; fileRef = F46FFBA92804F0A000D61023 /* VZVirtualMachineConfiguration+NVRAM.swift */; };\n\t\tF46FFBAC28059FF600D61023 /* VMInstance.swift in Sources */ = {isa = PBXBuildFile; fileRef = F46FFBAB28059FF600D61023 /* VMInstance.swift */; };\n\t\tF47BCDA12C5BE8FF00165191 /* ipsws_v2.json in Copy Software Catalog Feeds */ = {isa = PBXBuildFile; fileRef = F47BCD9F2C5BE8EF00165191 /* ipsws_v2.json */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };\n\t\tF47BCDA22C5BE8FF00165191 /* linux_v2.json in Copy Software Catalog Feeds */ = {isa = PBXBuildFile; fileRef = F47BCDA02C5BE8EF00165191 /* linux_v2.json */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };\n\t\tF47BCDCB2C5C01D100165191 /* BlurHashDecoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = F47BCDCA2C5C01CE00165191 /* BlurHashDecoding.swift */; };\n\t\tF47BCDCD2C5C01EF00165191 /* RemoteImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = F47BCDCC2C5C01EF00165191 /* RemoteImage.swift */; };\n\t\tF47BCDCF2C5C023E00165191 /* CatalogGroupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F47BCDCE2C5C023900165191 /* CatalogGroupView.swift */; };\n\t\tF47BCDD12C5C06CE00165191 /* CatalogGroupPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = F47BCDD02C5C06C900165191 /* CatalogGroupPicker.swift */; };\n\t\tF47BCDD32C5C0AB300165191 /* KeyboardNavigationModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = F47BCDD22C5C0AB300165191 /* KeyboardNavigationModifier.swift */; };\n\t\tF47BCDD52C5C0B8F00165191 /* Array+Navigation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F47BCDD42C5C0B8C00165191 /* Array+Navigation.swift */; };\n\t\tF47BCDD72C5D2B4600165191 /* CatalogExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F47BCDD62C5D2B4300165191 /* CatalogExtensions.swift */; };\n\t\tF47BCDD92C5D2EE300165191 /* RestoreImageBrowser.swift in Sources */ = {isa = PBXBuildFile; fileRef = F47BCDD82C5D2EDB00165191 /* RestoreImageBrowser.swift */; };\n\t\tF485B91B2BB22D2D004B3C2B /* VBSavedStateMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = F485B91A2BB22D2D004B3C2B /* VBSavedStateMetadata.swift */; };\n\t\tF485B91D2BB2F0D9004B3C2B /* ProcessInfo+ECID.swift in Sources */ = {isa = PBXBuildFile; fileRef = F485B91C2BB2F0D9004B3C2B /* ProcessInfo+ECID.swift */; };\n\t\tF485B91F2BB2F4AC004B3C2B /* Bundle+Version.swift in Sources */ = {isa = PBXBuildFile; fileRef = F485B91E2BB2F4AC004B3C2B /* Bundle+Version.swift */; };\n\t\tF485B9222BB306AF004B3C2B /* VBDebugUtil.h in Headers */ = {isa = PBXBuildFile; fileRef = F485B9202BB306AF004B3C2B /* VBDebugUtil.h */; settings = {ATTRIBUTES = (Public, ); }; };\n\t\tF485B9232BB306AF004B3C2B /* VBDebugUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = F485B9212BB306AF004B3C2B /* VBDebugUtil.m */; };\n\t\tF48E0D03288858E00080DDFA /* ManagedDiskImageEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = F48E0D02288858DF0080DDFA /* ManagedDiskImageEditor.swift */; };\n\t\tF48E0D0728885E140080DDFA /* PreviewSupport.swift in Sources */ = {isa = PBXBuildFile; fileRef = F48E0D0628885E140080DDFA /* PreviewSupport.swift */; };\n\t\tF48E0D0C2888760D0080DDFA /* VBMacDevice+Storage.swift in Sources */ = {isa = PBXBuildFile; fileRef = F48E0D0B2888760D0080DDFA /* VBMacDevice+Storage.swift */; };\n\t\tF48E0D1A288882BD0080DDFA /* InstallationWizardTitle.swift in Sources */ = {isa = PBXBuildFile; fileRef = F48E0D14288882BD0080DDFA /* InstallationWizardTitle.swift */; };\n\t\tF48E0D1B288882BD0080DDFA /* InstallMethodPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = F48E0D15288882BD0080DDFA /* InstallMethodPicker.swift */; };\n\t\tF48E0D1C288882BD0080DDFA /* RestoreImageSelectionStep.swift in Sources */ = {isa = PBXBuildFile; fileRef = F48E0D16288882BD0080DDFA /* RestoreImageSelectionStep.swift */; };\n\t\tF48E0D1D288882BD0080DDFA /* AuthenticatingWebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F48E0D17288882BD0080DDFA /* AuthenticatingWebView.swift */; };\n\t\tF48E0D1E288882BD0080DDFA /* VMInstallationWizard.swift in Sources */ = {isa = PBXBuildFile; fileRef = F48E0D18288882BD0080DDFA /* VMInstallationWizard.swift */; };\n\t\tF48E0D1F288882BD0080DDFA /* VMInstallationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F48E0D19288882BD0080DDFA /* VMInstallationViewModel.swift */; };\n\t\tF48E0D23288882E50080DDFA /* DecentFormView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F48E0D20288882E50080DDFA /* DecentFormView.swift */; };\n\t\tF48E0D24288882E50080DDFA /* NSAlert+Confirmation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F48E0D21288882E50080DDFA /* NSAlert+Confirmation.swift */; };\n\t\tF48E0D25288882E50080DDFA /* OnAppearOnce.swift in Sources */ = {isa = PBXBuildFile; fileRef = F48E0D22288882E50080DDFA /* OnAppearOnce.swift */; };\n\t\tF48E0D2A288883150080DDFA /* OpenCocoaWindowAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = F48E0D27288883150080DDFA /* OpenCocoaWindowAction.swift */; };\n\t\tF48E0D2B288883150080DDFA /* HostingWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F48E0D28288883150080DDFA /* HostingWindowController.swift */; };\n\t\tF48E0D2C288883150080DDFA /* WindowEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = F48E0D29288883150080DDFA /* WindowEnvironment.swift */; };\n\t\tF48E0D2F2888835A0080DDFA /* VirtualUIConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = F48E0D2E2888835A0080DDFA /* VirtualUIConstants.swift */; };\n\t\tF48E0D32288884A10080DDFA /* RestoreImageDownloadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F48E0D31288884A10080DDFA /* RestoreImageDownloadView.swift */; };\n\t\tF48E0D34288889E60080DDFA /* InstallConfigurationStepView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F48E0D33288889E60080DDFA /* InstallConfigurationStepView.swift */; };\n\t\tF4959F3C2992A284001DF4CB /* GuestAppInstaller.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4959F3B2992A284001DF4CB /* GuestAppInstaller.swift */; };\n\t\tF498AD012884BF13006F1C00 /* VirtualUI.h in Headers */ = {isa = PBXBuildFile; fileRef = F498AD002884BF13006F1C00 /* VirtualUI.h */; settings = {ATTRIBUTES = (Public, ); }; };\n\t\tF498AD042884BF13006F1C00 /* VirtualUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F498ACFE2884BF13006F1C00 /* VirtualUI.framework */; };\n\t\tF498AD052884BF13006F1C00 /* VirtualUI.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = F498ACFE2884BF13006F1C00 /* VirtualUI.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };\n\t\tF498AD0C2884BF67006F1C00 /* VirtualCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F4BE9C6527FF053A00B648F8 /* VirtualCore.framework */; };\n\t\tF498AD0E2884BF9D006F1C00 /* VMConfigurationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F498AD0D2884BF9D006F1C00 /* VMConfigurationView.swift */; };\n\t\tF498AD142884C36A006F1C00 /* NumericValueField.swift in Sources */ = {isa = PBXBuildFile; fileRef = F498AD112884C36A006F1C00 /* NumericValueField.swift */; };\n\t\tF498AD162884C36A006F1C00 /* ControlGroupChrome.swift in Sources */ = {isa = PBXBuildFile; fileRef = F498AD132884C36A006F1C00 /* ControlGroupChrome.swift */; };\n\t\tF498AD182884C593006F1C00 /* NumericPropertyControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = F498AD172884C593006F1C00 /* NumericPropertyControl.swift */; };\n\t\tF498AD1A2884C5FF006F1C00 /* SliderConversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = F498AD192884C5FF006F1C00 /* SliderConversion.swift */; };\n\t\tF49A68E12884917E00A17582 /* ConfigurationModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = F49A68E02884917E00A17582 /* ConfigurationModels.swift */; };\n\t\tF49AA2C329BA22A5009625F7 /* VBRestorableWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = F49AA2C229BA22A5009625F7 /* VBRestorableWindow.swift */; };\n\t\tF49AA2C529BA31CC009625F7 /* VirtualMachineSessionUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = F49AA2C429BA31CC009625F7 /* VirtualMachineSessionUI.swift */; };\n\t\tF49AA2C729BA3F2B009625F7 /* VBRestorableWindow+Resizing.swift in Sources */ = {isa = PBXBuildFile; fileRef = F49AA2C629BA3F2B009625F7 /* VBRestorableWindow+Resizing.swift */; };\n\t\tF49B82982E02F5A900395F87 /* VMArtworkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F49B82972E02F5A300395F87 /* VMArtworkView.swift */; };\n\t\tF49B82A82E0304D400395F87 /* PreviewMac.vbvm in Copy Preview Library */ = {isa = PBXBuildFile; fileRef = F49B829F2E02FCB100395F87 /* PreviewMac.vbvm */; };\n\t\tF49B82A92E0304D400395F87 /* PreviewMacBlurHash.vbvm in Copy Preview Library */ = {isa = PBXBuildFile; fileRef = F49B829D2E02FCB100395F87 /* PreviewMacBlurHash.vbvm */; };\n\t\tF49B82AA2E0304D400395F87 /* PreviewMacNoArtwork.vbvm in Copy Preview Library */ = {isa = PBXBuildFile; fileRef = F49B82A32E02FCF400395F87 /* PreviewMacNoArtwork.vbvm */; };\n\t\tF49B82AB2E0304D400395F87 /* PreviewLinux.vbvm in Copy Preview Library */ = {isa = PBXBuildFile; fileRef = F49B829E2E02FCB100395F87 /* PreviewLinux.vbvm */; };\n\t\tF49B82AC2E0304D400395F87 /* PreviewLinuxBlurHash.vbvm in Copy Preview Library */ = {isa = PBXBuildFile; fileRef = F49B82A52E03049C00395F87 /* PreviewLinuxBlurHash.vbvm */; };\n\t\tF49B82AD2E0304D400395F87 /* PreviewLinuxNoArtwork.vbvm in Copy Preview Library */ = {isa = PBXBuildFile; fileRef = F49B82A62E03049C00395F87 /* PreviewLinuxNoArtwork.vbvm */; };\n\t\tF49B82FC2E034EAD00395F87 /* WHDesktopPictureService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F49B82FB2E034EAD00395F87 /* WHDesktopPictureService.swift */; };\n\t\tF49B83002E034F6800395F87 /* NSImage+DesktopPicture.m in Sources */ = {isa = PBXBuildFile; fileRef = F49B82FF2E034F6800395F87 /* NSImage+DesktopPicture.m */; };\n\t\tF49B83012E034F6800395F87 /* NSImage+DesktopPicture.h in Headers */ = {isa = PBXBuildFile; fileRef = F49B82FE2E034F6800395F87 /* NSImage+DesktopPicture.h */; settings = {ATTRIBUTES = (Public, ); }; };\n\t\tF49B832B2E04593A00395F87 /* NSImage+DRMProtected.swift in Sources */ = {isa = PBXBuildFile; fileRef = F49B832A2E04593100395F87 /* NSImage+DRMProtected.swift */; };\n\t\tF49B832D2E046B8D00395F87 /* GuestAppConfigurationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F49B832C2E046B8D00395F87 /* GuestAppConfigurationView.swift */; };\n\t\tF49B83712E04837400395F87 /* CGImage+FullyTransparent.swift in Sources */ = {isa = PBXBuildFile; fileRef = F49B83702E04837400395F87 /* CGImage+FullyTransparent.swift */; };\n\t\tF49FD87D2DFB68F50019D638 /* VMImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F49FD87C2DFB68F20019D638 /* VMImporter.swift */; };\n\t\tF49FD87F2DFB6B670019D638 /* VMImporterRegistry.swift in Sources */ = {isa = PBXBuildFile; fileRef = F49FD87E2DFB6B630019D638 /* VMImporterRegistry.swift */; };\n\t\tF49FD8812DFB6CDD0019D638 /* UTMImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F49FD8802DFB6CD80019D638 /* UTMImporter.swift */; };\n\t\tF49FD8842DFB727B0019D638 /* VMImporter+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = F49FD8832DFB72790019D638 /* VMImporter+Helpers.swift */; };\n\t\tF49FD8862DFB728A0019D638 /* UTMAppleConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = F49FD8852DFB728A0019D638 /* UTMAppleConfiguration.swift */; };\n\t\tF4A21BF228032FD8001072B8 /* VMLibraryController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4A21BF128032FD8001072B8 /* VMLibraryController.swift */; };\n\t\tF4A21BF428033102001072B8 /* VBError.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4A21BF328033102001072B8 /* VBError.swift */; };\n\t\tF4A7FB3B2BB5E79100E4C12A /* DirectoryObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4A7FB3A2BB5E79100E4C12A /* DirectoryObserver.swift */; };\n\t\tF4A7FB3D2BB5E8A200E4C12A /* VMSavedStatesController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4A7FB3C2BB5E8A200E4C12A /* VMSavedStatesController.swift */; };\n\t\tF4A7FB3F2BB5EBEF00E4C12A /* SavedStatePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4A7FB3E2BB5EBEF00E4C12A /* SavedStatePicker.swift */; };\n\t\tF4A7FB472BB5ED7E00E4C12A /* Save-2024-03-27_16;06;24.vbst in Copy Preview Saved States */ = {isa = PBXBuildFile; fileRef = F4A7FB402BB5ED4A00E4C12A /* Save-2024-03-27_16;06;24.vbst */; };\n\t\tF4A7FB482BB5ED7E00E4C12A /* Save-2024-03-27_16;07;04.vbst in Copy Preview Saved States */ = {isa = PBXBuildFile; fileRef = F4A7FB412BB5ED4A00E4C12A /* Save-2024-03-27_16;07;04.vbst */; };\n\t\tF4A7FB492BB5ED7E00E4C12A /* Save-2024-03-27_16;08;06.vbst in Copy Preview Saved States */ = {isa = PBXBuildFile; fileRef = F4A7FB422BB5ED4A00E4C12A /* Save-2024-03-27_16;08;06.vbst */; };\n\t\tF4A7FB4A2BB5ED7E00E4C12A /* Save-2024-03-27_16;08;28.vbst in Copy Preview Saved States */ = {isa = PBXBuildFile; fileRef = F4A7FB432BB5ED4A00E4C12A /* Save-2024-03-27_16;08;28.vbst */; };\n\t\tF4A7FB4B2BB5ED7E00E4C12A /* Save-2024-03-27_16;08;51.vbst in Copy Preview Saved States */ = {isa = PBXBuildFile; fileRef = F4A7FB442BB5ED4A00E4C12A /* Save-2024-03-27_16;08;51.vbst */; };\n\t\tF4A7FB6E2BB7206C00E4C12A /* SelfSizingGroupedForm.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4A7FB6D2BB7206C00E4C12A /* SelfSizingGroupedForm.swift */; };\n\t\tF4A7FB732BB7238A00E4C12A /* SwiftUIIntrospect-Static in Frameworks */ = {isa = PBXBuildFile; productRef = F4A7FB722BB7238A00E4C12A /* SwiftUIIntrospect-Static */; };\n\t\tF4A7FB752BB7252A00E4C12A /* PreviewSupport-VirtualUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4A7FB742BB7252A00E4C12A /* PreviewSupport-VirtualUI.swift */; };\n\t\tF4B5C5D32886FA8D005AA632 /* SharedFoldersManagementView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4B5C5D22886FA8D005AA632 /* SharedFoldersManagementView.swift */; };\n\t\tF4B5C5D52886FFB5005AA632 /* PointingDeviceConfigurationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4B5C5D42886FFB5005AA632 /* PointingDeviceConfigurationView.swift */; };\n\t\tF4B5C5D728870619005AA632 /* ConfigurationModels+Validation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4B5C5D628870619005AA632 /* ConfigurationModels+Validation.swift */; };\n\t\tF4B5C5D928870BBF005AA632 /* ConfigurationModels+Summary.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4B5C5D828870BBF005AA632 /* ConfigurationModels+Summary.swift */; };\n\t\tF4B5C5DB28873628005AA632 /* GroupedList.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4B5C5DA28873628005AA632 /* GroupedList.swift */; };\n\t\tF4BE580E29BA6C8D00C5525C /* DefaultsDomains.plist in Resources */ = {isa = PBXBuildFile; fileRef = F4BE580D29BA6C8D00C5525C /* DefaultsDomains.plist */; };\n\t\tF4BE581129BA6DFC00C5525C /* DefaultsImportController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4BE581029BA6DFC00C5525C /* DefaultsImportController.swift */; };\n\t\tF4BE581329BA6E0D00C5525C /* DefaultsDomainDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4BE581229BA6E0D00C5525C /* DefaultsDomainDescriptor.swift */; };\n\t\tF4BE584029BA7B7A00C5525C /* DefaultsDomain+ExportImport.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4BE583F29BA7B7A00C5525C /* DefaultsDomain+ExportImport.swift */; };\n\t\tF4BE584229BA7BDB00C5525C /* WHDefaultsImportService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4BE584129BA7BDB00C5525C /* WHDefaultsImportService.swift */; };\n\t\tF4BE9C5227FF052100B648F8 /* VirtualBuddyApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4BE9C5127FF052100B648F8 /* VirtualBuddyApp.swift */; };\n\t\tF4BE9C5627FF052100B648F8 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F4BE9C5527FF052100B648F8 /* Assets.xcassets */; };\n\t\tF4BE9C5927FF052100B648F8 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F4BE9C5827FF052100B648F8 /* Preview Assets.xcassets */; };\n\t\tF4BE9C6827FF053A00B648F8 /* VirtualCore.h in Headers */ = {isa = PBXBuildFile; fileRef = F4BE9C6727FF053A00B648F8 /* VirtualCore.h */; settings = {ATTRIBUTES = (Public, ); }; };\n\t\tF4BE9C6B27FF053A00B648F8 /* VirtualCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F4BE9C6527FF053A00B648F8 /* VirtualCore.framework */; };\n\t\tF4BE9C6C27FF053A00B648F8 /* VirtualCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = F4BE9C6527FF053A00B648F8 /* VirtualCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };\n\t\tF4BE9C7627FF055100B648F8 /* VBVirtualMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4BE9C7327FF055100B648F8 /* VBVirtualMachine.swift */; };\n\t\tF4BE9C7827FF055100B648F8 /* MacOSVirtualMachineConfigurationHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4BE9C7527FF055100B648F8 /* MacOSVirtualMachineConfigurationHelper.swift */; };\n\t\tF4BE9C7A27FF05B900B648F8 /* VMController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4BE9C7927FF05B900B648F8 /* VMController.swift */; };\n\t\tF4BE9C8127FF111100B648F8 /* VirtualBuddyAppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4BE9C8027FF111100B648F8 /* VirtualBuddyAppDelegate.swift */; };\n\t\tF4BE9C8627FF140F00B648F8 /* VirtualizationPrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = F4BE9C8527FF13FF00B648F8 /* VirtualizationPrivate.h */; settings = {ATTRIBUTES = (Public, ); }; };\n\t\tF4C189E32848F59F00335EC7 /* VirtualWormhole.h in Headers */ = {isa = PBXBuildFile; fileRef = F4C189E22848F59F00335EC7 /* VirtualWormhole.h */; settings = {ATTRIBUTES = (Public, ); }; };\n\t\tF4C189E62848F59F00335EC7 /* VirtualWormhole.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F4C189E02848F59F00335EC7 /* VirtualWormhole.framework */; };\n\t\tF4C189E72848F59F00335EC7 /* VirtualWormhole.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = F4C189E02848F59F00335EC7 /* VirtualWormhole.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };\n\t\tF4C189EE2848F5B500335EC7 /* WormholeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4C189ED2848F5B500335EC7 /* WormholeManager.swift */; };\n\t\tF4C189F22848F5F500335EC7 /* VirtualWormhole.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F4C189E02848F59F00335EC7 /* VirtualWormhole.framework */; settings = {ATTRIBUTES = (Required, ); }; };\n\t\tF4C189F72848F6A600335EC7 /* VirtualCoreConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4C189F62848F6A600335EC7 /* VirtualCoreConstants.swift */; };\n\t\tF4C189FA2848F6F700335EC7 /* VirtualWormholeConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4C189F92848F6F700335EC7 /* VirtualWormholeConstants.swift */; };\n\t\tF4C189FD2848F8F600335EC7 /* WHSharedClipboardService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4C189FC2848F8F600335EC7 /* WHSharedClipboardService.swift */; };\n\t\tF4C189FF2848FB3F00335EC7 /* WormholeServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4C189FE2848FB3F00335EC7 /* WormholeServiceProtocol.swift */; };\n\t\tF4C18A4528491B8500335EC7 /* GuestAppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4C18A4428491B8500335EC7 /* GuestAppDelegate.swift */; };\n\t\tF4C18A4728491B8500335EC7 /* GuestDashboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4C18A4628491B8500335EC7 /* GuestDashboard.swift */; };\n\t\tF4C18A4928491B8500335EC7 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F4C18A4828491B8500335EC7 /* Assets.xcassets */; };\n\t\tF4C18A4C28491B8500335EC7 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F4C18A4B28491B8500335EC7 /* Preview Assets.xcassets */; };\n\t\tF4C18A5228491B9D00335EC7 /* VirtualWormhole.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F4C189E02848F59F00335EC7 /* VirtualWormhole.framework */; };\n\t\tF4C18A5328491B9D00335EC7 /* VirtualWormhole.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = F4C189E02848F59F00335EC7 /* VirtualWormhole.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };\n\t\tF4C2374D2888A462001FF286 /* VolumeUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4C2374C2888A462001FF286 /* VolumeUtils.swift */; };\n\t\tF4C237502888AF67001FF286 /* LogStreamer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4C2374F2888AF67001FF286 /* LogStreamer.swift */; };\n\t\tF4C947BF2E0B0F71001ACC91 /* URL+ExtendedAttributes.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4C947BE2E0B0F71001ACC91 /* URL+ExtendedAttributes.swift */; };\n\t\tF4C947D62E0B12D0001ACC91 /* String+AppleOSBuild.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4C947D52E0B12D0001ACC91 /* String+AppleOSBuild.swift */; };\n\t\tF4C947DA2E0B1E5D001ACC91 /* SoftwareCatalog+DownloadMatching.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4C947D92E0B1E5D001ACC91 /* SoftwareCatalog+DownloadMatching.swift */; };\n\t\tF4CD13202E05A5780067DC75 /* FileSystemPathFormControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4CD131F2E05A5780067DC75 /* FileSystemPathFormControl.swift */; };\n\t\tF4CD133C2E05A9DF0067DC75 /* OpenVirtualBuddySettingsAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4CD133B2E05A9DF0067DC75 /* OpenVirtualBuddySettingsAction.swift */; };\n\t\tF4CD133E2E05AB280067DC75 /* BackwardsCompatibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4CD133D2E05AB280067DC75 /* BackwardsCompatibility.swift */; };\n\t\tF4CD13402E05AB8F0067DC75 /* GeneralSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4CD133F2E05AB8F0067DC75 /* GeneralSettingsView.swift */; };\n\t\tF4CD13442E05AD400067DC75 /* VerticalLabeledContentStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4CD13432E05AD400067DC75 /* VerticalLabeledContentStyle.swift */; };\n\t\tF4CD13462E05B4DE0067DC75 /* VirtualizationSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4CD13452E05B4DE0067DC75 /* VirtualizationSettingsView.swift */; };\n\t\tF4CD13482E05B67E0067DC75 /* SettingsFooter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4CD13472E05B67E0067DC75 /* SettingsFooter.swift */; };\n\t\tF4CD134A2E05CB390067DC75 /* AutomationSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4CD13492E05CB390067DC75 /* AutomationSettingsView.swift */; };\n\t\tF4D0F71528667984004D5782 /* VBVirtualMachine+Screenshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4D0F71428667984004D5782 /* VBVirtualMachine+Screenshot.swift */; };\n\t\tF4D0F71828674E4B004D5782 /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = F4D0F71728674E4B004D5782 /* Sparkle */; };\n\t\tF4D0F71A28674E76004D5782 /* SoftwareUpdateController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4D0F71928674E76004D5782 /* SoftwareUpdateController.swift */; };\n\t\tF4D0F71F2867517A004D5782 /* AppUpdateChannel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4D0F71E2867517A004D5782 /* AppUpdateChannel.swift */; };\n\t\tF4D3059729B8D9D30006E748 /* WormholePacket.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4D3059629B8D9D30006E748 /* WormholePacket.swift */; };\n\t\tF4D3059F29B8DB700006E748 /* WormholePacketTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4D3059E29B8DB700006E748 /* WormholePacketTests.swift */; };\n\t\tF4D305A029B8DB700006E748 /* VirtualWormhole.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F4C189E02848F59F00335EC7 /* VirtualWormhole.framework */; };\n\t\tF4D305AA29B8E7120006E748 /* TestStream.bin in Resources */ = {isa = PBXBuildFile; fileRef = F4D305A929B8E7120006E748 /* TestStream.bin */; };\n\t\tF4D305AC29B8FEE90006E748 /* WHDarwinNotificationsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4D305AB29B8FEE90006E748 /* WHDarwinNotificationsService.swift */; };\n\t\tF4D305B029B900860006E748 /* SystemNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4D305AF29B900860006E748 /* SystemNotification.swift */; };\n\t\tF4D305B229B907A10006E748 /* WHPing.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4D305B129B907A10006E748 /* WHPing.swift */; };\n\t\tF4D725FE286677B8001818F7 /* VBVirtualMachine+Metadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4D725FD286677B8001818F7 /* VBVirtualMachine+Metadata.swift */; };\n\t\tF4DE1C0B2D6F54E700603527 /* VBSavedStateMetadata+Clone.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4DE1C0A2D6F54E000603527 /* VBSavedStateMetadata+Clone.swift */; };\n\t\tF4DE1C0F2D6F603300603527 /* VBSavedStatePackage+VM.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4DE1C0E2D6F603300603527 /* VBSavedStatePackage+VM.swift */; };\n\t\tF4DE1C112D6F642E00603527 /* VBStorageDeviceContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4DE1C102D6F642E00603527 /* VBStorageDeviceContainer.swift */; };\n\t\tF4E4F6C52DEF96C200B3B8BA /* ChromeBorderModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4E4F6C42DEF96C200B3B8BA /* ChromeBorderModifier.swift */; };\n\t\tF4E4F7202DF080FC00B3B8BA /* RestoreImageSelectionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4E4F71F2DF080FC00B3B8BA /* RestoreImageSelectionController.swift */; };\n\t\tF4E4F7262DF0A1CB00B3B8BA /* BuddyKit in Frameworks */ = {isa = PBXBuildFile; productRef = F4E4F7252DF0A1CB00B3B8BA /* BuddyKit */; };\n\t\tF4E7680A29B64C590075A897 /* GuestTypePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4E7680929B64C590075A897 /* GuestTypePicker.swift */; };\n\t\tF4E7680D29B651220075A897 /* VirtualUI.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F4E7680C29B651220075A897 /* VirtualUI.xcassets */; };\n\t\tF4E7680F29B655DD0075A897 /* InstallMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4E7680E29B655DD0075A897 /* InstallMethod.swift */; };\n\t\tF4E7DF922BB3338900C459FC /* NSImage+HEIC.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4E7DF912BB3338900C459FC /* NSImage+HEIC.swift */; };\n\t\tF4E7DF952BB336F600C459FC /* VBSavedStatePackage.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4E7DF942BB336F600C459FC /* VBSavedStatePackage.swift */; };\n\t\tF4E7DF972BB33E1700C459FC /* VMLibraryController+SavedState.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4E7DF962BB33E1700C459FC /* VMLibraryController+SavedState.swift */; };\n\t\tF4E7DFCF2BB3587D00C459FC /* VirtualMachineSessionUIManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4E7DFCE2BB3587D00C459FC /* VirtualMachineSessionUIManager.swift */; };\n\t\tF4ECC6D52C63BFD5001DAC1D /* NumberDisplayMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4ECC6D42C63BFD5001DAC1D /* NumberDisplayMode.swift */; };\n\t\tF4F9B416284CE0F900F21737 /* VBSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4F9B415284CE0F900F21737 /* VBSettings.swift */; };\n\t\tF4F9B418284CE12000F21737 /* VBSettingsContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4F9B417284CE12000F21737 /* VBSettingsContainer.swift */; };\n\t\tF4F9B41A284CE37C00F21737 /* Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4F9B419284CE37C00F21737 /* Logging.swift */; };\n\t\tF4FC276729BBAE350012CB65 /* WormholeServiceClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4FC276629BBAE350012CB65 /* WormholeServiceClient.swift */; };\n\t\tF4FC276A29BBAE590012CB65 /* WHDefaultsImportClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4FC276929BBAE590012CB65 /* WHDefaultsImportClient.swift */; };\n\t\tF4FC276C29BBB3030012CB65 /* GuestDefaultsImportView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4FC276B29BBB3030012CB65 /* GuestDefaultsImportView.swift */; };\n\t\tF4FC98392BB386A000E511C9 /* ContinuousProgressIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4FC98382BB386A000E511C9 /* ContinuousProgressIndicator.swift */; };\n\t\tF4FC983B2BB386B500E511C9 /* MaskProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4FC983A2BB386B500E511C9 /* MaskProgressView.swift */; };\n\t\tF4FC983D2BB386DD00E511C9 /* VMProgressOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4FC983C2BB386DD00E511C9 /* VMProgressOverlay.swift */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXContainerItemProxy section */\n\t\tF413699C299179F8002CE8D3 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = F4BE9C4627FF052100B648F8 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = F4BE9C6427FF053A00B648F8;\n\t\t\tremoteInfo = VirtualCore;\n\t\t};\n\t\tF41369BC29918617002CE8D3 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = F4BE9C4627FF052100B648F8 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = F41369AA29918576002CE8D3;\n\t\t\tremoteInfo = VirtualBuddyGuestHelper;\n\t\t};\n\t\tF42862302AE87D7E0052F029 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = F4BE9C4627FF052100B648F8 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = F43B01142AD858FE00164CD1;\n\t\t\tremoteInfo = DeepLinkSecurity;\n\t\t};\n\t\tF4510A792AE2B3AB00E24DD9 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = F4BE9C4627FF052100B648F8 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = F43B01142AD858FE00164CD1;\n\t\t\tremoteInfo = DeepLinkSecurity;\n\t\t};\n\t\tF453C4262DF0B602007EAD5F /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = F4BE9C4627FF052100B648F8 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = F498ACFD2884BF13006F1C00;\n\t\t\tremoteInfo = VirtualUI;\n\t\t};\n\t\tF453C4582DF0BCEB007EAD5F /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = F4BE9C4627FF052100B648F8 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = F4BE9C4D27FF052100B648F8;\n\t\t\tremoteInfo = VirtualBuddy;\n\t\t};\n\t\tF498AD022884BF13006F1C00 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = F4BE9C4627FF052100B648F8 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = F498ACFD2884BF13006F1C00;\n\t\t\tremoteInfo = VirtualUI;\n\t\t};\n\t\tF498AD0A2884BF60006F1C00 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = F4BE9C4627FF052100B648F8 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = F4BE9C6427FF053A00B648F8;\n\t\t\tremoteInfo = VirtualCore;\n\t\t};\n\t\tF4C189EF2848F5F100335EC7 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = F4BE9C4627FF052100B648F8 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = F4C189DF2848F59F00335EC7;\n\t\t\tremoteInfo = VirtualWormhole;\n\t\t};\n\t\tF4C18A5428491B9D00335EC7 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = F4BE9C4627FF052100B648F8 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = F4C189DF2848F59F00335EC7;\n\t\t\tremoteInfo = VirtualWormhole;\n\t\t};\n\t\tF4C18A5728491C0D00335EC7 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = F4BE9C4627FF052100B648F8 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = F4C18A4128491B8500335EC7;\n\t\t\tremoteInfo = VirtualBuddyGuest;\n\t\t};\n\t\tF4D305A129B8DB700006E748 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = F4BE9C4627FF052100B648F8 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = F4C189DF2848F59F00335EC7;\n\t\t\tremoteInfo = VirtualWormhole;\n\t\t};\n/* End PBXContainerItemProxy section */\n\n/* Begin PBXCopyFilesBuildPhase section */\n\t\tF41369BE2991861C002CE8D3 /* Embed Login Item */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = Contents/Library/LoginItems;\n\t\t\tdstSubfolderSpec = 1;\n\t\t\tfiles = (\n\t\t\t\tF41369BF2991863A002CE8D3 /* VirtualBuddyGuestHelper.app in Embed Login Item */,\n\t\t\t);\n\t\t\tname = \"Embed Login Item\";\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tF443620B29B79A5800745B43 /* Embed Guest App */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = \"\";\n\t\t\tdstSubfolderSpec = 12;\n\t\t\tfiles = (\n\t\t\t\tF443620C29B79A6800745B43 /* VirtualBuddyGuest.app in Embed Guest App */,\n\t\t\t);\n\t\t\tname = \"Embed Guest App\";\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tF45502222DF46386005582A4 /* Copy Preview Library Downloads */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = PreviewLibrary/_Downloads;\n\t\t\tdstSubfolderSpec = 7;\n\t\t\tfiles = (\n\t\t\t\tF45502232DF463A1005582A4 /* UniversalMac_13.3.1_22E261_Restore.ipsw in Copy Preview Library Downloads */,\n\t\t\t\tF45502242DF463A1005582A4 /* UniversalMac_14.0_23A344_Restore.ipsw in Copy Preview Library Downloads */,\n\t\t\t\tF45502252DF463A1005582A4 /* UniversalMac_14.5_23F79_Restore.ipsw in Copy Preview Library Downloads */,\n\t\t\t\tF45502262DF463A1005582A4 /* UniversalMac_15.3_24D60_Restore.ipsw in Copy Preview Library Downloads */,\n\t\t\t\tF45502272DF463A1005582A4 /* UniversalMac_15.5_24F74_Restore.ipsw in Copy Preview Library Downloads */,\n\t\t\t);\n\t\t\tname = \"Copy Preview Library Downloads\";\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tF47BCD9C2C5BE89E00165191 /* Copy Software Catalog Feeds */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = SoftwareCatalog;\n\t\t\tdstSubfolderSpec = 7;\n\t\t\tfiles = (\n\t\t\t\tF47BCDA12C5BE8FF00165191 /* ipsws_v2.json in Copy Software Catalog Feeds */,\n\t\t\t\tF47BCDA22C5BE8FF00165191 /* linux_v2.json in Copy Software Catalog Feeds */,\n\t\t\t);\n\t\t\tname = \"Copy Software Catalog Feeds\";\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tF4A7FB462BB5ED6400E4C12A /* Copy Preview Saved States */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = PreviewLibrary/_SavedStates;\n\t\t\tdstSubfolderSpec = 7;\n\t\t\tfiles = (\n\t\t\t\tF4A7FB472BB5ED7E00E4C12A /* Save-2024-03-27_16;06;24.vbst in Copy Preview Saved States */,\n\t\t\t\tF4A7FB482BB5ED7E00E4C12A /* Save-2024-03-27_16;07;04.vbst in Copy Preview Saved States */,\n\t\t\t\tF4A7FB492BB5ED7E00E4C12A /* Save-2024-03-27_16;08;06.vbst in Copy Preview Saved States */,\n\t\t\t\tF4A7FB4A2BB5ED7E00E4C12A /* Save-2024-03-27_16;08;28.vbst in Copy Preview Saved States */,\n\t\t\t\tF4A7FB4B2BB5ED7E00E4C12A /* Save-2024-03-27_16;08;51.vbst in Copy Preview Saved States */,\n\t\t\t);\n\t\t\tname = \"Copy Preview Saved States\";\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tF4A7FB4C2BB5F0B700E4C12A /* Copy Preview Library */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = PreviewLibrary;\n\t\t\tdstSubfolderSpec = 7;\n\t\t\tfiles = (\n\t\t\t\tF49B82A82E0304D400395F87 /* PreviewMac.vbvm in Copy Preview Library */,\n\t\t\t\tF49B82A92E0304D400395F87 /* PreviewMacBlurHash.vbvm in Copy Preview Library */,\n\t\t\t\tF49B82AA2E0304D400395F87 /* PreviewMacNoArtwork.vbvm in Copy Preview Library */,\n\t\t\t\tF49B82AB2E0304D400395F87 /* PreviewLinux.vbvm in Copy Preview Library */,\n\t\t\t\tF49B82AC2E0304D400395F87 /* PreviewLinuxBlurHash.vbvm in Copy Preview Library */,\n\t\t\t\tF49B82AD2E0304D400395F87 /* PreviewLinuxNoArtwork.vbvm in Copy Preview Library */,\n\t\t\t);\n\t\t\tname = \"Copy Preview Library\";\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tF4BE9C7027FF053A00B648F8 /* Embed Frameworks */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = \"\";\n\t\t\tdstSubfolderSpec = 10;\n\t\t\tfiles = (\n\t\t\t\tF4BE9C6C27FF053A00B648F8 /* VirtualCore.framework in Embed Frameworks */,\n\t\t\t\tF498AD052884BF13006F1C00 /* VirtualUI.framework in Embed Frameworks */,\n\t\t\t\tF43B011C2AD858FE00164CD1 /* DeepLinkSecurity.framework in Embed Frameworks */,\n\t\t\t\tF4C189E72848F59F00335EC7 /* VirtualWormhole.framework in Embed Frameworks */,\n\t\t\t);\n\t\t\tname = \"Embed Frameworks\";\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tF4C18A5628491B9D00335EC7 /* Embed Frameworks */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = \"\";\n\t\t\tdstSubfolderSpec = 10;\n\t\t\tfiles = (\n\t\t\t\tF453C4252DF0B602007EAD5F /* VirtualUI.framework in Embed Frameworks */,\n\t\t\t\tF4C18A5328491B9D00335EC7 /* VirtualWormhole.framework in Embed Frameworks */,\n\t\t\t\tF428622F2AE87D7E0052F029 /* DeepLinkSecurity.framework in Embed Frameworks */,\n\t\t\t\tF413699B299179F8002CE8D3 /* VirtualCore.framework in Embed Frameworks */,\n\t\t\t);\n\t\t\tname = \"Embed Frameworks\";\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXCopyFilesBuildPhase section */\n\n/* Begin PBXFileReference section */\n\t\t0196B45229292B2A00614EF1 /* LinuxVirtualMachineConfigurationHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LinuxVirtualMachineConfigurationHelper.swift; sourceTree = \"<group>\"; };\n\t\t4BA6BE7C293D22E500F396AE /* VirtualMachineConfigurationHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VirtualMachineConfigurationHelper.swift; sourceTree = \"<group>\"; };\n\t\tF40A1E9C2C1873C60033E47D /* VBBuildType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VBBuildType.swift; sourceTree = \"<group>\"; };\n\t\tF413695129916F6E002CE8D3 /* StatusItemButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusItemButton.swift; sourceTree = \"<group>\"; };\n\t\tF413695229916F6E002CE8D3 /* StatusBarPanelChrome.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusBarPanelChrome.swift; sourceTree = \"<group>\"; };\n\t\tF413695329916F6E002CE8D3 /* StatusItemProviderProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusItemProviderProtocol.swift; sourceTree = \"<group>\"; };\n\t\tF413695529916F6E002CE8D3 /* StatusItemPanelContentController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusItemPanelContentController.swift; sourceTree = \"<group>\"; };\n\t\tF413695729916F6E002CE8D3 /* VUIAppKitViewControllerHost.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VUIAppKitViewControllerHost.swift; sourceTree = \"<group>\"; };\n\t\tF413695829916F6E002CE8D3 /* StatusItemMenuBarExtraView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusItemMenuBarExtraView.swift; sourceTree = \"<group>\"; };\n\t\tF413695929916F6E002CE8D3 /* StatusBarContentPanel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusBarContentPanel.swift; sourceTree = \"<group>\"; };\n\t\tF413695A29916F6E002CE8D3 /* StatusBarHighlightView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusBarHighlightView.swift; sourceTree = \"<group>\"; };\n\t\tF413695C29916F6E002CE8D3 /* NSStatusItem+.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = \"NSStatusItem+.h\"; sourceTree = \"<group>\"; };\n\t\tF413695D29916F6E002CE8D3 /* NSApplication+MenuBar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = \"NSApplication+MenuBar.h\"; sourceTree = \"<group>\"; };\n\t\tF413695E29916F6E002CE8D3 /* NSStatusBarPrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSStatusBarPrivate.h; sourceTree = \"<group>\"; };\n\t\tF413695F29916F6E002CE8D3 /* NSApplication+MenuBar.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = \"NSApplication+MenuBar.m\"; sourceTree = \"<group>\"; };\n\t\tF413696029916F6E002CE8D3 /* NSStatusItem+.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = \"NSStatusItem+.m\"; sourceTree = \"<group>\"; };\n\t\tF413696129916F6E002CE8D3 /* StatusItemManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StatusItemManager.swift; sourceTree = \"<group>\"; };\n\t\tF413697829917135002CE8D3 /* CGFloat+OnePixel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = \"CGFloat+OnePixel.swift\"; sourceTree = \"<group>\"; };\n\t\tF4136998299179B1002CE8D3 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = \"<group>\"; };\n\t\tF41369A229917FA0002CE8D3 /* ScreenChangeModifier.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScreenChangeModifier.swift; sourceTree = \"<group>\"; };\n\t\tF41369A5299183C8002CE8D3 /* GuestLaunchAtLoginManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GuestLaunchAtLoginManager.swift; sourceTree = \"<group>\"; };\n\t\tF41369AB29918576002CE8D3 /* VirtualBuddyGuestHelper.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = VirtualBuddyGuestHelper.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tF41369AD29918576002CE8D3 /* GuestHelperAppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GuestHelperAppDelegate.swift; sourceTree = \"<group>\"; };\n\t\tF41369B129918576002CE8D3 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\tF41369B429918576002CE8D3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = \"<group>\"; };\n\t\tF41369B629918576002CE8D3 /* VirtualBuddyGuestHelper.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = VirtualBuddyGuestHelper.entitlements; sourceTree = \"<group>\"; };\n\t\tF41369C5299187E1002CE8D3 /* VirtualBuddyGuest-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = \"VirtualBuddyGuest-Bridging-Header.h\"; sourceTree = \"<group>\"; };\n\t\tF41369C92991A492002CE8D3 /* HostConnectionStateProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HostConnectionStateProvider.swift; sourceTree = \"<group>\"; };\n\t\tF41369CB2991A68F002CE8D3 /* GuestSharedFoldersManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GuestSharedFoldersManager.swift; sourceTree = \"<group>\"; };\n\t\tF417255C288604A8004FF8A7 /* SoundConfigurationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoundConfigurationView.swift; sourceTree = \"<group>\"; };\n\t\tF417255E28861604004FF8A7 /* DecodableDefault.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DecodableDefault.swift; sourceTree = \"<group>\"; };\n\t\tF417256028861A05004FF8A7 /* SharingConfigurationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharingConfigurationView.swift; sourceTree = \"<group>\"; };\n\t\tF41725622886DD37004FF8A7 /* SharedFolderListItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedFolderListItem.swift; sourceTree = \"<group>\"; };\n\t\tF41725652886DF58004FF8A7 /* OpenSavePanelUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenSavePanelUtils.swift; sourceTree = \"<group>\"; };\n\t\tF41725672886E5AD004FF8A7 /* MaterialView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MaterialView.swift; sourceTree = \"<group>\"; };\n\t\tF417256B2887500F004FF8A7 /* StorageConfigurationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageConfigurationView.swift; sourceTree = \"<group>\"; };\n\t\tF417256E2887544A004FF8A7 /* StorageDeviceDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageDeviceDetailView.swift; sourceTree = \"<group>\"; };\n\t\tF417257028877121004FF8A7 /* DiskImageGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiskImageGenerator.swift; sourceTree = \"<group>\"; };\n\t\tF417257328877478004FF8A7 /* VirtualCore.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = VirtualCore.xcassets; sourceTree = \"<group>\"; };\n\t\tF41725752887758A004FF8A7 /* RandomNameGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RandomNameGenerator.swift; sourceTree = \"<group>\"; };\n\t\tF417CB872E0EDECD0065B5D6 /* BackportedContentUnavailableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackportedContentUnavailableView.swift; sourceTree = \"<group>\"; };\n\t\tF417CB892E0EDF1A0065B5D6 /* EqualWidthHStack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EqualWidthHStack.swift; sourceTree = \"<group>\"; };\n\t\tF417CBB82E0F3D2E0065B5D6 /* FirstLaunchExperienceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirstLaunchExperienceView.swift; sourceTree = \"<group>\"; };\n\t\tF422586C2885CC9F009420AE /* SharedFocusEnvironment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedFocusEnvironment.swift; sourceTree = \"<group>\"; };\n\t\tF422586F2885D537009420AE /* EphemeralTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EphemeralTextField.swift; sourceTree = \"<group>\"; };\n\t\tF42258712885E100009420AE /* VMConfigurationSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VMConfigurationSheet.swift; sourceTree = \"<group>\"; };\n\t\tF42258732885E10B009420AE /* VMConfigurationViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VMConfigurationViewModel.swift; sourceTree = \"<group>\"; };\n\t\tF42258772885E14A009420AE /* DisplayConfigurationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayConfigurationView.swift; sourceTree = \"<group>\"; };\n\t\tF42258792885E17D009420AE /* ConfigurationSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationSection.swift; sourceTree = \"<group>\"; };\n\t\tF422587B2885E1CE009420AE /* NetworkConfigurationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkConfigurationView.swift; sourceTree = \"<group>\"; };\n\t\tF422587D2885E2ED009420AE /* HardwareConfigurationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HardwareConfigurationView.swift; sourceTree = \"<group>\"; };\n\t\tF422587F2885E71D009420AE /* PropertyControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PropertyControl.swift; sourceTree = \"<group>\"; };\n\t\tF428622C2AE8726D0052F029 /* VirtualMachineControls.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VirtualMachineControls.swift; sourceTree = \"<group>\"; };\n\t\tF42862362AE947C90052F029 /* WHPayload.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WHPayload.swift; sourceTree = \"<group>\"; };\n\t\tF42C01492888C2F800EB15CD /* InstallationConsole.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstallationConsole.swift; sourceTree = \"<group>\"; };\n\t\tF42C014B2888C34B00EB15CD /* LogConsole.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LogConsole.swift; sourceTree = \"<group>\"; };\n\t\tF42C014D2888CBCB00EB15CD /* InstallProgressStepView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstallProgressStepView.swift; sourceTree = \"<group>\"; };\n\t\tF42C01512888FC0C00EB15CD /* LibraryView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LibraryView.swift; sourceTree = \"<group>\"; };\n\t\tF42C01542888FC0C00EB15CD /* VMSessionConfigurationView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VMSessionConfigurationView.swift; sourceTree = \"<group>\"; };\n\t\tF42C01552888FC0C00EB15CD /* VirtualMachineSessionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VirtualMachineSessionView.swift; sourceTree = \"<group>\"; };\n\t\tF42C01572888FC0C00EB15CD /* SwiftUIVMView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftUIVMView.swift; sourceTree = \"<group>\"; };\n\t\tF42C01592888FC0C00EB15CD /* SettingsScreen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsScreen.swift; sourceTree = \"<group>\"; };\n\t\tF42C01602888FC3500EB15CD /* LibraryItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryItemView.swift; sourceTree = \"<group>\"; };\n\t\tF42CF4A72DF5FEC3001DE049 /* BlurHashToken.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlurHashToken.swift; sourceTree = \"<group>\"; };\n\t\tF43B01152AD858FE00164CD1 /* DeepLinkSecurity.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = DeepLinkSecurity.framework; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tF43B01172AD858FE00164CD1 /* DeepLinkSecurity.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DeepLinkSecurity.h; sourceTree = \"<group>\"; };\n\t\tF43B01242AD8590F00164CD1 /* DeepLinkAuthUI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeepLinkAuthUI.swift; sourceTree = \"<group>\"; };\n\t\tF43B01252AD8590F00164CD1 /* DeepLinkSentinel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeepLinkSentinel.swift; sourceTree = \"<group>\"; };\n\t\tF43B01272AD8590F00164CD1 /* OpenDeepLinkRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenDeepLinkRequest.swift; sourceTree = \"<group>\"; };\n\t\tF43B01282AD8590F00164CD1 /* DeepLinkClientDescriptor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeepLinkClientDescriptor.swift; sourceTree = \"<group>\"; };\n\t\tF43B012A2AD8590F00164CD1 /* DeepLinkClient+Crypto.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = \"DeepLinkClient+Crypto.swift\"; sourceTree = \"<group>\"; };\n\t\tF43B012B2AD8590F00164CD1 /* DeepLinkClientDescriptor+.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = \"DeepLinkClientDescriptor+.swift\"; sourceTree = \"<group>\"; };\n\t\tF43B012C2AD8590F00164CD1 /* DeepLinkClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeepLinkClient.swift; sourceTree = \"<group>\"; };\n\t\tF43B012E2AD8590F00164CD1 /* KeychainDeepLinkAuthStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeychainDeepLinkAuthStore.swift; sourceTree = \"<group>\"; };\n\t\tF43B012F2AD8590F00164CD1 /* MemoryDeepLinkAuthStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MemoryDeepLinkAuthStore.swift; sourceTree = \"<group>\"; };\n\t\tF43B01302AD8590F00164CD1 /* DeepLinkManagementStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeepLinkManagementStore.swift; sourceTree = \"<group>\"; };\n\t\tF43B01312AD8590F00164CD1 /* UserDefaultsDeepLinkManagementStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserDefaultsDeepLinkManagementStore.swift; sourceTree = \"<group>\"; };\n\t\tF43B01322AD8590F00164CD1 /* DeepLinkAuthStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeepLinkAuthStore.swift; sourceTree = \"<group>\"; };\n\t\tF43B01342AD8590F00164CD1 /* DeepLinkSecurityDefines.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeepLinkSecurityDefines.swift; sourceTree = \"<group>\"; };\n\t\tF43B01432AD85A6500164CD1 /* VirtualBuddyDeepLinks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VirtualBuddyDeepLinks.swift; sourceTree = \"<group>\"; };\n\t\tF43B01492AD85ABB00164CD1 /* DeepLinkAuthDialog.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeepLinkAuthDialog.swift; sourceTree = \"<group>\"; };\n\t\tF43B014D2AD86BFA00164CD1 /* DeepLinkHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeepLinkHandler.swift; sourceTree = \"<group>\"; };\n\t\tF443620929B7947A00745B43 /* GuestAdditionsDiskImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GuestAdditionsDiskImage.swift; sourceTree = \"<group>\"; };\n\t\tF443620E29B7A0C600745B43 /* CreateGuestImage.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = CreateGuestImage.sh; sourceTree = \"<group>\"; };\n\t\tF444D0C92DF321CD0086537A /* CatalogGroupPlaceholder.heic */ = {isa = PBXFileReference; lastKnownFileType = file; path = CatalogGroupPlaceholder.heic; sourceTree = \"<group>\"; };\n\t\tF444D0CB2DF322B50086537A /* SoftwareCatalog+Placeholder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = \"SoftwareCatalog+Placeholder.swift\"; sourceTree = \"<group>\"; };\n\t\tF444D0CD2DF32E100086537A /* BlurHashFullBleedBackground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlurHashFullBleedBackground.swift; sourceTree = \"<group>\"; };\n\t\tF444D0F32DF34BE40086537A /* CALayer+Asset.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = \"CALayer+Asset.swift\"; sourceTree = \"<group>\"; };\n\t\tF444D0F52DF37D170086537A /* VirtualBuddyMonoIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VirtualBuddyMonoIcon.swift; sourceTree = \"<group>\"; };\n\t\tF444D0F72DF37D410086537A /* VirtualDisplayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VirtualDisplayView.swift; sourceTree = \"<group>\"; };\n\t\tF444D0F92DF37DFB0086537A /* InstallProgressDisplayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstallProgressDisplayView.swift; sourceTree = \"<group>\"; };\n\t\tF444D0FB2DF37EF80086537A /* VirtualBuddyMonoProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VirtualBuddyMonoProgressView.swift; sourceTree = \"<group>\"; };\n\t\tF444D1332BB478AD00AB786F /* VBMemoryLeakDebugAssertions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VBMemoryLeakDebugAssertions.swift; sourceTree = \"<group>\"; };\n\t\tF4450CC92ACB0DB500092618 /* KeyboardDeviceConfigurationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardDeviceConfigurationView.swift; sourceTree = \"<group>\"; };\n\t\tF44C00FA2889CE1600640BF5 /* VBVirtualMachine+Virtualization.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = \"VBVirtualMachine+Virtualization.swift\"; sourceTree = \"<group>\"; };\n\t\tF4510A772AE2A16F00E24DD9 /* WeakReference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeakReference.swift; sourceTree = \"<group>\"; };\n\t\tF453C4162DF0B43D007EAD5F /* BlurHashEncode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlurHashEncode.swift; sourceTree = \"<group>\"; };\n\t\tF453C4182DF0B43D007EAD5F /* LegacyCatalog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacyCatalog.swift; sourceTree = \"<group>\"; };\n\t\tF453C4192DF0B43D007EAD5F /* MobileDeviceFramework.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MobileDeviceFramework.swift; sourceTree = \"<group>\"; };\n\t\tF453C41A2DF0B43D007EAD5F /* ResolvedCatalog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResolvedCatalog.swift; sourceTree = \"<group>\"; };\n\t\tF453C41B2DF0B43D007EAD5F /* SoftwareCatalog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoftwareCatalog.swift; sourceTree = \"<group>\"; };\n\t\tF453C4222DF0B5B1007EAD5F /* VirtualBuddyEntryPoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VirtualBuddyEntryPoint.swift; sourceTree = \"<group>\"; };\n\t\tF453C42C2DF0B7A5007EAD5F /* BuildManifest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BuildManifest.swift; sourceTree = \"<group>\"; };\n\t\tF453C42D2DF0B7A5007EAD5F /* BuildManifest+Fetch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = \"BuildManifest+Fetch.swift\"; sourceTree = \"<group>\"; };\n\t\tF453C42E2DF0B7A5007EAD5F /* Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Helpers.swift; sourceTree = \"<group>\"; };\n\t\tF453C42F2DF0B7A5007EAD5F /* TreeStringConvertible.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TreeStringConvertible.swift; sourceTree = \"<group>\"; };\n\t\tF453C4302DF0B7A5007EAD5F /* URL+ContentLength.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = \"URL+ContentLength.swift\"; sourceTree = \"<group>\"; };\n\t\tF453C4322DF0B7A5007EAD5F /* CatalogCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CatalogCommand.swift; sourceTree = \"<group>\"; };\n\t\tF453C4332DF0B7A5007EAD5F /* GroupCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupCommand.swift; sourceTree = \"<group>\"; };\n\t\tF453C4342DF0B7A5007EAD5F /* ImageCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCommand.swift; sourceTree = \"<group>\"; };\n\t\tF453C4352DF0B7A5007EAD5F /* IPSWCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IPSWCommand.swift; sourceTree = \"<group>\"; };\n\t\tF453C4362DF0B7A5007EAD5F /* MigrateCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrateCommand.swift; sourceTree = \"<group>\"; };\n\t\tF453C4372DF0B7A5007EAD5F /* MobileDeviceCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MobileDeviceCommand.swift; sourceTree = \"<group>\"; };\n\t\tF453C4382DF0B7A5007EAD5F /* ResolveCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResolveCommand.swift; sourceTree = \"<group>\"; };\n\t\tF453C4392DF0B7A5007EAD5F /* VCTool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VCTool.swift; sourceTree = \"<group>\"; };\n\t\tF453C44B2DF0B835007EAD5F /* libcurl.tbd */ = {isa = PBXFileReference; lastKnownFileType = \"sourcecode.text-based-dylib-definition\"; name = libcurl.tbd; path = usr/lib/libcurl.tbd; sourceTree = SDKROOT; };\n\t\tF453C44D2DF0B869007EAD5F /* VirtualBuddyCLI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VirtualBuddyCLI.swift; sourceTree = \"<group>\"; };\n\t\tF453C45C2DF0D286007EAD5F /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = \"<group>\"; };\n\t\tF453C4672DF10181007EAD5F /* BlurHashCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlurHashCommand.swift; sourceTree = \"<group>\"; };\n\t\tF453C4882DF1CDA0007EAD5F /* DownloadBackend.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadBackend.swift; sourceTree = \"<group>\"; };\n\t\tF453C4912DF1D213007EAD5F /* FakeRestoreImage.ipsw */ = {isa = PBXFileReference; lastKnownFileType = file; path = FakeRestoreImage.ipsw; sourceTree = \"<group>\"; };\n\t\tF453C49A2DF1D768007EAD5F /* SimulatedDownloadBackend.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimulatedDownloadBackend.swift; sourceTree = \"<group>\"; };\n\t\tF453C49F2DF1D7C0007EAD5F /* RestoreBackend.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RestoreBackend.swift; sourceTree = \"<group>\"; };\n\t\tF453C4A12DF1D7F6007EAD5F /* SimulatedRestoreBackend.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimulatedRestoreBackend.swift; sourceTree = \"<group>\"; };\n\t\tF453C4A32DF1D85C007EAD5F /* VirtualizationRestoreBackend.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VirtualizationRestoreBackend.swift; sourceTree = \"<group>\"; };\n\t\tF453C4B32DF20301007EAD5F /* RestoreImageURLInputView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RestoreImageURLInputView.swift; sourceTree = \"<group>\"; };\n\t\tF453C4B82DF21985007EAD5F /* VMInstallData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VMInstallData.swift; sourceTree = \"<group>\"; };\n\t\tF453C4BA2DF231B7007EAD5F /* PreventTerminationAssertion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreventTerminationAssertion.swift; sourceTree = \"<group>\"; };\n\t\tF45502132DF394DC005582A4 /* VirtualBuddyInstallerInputView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VirtualBuddyInstallerInputView.swift; sourceTree = \"<group>\"; };\n\t\tF45502152DF45E4D005582A4 /* VBSettings+CatalogDownload.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = \"VBSettings+CatalogDownload.swift\"; sourceTree = \"<group>\"; };\n\t\tF45502182DF46368005582A4 /* UniversalMac_13.3.1_22E261_Restore.ipsw */ = {isa = PBXFileReference; lastKnownFileType = text; path = UniversalMac_13.3.1_22E261_Restore.ipsw; sourceTree = \"<group>\"; };\n\t\tF45502192DF46368005582A4 /* UniversalMac_14.0_23A344_Restore.ipsw */ = {isa = PBXFileReference; lastKnownFileType = text; path = UniversalMac_14.0_23A344_Restore.ipsw; sourceTree = \"<group>\"; };\n\t\tF455021A2DF46368005582A4 /* UniversalMac_14.5_23F79_Restore.ipsw */ = {isa = PBXFileReference; lastKnownFileType = text; path = UniversalMac_14.5_23F79_Restore.ipsw; sourceTree = \"<group>\"; };\n\t\tF455021B2DF46368005582A4 /* UniversalMac_15.3_24D60_Restore.ipsw */ = {isa = PBXFileReference; lastKnownFileType = text; path = UniversalMac_15.3_24D60_Restore.ipsw; sourceTree = \"<group>\"; };\n\t\tF455021C2DF46368005582A4 /* UniversalMac_15.5_24F74_Restore.ipsw */ = {isa = PBXFileReference; lastKnownFileType = text; path = UniversalMac_15.5_24F74_Restore.ipsw; sourceTree = \"<group>\"; };\n\t\tF4561A6728981B4100055289 /* VirtualMachineNameInputView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VirtualMachineNameInputView.swift; sourceTree = \"<group>\"; };\n\t\tF462C9412E0C96D300C172E2 /* FB18383725Window.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FB18383725Window.swift; sourceTree = \"<group>\"; };\n\t\tF465C3AD284F93A5006E9ED4 /* VBAPIClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VBAPIClient.swift; sourceTree = \"<group>\"; };\n\t\tF465C3AF284F9660006E9ED4 /* VBRestoreImagesResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VBRestoreImagesResponse.swift; sourceTree = \"<group>\"; };\n\t\tF465C3B1284F9666006E9ED4 /* VBRestoreImageInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VBRestoreImageInfo.swift; sourceTree = \"<group>\"; };\n\t\tF465C3B7284FA252006E9ED4 /* URLSessionDownloadBackend.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLSessionDownloadBackend.swift; sourceTree = \"<group>\"; };\n\t\tF46FFBA72804F07400D61023 /* VBNVRAMVariable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VBNVRAMVariable.swift; sourceTree = \"<group>\"; };\n\t\tF46FFBA92804F0A000D61023 /* VZVirtualMachineConfiguration+NVRAM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = \"VZVirtualMachineConfiguration+NVRAM.swift\"; sourceTree = \"<group>\"; };\n\t\tF46FFBAB28059FF600D61023 /* VMInstance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VMInstance.swift; sourceTree = \"<group>\"; };\n\t\tF47BCD9F2C5BE8EF00165191 /* ipsws_v2.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; name = ipsws_v2.json; path = data/ipsws_v2.json; sourceTree = SOURCE_ROOT; };\n\t\tF47BCDA02C5BE8EF00165191 /* linux_v2.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; name = linux_v2.json; path = data/linux_v2.json; sourceTree = SOURCE_ROOT; };\n\t\tF47BCDCA2C5C01CE00165191 /* BlurHashDecoding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlurHashDecoding.swift; sourceTree = \"<group>\"; };\n\t\tF47BCDCC2C5C01EF00165191 /* RemoteImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteImage.swift; sourceTree = \"<group>\"; };\n\t\tF47BCDCE2C5C023900165191 /* CatalogGroupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CatalogGroupView.swift; sourceTree = \"<group>\"; };\n\t\tF47BCDD02C5C06C900165191 /* CatalogGroupPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CatalogGroupPicker.swift; sourceTree = \"<group>\"; };\n\t\tF47BCDD22C5C0AB300165191 /* KeyboardNavigationModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardNavigationModifier.swift; sourceTree = \"<group>\"; };\n\t\tF47BCDD42C5C0B8C00165191 /* Array+Navigation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = \"Array+Navigation.swift\"; sourceTree = \"<group>\"; };\n\t\tF47BCDD62C5D2B4300165191 /* CatalogExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CatalogExtensions.swift; sourceTree = \"<group>\"; };\n\t\tF47BCDD82C5D2EDB00165191 /* RestoreImageBrowser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RestoreImageBrowser.swift; sourceTree = \"<group>\"; };\n\t\tF482FC7228CB7A6C00F2BA4F /* InfoPlist.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = InfoPlist.xcconfig; sourceTree = \"<group>\"; };\n\t\tF485B91A2BB22D2D004B3C2B /* VBSavedStateMetadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VBSavedStateMetadata.swift; sourceTree = \"<group>\"; };\n\t\tF485B91C2BB2F0D9004B3C2B /* ProcessInfo+ECID.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = \"ProcessInfo+ECID.swift\"; sourceTree = \"<group>\"; };\n\t\tF485B91E2BB2F4AC004B3C2B /* Bundle+Version.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = \"Bundle+Version.swift\"; sourceTree = \"<group>\"; };\n\t\tF485B9202BB306AF004B3C2B /* VBDebugUtil.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VBDebugUtil.h; sourceTree = \"<group>\"; };\n\t\tF485B9212BB306AF004B3C2B /* VBDebugUtil.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VBDebugUtil.m; sourceTree = \"<group>\"; };\n\t\tF48E0D02288858DF0080DDFA /* ManagedDiskImageEditor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManagedDiskImageEditor.swift; sourceTree = \"<group>\"; };\n\t\tF48E0D0628885E140080DDFA /* PreviewSupport.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreviewSupport.swift; sourceTree = \"<group>\"; };\n\t\tF48E0D0B2888760D0080DDFA /* VBMacDevice+Storage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = \"VBMacDevice+Storage.swift\"; sourceTree = \"<group>\"; };\n\t\tF48E0D14288882BD0080DDFA /* InstallationWizardTitle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InstallationWizardTitle.swift; sourceTree = \"<group>\"; };\n\t\tF48E0D15288882BD0080DDFA /* InstallMethodPicker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InstallMethodPicker.swift; sourceTree = \"<group>\"; };\n\t\tF48E0D16288882BD0080DDFA /* RestoreImageSelectionStep.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RestoreImageSelectionStep.swift; sourceTree = \"<group>\"; };\n\t\tF48E0D17288882BD0080DDFA /* AuthenticatingWebView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthenticatingWebView.swift; sourceTree = \"<group>\"; };\n\t\tF48E0D18288882BD0080DDFA /* VMInstallationWizard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VMInstallationWizard.swift; sourceTree = \"<group>\"; };\n\t\tF48E0D19288882BD0080DDFA /* VMInstallationViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VMInstallationViewModel.swift; sourceTree = \"<group>\"; };\n\t\tF48E0D20288882E50080DDFA /* DecentFormView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DecentFormView.swift; sourceTree = \"<group>\"; };\n\t\tF48E0D21288882E50080DDFA /* NSAlert+Confirmation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = \"NSAlert+Confirmation.swift\"; sourceTree = \"<group>\"; };\n\t\tF48E0D22288882E50080DDFA /* OnAppearOnce.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OnAppearOnce.swift; sourceTree = \"<group>\"; };\n\t\tF48E0D27288883150080DDFA /* OpenCocoaWindowAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenCocoaWindowAction.swift; sourceTree = \"<group>\"; };\n\t\tF48E0D28288883150080DDFA /* HostingWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HostingWindowController.swift; sourceTree = \"<group>\"; };\n\t\tF48E0D29288883150080DDFA /* WindowEnvironment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WindowEnvironment.swift; sourceTree = \"<group>\"; };\n\t\tF48E0D2E2888835A0080DDFA /* VirtualUIConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VirtualUIConstants.swift; sourceTree = \"<group>\"; };\n\t\tF48E0D31288884A10080DDFA /* RestoreImageDownloadView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RestoreImageDownloadView.swift; sourceTree = \"<group>\"; };\n\t\tF48E0D33288889E60080DDFA /* InstallConfigurationStepView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstallConfigurationStepView.swift; sourceTree = \"<group>\"; };\n\t\tF4959F3B2992A284001DF4CB /* GuestAppInstaller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GuestAppInstaller.swift; sourceTree = \"<group>\"; };\n\t\tF498ACFE2884BF13006F1C00 /* VirtualUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = VirtualUI.framework; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tF498AD002884BF13006F1C00 /* VirtualUI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VirtualUI.h; sourceTree = \"<group>\"; };\n\t\tF498AD0D2884BF9D006F1C00 /* VMConfigurationView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VMConfigurationView.swift; sourceTree = \"<group>\"; };\n\t\tF498AD112884C36A006F1C00 /* NumericValueField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NumericValueField.swift; sourceTree = \"<group>\"; };\n\t\tF498AD132884C36A006F1C00 /* ControlGroupChrome.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControlGroupChrome.swift; sourceTree = \"<group>\"; };\n\t\tF498AD172884C593006F1C00 /* NumericPropertyControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NumericPropertyControl.swift; sourceTree = \"<group>\"; };\n\t\tF498AD192884C5FF006F1C00 /* SliderConversion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SliderConversion.swift; sourceTree = \"<group>\"; };\n\t\tF49A68E02884917E00A17582 /* ConfigurationModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationModels.swift; sourceTree = \"<group>\"; };\n\t\tF49AA2C229BA22A5009625F7 /* VBRestorableWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VBRestorableWindow.swift; sourceTree = \"<group>\"; };\n\t\tF49AA2C429BA31CC009625F7 /* VirtualMachineSessionUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VirtualMachineSessionUI.swift; sourceTree = \"<group>\"; };\n\t\tF49AA2C629BA3F2B009625F7 /* VBRestorableWindow+Resizing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = \"VBRestorableWindow+Resizing.swift\"; sourceTree = \"<group>\"; };\n\t\tF49B82972E02F5A300395F87 /* VMArtworkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VMArtworkView.swift; sourceTree = \"<group>\"; };\n\t\tF49B829D2E02FCB100395F87 /* PreviewMacBlurHash.vbvm */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = PreviewMacBlurHash.vbvm; sourceTree = \"<group>\"; };\n\t\tF49B829E2E02FCB100395F87 /* PreviewLinux.vbvm */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = PreviewLinux.vbvm; sourceTree = \"<group>\"; };\n\t\tF49B829F2E02FCB100395F87 /* PreviewMac.vbvm */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = PreviewMac.vbvm; sourceTree = \"<group>\"; };\n\t\tF49B82A32E02FCF400395F87 /* PreviewMacNoArtwork.vbvm */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = PreviewMacNoArtwork.vbvm; sourceTree = \"<group>\"; };\n\t\tF49B82A52E03049C00395F87 /* PreviewLinuxBlurHash.vbvm */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = PreviewLinuxBlurHash.vbvm; sourceTree = \"<group>\"; };\n\t\tF49B82A62E03049C00395F87 /* PreviewLinuxNoArtwork.vbvm */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = PreviewLinuxNoArtwork.vbvm; sourceTree = \"<group>\"; };\n\t\tF49B82FB2E034EAD00395F87 /* WHDesktopPictureService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WHDesktopPictureService.swift; sourceTree = \"<group>\"; };\n\t\tF49B82FE2E034F6800395F87 /* NSImage+DesktopPicture.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = \"NSImage+DesktopPicture.h\"; sourceTree = \"<group>\"; };\n\t\tF49B82FF2E034F6800395F87 /* NSImage+DesktopPicture.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = \"NSImage+DesktopPicture.m\"; sourceTree = \"<group>\"; };\n\t\tF49B832A2E04593100395F87 /* NSImage+DRMProtected.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = \"NSImage+DRMProtected.swift\"; sourceTree = \"<group>\"; };\n\t\tF49B832C2E046B8D00395F87 /* GuestAppConfigurationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GuestAppConfigurationView.swift; sourceTree = \"<group>\"; };\n\t\tF49B83702E04837400395F87 /* CGImage+FullyTransparent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = \"CGImage+FullyTransparent.swift\"; sourceTree = \"<group>\"; };\n\t\tF49FD87C2DFB68F20019D638 /* VMImporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VMImporter.swift; sourceTree = \"<group>\"; };\n\t\tF49FD87E2DFB6B630019D638 /* VMImporterRegistry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VMImporterRegistry.swift; sourceTree = \"<group>\"; };\n\t\tF49FD8802DFB6CD80019D638 /* UTMImporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UTMImporter.swift; sourceTree = \"<group>\"; };\n\t\tF49FD8832DFB72790019D638 /* VMImporter+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = \"VMImporter+Helpers.swift\"; sourceTree = \"<group>\"; };\n\t\tF49FD8852DFB728A0019D638 /* UTMAppleConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UTMAppleConfiguration.swift; sourceTree = \"<group>\"; };\n\t\tF4A21BF128032FD8001072B8 /* VMLibraryController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VMLibraryController.swift; sourceTree = \"<group>\"; };\n\t\tF4A21BF328033102001072B8 /* VBError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VBError.swift; sourceTree = \"<group>\"; };\n\t\tF4A7FB3A2BB5E79100E4C12A /* DirectoryObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DirectoryObserver.swift; sourceTree = \"<group>\"; };\n\t\tF4A7FB3C2BB5E8A200E4C12A /* VMSavedStatesController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VMSavedStatesController.swift; sourceTree = \"<group>\"; };\n\t\tF4A7FB3E2BB5EBEF00E4C12A /* SavedStatePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SavedStatePicker.swift; sourceTree = \"<group>\"; };\n\t\tF4A7FB402BB5ED4A00E4C12A /* Save-2024-03-27_16;06;24.vbst */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = \"Save-2024-03-27_16;06;24.vbst\"; sourceTree = \"<group>\"; };\n\t\tF4A7FB412BB5ED4A00E4C12A /* Save-2024-03-27_16;07;04.vbst */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = \"Save-2024-03-27_16;07;04.vbst\"; sourceTree = \"<group>\"; };\n\t\tF4A7FB422BB5ED4A00E4C12A /* Save-2024-03-27_16;08;06.vbst */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = \"Save-2024-03-27_16;08;06.vbst\"; sourceTree = \"<group>\"; };\n\t\tF4A7FB432BB5ED4A00E4C12A /* Save-2024-03-27_16;08;28.vbst */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = \"Save-2024-03-27_16;08;28.vbst\"; sourceTree = \"<group>\"; };\n\t\tF4A7FB442BB5ED4A00E4C12A /* Save-2024-03-27_16;08;51.vbst */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = \"Save-2024-03-27_16;08;51.vbst\"; sourceTree = \"<group>\"; };\n\t\tF4A7FB6D2BB7206C00E4C12A /* SelfSizingGroupedForm.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelfSizingGroupedForm.swift; sourceTree = \"<group>\"; };\n\t\tF4A7FB742BB7252A00E4C12A /* PreviewSupport-VirtualUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = \"PreviewSupport-VirtualUI.swift\"; sourceTree = \"<group>\"; };\n\t\tF4B068B428882EB7003743BF /* VirtualBuddy_Managed.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = VirtualBuddy_Managed.entitlements; sourceTree = \"<group>\"; };\n\t\tF4B068B528882F5D003743BF /* AppTarget.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppTarget.xcconfig; sourceTree = \"<group>\"; };\n\t\tF4B5C5D22886FA8D005AA632 /* SharedFoldersManagementView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedFoldersManagementView.swift; sourceTree = \"<group>\"; };\n\t\tF4B5C5D42886FFB5005AA632 /* PointingDeviceConfigurationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointingDeviceConfigurationView.swift; sourceTree = \"<group>\"; };\n\t\tF4B5C5D628870619005AA632 /* ConfigurationModels+Validation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = \"ConfigurationModels+Validation.swift\"; sourceTree = \"<group>\"; };\n\t\tF4B5C5D828870BBF005AA632 /* ConfigurationModels+Summary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = \"ConfigurationModels+Summary.swift\"; sourceTree = \"<group>\"; };\n\t\tF4B5C5DA28873628005AA632 /* GroupedList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupedList.swift; sourceTree = \"<group>\"; };\n\t\tF4BE580D29BA6C8D00C5525C /* DefaultsDomains.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = DefaultsDomains.plist; sourceTree = \"<group>\"; };\n\t\tF4BE581029BA6DFC00C5525C /* DefaultsImportController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultsImportController.swift; sourceTree = \"<group>\"; };\n\t\tF4BE581229BA6E0D00C5525C /* DefaultsDomainDescriptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultsDomainDescriptor.swift; sourceTree = \"<group>\"; };\n\t\tF4BE583F29BA7B7A00C5525C /* DefaultsDomain+ExportImport.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = \"DefaultsDomain+ExportImport.swift\"; sourceTree = \"<group>\"; };\n\t\tF4BE584129BA7BDB00C5525C /* WHDefaultsImportService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WHDefaultsImportService.swift; sourceTree = \"<group>\"; };\n\t\tF4BE9C4E27FF052100B648F8 /* VirtualBuddy.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = VirtualBuddy.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tF4BE9C5127FF052100B648F8 /* VirtualBuddyApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VirtualBuddyApp.swift; sourceTree = \"<group>\"; };\n\t\tF4BE9C5527FF052100B648F8 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\tF4BE9C5827FF052100B648F8 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = \"Preview Assets.xcassets\"; sourceTree = \"<group>\"; };\n\t\tF4BE9C5A27FF052100B648F8 /* VirtualBuddy.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = VirtualBuddy.entitlements; sourceTree = \"<group>\"; };\n\t\tF4BE9C6527FF053A00B648F8 /* VirtualCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = VirtualCore.framework; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tF4BE9C6727FF053A00B648F8 /* VirtualCore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VirtualCore.h; sourceTree = \"<group>\"; };\n\t\tF4BE9C7327FF055100B648F8 /* VBVirtualMachine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VBVirtualMachine.swift; sourceTree = \"<group>\"; };\n\t\tF4BE9C7527FF055100B648F8 /* MacOSVirtualMachineConfigurationHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MacOSVirtualMachineConfigurationHelper.swift; sourceTree = \"<group>\"; };\n\t\tF4BE9C7927FF05B900B648F8 /* VMController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VMController.swift; sourceTree = \"<group>\"; };\n\t\tF4BE9C7F27FF10FB00B648F8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = \"<group>\"; };\n\t\tF4BE9C8027FF111100B648F8 /* VirtualBuddyAppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VirtualBuddyAppDelegate.swift; sourceTree = \"<group>\"; };\n\t\tF4BE9C8527FF13FF00B648F8 /* VirtualizationPrivate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VirtualizationPrivate.h; sourceTree = \"<group>\"; };\n\t\tF4C122482807146200D359E2 /* Versions.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Versions.xcconfig; sourceTree = \"<group>\"; };\n\t\tF4C1224A2807156200D359E2 /* Signing.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Signing.xcconfig; sourceTree = \"<group>\"; };\n\t\tF4C1224D280715B500D359E2 /* Paths.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Paths.xcconfig; sourceTree = \"<group>\"; };\n\t\tF4C1224E280715F200D359E2 /* Main.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Main.xcconfig; sourceTree = \"<group>\"; };\n\t\tF4C189E02848F59F00335EC7 /* VirtualWormhole.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = VirtualWormhole.framework; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tF4C189E22848F59F00335EC7 /* VirtualWormhole.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VirtualWormhole.h; sourceTree = \"<group>\"; };\n\t\tF4C189ED2848F5B500335EC7 /* WormholeManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WormholeManager.swift; sourceTree = \"<group>\"; };\n\t\tF4C189F32848F61E00335EC7 /* Virtualization.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Virtualization.framework; path = System/Library/Frameworks/Virtualization.framework; sourceTree = SDKROOT; };\n\t\tF4C189F62848F6A600335EC7 /* VirtualCoreConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VirtualCoreConstants.swift; sourceTree = \"<group>\"; };\n\t\tF4C189F92848F6F700335EC7 /* VirtualWormholeConstants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VirtualWormholeConstants.swift; sourceTree = \"<group>\"; };\n\t\tF4C189FC2848F8F600335EC7 /* WHSharedClipboardService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WHSharedClipboardService.swift; sourceTree = \"<group>\"; };\n\t\tF4C189FE2848FB3F00335EC7 /* WormholeServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WormholeServiceProtocol.swift; sourceTree = \"<group>\"; };\n\t\tF4C18A4228491B8500335EC7 /* VirtualBuddyGuest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = VirtualBuddyGuest.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tF4C18A4428491B8500335EC7 /* GuestAppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GuestAppDelegate.swift; sourceTree = \"<group>\"; };\n\t\tF4C18A4628491B8500335EC7 /* GuestDashboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GuestDashboard.swift; sourceTree = \"<group>\"; };\n\t\tF4C18A4828491B8500335EC7 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\tF4C18A4B28491B8500335EC7 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = \"Preview Assets.xcassets\"; sourceTree = \"<group>\"; };\n\t\tF4C18A4D28491B8500335EC7 /* VirtualBuddyGuest.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = VirtualBuddyGuest.entitlements; sourceTree = \"<group>\"; };\n\t\tF4C2374C2888A462001FF286 /* VolumeUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VolumeUtils.swift; sourceTree = \"<group>\"; };\n\t\tF4C2374F2888AF67001FF286 /* LogStreamer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LogStreamer.swift; sourceTree = \"<group>\"; };\n\t\tF4C947BE2E0B0F71001ACC91 /* URL+ExtendedAttributes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = \"URL+ExtendedAttributes.swift\"; sourceTree = \"<group>\"; };\n\t\tF4C947D52E0B12D0001ACC91 /* String+AppleOSBuild.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = \"String+AppleOSBuild.swift\"; sourceTree = \"<group>\"; };\n\t\tF4C947D92E0B1E5D001ACC91 /* SoftwareCatalog+DownloadMatching.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = \"SoftwareCatalog+DownloadMatching.swift\"; sourceTree = \"<group>\"; };\n\t\tF4CD131F2E05A5780067DC75 /* FileSystemPathFormControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileSystemPathFormControl.swift; sourceTree = \"<group>\"; };\n\t\tF4CD133B2E05A9DF0067DC75 /* OpenVirtualBuddySettingsAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenVirtualBuddySettingsAction.swift; sourceTree = \"<group>\"; };\n\t\tF4CD133D2E05AB280067DC75 /* BackwardsCompatibility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackwardsCompatibility.swift; sourceTree = \"<group>\"; };\n\t\tF4CD133F2E05AB8F0067DC75 /* GeneralSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralSettingsView.swift; sourceTree = \"<group>\"; };\n\t\tF4CD13432E05AD400067DC75 /* VerticalLabeledContentStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerticalLabeledContentStyle.swift; sourceTree = \"<group>\"; };\n\t\tF4CD13452E05B4DE0067DC75 /* VirtualizationSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VirtualizationSettingsView.swift; sourceTree = \"<group>\"; };\n\t\tF4CD13472E05B67E0067DC75 /* SettingsFooter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsFooter.swift; sourceTree = \"<group>\"; };\n\t\tF4CD13492E05CB390067DC75 /* AutomationSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutomationSettingsView.swift; sourceTree = \"<group>\"; };\n\t\tF4D0F71428667984004D5782 /* VBVirtualMachine+Screenshot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = \"VBVirtualMachine+Screenshot.swift\"; sourceTree = \"<group>\"; };\n\t\tF4D0F71928674E76004D5782 /* SoftwareUpdateController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoftwareUpdateController.swift; sourceTree = \"<group>\"; };\n\t\tF4D0F71B28674F24004D5782 /* Features.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Features.xcconfig; sourceTree = \"<group>\"; };\n\t\tF4D0F71E2867517A004D5782 /* AppUpdateChannel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppUpdateChannel.swift; sourceTree = \"<group>\"; };\n\t\tF4D3059629B8D9D30006E748 /* WormholePacket.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WormholePacket.swift; sourceTree = \"<group>\"; };\n\t\tF4D3059C29B8DB700006E748 /* VirtualWormholeTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = VirtualWormholeTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tF4D3059E29B8DB700006E748 /* WormholePacketTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WormholePacketTests.swift; sourceTree = \"<group>\"; };\n\t\tF4D305A929B8E7120006E748 /* TestStream.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; path = TestStream.bin; sourceTree = \"<group>\"; };\n\t\tF4D305AB29B8FEE90006E748 /* WHDarwinNotificationsService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WHDarwinNotificationsService.swift; sourceTree = \"<group>\"; };\n\t\tF4D305AF29B900860006E748 /* SystemNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SystemNotification.swift; sourceTree = \"<group>\"; };\n\t\tF4D305B129B907A10006E748 /* WHPing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WHPing.swift; sourceTree = \"<group>\"; };\n\t\tF4D725FD286677B8001818F7 /* VBVirtualMachine+Metadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = \"VBVirtualMachine+Metadata.swift\"; sourceTree = \"<group>\"; };\n\t\tF4DE1C0A2D6F54E000603527 /* VBSavedStateMetadata+Clone.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = \"VBSavedStateMetadata+Clone.swift\"; sourceTree = \"<group>\"; };\n\t\tF4DE1C0E2D6F603300603527 /* VBSavedStatePackage+VM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = \"VBSavedStatePackage+VM.swift\"; sourceTree = \"<group>\"; };\n\t\tF4DE1C102D6F642E00603527 /* VBStorageDeviceContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VBStorageDeviceContainer.swift; sourceTree = \"<group>\"; };\n\t\tF4E4F6C42DEF96C200B3B8BA /* ChromeBorderModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChromeBorderModifier.swift; sourceTree = \"<group>\"; };\n\t\tF4E4F71F2DF080FC00B3B8BA /* RestoreImageSelectionController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RestoreImageSelectionController.swift; sourceTree = \"<group>\"; };\n\t\tF4E7680929B64C590075A897 /* GuestTypePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GuestTypePicker.swift; sourceTree = \"<group>\"; };\n\t\tF4E7680C29B651220075A897 /* VirtualUI.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = VirtualUI.xcassets; sourceTree = \"<group>\"; };\n\t\tF4E7680E29B655DD0075A897 /* InstallMethod.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstallMethod.swift; sourceTree = \"<group>\"; };\n\t\tF4E7DF912BB3338900C459FC /* NSImage+HEIC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = \"NSImage+HEIC.swift\"; sourceTree = \"<group>\"; };\n\t\tF4E7DF942BB336F600C459FC /* VBSavedStatePackage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VBSavedStatePackage.swift; sourceTree = \"<group>\"; };\n\t\tF4E7DF962BB33E1700C459FC /* VMLibraryController+SavedState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = \"VMLibraryController+SavedState.swift\"; sourceTree = \"<group>\"; };\n\t\tF4E7DFCE2BB3587D00C459FC /* VirtualMachineSessionUIManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VirtualMachineSessionUIManager.swift; sourceTree = \"<group>\"; };\n\t\tF4ECC6D42C63BFD5001DAC1D /* NumberDisplayMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NumberDisplayMode.swift; sourceTree = \"<group>\"; };\n\t\tF4F9B415284CE0F900F21737 /* VBSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VBSettings.swift; sourceTree = \"<group>\"; };\n\t\tF4F9B417284CE12000F21737 /* VBSettingsContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VBSettingsContainer.swift; sourceTree = \"<group>\"; };\n\t\tF4F9B419284CE37C00F21737 /* Logging.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logging.swift; sourceTree = \"<group>\"; };\n\t\tF4FC276629BBAE350012CB65 /* WormholeServiceClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WormholeServiceClient.swift; sourceTree = \"<group>\"; };\n\t\tF4FC276929BBAE590012CB65 /* WHDefaultsImportClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WHDefaultsImportClient.swift; sourceTree = \"<group>\"; };\n\t\tF4FC276B29BBB3030012CB65 /* GuestDefaultsImportView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GuestDefaultsImportView.swift; sourceTree = \"<group>\"; };\n\t\tF4FC98382BB386A000E511C9 /* ContinuousProgressIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContinuousProgressIndicator.swift; sourceTree = \"<group>\"; };\n\t\tF4FC983A2BB386B500E511C9 /* MaskProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaskProgressView.swift; sourceTree = \"<group>\"; };\n\t\tF4FC983C2BB386DD00E511C9 /* VMProgressOverlay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VMProgressOverlay.swift; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\tF41369A829918576002CE8D3 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tF43B01122AD858FE00164CD1 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tF498ACFB2884BF13006F1C00 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tF453C4632DF0E609007EAD5F /* BuddyKit in Frameworks */,\n\t\t\t\tF4510A7B2AE2B3B300E24DD9 /* DeepLinkSecurity.framework in Frameworks */,\n\t\t\t\tF498AD0C2884BF67006F1C00 /* VirtualCore.framework in Frameworks */,\n\t\t\t\tF4A7FB732BB7238A00E4C12A /* SwiftUIIntrospect-Static in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tF4BE9C4B27FF052100B648F8 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tF453C44C2DF0B835007EAD5F /* libcurl.tbd in Frameworks */,\n\t\t\t\tF453C4122DF0B1ED007EAD5F /* BuddyKit in Frameworks */,\n\t\t\t\tF43B01472AD85A7D00164CD1 /* URLQueryItemCoder in Frameworks */,\n\t\t\t\tF498AD042884BF13006F1C00 /* VirtualUI.framework in Frameworks */,\n\t\t\t\tF4D0F71828674E4B004D5782 /* Sparkle in Frameworks */,\n\t\t\t\tF453C44A2DF0B7F6007EAD5F /* FragmentZip in Frameworks */,\n\t\t\t\tF4BE9C6B27FF053A00B648F8 /* VirtualCore.framework in Frameworks */,\n\t\t\t\tF43B011B2AD858FE00164CD1 /* DeepLinkSecurity.framework in Frameworks */,\n\t\t\t\tF453C42B2DF0B792007EAD5F /* ArgumentParser in Frameworks */,\n\t\t\t\tF4C189E62848F59F00335EC7 /* VirtualWormhole.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tF4BE9C6227FF053A00B648F8 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tF4E4F7262DF0A1CB00B3B8BA /* BuddyKit in Frameworks */,\n\t\t\t\tF4C189F22848F5F500335EC7 /* VirtualWormhole.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tF4C189DD2848F59F00335EC7 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tF4C18A3F28491B8500335EC7 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tF453C4242DF0B602007EAD5F /* VirtualUI.framework in Frameworks */,\n\t\t\t\tF4C18A5228491B9D00335EC7 /* VirtualWormhole.framework in Frameworks */,\n\t\t\t\tF453C45B2DF0C4BE007EAD5F /* BuddyKit in Frameworks */,\n\t\t\t\tF428622E2AE87D7E0052F029 /* DeepLinkSecurity.framework in Frameworks */,\n\t\t\t\tF413699A299179F8002CE8D3 /* VirtualCore.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tF4D3059929B8DB700006E748 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tF4D305A029B8DB700006E748 /* VirtualWormhole.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\tF40A1E9B2C1873B90033E47D /* ReleaseTrains */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF40A1E9C2C1873C60033E47D /* VBBuildType.swift */,\n\t\t\t\tF4D0F71E2867517A004D5782 /* AppUpdateChannel.swift */,\n\t\t\t);\n\t\t\tpath = ReleaseTrains;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF413694F29916F6E002CE8D3 /* SwiftUI Status Item */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF413695029916F6E002CE8D3 /* Components */,\n\t\t\t\tF413696129916F6E002CE8D3 /* StatusItemManager.swift */,\n\t\t\t);\n\t\t\tpath = \"SwiftUI Status Item\";\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF413695029916F6E002CE8D3 /* Components */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF413695429916F6E002CE8D3 /* Cocoa */,\n\t\t\t\tF413695B29916F6E002CE8D3 /* ObjC */,\n\t\t\t\tF413695129916F6E002CE8D3 /* StatusItemButton.swift */,\n\t\t\t\tF413695229916F6E002CE8D3 /* StatusBarPanelChrome.swift */,\n\t\t\t\tF413695329916F6E002CE8D3 /* StatusItemProviderProtocol.swift */,\n\t\t\t\tF41369A229917FA0002CE8D3 /* ScreenChangeModifier.swift */,\n\t\t\t);\n\t\t\tpath = Components;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF413695429916F6E002CE8D3 /* Cocoa */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF413695529916F6E002CE8D3 /* StatusItemPanelContentController.swift */,\n\t\t\t\tF413695729916F6E002CE8D3 /* VUIAppKitViewControllerHost.swift */,\n\t\t\t\tF413695829916F6E002CE8D3 /* StatusItemMenuBarExtraView.swift */,\n\t\t\t\tF413695929916F6E002CE8D3 /* StatusBarContentPanel.swift */,\n\t\t\t\tF413695A29916F6E002CE8D3 /* StatusBarHighlightView.swift */,\n\t\t\t);\n\t\t\tpath = Cocoa;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF413695B29916F6E002CE8D3 /* ObjC */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF413695E29916F6E002CE8D3 /* NSStatusBarPrivate.h */,\n\t\t\t\tF413695C29916F6E002CE8D3 /* NSStatusItem+.h */,\n\t\t\t\tF413696029916F6E002CE8D3 /* NSStatusItem+.m */,\n\t\t\t\tF413695D29916F6E002CE8D3 /* NSApplication+MenuBar.h */,\n\t\t\t\tF413695F29916F6E002CE8D3 /* NSApplication+MenuBar.m */,\n\t\t\t);\n\t\t\tpath = ObjC;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF41369A4299183BA002CE8D3 /* Dashboard */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF41369C4299187D8002CE8D3 /* Support */,\n\t\t\t\tF41369C92991A492002CE8D3 /* HostConnectionStateProvider.swift */,\n\t\t\t\tF41369CB2991A68F002CE8D3 /* GuestSharedFoldersManager.swift */,\n\t\t\t\tF4FC276B29BBB3030012CB65 /* GuestDefaultsImportView.swift */,\n\t\t\t\tF4C18A4628491B8500335EC7 /* GuestDashboard.swift */,\n\t\t\t);\n\t\t\tpath = Dashboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF41369AC29918576002CE8D3 /* VirtualBuddyGuestHelper */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF41369AD29918576002CE8D3 /* GuestHelperAppDelegate.swift */,\n\t\t\t\tF41369B129918576002CE8D3 /* Assets.xcassets */,\n\t\t\t\tF41369B329918576002CE8D3 /* Main.storyboard */,\n\t\t\t\tF41369B629918576002CE8D3 /* VirtualBuddyGuestHelper.entitlements */,\n\t\t\t);\n\t\t\tpath = VirtualBuddyGuestHelper;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF41369C4299187D8002CE8D3 /* Support */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF41369A5299183C8002CE8D3 /* GuestLaunchAtLoginManager.swift */,\n\t\t\t\tF41369C5299187E1002CE8D3 /* VirtualBuddyGuest-Bridging-Header.h */,\n\t\t\t);\n\t\t\tpath = Support;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF41725642886DF4A004FF8A7 /* Components */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF453C4642DF0F7F4007EAD5F /* BlurHash */,\n\t\t\t\tF47BCDC92C5C01C600165191 /* RemoteImage */,\n\t\t\t\tF413694F29916F6E002CE8D3 /* SwiftUI Status Item */,\n\t\t\t\tF48E0D26288883150080DDFA /* HostingWindowController */,\n\t\t\t\tF48E0D20288882E50080DDFA /* DecentFormView.swift */,\n\t\t\t\tF48E0D21288882E50080DDFA /* NSAlert+Confirmation.swift */,\n\t\t\t\tF48E0D22288882E50080DDFA /* OnAppearOnce.swift */,\n\t\t\t\tF41725672886E5AD004FF8A7 /* MaterialView.swift */,\n\t\t\t\tF41725652886DF58004FF8A7 /* OpenSavePanelUtils.swift */,\n\t\t\t\tF42C014B2888C34B00EB15CD /* LogConsole.swift */,\n\t\t\t\tF4A7FB6D2BB7206C00E4C12A /* SelfSizingGroupedForm.swift */,\n\t\t\t\tF47BCDD22C5C0AB300165191 /* KeyboardNavigationModifier.swift */,\n\t\t\t\tF47BCDD42C5C0B8C00165191 /* Array+Navigation.swift */,\n\t\t\t\tF444D0F32DF34BE40086537A /* CALayer+Asset.swift */,\n\t\t\t\tF49B82972E02F5A300395F87 /* VMArtworkView.swift */,\n\t\t\t\tF417CB872E0EDECD0065B5D6 /* BackportedContentUnavailableView.swift */,\n\t\t\t\tF417CB892E0EDF1A0065B5D6 /* EqualWidthHStack.swift */,\n\t\t\t);\n\t\t\tpath = Components;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF417256D2887543B004FF8A7 /* Storage */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF417256B2887500F004FF8A7 /* StorageConfigurationView.swift */,\n\t\t\t\tF417256E2887544A004FF8A7 /* StorageDeviceDetailView.swift */,\n\t\t\t\tF48E0D02288858DF0080DDFA /* ManagedDiskImageEditor.swift */,\n\t\t\t);\n\t\t\tpath = Storage;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF417257228877453004FF8A7 /* Resources */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF47BCD9E2C5BE8D300165191 /* SoftwareCatalog */,\n\t\t\t\tF48E0D0828885E510080DDFA /* Preview */,\n\t\t\t\tF417257328877478004FF8A7 /* VirtualCore.xcassets */,\n\t\t\t);\n\t\t\tpath = Resources;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF422586E2885D518009420AE /* Configuration Controls */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF422586C2885CC9F009420AE /* SharedFocusEnvironment.swift */,\n\t\t\t\tF498AD112884C36A006F1C00 /* NumericValueField.swift */,\n\t\t\t\tF498AD172884C593006F1C00 /* NumericPropertyControl.swift */,\n\t\t\t\tF422586F2885D537009420AE /* EphemeralTextField.swift */,\n\t\t\t\tF422587F2885E71D009420AE /* PropertyControl.swift */,\n\t\t\t);\n\t\t\tpath = \"Configuration Controls\";\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF42258752885E132009420AE /* Sections */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF417256D2887543B004FF8A7 /* Storage */,\n\t\t\t\tF4B5C5D12886FA77005AA632 /* Sharing */,\n\t\t\t\tF422587D2885E2ED009420AE /* HardwareConfigurationView.swift */,\n\t\t\t\tF42258772885E14A009420AE /* DisplayConfigurationView.swift */,\n\t\t\t\tF422587B2885E1CE009420AE /* NetworkConfigurationView.swift */,\n\t\t\t\tF417255C288604A8004FF8A7 /* SoundConfigurationView.swift */,\n\t\t\t\tF4B5C5D42886FFB5005AA632 /* PointingDeviceConfigurationView.swift */,\n\t\t\t\tF4450CC92ACB0DB500092618 /* KeyboardDeviceConfigurationView.swift */,\n\t\t\t\tF49B832C2E046B8D00395F87 /* GuestAppConfigurationView.swift */,\n\t\t\t);\n\t\t\tpath = Sections;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF42258762885E139009420AE /* Components */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF42258792885E17D009420AE /* ConfigurationSection.swift */,\n\t\t\t\tF4B5C5DA28873628005AA632 /* GroupedList.swift */,\n\t\t\t);\n\t\t\tpath = Components;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF42C01502888FC0C00EB15CD /* Library */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF42C015F2888FC2500EB15CD /* Components */,\n\t\t\t\tF42C01512888FC0C00EB15CD /* LibraryView.swift */,\n\t\t\t);\n\t\t\tpath = Library;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF42C01522888FC0C00EB15CD /* Session */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF42C01562888FC0C00EB15CD /* Components */,\n\t\t\t\tF42C01532888FC0C00EB15CD /* Configuration */,\n\t\t\t\tF4E7DFCE2BB3587D00C459FC /* VirtualMachineSessionUIManager.swift */,\n\t\t\t\tF49AA2C429BA31CC009625F7 /* VirtualMachineSessionUI.swift */,\n\t\t\t\tF42C01552888FC0C00EB15CD /* VirtualMachineSessionView.swift */,\n\t\t\t);\n\t\t\tpath = Session;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF42C01532888FC0C00EB15CD /* Configuration */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF42C01542888FC0C00EB15CD /* VMSessionConfigurationView.swift */,\n\t\t\t);\n\t\t\tpath = Configuration;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF42C01562888FC0C00EB15CD /* Components */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF42C01572888FC0C00EB15CD /* SwiftUIVMView.swift */,\n\t\t\t\tF4ECC6D42C63BFD5001DAC1D /* NumberDisplayMode.swift */,\n\t\t\t\tF428622C2AE8726D0052F029 /* VirtualMachineControls.swift */,\n\t\t\t\tF4FC98382BB386A000E511C9 /* ContinuousProgressIndicator.swift */,\n\t\t\t\tF4FC983A2BB386B500E511C9 /* MaskProgressView.swift */,\n\t\t\t\tF4FC983C2BB386DD00E511C9 /* VMProgressOverlay.swift */,\n\t\t\t\tF4A7FB3E2BB5EBEF00E4C12A /* SavedStatePicker.swift */,\n\t\t\t);\n\t\t\tpath = Components;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF42C01582888FC0C00EB15CD /* Settings */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF4CD131E2E05A5760067DC75 /* Components */,\n\t\t\t\tF42C01592888FC0C00EB15CD /* SettingsScreen.swift */,\n\t\t\t\tF4CD133F2E05AB8F0067DC75 /* GeneralSettingsView.swift */,\n\t\t\t\tF4CD13452E05B4DE0067DC75 /* VirtualizationSettingsView.swift */,\n\t\t\t\tF4CD13492E05CB390067DC75 /* AutomationSettingsView.swift */,\n\t\t\t);\n\t\t\tpath = Settings;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF42C015F2888FC2500EB15CD /* Components */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF42C01602888FC3500EB15CD /* LibraryItemView.swift */,\n\t\t\t\tF417CBB82E0F3D2E0065B5D6 /* FirstLaunchExperienceView.swift */,\n\t\t\t);\n\t\t\tpath = Components;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF43B01162AD858FE00164CD1 /* DeepLinkSecurity */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF43B01222AD8590F00164CD1 /* Source */,\n\t\t\t\tF43B01172AD858FE00164CD1 /* DeepLinkSecurity.h */,\n\t\t\t);\n\t\t\tpath = DeepLinkSecurity;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF43B01222AD8590F00164CD1 /* Source */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF43B01332AD8590F00164CD1 /* Base */,\n\t\t\t\tF43B01232AD8590F00164CD1 /* UI */,\n\t\t\t\tF43B01262AD8590F00164CD1 /* Models */,\n\t\t\t\tF43B012D2AD8590F00164CD1 /* Storage */,\n\t\t\t\tF43B01252AD8590F00164CD1 /* DeepLinkSentinel.swift */,\n\t\t\t);\n\t\t\tpath = Source;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF43B01232AD8590F00164CD1 /* UI */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF43B01242AD8590F00164CD1 /* DeepLinkAuthUI.swift */,\n\t\t\t);\n\t\t\tpath = UI;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF43B01262AD8590F00164CD1 /* Models */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF43B01272AD8590F00164CD1 /* OpenDeepLinkRequest.swift */,\n\t\t\t\tF43B01282AD8590F00164CD1 /* DeepLinkClientDescriptor.swift */,\n\t\t\t\tF43B01292AD8590F00164CD1 /* Extensions */,\n\t\t\t\tF43B012C2AD8590F00164CD1 /* DeepLinkClient.swift */,\n\t\t\t);\n\t\t\tpath = Models;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF43B01292AD8590F00164CD1 /* Extensions */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF43B012A2AD8590F00164CD1 /* DeepLinkClient+Crypto.swift */,\n\t\t\t\tF43B012B2AD8590F00164CD1 /* DeepLinkClientDescriptor+.swift */,\n\t\t\t);\n\t\t\tpath = Extensions;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF43B012D2AD8590F00164CD1 /* Storage */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF43B012E2AD8590F00164CD1 /* KeychainDeepLinkAuthStore.swift */,\n\t\t\t\tF43B012F2AD8590F00164CD1 /* MemoryDeepLinkAuthStore.swift */,\n\t\t\t\tF43B01302AD8590F00164CD1 /* DeepLinkManagementStore.swift */,\n\t\t\t\tF43B01312AD8590F00164CD1 /* UserDefaultsDeepLinkManagementStore.swift */,\n\t\t\t\tF43B01322AD8590F00164CD1 /* DeepLinkAuthStore.swift */,\n\t\t\t);\n\t\t\tpath = Storage;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF43B01332AD8590F00164CD1 /* Base */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF43B01342AD8590F00164CD1 /* DeepLinkSecurityDefines.swift */,\n\t\t\t);\n\t\t\tpath = Base;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF43B01422AD85A5200164CD1 /* Automation */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF43B01482AD85AAF00164CD1 /* Support */,\n\t\t\t\tF43B01432AD85A6500164CD1 /* VirtualBuddyDeepLinks.swift */,\n\t\t\t\tF43B014D2AD86BFA00164CD1 /* DeepLinkHandler.swift */,\n\t\t\t);\n\t\t\tpath = Automation;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF43B01482AD85AAF00164CD1 /* Support */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF43B01492AD85ABB00164CD1 /* DeepLinkAuthDialog.swift */,\n\t\t\t);\n\t\t\tpath = Support;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF443620D29B79D6800745B43 /* GuestSupport */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF443620E29B7A0C600745B43 /* CreateGuestImage.sh */,\n\t\t\t\tF443620929B7947A00745B43 /* GuestAdditionsDiskImage.swift */,\n\t\t\t);\n\t\t\tpath = GuestSupport;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF453C4172DF0B43D007EAD5F /* Utilities */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF453C4162DF0B43D007EAD5F /* BlurHashEncode.swift */,\n\t\t\t\tF4C947BE2E0B0F71001ACC91 /* URL+ExtendedAttributes.swift */,\n\t\t\t\tF4C947D52E0B12D0001ACC91 /* String+AppleOSBuild.swift */,\n\t\t\t);\n\t\t\tpath = Utilities;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF453C41C2DF0B43D007EAD5F /* VirtualCatalog */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF453C45C2DF0D286007EAD5F /* README.md */,\n\t\t\t\tF453C4172DF0B43D007EAD5F /* Utilities */,\n\t\t\t\tF453C4182DF0B43D007EAD5F /* LegacyCatalog.swift */,\n\t\t\t\tF453C4192DF0B43D007EAD5F /* MobileDeviceFramework.swift */,\n\t\t\t\tF453C41B2DF0B43D007EAD5F /* SoftwareCatalog.swift */,\n\t\t\t\tF453C41A2DF0B43D007EAD5F /* ResolvedCatalog.swift */,\n\t\t\t\tF4C947D92E0B1E5D001ACC91 /* SoftwareCatalog+DownloadMatching.swift */,\n\t\t\t);\n\t\t\tpath = VirtualCatalog;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF453C4292DF0B785007EAD5F /* CommandLine */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF453C44D2DF0B869007EAD5F /* VirtualBuddyCLI.swift */,\n\t\t\t\tF453C43A2DF0B7A5007EAD5F /* vctool */,\n\t\t\t);\n\t\t\tpath = CommandLine;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF453C4312DF0B7A5007EAD5F /* Core */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF453C42C2DF0B7A5007EAD5F /* BuildManifest.swift */,\n\t\t\t\tF453C42D2DF0B7A5007EAD5F /* BuildManifest+Fetch.swift */,\n\t\t\t\tF453C42E2DF0B7A5007EAD5F /* Helpers.swift */,\n\t\t\t\tF453C42F2DF0B7A5007EAD5F /* TreeStringConvertible.swift */,\n\t\t\t\tF453C4302DF0B7A5007EAD5F /* URL+ContentLength.swift */,\n\t\t\t);\n\t\t\tpath = Core;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF453C43A2DF0B7A5007EAD5F /* vctool */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF453C4312DF0B7A5007EAD5F /* Core */,\n\t\t\t\tF453C4322DF0B7A5007EAD5F /* CatalogCommand.swift */,\n\t\t\t\tF453C4332DF0B7A5007EAD5F /* GroupCommand.swift */,\n\t\t\t\tF453C4342DF0B7A5007EAD5F /* ImageCommand.swift */,\n\t\t\t\tF453C4352DF0B7A5007EAD5F /* IPSWCommand.swift */,\n\t\t\t\tF453C4362DF0B7A5007EAD5F /* MigrateCommand.swift */,\n\t\t\t\tF453C4372DF0B7A5007EAD5F /* MobileDeviceCommand.swift */,\n\t\t\t\tF453C4382DF0B7A5007EAD5F /* ResolveCommand.swift */,\n\t\t\t\tF453C4672DF10181007EAD5F /* BlurHashCommand.swift */,\n\t\t\t\tF453C4392DF0B7A5007EAD5F /* VCTool.swift */,\n\t\t\t);\n\t\t\tpath = vctool;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF453C4642DF0F7F4007EAD5F /* BlurHash */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF47BCDCA2C5C01CE00165191 /* BlurHashDecoding.swift */,\n\t\t\t);\n\t\t\tpath = BlurHash;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF453C49C2DF1D788007EAD5F /* Restore */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF453C49D2DF1D79F007EAD5F /* Download */,\n\t\t\t\tF453C49E2DF1D7A7007EAD5F /* Installation */,\n\t\t\t);\n\t\t\tpath = Restore;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF453C49D2DF1D79F007EAD5F /* Download */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF453C4882DF1CDA0007EAD5F /* DownloadBackend.swift */,\n\t\t\t\tF453C49A2DF1D768007EAD5F /* SimulatedDownloadBackend.swift */,\n\t\t\t\tF465C3B7284FA252006E9ED4 /* URLSessionDownloadBackend.swift */,\n\t\t\t);\n\t\t\tpath = Download;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF453C49E2DF1D7A7007EAD5F /* Installation */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF453C49F2DF1D7C0007EAD5F /* RestoreBackend.swift */,\n\t\t\t\tF453C4A12DF1D7F6007EAD5F /* SimulatedRestoreBackend.swift */,\n\t\t\t\tF453C4A32DF1D85C007EAD5F /* VirtualizationRestoreBackend.swift */,\n\t\t\t);\n\t\t\tpath = Installation;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF45502172DF4635C005582A4 /* _Downloads */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF45502182DF46368005582A4 /* UniversalMac_13.3.1_22E261_Restore.ipsw */,\n\t\t\t\tF45502192DF46368005582A4 /* UniversalMac_14.0_23A344_Restore.ipsw */,\n\t\t\t\tF455021A2DF46368005582A4 /* UniversalMac_14.5_23F79_Restore.ipsw */,\n\t\t\t\tF455021B2DF46368005582A4 /* UniversalMac_15.3_24D60_Restore.ipsw */,\n\t\t\t\tF455021C2DF46368005582A4 /* UniversalMac_15.5_24F74_Restore.ipsw */,\n\t\t\t);\n\t\t\tpath = _Downloads;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF465C3AC284F939C006E9ED4 /* Restore Images */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF465C3B3284F967C006E9ED4 /* Models */,\n\t\t\t\tF465C3AD284F93A5006E9ED4 /* VBAPIClient.swift */,\n\t\t\t);\n\t\t\tpath = \"Restore Images\";\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF465C3B3284F967C006E9ED4 /* Models */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF465C3AF284F9660006E9ED4 /* VBRestoreImagesResponse.swift */,\n\t\t\t\tF465C3B1284F9666006E9ED4 /* VBRestoreImageInfo.swift */,\n\t\t\t);\n\t\t\tpath = Models;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF47BCD9E2C5BE8D300165191 /* SoftwareCatalog */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF47BCD9F2C5BE8EF00165191 /* ipsws_v2.json */,\n\t\t\t\tF47BCDA02C5BE8EF00165191 /* linux_v2.json */,\n\t\t\t);\n\t\t\tname = SoftwareCatalog;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF47BCDC92C5C01C600165191 /* RemoteImage */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF47BCDCC2C5C01EF00165191 /* RemoteImage.swift */,\n\t\t\t);\n\t\t\tpath = RemoteImage;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF48E0D0828885E510080DDFA /* Preview */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF45502172DF4635C005582A4 /* _Downloads */,\n\t\t\t\tF4A7FB452BB5ED4A00E4C12A /* PreviewSavedStates */,\n\t\t\t\tF453C4912DF1D213007EAD5F /* FakeRestoreImage.ipsw */,\n\t\t\t\tF49B829F2E02FCB100395F87 /* PreviewMac.vbvm */,\n\t\t\t\tF49B829D2E02FCB100395F87 /* PreviewMacBlurHash.vbvm */,\n\t\t\t\tF49B82A32E02FCF400395F87 /* PreviewMacNoArtwork.vbvm */,\n\t\t\t\tF49B829E2E02FCB100395F87 /* PreviewLinux.vbvm */,\n\t\t\t\tF49B82A52E03049C00395F87 /* PreviewLinuxBlurHash.vbvm */,\n\t\t\t\tF49B82A62E03049C00395F87 /* PreviewLinuxNoArtwork.vbvm */,\n\t\t\t);\n\t\t\tpath = Preview;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF48E0D12288882BD0080DDFA /* Installer */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF48E0D13288882BD0080DDFA /* Components */,\n\t\t\t\tF48E0D30288884780080DDFA /* Steps */,\n\t\t\t\tF48E0D18288882BD0080DDFA /* VMInstallationWizard.swift */,\n\t\t\t\tF453C4B82DF21985007EAD5F /* VMInstallData.swift */,\n\t\t\t\tF48E0D19288882BD0080DDFA /* VMInstallationViewModel.swift */,\n\t\t\t);\n\t\t\tpath = Installer;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF48E0D13288882BD0080DDFA /* Components */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF45502132DF394DC005582A4 /* VirtualBuddyInstallerInputView.swift */,\n\t\t\t\tF48E0D14288882BD0080DDFA /* InstallationWizardTitle.swift */,\n\t\t\t\tF48E0D17288882BD0080DDFA /* AuthenticatingWebView.swift */,\n\t\t\t\tF42C01492888C2F800EB15CD /* InstallationConsole.swift */,\n\t\t\t\tF4561A6728981B4100055289 /* VirtualMachineNameInputView.swift */,\n\t\t\t\tF453C4B32DF20301007EAD5F /* RestoreImageURLInputView.swift */,\n\t\t\t);\n\t\t\tpath = Components;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF48E0D26288883150080DDFA /* HostingWindowController */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF48E0D27288883150080DDFA /* OpenCocoaWindowAction.swift */,\n\t\t\t\tF48E0D28288883150080DDFA /* HostingWindowController.swift */,\n\t\t\t\tF48E0D29288883150080DDFA /* WindowEnvironment.swift */,\n\t\t\t\tF462C9412E0C96D300C172E2 /* FB18383725Window.swift */,\n\t\t\t\tF49AA2C229BA22A5009625F7 /* VBRestorableWindow.swift */,\n\t\t\t\tF49AA2C629BA3F2B009625F7 /* VBRestorableWindow+Resizing.swift */,\n\t\t\t);\n\t\t\tpath = HostingWindowController;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF48E0D2D288883450080DDFA /* Definitions */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF48E0D2E2888835A0080DDFA /* VirtualUIConstants.swift */,\n\t\t\t\tF4A7FB742BB7252A00E4C12A /* PreviewSupport-VirtualUI.swift */,\n\t\t\t);\n\t\t\tpath = Definitions;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF48E0D30288884780080DDFA /* Steps */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF4E4F71D2DF080D200B3B8BA /* Restore Image Selection */,\n\t\t\t\tF4E7680E29B655DD0075A897 /* InstallMethod.swift */,\n\t\t\t\tF48E0D15288882BD0080DDFA /* InstallMethodPicker.swift */,\n\t\t\t\tF4E7680929B64C590075A897 /* GuestTypePicker.swift */,\n\t\t\t\tF48E0D31288884A10080DDFA /* RestoreImageDownloadView.swift */,\n\t\t\t\tF48E0D33288889E60080DDFA /* InstallConfigurationStepView.swift */,\n\t\t\t\tF42C014D2888CBCB00EB15CD /* InstallProgressStepView.swift */,\n\t\t\t);\n\t\t\tpath = Steps;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF498ACFF2884BF13006F1C00 /* VirtualUI */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF4E7680B29B651180075A897 /* Resources */,\n\t\t\t\tF498AD092884BF20006F1C00 /* Source */,\n\t\t\t\tF498AD002884BF13006F1C00 /* VirtualUI.h */,\n\t\t\t);\n\t\t\tpath = VirtualUI;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF498AD092884BF20006F1C00 /* Source */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF42C01502888FC0C00EB15CD /* Library */,\n\t\t\t\tF42C01522888FC0C00EB15CD /* Session */,\n\t\t\t\tF42C01582888FC0C00EB15CD /* Settings */,\n\t\t\t\tF48E0D2D288883450080DDFA /* Definitions */,\n\t\t\t\tF48E0D12288882BD0080DDFA /* Installer */,\n\t\t\t\tF41725642886DF4A004FF8A7 /* Components */,\n\t\t\t\tF498AD102884C35F006F1C00 /* VM Configuration */,\n\t\t\t\tF498AD0F2884C35A006F1C00 /* Building Blocks */,\n\t\t\t);\n\t\t\tpath = Source;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF498AD0F2884C35A006F1C00 /* Building Blocks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF422586E2885D518009420AE /* Configuration Controls */,\n\t\t\t\tF498AD132884C36A006F1C00 /* ControlGroupChrome.swift */,\n\t\t\t\tF498AD192884C5FF006F1C00 /* SliderConversion.swift */,\n\t\t\t\tF413697829917135002CE8D3 /* CGFloat+OnePixel.swift */,\n\t\t\t\tF4E4F6C42DEF96C200B3B8BA /* ChromeBorderModifier.swift */,\n\t\t\t);\n\t\t\tpath = \"Building Blocks\";\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF498AD102884C35F006F1C00 /* VM Configuration */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF42258762885E139009420AE /* Components */,\n\t\t\t\tF42258752885E132009420AE /* Sections */,\n\t\t\t\tF42258732885E10B009420AE /* VMConfigurationViewModel.swift */,\n\t\t\t\tF42258712885E100009420AE /* VMConfigurationSheet.swift */,\n\t\t\t\tF498AD0D2884BF9D006F1C00 /* VMConfigurationView.swift */,\n\t\t\t);\n\t\t\tpath = \"VM Configuration\";\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF49A68DF2884917400A17582 /* Configuration */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF417255E28861604004FF8A7 /* DecodableDefault.swift */,\n\t\t\t\tF49A68E02884917E00A17582 /* ConfigurationModels.swift */,\n\t\t\t\tF4B5C5D628870619005AA632 /* ConfigurationModels+Validation.swift */,\n\t\t\t\tF4B5C5D828870BBF005AA632 /* ConfigurationModels+Summary.swift */,\n\t\t\t\tF48E0D0B2888760D0080DDFA /* VBMacDevice+Storage.swift */,\n\t\t\t);\n\t\t\tpath = Configuration;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF49B82FD2E034F5600395F87 /* DesktopPicture */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF49B82FE2E034F6800395F87 /* NSImage+DesktopPicture.h */,\n\t\t\t\tF49B82FF2E034F6800395F87 /* NSImage+DesktopPicture.m */,\n\t\t\t\tF49B83702E04837400395F87 /* CGImage+FullyTransparent.swift */,\n\t\t\t\tF49B82FB2E034EAD00395F87 /* WHDesktopPictureService.swift */,\n\t\t\t);\n\t\t\tpath = DesktopPicture;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF49FD87B2DFB68CC0019D638 /* Import */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF49FD87E2DFB6B630019D638 /* VMImporterRegistry.swift */,\n\t\t\t\tF49FD87C2DFB68F20019D638 /* VMImporter.swift */,\n\t\t\t\tF49FD8832DFB72790019D638 /* VMImporter+Helpers.swift */,\n\t\t\t\tF49FD8822DFB726C0019D638 /* UTM */,\n\t\t\t);\n\t\t\tpath = Import;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF49FD8822DFB726C0019D638 /* UTM */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF49FD8802DFB6CD80019D638 /* UTMImporter.swift */,\n\t\t\t\tF49FD8852DFB728A0019D638 /* UTMAppleConfiguration.swift */,\n\t\t\t);\n\t\t\tpath = UTM;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF4A21BEE28032F97001072B8 /* Models */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF49A68DF2884917400A17582 /* Configuration */,\n\t\t\t\tF4E7DF932BB336E000C459FC /* SavedState */,\n\t\t\t\tF4A21BF328033102001072B8 /* VBError.swift */,\n\t\t\t\tF42CF4A72DF5FEC3001DE049 /* BlurHashToken.swift */,\n\t\t\t\tF4BE9C7327FF055100B648F8 /* VBVirtualMachine.swift */,\n\t\t\t\tF4DE1C102D6F642E00603527 /* VBStorageDeviceContainer.swift */,\n\t\t\t\tF46FFBA72804F07400D61023 /* VBNVRAMVariable.swift */,\n\t\t\t\tF4D725FD286677B8001818F7 /* VBVirtualMachine+Metadata.swift */,\n\t\t\t\tF4D0F71428667984004D5782 /* VBVirtualMachine+Screenshot.swift */,\n\t\t\t);\n\t\t\tpath = Models;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF4A21BEF28032FB0001072B8 /* Virtualization */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF4E7DF832BB30D8200C459FC /* Screenshot */,\n\t\t\t\tF4A21BF028032FBD001072B8 /* Helpers */,\n\t\t\t\tF4A21BF128032FD8001072B8 /* VMLibraryController.swift */,\n\t\t\t\tF4A7FB3C2BB5E8A200E4C12A /* VMSavedStatesController.swift */,\n\t\t\t\tF46FFBAB28059FF600D61023 /* VMInstance.swift */,\n\t\t\t\tF4BE9C7927FF05B900B648F8 /* VMController.swift */,\n\t\t\t\tF44C00FA2889CE1600640BF5 /* VBVirtualMachine+Virtualization.swift */,\n\t\t\t);\n\t\t\tpath = Virtualization;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF4A21BF028032FBD001072B8 /* Helpers */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF47BCDD62C5D2B4300165191 /* CatalogExtensions.swift */,\n\t\t\t\t4BA6BE7C293D22E500F396AE /* VirtualMachineConfigurationHelper.swift */,\n\t\t\t\tF4BE9C7527FF055100B648F8 /* MacOSVirtualMachineConfigurationHelper.swift */,\n\t\t\t\t0196B45229292B2A00614EF1 /* LinuxVirtualMachineConfigurationHelper.swift */,\n\t\t\t\tF46FFBA92804F0A000D61023 /* VZVirtualMachineConfiguration+NVRAM.swift */,\n\t\t\t\tF417257028877121004FF8A7 /* DiskImageGenerator.swift */,\n\t\t\t\tF41725752887758A004FF8A7 /* RandomNameGenerator.swift */,\n\t\t\t\tF4A7FB3A2BB5E79100E4C12A /* DirectoryObserver.swift */,\n\t\t\t\tF485B9202BB306AF004B3C2B /* VBDebugUtil.h */,\n\t\t\t\tF485B9212BB306AF004B3C2B /* VBDebugUtil.m */,\n\t\t\t);\n\t\t\tpath = Helpers;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF4A21BFB28033968001072B8 /* Bootstrap */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF453C4222DF0B5B1007EAD5F /* VirtualBuddyEntryPoint.swift */,\n\t\t\t\tF4BE9C5127FF052100B648F8 /* VirtualBuddyApp.swift */,\n\t\t\t\tF4BE9C8027FF111100B648F8 /* VirtualBuddyAppDelegate.swift */,\n\t\t\t\tF4D0F71928674E76004D5782 /* SoftwareUpdateController.swift */,\n\t\t\t);\n\t\t\tpath = Bootstrap;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF4A7FB452BB5ED4A00E4C12A /* PreviewSavedStates */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF4A7FB402BB5ED4A00E4C12A /* Save-2024-03-27_16;06;24.vbst */,\n\t\t\t\tF4A7FB412BB5ED4A00E4C12A /* Save-2024-03-27_16;07;04.vbst */,\n\t\t\t\tF4A7FB422BB5ED4A00E4C12A /* Save-2024-03-27_16;08;06.vbst */,\n\t\t\t\tF4A7FB432BB5ED4A00E4C12A /* Save-2024-03-27_16;08;28.vbst */,\n\t\t\t\tF4A7FB442BB5ED4A00E4C12A /* Save-2024-03-27_16;08;51.vbst */,\n\t\t\t);\n\t\t\tpath = PreviewSavedStates;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF4B5C5D12886FA77005AA632 /* Sharing */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF417256028861A05004FF8A7 /* SharingConfigurationView.swift */,\n\t\t\t\tF4B5C5D22886FA8D005AA632 /* SharedFoldersManagementView.swift */,\n\t\t\t\tF41725622886DD37004FF8A7 /* SharedFolderListItem.swift */,\n\t\t\t);\n\t\t\tpath = Sharing;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF4BE580B29BA6C6300C5525C /* DefaultsImport */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF4FC276829BBAE470012CB65 /* Implementation */,\n\t\t\t\tF4BE580C29BA6C7A00C5525C /* Resources */,\n\t\t\t\tF4BE584129BA7BDB00C5525C /* WHDefaultsImportService.swift */,\n\t\t\t\tF4FC276929BBAE590012CB65 /* WHDefaultsImportClient.swift */,\n\t\t\t);\n\t\t\tpath = DefaultsImport;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF4BE580C29BA6C7A00C5525C /* Resources */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF4BE580D29BA6C8D00C5525C /* DefaultsDomains.plist */,\n\t\t\t);\n\t\t\tpath = Resources;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF4BE9C4527FF052100B648F8 = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF4BE9C5027FF052100B648F8 /* VirtualBuddy */,\n\t\t\t\tF4BE9C6627FF053A00B648F8 /* VirtualCore */,\n\t\t\t\tF498ACFF2884BF13006F1C00 /* VirtualUI */,\n\t\t\t\tF4C189E12848F59F00335EC7 /* VirtualWormhole */,\n\t\t\t\tF4C18A4328491B8500335EC7 /* VirtualBuddyGuest */,\n\t\t\t\tF41369AC29918576002CE8D3 /* VirtualBuddyGuestHelper */,\n\t\t\t\tF4D3059D29B8DB700006E748 /* VirtualWormholeTests */,\n\t\t\t\tF43B01162AD858FE00164CD1 /* DeepLinkSecurity */,\n\t\t\t\tF4BE9C4F27FF052100B648F8 /* Products */,\n\t\t\t\tF4C189F12848F5F500335EC7 /* Frameworks */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF4BE9C4F27FF052100B648F8 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF4BE9C4E27FF052100B648F8 /* VirtualBuddy.app */,\n\t\t\t\tF4BE9C6527FF053A00B648F8 /* VirtualCore.framework */,\n\t\t\t\tF4C189E02848F59F00335EC7 /* VirtualWormhole.framework */,\n\t\t\t\tF4C18A4228491B8500335EC7 /* VirtualBuddyGuest.app */,\n\t\t\t\tF498ACFE2884BF13006F1C00 /* VirtualUI.framework */,\n\t\t\t\tF41369AB29918576002CE8D3 /* VirtualBuddyGuestHelper.app */,\n\t\t\t\tF4D3059C29B8DB700006E748 /* VirtualWormholeTests.xctest */,\n\t\t\t\tF43B01152AD858FE00164CD1 /* DeepLinkSecurity.framework */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF4BE9C5027FF052100B648F8 /* VirtualBuddy */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF453C4292DF0B785007EAD5F /* CommandLine */,\n\t\t\t\tF4C122472807145600D359E2 /* Config */,\n\t\t\t\tF4A21BFB28033968001072B8 /* Bootstrap */,\n\t\t\t\tF43B01422AD85A5200164CD1 /* Automation */,\n\t\t\t\tF4BE9C7F27FF10FB00B648F8 /* Info.plist */,\n\t\t\t\tF4BE9C5527FF052100B648F8 /* Assets.xcassets */,\n\t\t\t\tF4BE9C5727FF052100B648F8 /* Preview Content */,\n\t\t\t);\n\t\t\tpath = VirtualBuddy;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF4BE9C5727FF052100B648F8 /* Preview Content */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF4BE9C5827FF052100B648F8 /* Preview Assets.xcassets */,\n\t\t\t);\n\t\t\tpath = \"Preview Content\";\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF4BE9C6627FF053A00B648F8 /* VirtualCore */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF4BE9C7127FF053D00B648F8 /* Source */,\n\t\t\t\tF4BE9C6727FF053A00B648F8 /* VirtualCore.h */,\n\t\t\t);\n\t\t\tpath = VirtualCore;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF4BE9C7127FF053D00B648F8 /* Source */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF49FD87B2DFB68CC0019D638 /* Import */,\n\t\t\t\tF453C49C2DF1D788007EAD5F /* Restore */,\n\t\t\t\tF453C41C2DF0B43D007EAD5F /* VirtualCatalog */,\n\t\t\t\tF40A1E9B2C1873B90033E47D /* ReleaseTrains */,\n\t\t\t\tF4C2374E2888AF5B001FF286 /* Utilities */,\n\t\t\t\tF417257228877453004FF8A7 /* Resources */,\n\t\t\t\tF465C3AC284F939C006E9ED4 /* Restore Images */,\n\t\t\t\tF4F9B414284CE0DC00F21737 /* Settings */,\n\t\t\t\tF4C189F52848F69B00335EC7 /* Definitions */,\n\t\t\t\tF4A21BEF28032FB0001072B8 /* Virtualization */,\n\t\t\t\tF443620D29B79D6800745B43 /* GuestSupport */,\n\t\t\t\tF4A21BEE28032F97001072B8 /* Models */,\n\t\t\t\tF4BE9C8427FF13F600B648F8 /* Headers */,\n\t\t\t);\n\t\t\tpath = Source;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF4BE9C8427FF13F600B648F8 /* Headers */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF4BE9C8527FF13FF00B648F8 /* VirtualizationPrivate.h */,\n\t\t\t);\n\t\t\tpath = Headers;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF4C122472807145600D359E2 /* Config */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF4C1224C2807158A00D359E2 /* Entitlements */,\n\t\t\t\tF4C1224D280715B500D359E2 /* Paths.xcconfig */,\n\t\t\t\tF4C122482807146200D359E2 /* Versions.xcconfig */,\n\t\t\t\tF4C1224A2807156200D359E2 /* Signing.xcconfig */,\n\t\t\t\tF482FC7228CB7A6C00F2BA4F /* InfoPlist.xcconfig */,\n\t\t\t\tF4B068B528882F5D003743BF /* AppTarget.xcconfig */,\n\t\t\t\tF4D0F71B28674F24004D5782 /* Features.xcconfig */,\n\t\t\t\tF4C1224E280715F200D359E2 /* Main.xcconfig */,\n\t\t\t);\n\t\t\tpath = Config;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF4C1224C2807158A00D359E2 /* Entitlements */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF4BE9C5A27FF052100B648F8 /* VirtualBuddy.entitlements */,\n\t\t\t\tF4B068B428882EB7003743BF /* VirtualBuddy_Managed.entitlements */,\n\t\t\t);\n\t\t\tpath = Entitlements;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF4C189E12848F59F00335EC7 /* VirtualWormhole */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF4C189EC2848F5A300335EC7 /* Source */,\n\t\t\t\tF4C189E22848F59F00335EC7 /* VirtualWormhole.h */,\n\t\t\t);\n\t\t\tpath = VirtualWormhole;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF4C189EC2848F5A300335EC7 /* Source */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF4D3059529B8D9C70006E748 /* WireProtocol */,\n\t\t\t\tF4C189F82848F6F700335EC7 /* Definitions */,\n\t\t\t\tF4C189FB2848F8E800335EC7 /* Services */,\n\t\t\t\tF4C189ED2848F5B500335EC7 /* WormholeManager.swift */,\n\t\t\t);\n\t\t\tpath = Source;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF4C189F12848F5F500335EC7 /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF453C44B2DF0B835007EAD5F /* libcurl.tbd */,\n\t\t\t\tF4C189F32848F61E00335EC7 /* Virtualization.framework */,\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF4C189F52848F69B00335EC7 /* Definitions */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF48E0D0628885E140080DDFA /* PreviewSupport.swift */,\n\t\t\t\tF4C189F62848F6A600335EC7 /* VirtualCoreConstants.swift */,\n\t\t\t\tF4F9B419284CE37C00F21737 /* Logging.swift */,\n\t\t\t);\n\t\t\tpath = Definitions;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF4C189F82848F6F700335EC7 /* Definitions */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF4C189F92848F6F700335EC7 /* VirtualWormholeConstants.swift */,\n\t\t\t);\n\t\t\tpath = Definitions;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF4C189FB2848F8E800335EC7 /* Services */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF4D305AE29B900790006E748 /* Base */,\n\t\t\t\tF4BE580B29BA6C6300C5525C /* DefaultsImport */,\n\t\t\t\tF4D305AD29B9006E0006E748 /* DarwinNotifications */,\n\t\t\t\tF49B82FD2E034F5600395F87 /* DesktopPicture */,\n\t\t\t\tF4C189FC2848F8F600335EC7 /* WHSharedClipboardService.swift */,\n\t\t\t);\n\t\t\tpath = Services;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF4C18A4328491B8500335EC7 /* VirtualBuddyGuest */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF41369A4299183BA002CE8D3 /* Dashboard */,\n\t\t\t\tF4C18A4428491B8500335EC7 /* GuestAppDelegate.swift */,\n\t\t\t\tF4959F3B2992A284001DF4CB /* GuestAppInstaller.swift */,\n\t\t\t\tF4C18A4828491B8500335EC7 /* Assets.xcassets */,\n\t\t\t\tF4C18A4D28491B8500335EC7 /* VirtualBuddyGuest.entitlements */,\n\t\t\t\tF4136998299179B1002CE8D3 /* Main.storyboard */,\n\t\t\t\tF4C18A4A28491B8500335EC7 /* Preview Content */,\n\t\t\t);\n\t\t\tpath = VirtualBuddyGuest;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF4C18A4A28491B8500335EC7 /* Preview Content */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF4C18A4B28491B8500335EC7 /* Preview Assets.xcassets */,\n\t\t\t);\n\t\t\tpath = \"Preview Content\";\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF4C2374E2888AF5B001FF286 /* Utilities */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF4C2374F2888AF67001FF286 /* LogStreamer.swift */,\n\t\t\t\tF4C2374C2888A462001FF286 /* VolumeUtils.swift */,\n\t\t\t\tF4510A772AE2A16F00E24DD9 /* WeakReference.swift */,\n\t\t\t\tF485B91C2BB2F0D9004B3C2B /* ProcessInfo+ECID.swift */,\n\t\t\t\tF485B91E2BB2F4AC004B3C2B /* Bundle+Version.swift */,\n\t\t\t\tF444D1332BB478AD00AB786F /* VBMemoryLeakDebugAssertions.swift */,\n\t\t\t\tF453C4BA2DF231B7007EAD5F /* PreventTerminationAssertion.swift */,\n\t\t\t);\n\t\t\tpath = Utilities;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF4CD131E2E05A5760067DC75 /* Components */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF4CD13432E05AD400067DC75 /* VerticalLabeledContentStyle.swift */,\n\t\t\t\tF4CD131F2E05A5780067DC75 /* FileSystemPathFormControl.swift */,\n\t\t\t\tF4CD133B2E05A9DF0067DC75 /* OpenVirtualBuddySettingsAction.swift */,\n\t\t\t\tF4CD133D2E05AB280067DC75 /* BackwardsCompatibility.swift */,\n\t\t\t\tF4CD13472E05B67E0067DC75 /* SettingsFooter.swift */,\n\t\t\t);\n\t\t\tpath = Components;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF4D3059529B8D9C70006E748 /* WireProtocol */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF4D3059629B8D9D30006E748 /* WormholePacket.swift */,\n\t\t\t\tF4D305B129B907A10006E748 /* WHPing.swift */,\n\t\t\t\tF42862362AE947C90052F029 /* WHPayload.swift */,\n\t\t\t);\n\t\t\tpath = WireProtocol;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF4D3059D29B8DB700006E748 /* VirtualWormholeTests */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF4D305A829B8E70A0006E748 /* Resources */,\n\t\t\t\tF4D3059E29B8DB700006E748 /* WormholePacketTests.swift */,\n\t\t\t);\n\t\t\tpath = VirtualWormholeTests;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF4D305A829B8E70A0006E748 /* Resources */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF4D305A929B8E7120006E748 /* TestStream.bin */,\n\t\t\t);\n\t\t\tpath = Resources;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF4D305AD29B9006E0006E748 /* DarwinNotifications */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF4D305AB29B8FEE90006E748 /* WHDarwinNotificationsService.swift */,\n\t\t\t\tF4D305AF29B900860006E748 /* SystemNotification.swift */,\n\t\t\t);\n\t\t\tpath = DarwinNotifications;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF4D305AE29B900790006E748 /* Base */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF4C189FE2848FB3F00335EC7 /* WormholeServiceProtocol.swift */,\n\t\t\t\tF4FC276629BBAE350012CB65 /* WormholeServiceClient.swift */,\n\t\t\t);\n\t\t\tpath = Base;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF4E4F71D2DF080D200B3B8BA /* Restore Image Selection */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF4E4F71E2DF080DF00B3B8BA /* Components */,\n\t\t\t\tF4E4F71F2DF080FC00B3B8BA /* RestoreImageSelectionController.swift */,\n\t\t\t\tF48E0D16288882BD0080DDFA /* RestoreImageSelectionStep.swift */,\n\t\t\t);\n\t\t\tpath = \"Restore Image Selection\";\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF4E4F71E2DF080DF00B3B8BA /* Components */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF444D0CB2DF322B50086537A /* SoftwareCatalog+Placeholder.swift */,\n\t\t\t\tF47BCDD02C5C06C900165191 /* CatalogGroupPicker.swift */,\n\t\t\t\tF47BCDD82C5D2EDB00165191 /* RestoreImageBrowser.swift */,\n\t\t\t\tF47BCDCE2C5C023900165191 /* CatalogGroupView.swift */,\n\t\t\t\tF444D0CD2DF32E100086537A /* BlurHashFullBleedBackground.swift */,\n\t\t\t\tF444D0F52DF37D170086537A /* VirtualBuddyMonoIcon.swift */,\n\t\t\t\tF444D0FB2DF37EF80086537A /* VirtualBuddyMonoProgressView.swift */,\n\t\t\t\tF444D0F72DF37D410086537A /* VirtualDisplayView.swift */,\n\t\t\t\tF444D0F92DF37DFB0086537A /* InstallProgressDisplayView.swift */,\n\t\t\t);\n\t\t\tpath = Components;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF4E7680B29B651180075A897 /* Resources */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF444D0C92DF321CD0086537A /* CatalogGroupPlaceholder.heic */,\n\t\t\t\tF4E7680C29B651220075A897 /* VirtualUI.xcassets */,\n\t\t\t);\n\t\t\tpath = Resources;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF4E7DF832BB30D8200C459FC /* Screenshot */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF4E7DF912BB3338900C459FC /* NSImage+HEIC.swift */,\n\t\t\t\tF49B832A2E04593100395F87 /* NSImage+DRMProtected.swift */,\n\t\t\t);\n\t\t\tpath = Screenshot;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF4E7DF932BB336E000C459FC /* SavedState */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF485B91A2BB22D2D004B3C2B /* VBSavedStateMetadata.swift */,\n\t\t\t\tF4E7DF942BB336F600C459FC /* VBSavedStatePackage.swift */,\n\t\t\t\tF4DE1C0A2D6F54E000603527 /* VBSavedStateMetadata+Clone.swift */,\n\t\t\t\tF4DE1C0E2D6F603300603527 /* VBSavedStatePackage+VM.swift */,\n\t\t\t\tF4E7DF962BB33E1700C459FC /* VMLibraryController+SavedState.swift */,\n\t\t\t);\n\t\t\tpath = SavedState;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF4F9B414284CE0DC00F21737 /* Settings */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF4F9B415284CE0F900F21737 /* VBSettings.swift */,\n\t\t\t\tF4F9B417284CE12000F21737 /* VBSettingsContainer.swift */,\n\t\t\t\tF45502152DF45E4D005582A4 /* VBSettings+CatalogDownload.swift */,\n\t\t\t);\n\t\t\tpath = Settings;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tF4FC276829BBAE470012CB65 /* Implementation */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF4BE581229BA6E0D00C5525C /* DefaultsDomainDescriptor.swift */,\n\t\t\t\tF4BE583F29BA7B7A00C5525C /* DefaultsDomain+ExportImport.swift */,\n\t\t\t\tF4BE581029BA6DFC00C5525C /* DefaultsImportController.swift */,\n\t\t\t);\n\t\t\tpath = Implementation;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXHeadersBuildPhase section */\n\t\tF43B01102AD858FE00164CD1 /* Headers */ = {\n\t\t\tisa = PBXHeadersBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tF43B01182AD858FE00164CD1 /* DeepLinkSecurity.h in Headers */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tF498ACF92884BF13006F1C00 /* Headers */ = {\n\t\t\tisa = PBXHeadersBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tF413696B29916F6E002CE8D3 /* NSStatusItem+.h in Headers */,\n\t\t\t\tF498AD012884BF13006F1C00 /* VirtualUI.h in Headers */,\n\t\t\t\tF413696C29916F6E002CE8D3 /* NSApplication+MenuBar.h in Headers */,\n\t\t\t\tF413696D29916F6E002CE8D3 /* NSStatusBarPrivate.h in Headers */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tF4BE9C6027FF053A00B648F8 /* Headers */ = {\n\t\t\tisa = PBXHeadersBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tF4BE9C6827FF053A00B648F8 /* VirtualCore.h in Headers */,\n\t\t\t\tF485B9222BB306AF004B3C2B /* VBDebugUtil.h in Headers */,\n\t\t\t\tF4BE9C8627FF140F00B648F8 /* VirtualizationPrivate.h in Headers */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tF4C189DB2848F59F00335EC7 /* Headers */ = {\n\t\t\tisa = PBXHeadersBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tF4C189E32848F59F00335EC7 /* VirtualWormhole.h in Headers */,\n\t\t\t\tF49B83012E034F6800395F87 /* NSImage+DesktopPicture.h in Headers */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXHeadersBuildPhase section */\n\n/* Begin PBXNativeTarget section */\n\t\tF41369AA29918576002CE8D3 /* VirtualBuddyGuestHelper */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = F41369B729918576002CE8D3 /* Build configuration list for PBXNativeTarget \"VirtualBuddyGuestHelper\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tF41369A729918576002CE8D3 /* Sources */,\n\t\t\t\tF41369A829918576002CE8D3 /* Frameworks */,\n\t\t\t\tF41369A929918576002CE8D3 /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = VirtualBuddyGuestHelper;\n\t\t\tproductName = VirtualBuddyGuestHelper;\n\t\t\tproductReference = F41369AB29918576002CE8D3 /* VirtualBuddyGuestHelper.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n\t\tF43B01142AD858FE00164CD1 /* DeepLinkSecurity */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = F43B01212AD858FE00164CD1 /* Build configuration list for PBXNativeTarget \"DeepLinkSecurity\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tF43B01102AD858FE00164CD1 /* Headers */,\n\t\t\t\tF43B01112AD858FE00164CD1 /* Sources */,\n\t\t\t\tF43B01122AD858FE00164CD1 /* Frameworks */,\n\t\t\t\tF43B01132AD858FE00164CD1 /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = DeepLinkSecurity;\n\t\t\tproductName = DeepLinkSecurity;\n\t\t\tproductReference = F43B01152AD858FE00164CD1 /* DeepLinkSecurity.framework */;\n\t\t\tproductType = \"com.apple.product-type.framework\";\n\t\t};\n\t\tF498ACFD2884BF13006F1C00 /* VirtualUI */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = F498AD082884BF13006F1C00 /* Build configuration list for PBXNativeTarget \"VirtualUI\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tF498ACF92884BF13006F1C00 /* Headers */,\n\t\t\t\tF498ACFA2884BF13006F1C00 /* Sources */,\n\t\t\t\tF498ACFB2884BF13006F1C00 /* Frameworks */,\n\t\t\t\tF498ACFC2884BF13006F1C00 /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\tF4510A7A2AE2B3AB00E24DD9 /* PBXTargetDependency */,\n\t\t\t\tF498AD0B2884BF60006F1C00 /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = VirtualUI;\n\t\t\tpackageProductDependencies = (\n\t\t\t\tF4A7FB722BB7238A00E4C12A /* SwiftUIIntrospect-Static */,\n\t\t\t\tF453C4622DF0E609007EAD5F /* BuddyKit */,\n\t\t\t);\n\t\t\tproductName = VirtualUI;\n\t\t\tproductReference = F498ACFE2884BF13006F1C00 /* VirtualUI.framework */;\n\t\t\tproductType = \"com.apple.product-type.framework\";\n\t\t};\n\t\tF4BE9C4D27FF052100B648F8 /* VirtualBuddy */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = F4BE9C5D27FF052100B648F8 /* Build configuration list for PBXNativeTarget \"VirtualBuddy\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tF4BE9C4A27FF052100B648F8 /* Sources */,\n\t\t\t\tF4BE9C4B27FF052100B648F8 /* Frameworks */,\n\t\t\t\tF4BE9C4C27FF052100B648F8 /* Resources */,\n\t\t\t\tF4BE9C7027FF053A00B648F8 /* Embed Frameworks */,\n\t\t\t\tF443620B29B79A5800745B43 /* Embed Guest App */,\n\t\t\t\tF453C4282DF0B65B007EAD5F /* Create Command-Line Tool Symlinks */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\tF498AD032884BF13006F1C00 /* PBXTargetDependency */,\n\t\t\t\tF4C18A5828491C0D00335EC7 /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = VirtualBuddy;\n\t\t\tpackageProductDependencies = (\n\t\t\t\tF4D0F71728674E4B004D5782 /* Sparkle */,\n\t\t\t\tF43B01462AD85A7D00164CD1 /* URLQueryItemCoder */,\n\t\t\t\tF453C4112DF0B1ED007EAD5F /* BuddyKit */,\n\t\t\t\tF453C42A2DF0B792007EAD5F /* ArgumentParser */,\n\t\t\t\tF453C4492DF0B7F6007EAD5F /* FragmentZip */,\n\t\t\t);\n\t\t\tproductName = VirtualBuddy;\n\t\t\tproductReference = F4BE9C4E27FF052100B648F8 /* VirtualBuddy.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n\t\tF4BE9C6427FF053A00B648F8 /* VirtualCore */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = F4BE9C6D27FF053A00B648F8 /* Build configuration list for PBXNativeTarget \"VirtualCore\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tF4BE9C6027FF053A00B648F8 /* Headers */,\n\t\t\t\tF4BE9C6127FF053A00B648F8 /* Sources */,\n\t\t\t\tF4BE9C6227FF053A00B648F8 /* Frameworks */,\n\t\t\t\tF4BE9C6327FF053A00B648F8 /* Resources */,\n\t\t\t\tF47BCD9C2C5BE89E00165191 /* Copy Software Catalog Feeds */,\n\t\t\t\tF4A7FB4C2BB5F0B700E4C12A /* Copy Preview Library */,\n\t\t\t\tF45502222DF46386005582A4 /* Copy Preview Library Downloads */,\n\t\t\t\tF4A7FB462BB5ED6400E4C12A /* Copy Preview Saved States */,\n\t\t\t\tF4A277142BF51C480011B626 /* Strip Preview Content in Release Builds */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\tF453C3ED2DF0A4D1007EAD5F /* PBXTargetDependency */,\n\t\t\t\tF4C189F02848F5F100335EC7 /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = VirtualCore;\n\t\t\tproductName = VirtualCore;\n\t\t\tproductReference = F4BE9C6527FF053A00B648F8 /* VirtualCore.framework */;\n\t\t\tproductType = \"com.apple.product-type.framework\";\n\t\t};\n\t\tF4C189DF2848F59F00335EC7 /* VirtualWormhole */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = F4C189EB2848F59F00335EC7 /* Build configuration list for PBXNativeTarget \"VirtualWormhole\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tF4C189DB2848F59F00335EC7 /* Headers */,\n\t\t\t\tF4C189DC2848F59F00335EC7 /* Sources */,\n\t\t\t\tF4C189DD2848F59F00335EC7 /* Frameworks */,\n\t\t\t\tF4C189DE2848F59F00335EC7 /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = VirtualWormhole;\n\t\t\tpackageProductDependencies = (\n\t\t\t);\n\t\t\tproductName = VirtualWormhole;\n\t\t\tproductReference = F4C189E02848F59F00335EC7 /* VirtualWormhole.framework */;\n\t\t\tproductType = \"com.apple.product-type.framework\";\n\t\t};\n\t\tF4C18A4128491B8500335EC7 /* VirtualBuddyGuest */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = F4C18A4E28491B8500335EC7 /* Build configuration list for PBXNativeTarget \"VirtualBuddyGuest\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tF4552F4729BA60C7002A21D8 /* Set VBGuestBuildID in Info.plist */,\n\t\t\t\tF4C18A3E28491B8500335EC7 /* Sources */,\n\t\t\t\tF4C18A3F28491B8500335EC7 /* Frameworks */,\n\t\t\t\tF4C18A4028491B8500335EC7 /* Resources */,\n\t\t\t\tF4C18A5628491B9D00335EC7 /* Embed Frameworks */,\n\t\t\t\tF41369BE2991861C002CE8D3 /* Embed Login Item */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\tF41369BD29918617002CE8D3 /* PBXTargetDependency */,\n\t\t\t\tF4C18A5528491B9D00335EC7 /* PBXTargetDependency */,\n\t\t\t\tF413699D299179F8002CE8D3 /* PBXTargetDependency */,\n\t\t\t\tF42862312AE87D7E0052F029 /* PBXTargetDependency */,\n\t\t\t\tF453C4272DF0B602007EAD5F /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = VirtualBuddyGuest;\n\t\t\tproductName = VirtualBuddyGuest;\n\t\t\tproductReference = F4C18A4228491B8500335EC7 /* VirtualBuddyGuest.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n\t\tF4D3059B29B8DB700006E748 /* VirtualWormholeTests */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = F4D305A329B8DB700006E748 /* Build configuration list for PBXNativeTarget \"VirtualWormholeTests\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tF4D3059829B8DB700006E748 /* Sources */,\n\t\t\t\tF4D3059929B8DB700006E748 /* Frameworks */,\n\t\t\t\tF4D3059A29B8DB700006E748 /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\tF4D305A229B8DB700006E748 /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = VirtualWormholeTests;\n\t\t\tproductName = VirtualWormholeTests;\n\t\t\tproductReference = F4D3059C29B8DB700006E748 /* VirtualWormholeTests.xctest */;\n\t\t\tproductType = \"com.apple.product-type.bundle.unit-test\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\tF4BE9C4627FF052100B648F8 /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tBuildIndependentTargetsInParallel = 1;\n\t\t\t\tLastSwiftUpdateCheck = 1640;\n\t\t\t\tLastUpgradeCheck = 1640;\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\tF41369AA29918576002CE8D3 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 14.2;\n\t\t\t\t\t\tLastSwiftMigration = 1420;\n\t\t\t\t\t};\n\t\t\t\t\tF43B01142AD858FE00164CD1 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 15.1;\n\t\t\t\t\t};\n\t\t\t\t\tF453C44F2DF0BCE3007EAD5F = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 16.4;\n\t\t\t\t\t};\n\t\t\t\t\tF498ACFD2884BF13006F1C00 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 14.0;\n\t\t\t\t\t\tLastSwiftMigration = 1400;\n\t\t\t\t\t};\n\t\t\t\t\tF4BE9C4D27FF052100B648F8 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 13.3;\n\t\t\t\t\t\tLastSwiftMigration = 1330;\n\t\t\t\t\t};\n\t\t\t\t\tF4BE9C6427FF053A00B648F8 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 13.3;\n\t\t\t\t\t\tLastSwiftMigration = 1530;\n\t\t\t\t\t};\n\t\t\t\t\tF4C189DF2848F59F00335EC7 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 13.4;\n\t\t\t\t\t\tLastSwiftMigration = 2600;\n\t\t\t\t\t};\n\t\t\t\t\tF4C18A4128491B8500335EC7 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 13.4;\n\t\t\t\t\t\tLastSwiftMigration = 1420;\n\t\t\t\t\t};\n\t\t\t\t\tF4D3059B29B8DB700006E748 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 14.2;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = F4BE9C4927FF052100B648F8 /* Build configuration list for PBXProject \"VirtualBuddy\" */;\n\t\t\tcompatibilityVersion = \"Xcode 13.0\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = F4BE9C4527FF052100B648F8;\n\t\t\tpackageReferences = (\n\t\t\t\tF4D0F71628674E4B004D5782 /* XCRemoteSwiftPackageReference \"Sparkle\" */,\n\t\t\t\tF43B01452AD85A7D00164CD1 /* XCRemoteSwiftPackageReference \"URLQueryItemCoder\" */,\n\t\t\t\tF4A7FB712BB7238A00E4C12A /* XCRemoteSwiftPackageReference \"swiftui-introspect\" */,\n\t\t\t\tF453C3D82DF0A426007EAD5F /* XCRemoteSwiftPackageReference \"BuddyKit\" */,\n\t\t\t\tF453C4012DF0AEF5007EAD5F /* XCRemoteSwiftPackageReference \"swift-argument-parser\" */,\n\t\t\t\tF453C4482DF0B7F6007EAD5F /* XCRemoteSwiftPackageReference \"libfragmentzip\" */,\n\t\t\t);\n\t\t\tproductRefGroup = F4BE9C4F27FF052100B648F8 /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\tF4BE9C4D27FF052100B648F8 /* VirtualBuddy */,\n\t\t\t\tF4C18A4128491B8500335EC7 /* VirtualBuddyGuest */,\n\t\t\t\tF41369AA29918576002CE8D3 /* VirtualBuddyGuestHelper */,\n\t\t\t\tF43B01142AD858FE00164CD1 /* DeepLinkSecurity */,\n\t\t\t\tF4BE9C6427FF053A00B648F8 /* VirtualCore */,\n\t\t\t\tF498ACFD2884BF13006F1C00 /* VirtualUI */,\n\t\t\t\tF4D3059B29B8DB700006E748 /* VirtualWormholeTests */,\n\t\t\t\tF4C189DF2848F59F00335EC7 /* VirtualWormhole */,\n\t\t\t\tF453C44F2DF0BCE3007EAD5F /* vctool */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\tF41369A929918576002CE8D3 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tF41369B229918576002CE8D3 /* Assets.xcassets in Resources */,\n\t\t\t\tF41369B529918576002CE8D3 /* Main.storyboard in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tF43B01132AD858FE00164CD1 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tF498ACFC2884BF13006F1C00 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tF444D0CA2DF321CD0086537A /* CatalogGroupPlaceholder.heic in Resources */,\n\t\t\t\tF4E7680D29B651220075A897 /* VirtualUI.xcassets in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tF4BE9C4C27FF052100B648F8 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tF4BE9C5927FF052100B648F8 /* Preview Assets.xcassets in Resources */,\n\t\t\t\tF4BE9C5627FF052100B648F8 /* Assets.xcassets in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tF4BE9C6327FF053A00B648F8 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tF453C4922DF1D213007EAD5F /* FakeRestoreImage.ipsw in Resources */,\n\t\t\t\tF453C45D2DF0D28A007EAD5F /* README.md in Resources */,\n\t\t\t\tF417257428877478004FF8A7 /* VirtualCore.xcassets in Resources */,\n\t\t\t\tF443620F29B7A0C600745B43 /* CreateGuestImage.sh in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tF4C189DE2848F59F00335EC7 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tF4BE580E29BA6C8D00C5525C /* DefaultsDomains.plist in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tF4C18A4028491B8500335EC7 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tF4136999299179B1002CE8D3 /* Main.storyboard in Resources */,\n\t\t\t\tF4C18A4C28491B8500335EC7 /* Preview Assets.xcassets in Resources */,\n\t\t\t\tF4C18A4928491B8500335EC7 /* Assets.xcassets in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tF4D3059A29B8DB700006E748 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tF4D305AA29B8E7120006E748 /* TestStream.bin in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXShellScriptBuildPhase section */\n\t\tF453C4282DF0B65B007EAD5F /* Create Command-Line Tool Symlinks */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\t\"$(BUILT_PRODUCTS_DIR)/$(EXECUTABLE_PATH)\",\n\t\t\t);\n\t\t\tname = \"Create Command-Line Tool Symlinks\";\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t\t\"$(BUILT_PRODUCTS_DIR)/$(CONTENTS_FOLDER_PATH)/MacOS/vctool\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"# Go into the VirtualBuddy.app/Contents/MacOS path\\ncd \\\"$BUILT_PRODUCTS_DIR/$CONTENTS_FOLDER_PATH/MacOS\\\"\\n\\n# Symlink VirtualBuddy as vctool for VirtualCatalog command-line tool\\nln -fs $EXECUTABLE_NAME vctool\\n\";\n\t\t};\n\t\tF4552F4729BA60C7002A21D8 /* Set VBGuestBuildID in Info.plist */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\talwaysOutOfDate = 1;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\tname = \"Set VBGuestBuildID in Info.plist\";\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t\t\"$(DERIVED_FILE_DIR)/VBGenerated-Info.plist\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"# Ensures VirtualBuddyGuest has a VBGuestBuildID entry in its Info.plist file\\n# This entry is used by the app itself when running in a VM to determine when it needs to be updated.\\nTEMPLATE=\\\"<?xml version=\\\\\\\"1.0\\\\\\\" encoding=\\\\\\\"UTF-8\\\\\\\"?><!DOCTYPE plist PUBLIC \\\\\\\"-//Apple//DTD PLIST 1.0//EN\\\\\\\" \\\\\\\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\\\\\\\"><plist version=\\\\\\\"1.0\\\\\\\"><dict/></plist>\\\"\\nPLISTPATH=\\\"$DERIVED_FILE_DIR/VBGenerated-Info.plist\\\"\\n\\necho $TEMPLATE > \\\"$PLISTPATH\\\"\\n\\n/usr/libexec/PlistBuddy -c \\\"Add :VBGuestBuildID string xxxxxx\\\" \\\"$PLISTPATH\\\" 2>/dev/null || echo \\\"\\\"\\n\\n/usr/libexec/PlistBuddy -c \\\"Set :VBGuestBuildID `uuidgen`\\\" \\\"$PLISTPATH\\\"\\n\";\n\t\t};\n\t\tF4A277142BF51C480011B626 /* Strip Preview Content in Release Builds */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\talwaysOutOfDate = 1;\n\t\t\tbuildActionMask = 8;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\tname = \"Strip Preview Content in Release Builds\";\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 1;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"if [ \\\"${CONFIGURATION}\\\" = \\\"Release_Managed\\\" ] || [ \\\"${CONFIGURATION}\\\" = \\\"Dev_Release\\\" ] ]; then\\n    PREVIEW_CONTENT_PATH=\\\"$CODESIGNING_FOLDER_PATH/Resources/PreviewLibrary\\\"\\n    \\n    if [ -d \\\"$PREVIEW_CONTENT_PATH\\\" ]; then\\n        echo \\\"Stripping VirtualCore preview content from release build at $PREVIEW_CONTENT_PATH\\\"\\n        rm -r \\\"$PREVIEW_CONTENT_PATH\\\"\\n    fi\\nfi\\n\";\n\t\t};\n/* End PBXShellScriptBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\tF41369A729918576002CE8D3 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tF41369AE29918576002CE8D3 /* GuestHelperAppDelegate.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tF43B01112AD858FE00164CD1 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tF43B013E2AD8590F00164CD1 /* DeepLinkManagementStore.swift in Sources */,\n\t\t\t\tF43B01392AD8590F00164CD1 /* DeepLinkClient+Crypto.swift in Sources */,\n\t\t\t\tF43B013C2AD8590F00164CD1 /* KeychainDeepLinkAuthStore.swift in Sources */,\n\t\t\t\tF43B013A2AD8590F00164CD1 /* DeepLinkClientDescriptor+.swift in Sources */,\n\t\t\t\tF43B01352AD8590F00164CD1 /* DeepLinkAuthUI.swift in Sources */,\n\t\t\t\tF43B01402AD8590F00164CD1 /* DeepLinkAuthStore.swift in Sources */,\n\t\t\t\tF43B01362AD8590F00164CD1 /* DeepLinkSentinel.swift in Sources */,\n\t\t\t\tF43B01372AD8590F00164CD1 /* OpenDeepLinkRequest.swift in Sources */,\n\t\t\t\tF43B013F2AD8590F00164CD1 /* UserDefaultsDeepLinkManagementStore.swift in Sources */,\n\t\t\t\tF43B013D2AD8590F00164CD1 /* MemoryDeepLinkAuthStore.swift in Sources */,\n\t\t\t\tF43B01382AD8590F00164CD1 /* DeepLinkClientDescriptor.swift in Sources */,\n\t\t\t\tF43B01412AD8590F00164CD1 /* DeepLinkSecurityDefines.swift in Sources */,\n\t\t\t\tF43B013B2AD8590F00164CD1 /* DeepLinkClient.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tF498ACFA2884BF13006F1C00 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tF417256128861A05004FF8A7 /* SharingConfigurationView.swift in Sources */,\n\t\t\t\tF413696A29916F6E002CE8D3 /* StatusBarHighlightView.swift in Sources */,\n\t\t\t\tF47BCDD92C5D2EE300165191 /* RestoreImageBrowser.swift in Sources */,\n\t\t\t\tF498AD0E2884BF9D006F1C00 /* VMConfigurationView.swift in Sources */,\n\t\t\t\tF48E0D25288882E50080DDFA /* OnAppearOnce.swift in Sources */,\n\t\t\t\tF413696429916F6E002CE8D3 /* StatusItemProviderProtocol.swift in Sources */,\n\t\t\t\tF422587E2885E2ED009420AE /* HardwareConfigurationView.swift in Sources */,\n\t\t\t\tF444D0CC2DF322B90086537A /* SoftwareCatalog+Placeholder.swift in Sources */,\n\t\t\t\tF4CD134A2E05CB390067DC75 /* AutomationSettingsView.swift in Sources */,\n\t\t\t\tF4FC98392BB386A000E511C9 /* ContinuousProgressIndicator.swift in Sources */,\n\t\t\t\tF4E4F7202DF080FC00B3B8BA /* RestoreImageSelectionController.swift in Sources */,\n\t\t\t\tF48E0D1D288882BD0080DDFA /* AuthenticatingWebView.swift in Sources */,\n\t\t\t\tF4CD13442E05AD400067DC75 /* VerticalLabeledContentStyle.swift in Sources */,\n\t\t\t\tF47BCDCF2C5C023E00165191 /* CatalogGroupView.swift in Sources */,\n\t\t\t\tF413696529916F6E002CE8D3 /* StatusItemPanelContentController.swift in Sources */,\n\t\t\t\tF4E7DFCF2BB3587D00C459FC /* VirtualMachineSessionUIManager.swift in Sources */,\n\t\t\t\tF4CD13462E05B4DE0067DC75 /* VirtualizationSettingsView.swift in Sources */,\n\t\t\t\tF4CD13202E05A5780067DC75 /* FileSystemPathFormControl.swift in Sources */,\n\t\t\t\tF417CBB92E0F3D2E0065B5D6 /* FirstLaunchExperienceView.swift in Sources */,\n\t\t\t\tF4561A6828981B4100055289 /* VirtualMachineNameInputView.swift in Sources */,\n\t\t\t\tF413696229916F6E002CE8D3 /* StatusItemButton.swift in Sources */,\n\t\t\t\tF49AA2C529BA31CC009625F7 /* VirtualMachineSessionUI.swift in Sources */,\n\t\t\t\tF48E0D34288889E60080DDFA /* InstallConfigurationStepView.swift in Sources */,\n\t\t\t\tF444D0F82DF37D410086537A /* VirtualDisplayView.swift in Sources */,\n\t\t\t\tF42C015B2888FC0C00EB15CD /* VMSessionConfigurationView.swift in Sources */,\n\t\t\t\tF42C015D2888FC0C00EB15CD /* SwiftUIVMView.swift in Sources */,\n\t\t\t\tF48E0D23288882E50080DDFA /* DecentFormView.swift in Sources */,\n\t\t\t\tF417256F2887544A004FF8A7 /* StorageDeviceDetailView.swift in Sources */,\n\t\t\t\tF498AD142884C36A006F1C00 /* NumericValueField.swift in Sources */,\n\t\t\t\tF417255D288604A8004FF8A7 /* SoundConfigurationView.swift in Sources */,\n\t\t\t\tF48E0D1E288882BD0080DDFA /* VMInstallationWizard.swift in Sources */,\n\t\t\t\tF462C9422E0C96D300C172E2 /* FB18383725Window.swift in Sources */,\n\t\t\t\tF48E0D03288858E00080DDFA /* ManagedDiskImageEditor.swift in Sources */,\n\t\t\t\tF413696F29916F6E002CE8D3 /* NSStatusItem+.m in Sources */,\n\t\t\t\tF417CB882E0EDECD0065B5D6 /* BackportedContentUnavailableView.swift in Sources */,\n\t\t\t\tF453C4B42DF20301007EAD5F /* RestoreImageURLInputView.swift in Sources */,\n\t\t\t\tF42C015A2888FC0C00EB15CD /* LibraryView.swift in Sources */,\n\t\t\t\tF444D0FA2DF37DFB0086537A /* InstallProgressDisplayView.swift in Sources */,\n\t\t\t\tF413696329916F6E002CE8D3 /* StatusBarPanelChrome.swift in Sources */,\n\t\t\t\tF49AA2C729BA3F2B009625F7 /* VBRestorableWindow+Resizing.swift in Sources */,\n\t\t\t\tF47BCDCD2C5C01EF00165191 /* RemoteImage.swift in Sources */,\n\t\t\t\tF4CD133E2E05AB280067DC75 /* BackwardsCompatibility.swift in Sources */,\n\t\t\t\tF4E7680A29B64C590075A897 /* GuestTypePicker.swift in Sources */,\n\t\t\t\tF4B5C5DB28873628005AA632 /* GroupedList.swift in Sources */,\n\t\t\t\tF4FC983B2BB386B500E511C9 /* MaskProgressView.swift in Sources */,\n\t\t\t\tF417256C2887500F004FF8A7 /* StorageConfigurationView.swift in Sources */,\n\t\t\t\tF42C014C2888C34B00EB15CD /* LogConsole.swift in Sources */,\n\t\t\t\tF49B832D2E046B8D00395F87 /* GuestAppConfigurationView.swift in Sources */,\n\t\t\t\tF42C015C2888FC0C00EB15CD /* VirtualMachineSessionView.swift in Sources */,\n\t\t\t\tF498AD182884C593006F1C00 /* NumericPropertyControl.swift in Sources */,\n\t\t\t\tF4CD133C2E05A9DF0067DC75 /* OpenVirtualBuddySettingsAction.swift in Sources */,\n\t\t\t\tF4CD13402E05AB8F0067DC75 /* GeneralSettingsView.swift in Sources */,\n\t\t\t\tF413696729916F6E002CE8D3 /* VUIAppKitViewControllerHost.swift in Sources */,\n\t\t\t\tF4450CCA2ACB0DB500092618 /* KeyboardDeviceConfigurationView.swift in Sources */,\n\t\t\t\tF444D0FC2DF37EF80086537A /* VirtualBuddyMonoProgressView.swift in Sources */,\n\t\t\t\tF444D0CE2DF32E100086537A /* BlurHashFullBleedBackground.swift in Sources */,\n\t\t\t\tF42C01612888FC3500EB15CD /* LibraryItemView.swift in Sources */,\n\t\t\t\tF41725682886E5AD004FF8A7 /* MaterialView.swift in Sources */,\n\t\t\t\tF413696E29916F6E002CE8D3 /* NSApplication+MenuBar.m in Sources */,\n\t\t\t\tF48E0D2B288883150080DDFA /* HostingWindowController.swift in Sources */,\n\t\t\t\tF47BCDCB2C5C01D100165191 /* BlurHashDecoding.swift in Sources */,\n\t\t\t\tF42258802885E71D009420AE /* PropertyControl.swift in Sources */,\n\t\t\t\tF4E7680F29B655DD0075A897 /* InstallMethod.swift in Sources */,\n\t\t\t\tF42C014A2888C2F800EB15CD /* InstallationConsole.swift in Sources */,\n\t\t\t\tF444D0F42DF34BE80086537A /* CALayer+Asset.swift in Sources */,\n\t\t\t\tF413696829916F6E002CE8D3 /* StatusItemMenuBarExtraView.swift in Sources */,\n\t\t\t\tF453C4B92DF21985007EAD5F /* VMInstallData.swift in Sources */,\n\t\t\t\tF48E0D24288882E50080DDFA /* NSAlert+Confirmation.swift in Sources */,\n\t\t\t\tF42258742885E10B009420AE /* VMConfigurationViewModel.swift in Sources */,\n\t\t\t\tF48E0D1A288882BD0080DDFA /* InstallationWizardTitle.swift in Sources */,\n\t\t\t\tF41725632886DD37004FF8A7 /* SharedFolderListItem.swift in Sources */,\n\t\t\t\tF42258782885E14A009420AE /* DisplayConfigurationView.swift in Sources */,\n\t\t\t\tF413697029916F6E002CE8D3 /* StatusItemManager.swift in Sources */,\n\t\t\t\tF48E0D1C288882BD0080DDFA /* RestoreImageSelectionStep.swift in Sources */,\n\t\t\t\tF47BCDD52C5C0B8F00165191 /* Array+Navigation.swift in Sources */,\n\t\t\t\tF42C014E2888CBCB00EB15CD /* InstallProgressStepView.swift in Sources */,\n\t\t\t\tF428622D2AE8726D0052F029 /* VirtualMachineControls.swift in Sources */,\n\t\t\t\tF413696929916F6E002CE8D3 /* StatusBarContentPanel.swift in Sources */,\n\t\t\t\tF4FC983D2BB386DD00E511C9 /* VMProgressOverlay.swift in Sources */,\n\t\t\t\tF48E0D2A288883150080DDFA /* OpenCocoaWindowAction.swift in Sources */,\n\t\t\t\tF4E4F6C52DEF96C200B3B8BA /* ChromeBorderModifier.swift in Sources */,\n\t\t\t\tF41725662886DF58004FF8A7 /* OpenSavePanelUtils.swift in Sources */,\n\t\t\t\tF498AD162884C36A006F1C00 /* ControlGroupChrome.swift in Sources */,\n\t\t\t\tF48E0D2C288883150080DDFA /* WindowEnvironment.swift in Sources */,\n\t\t\t\tF498AD1A2884C5FF006F1C00 /* SliderConversion.swift in Sources */,\n\t\t\t\tF422586D2885CC9F009420AE /* SharedFocusEnvironment.swift in Sources */,\n\t\t\t\tF422587C2885E1CE009420AE /* NetworkConfigurationView.swift in Sources */,\n\t\t\t\tF41369A329917FA0002CE8D3 /* ScreenChangeModifier.swift in Sources */,\n\t\t\t\tF48E0D1F288882BD0080DDFA /* VMInstallationViewModel.swift in Sources */,\n\t\t\t\tF4ECC6D52C63BFD5001DAC1D /* NumberDisplayMode.swift in Sources */,\n\t\t\t\tF4CD13482E05B67E0067DC75 /* SettingsFooter.swift in Sources */,\n\t\t\t\tF42258702885D537009420AE /* EphemeralTextField.swift in Sources */,\n\t\t\t\tF417CB8A2E0EDF1A0065B5D6 /* EqualWidthHStack.swift in Sources */,\n\t\t\t\tF49AA2C329BA22A5009625F7 /* VBRestorableWindow.swift in Sources */,\n\t\t\t\tF422587A2885E17D009420AE /* ConfigurationSection.swift in Sources */,\n\t\t\t\tF4B5C5D32886FA8D005AA632 /* SharedFoldersManagementView.swift in Sources */,\n\t\t\t\tF48E0D32288884A10080DDFA /* RestoreImageDownloadView.swift in Sources */,\n\t\t\t\tF4B5C5D52886FFB5005AA632 /* PointingDeviceConfigurationView.swift in Sources */,\n\t\t\t\tF49B82982E02F5A900395F87 /* VMArtworkView.swift in Sources */,\n\t\t\t\tF48E0D2F2888835A0080DDFA /* VirtualUIConstants.swift in Sources */,\n\t\t\t\tF47BCDD32C5C0AB300165191 /* KeyboardNavigationModifier.swift in Sources */,\n\t\t\t\tF444D0F62DF37D170086537A /* VirtualBuddyMonoIcon.swift in Sources */,\n\t\t\t\tF4A7FB6E2BB7206C00E4C12A /* SelfSizingGroupedForm.swift in Sources */,\n\t\t\t\tF47BCDD12C5C06CE00165191 /* CatalogGroupPicker.swift in Sources */,\n\t\t\t\tF4A7FB752BB7252A00E4C12A /* PreviewSupport-VirtualUI.swift in Sources */,\n\t\t\t\tF48E0D1B288882BD0080DDFA /* InstallMethodPicker.swift in Sources */,\n\t\t\t\tF42258722885E100009420AE /* VMConfigurationSheet.swift in Sources */,\n\t\t\t\tF4A7FB3F2BB5EBEF00E4C12A /* SavedStatePicker.swift in Sources */,\n\t\t\t\tF413697929917135002CE8D3 /* CGFloat+OnePixel.swift in Sources */,\n\t\t\t\tF42C015E2888FC0C00EB15CD /* SettingsScreen.swift in Sources */,\n\t\t\t\tF45502142DF394DC005582A4 /* VirtualBuddyInstallerInputView.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tF4BE9C4A27FF052100B648F8 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tF43B01442AD85A6500164CD1 /* VirtualBuddyDeepLinks.swift in Sources */,\n\t\t\t\tF43B014B2AD85ABB00164CD1 /* DeepLinkAuthDialog.swift in Sources */,\n\t\t\t\tF4D0F71A28674E76004D5782 /* SoftwareUpdateController.swift in Sources */,\n\t\t\t\tF4BE9C8127FF111100B648F8 /* VirtualBuddyAppDelegate.swift in Sources */,\n\t\t\t\tF453C43B2DF0B7A5007EAD5F /* Helpers.swift in Sources */,\n\t\t\t\tF453C43C2DF0B7A5007EAD5F /* VCTool.swift in Sources */,\n\t\t\t\tF453C43D2DF0B7A5007EAD5F /* URL+ContentLength.swift in Sources */,\n\t\t\t\tF453C43E2DF0B7A5007EAD5F /* IPSWCommand.swift in Sources */,\n\t\t\t\tF453C43F2DF0B7A5007EAD5F /* ImageCommand.swift in Sources */,\n\t\t\t\tF453C4402DF0B7A5007EAD5F /* BuildManifest.swift in Sources */,\n\t\t\t\tF453C4412DF0B7A5007EAD5F /* GroupCommand.swift in Sources */,\n\t\t\t\tF453C4422DF0B7A5007EAD5F /* MobileDeviceCommand.swift in Sources */,\n\t\t\t\tF453C4432DF0B7A5007EAD5F /* BuildManifest+Fetch.swift in Sources */,\n\t\t\t\tF453C4682DF10181007EAD5F /* BlurHashCommand.swift in Sources */,\n\t\t\t\tF453C44E2DF0B870007EAD5F /* VirtualBuddyCLI.swift in Sources */,\n\t\t\t\tF453C4442DF0B7A5007EAD5F /* TreeStringConvertible.swift in Sources */,\n\t\t\t\tF453C4452DF0B7A5007EAD5F /* MigrateCommand.swift in Sources */,\n\t\t\t\tF453C4462DF0B7A5007EAD5F /* ResolveCommand.swift in Sources */,\n\t\t\t\tF453C4472DF0B7A5007EAD5F /* CatalogCommand.swift in Sources */,\n\t\t\t\tF4BE9C5227FF052100B648F8 /* VirtualBuddyApp.swift in Sources */,\n\t\t\t\tF43B014E2AD86BFA00164CD1 /* DeepLinkHandler.swift in Sources */,\n\t\t\t\tF453C4232DF0B5B1007EAD5F /* VirtualBuddyEntryPoint.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tF4BE9C6127FF053A00B648F8 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tF4E7DF922BB3338900C459FC /* NSImage+HEIC.swift in Sources */,\n\t\t\t\tF417257128877121004FF8A7 /* DiskImageGenerator.swift in Sources */,\n\t\t\t\tF4F9B416284CE0F900F21737 /* VBSettings.swift in Sources */,\n\t\t\t\tF42CF4A82DF5FEC3001DE049 /* BlurHashToken.swift in Sources */,\n\t\t\t\tF41725762887758A004FF8A7 /* RandomNameGenerator.swift in Sources */,\n\t\t\t\tF45502162DF45E53005582A4 /* VBSettings+CatalogDownload.swift in Sources */,\n\t\t\t\tF44C00FB2889CE1600640BF5 /* VBVirtualMachine+Virtualization.swift in Sources */,\n\t\t\t\tF453C4892DF1CDA0007EAD5F /* DownloadBackend.swift in Sources */,\n\t\t\t\tF4C947DA2E0B1E5D001ACC91 /* SoftwareCatalog+DownloadMatching.swift in Sources */,\n\t\t\t\tF4F9B418284CE12000F21737 /* VBSettingsContainer.swift in Sources */,\n\t\t\t\tF49B832B2E04593A00395F87 /* NSImage+DRMProtected.swift in Sources */,\n\t\t\t\tF4E7DF972BB33E1700C459FC /* VMLibraryController+SavedState.swift in Sources */,\n\t\t\t\tF48E0D0C2888760D0080DDFA /* VBMacDevice+Storage.swift in Sources */,\n\t\t\t\tF465C3AE284F93A5006E9ED4 /* VBAPIClient.swift in Sources */,\n\t\t\t\tF453C4A22DF1D7F6007EAD5F /* SimulatedRestoreBackend.swift in Sources */,\n\t\t\t\tF4DE1C0B2D6F54E700603527 /* VBSavedStateMetadata+Clone.swift in Sources */,\n\t\t\t\tF4D725FE286677B8001818F7 /* VBVirtualMachine+Metadata.swift in Sources */,\n\t\t\t\tF4A21BF428033102001072B8 /* VBError.swift in Sources */,\n\t\t\t\tF4D0F71F2867517A004D5782 /* AppUpdateChannel.swift in Sources */,\n\t\t\t\tF49FD8842DFB727B0019D638 /* VMImporter+Helpers.swift in Sources */,\n\t\t\t\tF453C49B2DF1D768007EAD5F /* SimulatedDownloadBackend.swift in Sources */,\n\t\t\t\t4BA6BE7D293D22E500F396AE /* VirtualMachineConfigurationHelper.swift in Sources */,\n\t\t\t\tF48E0D0728885E140080DDFA /* PreviewSupport.swift in Sources */,\n\t\t\t\tF46FFBAA2804F0A000D61023 /* VZVirtualMachineConfiguration+NVRAM.swift in Sources */,\n\t\t\t\tF4C189F72848F6A600335EC7 /* VirtualCoreConstants.swift in Sources */,\n\t\t\t\tF46FFBA82804F07400D61023 /* VBNVRAMVariable.swift in Sources */,\n\t\t\t\tF453C4A42DF1D861007EAD5F /* VirtualizationRestoreBackend.swift in Sources */,\n\t\t\t\tF4BE9C7A27FF05B900B648F8 /* VMController.swift in Sources */,\n\t\t\t\tF453C4BB2DF231BB007EAD5F /* PreventTerminationAssertion.swift in Sources */,\n\t\t\t\tF417255F28861604004FF8A7 /* DecodableDefault.swift in Sources */,\n\t\t\t\tF485B91B2BB22D2D004B3C2B /* VBSavedStateMetadata.swift in Sources */,\n\t\t\t\tF465C3B8284FA252006E9ED4 /* URLSessionDownloadBackend.swift in Sources */,\n\t\t\t\tF4DE1C0F2D6F603300603527 /* VBSavedStatePackage+VM.swift in Sources */,\n\t\t\t\tF4A7FB3D2BB5E8A200E4C12A /* VMSavedStatesController.swift in Sources */,\n\t\t\t\tF49FD87D2DFB68F50019D638 /* VMImporter.swift in Sources */,\n\t\t\t\tF4A21BF228032FD8001072B8 /* VMLibraryController.swift in Sources */,\n\t\t\t\tF485B91F2BB2F4AC004B3C2B /* Bundle+Version.swift in Sources */,\n\t\t\t\tF49A68E12884917E00A17582 /* ConfigurationModels.swift in Sources */,\n\t\t\t\tF485B91D2BB2F0D9004B3C2B /* ProcessInfo+ECID.swift in Sources */,\n\t\t\t\tF444D1342BB478AD00AB786F /* VBMemoryLeakDebugAssertions.swift in Sources */,\n\t\t\t\tF4C237502888AF67001FF286 /* LogStreamer.swift in Sources */,\n\t\t\t\tF4F9B41A284CE37C00F21737 /* Logging.swift in Sources */,\n\t\t\t\tF4B5C5D728870619005AA632 /* ConfigurationModels+Validation.swift in Sources */,\n\t\t\t\tF4A7FB3B2BB5E79100E4C12A /* DirectoryObserver.swift in Sources */,\n\t\t\t\tF49FD8862DFB728A0019D638 /* UTMAppleConfiguration.swift in Sources */,\n\t\t\t\tF4B5C5D928870BBF005AA632 /* ConfigurationModels+Summary.swift in Sources */,\n\t\t\t\tF4C2374D2888A462001FF286 /* VolumeUtils.swift in Sources */,\n\t\t\t\tF4BE9C7627FF055100B648F8 /* VBVirtualMachine.swift in Sources */,\n\t\t\t\tF4D0F71528667984004D5782 /* VBVirtualMachine+Screenshot.swift in Sources */,\n\t\t\t\tF49FD87F2DFB6B670019D638 /* VMImporterRegistry.swift in Sources */,\n\t\t\t\tF49FD8812DFB6CDD0019D638 /* UTMImporter.swift in Sources */,\n\t\t\t\tF443620A29B7947A00745B43 /* GuestAdditionsDiskImage.swift in Sources */,\n\t\t\t\tF485B9232BB306AF004B3C2B /* VBDebugUtil.m in Sources */,\n\t\t\t\tF4C947BF2E0B0F71001ACC91 /* URL+ExtendedAttributes.swift in Sources */,\n\t\t\t\tF47BCDD72C5D2B4600165191 /* CatalogExtensions.swift in Sources */,\n\t\t\t\tF4BE9C7827FF055100B648F8 /* MacOSVirtualMachineConfigurationHelper.swift in Sources */,\n\t\t\t\tF4510A782AE2A16F00E24DD9 /* WeakReference.swift in Sources */,\n\t\t\t\tF4DE1C112D6F642E00603527 /* VBStorageDeviceContainer.swift in Sources */,\n\t\t\t\tF46FFBAC28059FF600D61023 /* VMInstance.swift in Sources */,\n\t\t\t\tF4E7DF952BB336F600C459FC /* VBSavedStatePackage.swift in Sources */,\n\t\t\t\tF40A1E9D2C1873CA0033E47D /* VBBuildType.swift in Sources */,\n\t\t\t\tF465C3B0284F9660006E9ED4 /* VBRestoreImagesResponse.swift in Sources */,\n\t\t\t\tF453C41D2DF0B43D007EAD5F /* ResolvedCatalog.swift in Sources */,\n\t\t\t\tF453C41E2DF0B43D007EAD5F /* LegacyCatalog.swift in Sources */,\n\t\t\t\tF453C41F2DF0B43D007EAD5F /* BlurHashEncode.swift in Sources */,\n\t\t\t\tF453C4A02DF1D7C0007EAD5F /* RestoreBackend.swift in Sources */,\n\t\t\t\tF4C947D62E0B12D0001ACC91 /* String+AppleOSBuild.swift in Sources */,\n\t\t\t\tF453C4202DF0B43D007EAD5F /* SoftwareCatalog.swift in Sources */,\n\t\t\t\tF453C4212DF0B43D007EAD5F /* MobileDeviceFramework.swift in Sources */,\n\t\t\t\t0196B45329292B2A00614EF1 /* LinuxVirtualMachineConfigurationHelper.swift in Sources */,\n\t\t\t\tF465C3B2284F9666006E9ED4 /* VBRestoreImageInfo.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tF4C189DC2848F59F00335EC7 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tF4D305AC29B8FEE90006E748 /* WHDarwinNotificationsService.swift in Sources */,\n\t\t\t\tF4C189FF2848FB3F00335EC7 /* WormholeServiceProtocol.swift in Sources */,\n\t\t\t\tF4C189FA2848F6F700335EC7 /* VirtualWormholeConstants.swift in Sources */,\n\t\t\t\tF4BE584229BA7BDB00C5525C /* WHDefaultsImportService.swift in Sources */,\n\t\t\t\tF4D305B029B900860006E748 /* SystemNotification.swift in Sources */,\n\t\t\t\tF42862372AE947C90052F029 /* WHPayload.swift in Sources */,\n\t\t\t\tF4BE581329BA6E0D00C5525C /* DefaultsDomainDescriptor.swift in Sources */,\n\t\t\t\tF49B83712E04837400395F87 /* CGImage+FullyTransparent.swift in Sources */,\n\t\t\t\tF4BE581129BA6DFC00C5525C /* DefaultsImportController.swift in Sources */,\n\t\t\t\tF49B82FC2E034EAD00395F87 /* WHDesktopPictureService.swift in Sources */,\n\t\t\t\tF4D305B229B907A10006E748 /* WHPing.swift in Sources */,\n\t\t\t\tF4D3059729B8D9D30006E748 /* WormholePacket.swift in Sources */,\n\t\t\t\tF4BE584029BA7B7A00C5525C /* DefaultsDomain+ExportImport.swift in Sources */,\n\t\t\t\tF4FC276A29BBAE590012CB65 /* WHDefaultsImportClient.swift in Sources */,\n\t\t\t\tF4C189FD2848F8F600335EC7 /* WHSharedClipboardService.swift in Sources */,\n\t\t\t\tF4FC276729BBAE350012CB65 /* WormholeServiceClient.swift in Sources */,\n\t\t\t\tF4C189EE2848F5B500335EC7 /* WormholeManager.swift in Sources */,\n\t\t\t\tF49B83002E034F6800395F87 /* NSImage+DesktopPicture.m in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tF4C18A3E28491B8500335EC7 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tF4959F3C2992A284001DF4CB /* GuestAppInstaller.swift in Sources */,\n\t\t\t\tF41369CA2991A492002CE8D3 /* HostConnectionStateProvider.swift in Sources */,\n\t\t\t\tF4C18A4728491B8500335EC7 /* GuestDashboard.swift in Sources */,\n\t\t\t\tF41369CC2991A68F002CE8D3 /* GuestSharedFoldersManager.swift in Sources */,\n\t\t\t\tF4C18A4528491B8500335EC7 /* GuestAppDelegate.swift in Sources */,\n\t\t\t\tF4FC276C29BBB3030012CB65 /* GuestDefaultsImportView.swift in Sources */,\n\t\t\t\tF41369A6299183C8002CE8D3 /* GuestLaunchAtLoginManager.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tF4D3059829B8DB700006E748 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tF4D3059F29B8DB700006E748 /* WormholePacketTests.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXTargetDependency section */\n\t\tF413699D299179F8002CE8D3 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = F4BE9C6427FF053A00B648F8 /* VirtualCore */;\n\t\t\ttargetProxy = F413699C299179F8002CE8D3 /* PBXContainerItemProxy */;\n\t\t};\n\t\tF41369BD29918617002CE8D3 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = F41369AA29918576002CE8D3 /* VirtualBuddyGuestHelper */;\n\t\t\ttargetProxy = F41369BC29918617002CE8D3 /* PBXContainerItemProxy */;\n\t\t};\n\t\tF42862312AE87D7E0052F029 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = F43B01142AD858FE00164CD1 /* DeepLinkSecurity */;\n\t\t\ttargetProxy = F42862302AE87D7E0052F029 /* PBXContainerItemProxy */;\n\t\t};\n\t\tF4510A7A2AE2B3AB00E24DD9 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = F43B01142AD858FE00164CD1 /* DeepLinkSecurity */;\n\t\t\ttargetProxy = F4510A792AE2B3AB00E24DD9 /* PBXContainerItemProxy */;\n\t\t};\n\t\tF453C3ED2DF0A4D1007EAD5F /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\tproductRef = F453C3EC2DF0A4D1007EAD5F /* BuddyKit */;\n\t\t};\n\t\tF453C4272DF0B602007EAD5F /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = F498ACFD2884BF13006F1C00 /* VirtualUI */;\n\t\t\ttargetProxy = F453C4262DF0B602007EAD5F /* PBXContainerItemProxy */;\n\t\t};\n\t\tF453C4592DF0BCEB007EAD5F /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = F4BE9C4D27FF052100B648F8 /* VirtualBuddy */;\n\t\t\ttargetProxy = F453C4582DF0BCEB007EAD5F /* PBXContainerItemProxy */;\n\t\t};\n\t\tF498AD032884BF13006F1C00 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = F498ACFD2884BF13006F1C00 /* VirtualUI */;\n\t\t\ttargetProxy = F498AD022884BF13006F1C00 /* PBXContainerItemProxy */;\n\t\t};\n\t\tF498AD0B2884BF60006F1C00 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = F4BE9C6427FF053A00B648F8 /* VirtualCore */;\n\t\t\ttargetProxy = F498AD0A2884BF60006F1C00 /* PBXContainerItemProxy */;\n\t\t};\n\t\tF4C189F02848F5F100335EC7 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = F4C189DF2848F59F00335EC7 /* VirtualWormhole */;\n\t\t\ttargetProxy = F4C189EF2848F5F100335EC7 /* PBXContainerItemProxy */;\n\t\t};\n\t\tF4C18A5528491B9D00335EC7 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = F4C189DF2848F59F00335EC7 /* VirtualWormhole */;\n\t\t\ttargetProxy = F4C18A5428491B9D00335EC7 /* PBXContainerItemProxy */;\n\t\t};\n\t\tF4C18A5828491C0D00335EC7 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = F4C18A4128491B8500335EC7 /* VirtualBuddyGuest */;\n\t\t\ttargetProxy = F4C18A5728491C0D00335EC7 /* PBXContainerItemProxy */;\n\t\t};\n\t\tF4D305A229B8DB700006E748 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = F4C189DF2848F59F00335EC7 /* VirtualWormhole */;\n\t\t\ttargetProxy = F4D305A129B8DB700006E748 /* PBXContainerItemProxy */;\n\t\t};\n/* End PBXTargetDependency section */\n\n/* Begin PBXVariantGroup section */\n\t\tF41369B329918576002CE8D3 /* Main.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\tF41369B429918576002CE8D3 /* Base */,\n\t\t\t);\n\t\t\tname = Main.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\tF40A1E872C1869680033E47D /* Beta_Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = F4C1224E280715F200D359E2 /* Main.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tARCHS = arm64;\n\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++17\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t};\n\t\t\tname = Beta_Debug;\n\t\t};\n\t\tF40A1E882C1869680033E47D /* Beta_Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = F4B068B528882F5D003743BF /* AppTarget.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=macosx*]\" = \"Mac Developer\";\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEVELOPMENT_ASSET_PATHS = \"\\\"VirtualBuddy/Preview Content\\\"\";\n\t\t\t\tDEVELOPMENT_TEAM = 8C7439RJLG;\n\t\t\t\t\"DEVELOPMENT_TEAM[sdk=macosx*]\" = 8C7439RJLG;\n\t\t\t\tENABLE_HARDENED_RUNTIME = YES;\n\t\t\t\tENABLE_PREVIEWS = YES;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_FILE = VirtualBuddy/Info.plist;\n\t\t\t\tINFOPLIST_KEY_LSApplicationCategoryType = \"public.app-category.developer-tools\";\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"© 2023 Buddy Software LTD\";\n\t\t\t\tINFOPLIST_KEY_NSMicrophoneUsageDescription = \"Enable audio input for your virtual machines. Without this permission, virtual machines won't be able to record any audio.\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_APP_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tOTHER_LDFLAGS = \"\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(VB_BUNDLE_ID_PREFIX)codes.rambo.VirtualBuddy\";\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Beta_Debug;\n\t\t};\n\t\tF40A1E892C1869680033E47D /* Beta_Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = VirtualBuddyGuest/VirtualBuddyGuest.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEVELOPMENT_ASSET_PATHS = \"\\\"VirtualBuddyGuest/Preview Content\\\"\";\n\t\t\t\tDEVELOPMENT_TEAM = 8C7439RJLG;\n\t\t\t\tENABLE_HARDENED_RUNTIME = YES;\n\t\t\t\tENABLE_PREVIEWS = YES;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_FILE = \"$(DERIVED_FILE_DIR)/VBGenerated-Info.plist\";\n\t\t\t\tINFOPLIST_KEY_LSApplicationCategoryType = \"public.app-category.utilities\";\n\t\t\t\tINFOPLIST_KEY_LSUIElement = YES;\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"\";\n\t\t\t\tINFOPLIST_KEY_NSMainStoryboardFile = Main;\n\t\t\t\tINFOPLIST_KEY_NSMicrophoneUsageDescription = \"Enable audio input for your virtual machines. Without this permission, virtual machines won't be able to record any audio.\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_APP_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(VB_BUNDLE_ID_PREFIX)codes.rambo.VirtualBuddyGuest\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"VirtualBuddyGuest/Dashboard/Support/VirtualBuddyGuest-Bridging-Header.h\";\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Beta_Debug;\n\t\t};\n\t\tF40A1E8A2C1869680033E47D /* Beta_Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++20\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = VirtualBuddyGuestHelper/VirtualBuddyGuestHelper.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEVELOPMENT_TEAM = 8C7439RJLG;\n\t\t\t\tENABLE_HARDENED_RUNTIME = YES;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_KEY_LSApplicationCategoryType = \"public.app-category.utilities\";\n\t\t\t\tINFOPLIST_KEY_LSBackgroundOnly = YES;\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"\";\n\t\t\t\tINFOPLIST_KEY_NSMainStoryboardFile = Main;\n\t\t\t\tINFOPLIST_KEY_NSMicrophoneUsageDescription = \"Enable audio input for your virtual machines. Without this permission, virtual machines won't be able to record any audio.\";\n\t\t\t\tINFOPLIST_KEY_NSPrincipalClass = NSApplication;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_APP_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(GUEST_LAUNCH_AT_LOGIN_HELPER_BUNDLE_ID)\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Beta_Debug;\n\t\t};\n\t\tF40A1E8B2C1869680033E47D /* Beta_Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++20\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDEVELOPMENT_TEAM = 8C7439RJLG;\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_CURRENT_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tENABLE_MODULE_VERIFIER = NO;\n\t\t\t\tENABLE_USER_SCRIPT_SANDBOXING = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu17;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"\";\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_FRAMEWORK_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tLOCALIZATION_PREFERS_STRING_CATALOGS = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGES = \"objective-c objective-c++\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = \"gnu17 gnu++20\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = codes.rambo.DeepLinkSecurity;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME:c99extidentifier)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = \"DEBUG $(inherited)\";\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t\tVERSION_INFO_PREFIX = \"\";\n\t\t\t};\n\t\t\tname = Beta_Debug;\n\t\t};\n\t\tF40A1E8C2C1869680033E47D /* Beta_Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDEVELOPMENT_ASSET_PATHS = VirtualCore/Source/Resources/Preview;\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tENABLE_MODULE_VERIFIER = NO;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_FRAMEWORK_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGES = \"objective-c objective-c++\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = \"gnu11 gnu++17\";\n\t\t\t\tOTHER_LDFLAGS = \"-Wl,-U,_OBJC_CLASS_$__VZVirtualMachineStartOptions\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(VB_BUNDLE_ID_PREFIX)codes.rambo.VirtualCore\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME:c99extidentifier)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Beta_Debug;\n\t\t};\n\t\tF40A1E8D2C1869680033E47D /* Beta_Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++20\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDEVELOPMENT_ASSET_PATHS = \"\";\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tENABLE_MODULE_VERIFIER = NO;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"\";\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_FRAMEWORK_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGES = \"objective-c objective-c++\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = \"gnu11 gnu++20\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(VB_BUNDLE_ID_PREFIX)codes.rambo.VirtualUI\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME:c99extidentifier)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Beta_Debug;\n\t\t};\n\t\tF40A1E8E2C1869680033E47D /* Beta_Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++20\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEVELOPMENT_TEAM = 8C7439RJLG;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = codes.rambo.VirtualWormholeTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = NO;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Beta_Debug;\n\t\t};\n\t\tF40A1E8F2C1869680033E47D /* Beta_Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tENABLE_MODULE_VERIFIER = NO;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"\";\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_FRAMEWORK_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGES = \"objective-c objective-c++\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = \"gnu11 gnu++17\";\n\t\t\t\tOTHER_LDFLAGS = \"\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(VB_BUNDLE_ID_PREFIX)codes.rambo.VirtualWormhole\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME:c99extidentifier)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Beta_Debug;\n\t\t};\n\t\tF40A1E902C18697B0033E47D /* Beta_Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = F4C1224E280715F200D359E2 /* Main.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tARCHS = arm64;\n\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++17\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t};\n\t\t\tname = Beta_Release;\n\t\t};\n\t\tF40A1E912C18697B0033E47D /* Beta_Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = F4B068B528882F5D003743BF /* AppTarget.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=macosx*]\" = \"Mac Developer\";\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEVELOPMENT_ASSET_PATHS = \"\\\"VirtualBuddy/Preview Content\\\"\";\n\t\t\t\tDEVELOPMENT_TEAM = 8C7439RJLG;\n\t\t\t\t\"DEVELOPMENT_TEAM[sdk=macosx*]\" = 8C7439RJLG;\n\t\t\t\tENABLE_HARDENED_RUNTIME = YES;\n\t\t\t\tENABLE_PREVIEWS = YES;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_FILE = VirtualBuddy/Info.plist;\n\t\t\t\tINFOPLIST_KEY_LSApplicationCategoryType = \"public.app-category.developer-tools\";\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"© 2023 Buddy Software LTD\";\n\t\t\t\tINFOPLIST_KEY_NSMicrophoneUsageDescription = \"Enable audio input for your virtual machines. Without this permission, virtual machines won't be able to record any audio.\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_APP_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tOTHER_LDFLAGS = \"\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(VB_BUNDLE_ID_PREFIX)codes.rambo.VirtualBuddy\";\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Beta_Release;\n\t\t};\n\t\tF40A1E922C18697B0033E47D /* Beta_Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = VirtualBuddyGuest/VirtualBuddyGuest.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEVELOPMENT_ASSET_PATHS = \"\\\"VirtualBuddyGuest/Preview Content\\\"\";\n\t\t\t\tDEVELOPMENT_TEAM = 8C7439RJLG;\n\t\t\t\tENABLE_HARDENED_RUNTIME = YES;\n\t\t\t\tENABLE_PREVIEWS = YES;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_FILE = \"$(DERIVED_FILE_DIR)/VBGenerated-Info.plist\";\n\t\t\t\tINFOPLIST_KEY_LSApplicationCategoryType = \"public.app-category.utilities\";\n\t\t\t\tINFOPLIST_KEY_LSUIElement = YES;\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"\";\n\t\t\t\tINFOPLIST_KEY_NSMainStoryboardFile = Main;\n\t\t\t\tINFOPLIST_KEY_NSMicrophoneUsageDescription = \"Enable audio input for your virtual machines. Without this permission, virtual machines won't be able to record any audio.\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_APP_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(VB_BUNDLE_ID_PREFIX)codes.rambo.VirtualBuddyGuest\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"VirtualBuddyGuest/Dashboard/Support/VirtualBuddyGuest-Bridging-Header.h\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Beta_Release;\n\t\t};\n\t\tF40A1E932C18697B0033E47D /* Beta_Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++20\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = VirtualBuddyGuestHelper/VirtualBuddyGuestHelper.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEVELOPMENT_TEAM = 8C7439RJLG;\n\t\t\t\tENABLE_HARDENED_RUNTIME = YES;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_KEY_LSApplicationCategoryType = \"public.app-category.utilities\";\n\t\t\t\tINFOPLIST_KEY_LSBackgroundOnly = YES;\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"\";\n\t\t\t\tINFOPLIST_KEY_NSMainStoryboardFile = Main;\n\t\t\t\tINFOPLIST_KEY_NSMicrophoneUsageDescription = \"Enable audio input for your virtual machines. Without this permission, virtual machines won't be able to record any audio.\";\n\t\t\t\tINFOPLIST_KEY_NSPrincipalClass = NSApplication;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_APP_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(GUEST_LAUNCH_AT_LOGIN_HELPER_BUNDLE_ID)\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Beta_Release;\n\t\t};\n\t\tF40A1E942C18697B0033E47D /* Beta_Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++20\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDEVELOPMENT_TEAM = 8C7439RJLG;\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_CURRENT_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tENABLE_MODULE_VERIFIER = NO;\n\t\t\t\tENABLE_USER_SCRIPT_SANDBOXING = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu17;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"\";\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_FRAMEWORK_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tLOCALIZATION_PREFERS_STRING_CATALOGS = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGES = \"objective-c objective-c++\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = \"gnu17 gnu++20\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = codes.rambo.DeepLinkSecurity;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME:c99extidentifier)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t\tVERSION_INFO_PREFIX = \"\";\n\t\t\t};\n\t\t\tname = Beta_Release;\n\t\t};\n\t\tF40A1E952C18697B0033E47D /* Beta_Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDEVELOPMENT_ASSET_PATHS = VirtualCore/Source/Resources/Preview;\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tENABLE_MODULE_VERIFIER = NO;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_FRAMEWORK_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGES = \"objective-c objective-c++\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = \"gnu11 gnu++17\";\n\t\t\t\tOTHER_LDFLAGS = \"-Wl,-U,_OBJC_CLASS_$__VZVirtualMachineStartOptions\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(VB_BUNDLE_ID_PREFIX)codes.rambo.VirtualCore\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME:c99extidentifier)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Beta_Release;\n\t\t};\n\t\tF40A1E962C18697B0033E47D /* Beta_Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++20\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDEVELOPMENT_ASSET_PATHS = \"\";\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tENABLE_MODULE_VERIFIER = NO;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"\";\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_FRAMEWORK_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGES = \"objective-c objective-c++\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = \"gnu11 gnu++20\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(VB_BUNDLE_ID_PREFIX)codes.rambo.VirtualUI\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME:c99extidentifier)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Beta_Release;\n\t\t};\n\t\tF40A1E972C18697B0033E47D /* Beta_Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++20\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEVELOPMENT_TEAM = 8C7439RJLG;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = codes.rambo.VirtualWormholeTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = NO;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Beta_Release;\n\t\t};\n\t\tF40A1E982C18697B0033E47D /* Beta_Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tENABLE_MODULE_VERIFIER = NO;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"\";\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_FRAMEWORK_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGES = \"objective-c objective-c++\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = \"gnu11 gnu++17\";\n\t\t\t\tOTHER_LDFLAGS = \"\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(VB_BUNDLE_ID_PREFIX)codes.rambo.VirtualWormhole\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME:c99extidentifier)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Beta_Release;\n\t\t};\n\t\tF41369B829918576002CE8D3 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++20\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = VirtualBuddyGuestHelper/VirtualBuddyGuestHelper.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEVELOPMENT_TEAM = 8C7439RJLG;\n\t\t\t\tENABLE_HARDENED_RUNTIME = YES;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_KEY_LSApplicationCategoryType = \"public.app-category.utilities\";\n\t\t\t\tINFOPLIST_KEY_LSBackgroundOnly = YES;\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"\";\n\t\t\t\tINFOPLIST_KEY_NSMainStoryboardFile = Main;\n\t\t\t\tINFOPLIST_KEY_NSMicrophoneUsageDescription = \"Enable audio input for your virtual machines. Without this permission, virtual machines won't be able to record any audio.\";\n\t\t\t\tINFOPLIST_KEY_NSPrincipalClass = NSApplication;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_APP_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(GUEST_LAUNCH_AT_LOGIN_HELPER_BUNDLE_ID)\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tF41369B929918576002CE8D3 /* Debug_Managed */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++20\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = VirtualBuddyGuestHelper/VirtualBuddyGuestHelper.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEVELOPMENT_TEAM = 8C7439RJLG;\n\t\t\t\tENABLE_HARDENED_RUNTIME = YES;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_KEY_LSApplicationCategoryType = \"public.app-category.utilities\";\n\t\t\t\tINFOPLIST_KEY_LSBackgroundOnly = YES;\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"\";\n\t\t\t\tINFOPLIST_KEY_NSMainStoryboardFile = Main;\n\t\t\t\tINFOPLIST_KEY_NSMicrophoneUsageDescription = \"Enable audio input for your virtual machines. Without this permission, virtual machines won't be able to record any audio.\";\n\t\t\t\tINFOPLIST_KEY_NSPrincipalClass = NSApplication;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_APP_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(GUEST_LAUNCH_AT_LOGIN_HELPER_BUNDLE_ID)\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Debug_Managed;\n\t\t};\n\t\tF41369BA29918576002CE8D3 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++20\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = VirtualBuddyGuestHelper/VirtualBuddyGuestHelper.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEVELOPMENT_TEAM = 8C7439RJLG;\n\t\t\t\tENABLE_HARDENED_RUNTIME = YES;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_KEY_LSApplicationCategoryType = \"public.app-category.utilities\";\n\t\t\t\tINFOPLIST_KEY_LSBackgroundOnly = YES;\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"\";\n\t\t\t\tINFOPLIST_KEY_NSMainStoryboardFile = Main;\n\t\t\t\tINFOPLIST_KEY_NSMicrophoneUsageDescription = \"Enable audio input for your virtual machines. Without this permission, virtual machines won't be able to record any audio.\";\n\t\t\t\tINFOPLIST_KEY_NSPrincipalClass = NSApplication;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_APP_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(GUEST_LAUNCH_AT_LOGIN_HELPER_BUNDLE_ID)\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tF41369BB29918576002CE8D3 /* Release_Managed */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++20\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = VirtualBuddyGuestHelper/VirtualBuddyGuestHelper.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEVELOPMENT_TEAM = 8C7439RJLG;\n\t\t\t\tENABLE_HARDENED_RUNTIME = YES;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_KEY_LSApplicationCategoryType = \"public.app-category.utilities\";\n\t\t\t\tINFOPLIST_KEY_LSBackgroundOnly = YES;\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"\";\n\t\t\t\tINFOPLIST_KEY_NSMainStoryboardFile = Main;\n\t\t\t\tINFOPLIST_KEY_NSMicrophoneUsageDescription = \"Enable audio input for your virtual machines. Without this permission, virtual machines won't be able to record any audio.\";\n\t\t\t\tINFOPLIST_KEY_NSPrincipalClass = NSApplication;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_APP_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(GUEST_LAUNCH_AT_LOGIN_HELPER_BUNDLE_ID)\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Release_Managed;\n\t\t};\n\t\tF43B011D2AD858FE00164CD1 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++20\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDEVELOPMENT_TEAM = 8C7439RJLG;\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_CURRENT_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tENABLE_MODULE_VERIFIER = NO;\n\t\t\t\tENABLE_USER_SCRIPT_SANDBOXING = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu17;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"\";\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_FRAMEWORK_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tLOCALIZATION_PREFERS_STRING_CATALOGS = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGES = \"objective-c objective-c++\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = \"gnu17 gnu++20\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = codes.rambo.DeepLinkSecurity;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME:c99extidentifier)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = \"DEBUG $(inherited)\";\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t\tVERSION_INFO_PREFIX = \"\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tF43B011E2AD858FE00164CD1 /* Debug_Managed */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++20\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDEVELOPMENT_TEAM = 8C7439RJLG;\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_CURRENT_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tENABLE_MODULE_VERIFIER = NO;\n\t\t\t\tENABLE_USER_SCRIPT_SANDBOXING = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu17;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"\";\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_FRAMEWORK_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tLOCALIZATION_PREFERS_STRING_CATALOGS = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGES = \"objective-c objective-c++\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = \"gnu17 gnu++20\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = codes.rambo.DeepLinkSecurity;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME:c99extidentifier)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = \"DEBUG $(inherited)\";\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t\tVERSION_INFO_PREFIX = \"\";\n\t\t\t};\n\t\t\tname = Debug_Managed;\n\t\t};\n\t\tF43B011F2AD858FE00164CD1 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++20\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDEVELOPMENT_TEAM = 8C7439RJLG;\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_CURRENT_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tENABLE_MODULE_VERIFIER = NO;\n\t\t\t\tENABLE_USER_SCRIPT_SANDBOXING = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu17;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"\";\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_FRAMEWORK_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tLOCALIZATION_PREFERS_STRING_CATALOGS = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGES = \"objective-c objective-c++\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = \"gnu17 gnu++20\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = codes.rambo.DeepLinkSecurity;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME:c99extidentifier)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t\tVERSION_INFO_PREFIX = \"\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tF43B01202AD858FE00164CD1 /* Release_Managed */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++20\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDEVELOPMENT_TEAM = 8C7439RJLG;\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_CURRENT_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tENABLE_MODULE_VERIFIER = NO;\n\t\t\t\tENABLE_USER_SCRIPT_SANDBOXING = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu17;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"\";\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_FRAMEWORK_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tLOCALIZATION_PREFERS_STRING_CATALOGS = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGES = \"objective-c objective-c++\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = \"gnu17 gnu++20\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = codes.rambo.DeepLinkSecurity;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME:c99extidentifier)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t\tVERSION_INFO_PREFIX = \"\";\n\t\t\t};\n\t\t\tname = Release_Managed;\n\t\t};\n\t\tF453C4512DF0BCE3007EAD5F /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tDEVELOPMENT_TEAM = 8C7439RJLG;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tF453C4522DF0BCE3007EAD5F /* Debug_Managed */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tDEVELOPMENT_TEAM = 8C7439RJLG;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Debug_Managed;\n\t\t};\n\t\tF453C4532DF0BCE3007EAD5F /* Beta_Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tDEVELOPMENT_TEAM = 8C7439RJLG;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Beta_Debug;\n\t\t};\n\t\tF453C4542DF0BCE3007EAD5F /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tDEVELOPMENT_TEAM = 8C7439RJLG;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tF453C4552DF0BCE3007EAD5F /* Release_Managed */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tDEVELOPMENT_TEAM = 8C7439RJLG;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Release_Managed;\n\t\t};\n\t\tF453C4562DF0BCE3007EAD5F /* Beta_Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tDEVELOPMENT_TEAM = 8C7439RJLG;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Beta_Release;\n\t\t};\n\t\tF453C4572DF0BCE3007EAD5F /* Dev_Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tDEVELOPMENT_TEAM = 8C7439RJLG;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Dev_Release;\n\t\t};\n\t\tF498AD062884BF13006F1C00 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++20\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDEVELOPMENT_ASSET_PATHS = \"\";\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tENABLE_MODULE_VERIFIER = NO;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"\";\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_FRAMEWORK_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGES = \"objective-c objective-c++\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = \"gnu11 gnu++20\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(VB_BUNDLE_ID_PREFIX)codes.rambo.VirtualUI\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME:c99extidentifier)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tF498AD072884BF13006F1C00 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++20\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDEVELOPMENT_ASSET_PATHS = \"\";\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tENABLE_MODULE_VERIFIER = NO;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"\";\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_FRAMEWORK_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGES = \"objective-c objective-c++\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = \"gnu11 gnu++20\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(VB_BUNDLE_ID_PREFIX)codes.rambo.VirtualUI\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME:c99extidentifier)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tF4A2770B2BF50EA00011B626 /* Dev_Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = F4C1224E280715F200D359E2 /* Main.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tARCHS = arm64;\n\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++17\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t};\n\t\t\tname = Dev_Release;\n\t\t};\n\t\tF4A2770C2BF50EA00011B626 /* Dev_Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = F4B068B528882F5D003743BF /* AppTarget.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=macosx*]\" = \"Mac Developer\";\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEVELOPMENT_ASSET_PATHS = \"\\\"VirtualBuddy/Preview Content\\\"\";\n\t\t\t\tDEVELOPMENT_TEAM = 8C7439RJLG;\n\t\t\t\t\"DEVELOPMENT_TEAM[sdk=macosx*]\" = 8C7439RJLG;\n\t\t\t\tENABLE_HARDENED_RUNTIME = YES;\n\t\t\t\tENABLE_PREVIEWS = YES;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_FILE = VirtualBuddy/Info.plist;\n\t\t\t\tINFOPLIST_KEY_LSApplicationCategoryType = \"public.app-category.developer-tools\";\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"© 2023 Buddy Software LTD\";\n\t\t\t\tINFOPLIST_KEY_NSMicrophoneUsageDescription = \"Enable audio input for your virtual machines. Without this permission, virtual machines won't be able to record any audio.\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_APP_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tOTHER_LDFLAGS = \"\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(VB_BUNDLE_ID_PREFIX)codes.rambo.VirtualBuddy\";\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Dev_Release;\n\t\t};\n\t\tF4A2770D2BF50EA00011B626 /* Dev_Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = VirtualBuddyGuest/VirtualBuddyGuest.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEVELOPMENT_ASSET_PATHS = \"\\\"VirtualBuddyGuest/Preview Content\\\"\";\n\t\t\t\tDEVELOPMENT_TEAM = 8C7439RJLG;\n\t\t\t\tENABLE_HARDENED_RUNTIME = YES;\n\t\t\t\tENABLE_PREVIEWS = YES;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_FILE = \"$(DERIVED_FILE_DIR)/VBGenerated-Info.plist\";\n\t\t\t\tINFOPLIST_KEY_LSApplicationCategoryType = \"public.app-category.utilities\";\n\t\t\t\tINFOPLIST_KEY_LSUIElement = YES;\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"\";\n\t\t\t\tINFOPLIST_KEY_NSMainStoryboardFile = Main;\n\t\t\t\tINFOPLIST_KEY_NSMicrophoneUsageDescription = \"Enable audio input for your virtual machines. Without this permission, virtual machines won't be able to record any audio.\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_APP_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(VB_BUNDLE_ID_PREFIX)codes.rambo.VirtualBuddyGuest\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"VirtualBuddyGuest/Dashboard/Support/VirtualBuddyGuest-Bridging-Header.h\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Dev_Release;\n\t\t};\n\t\tF4A2770E2BF50EA00011B626 /* Dev_Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++20\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = VirtualBuddyGuestHelper/VirtualBuddyGuestHelper.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEVELOPMENT_TEAM = 8C7439RJLG;\n\t\t\t\tENABLE_HARDENED_RUNTIME = YES;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_KEY_LSApplicationCategoryType = \"public.app-category.utilities\";\n\t\t\t\tINFOPLIST_KEY_LSBackgroundOnly = YES;\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"\";\n\t\t\t\tINFOPLIST_KEY_NSMainStoryboardFile = Main;\n\t\t\t\tINFOPLIST_KEY_NSMicrophoneUsageDescription = \"Enable audio input for your virtual machines. Without this permission, virtual machines won't be able to record any audio.\";\n\t\t\t\tINFOPLIST_KEY_NSPrincipalClass = NSApplication;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_APP_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(GUEST_LAUNCH_AT_LOGIN_HELPER_BUNDLE_ID)\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Dev_Release;\n\t\t};\n\t\tF4A2770F2BF50EA00011B626 /* Dev_Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++20\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDEVELOPMENT_TEAM = 8C7439RJLG;\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_CURRENT_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tENABLE_MODULE_VERIFIER = NO;\n\t\t\t\tENABLE_USER_SCRIPT_SANDBOXING = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu17;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"\";\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_FRAMEWORK_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tLOCALIZATION_PREFERS_STRING_CATALOGS = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGES = \"objective-c objective-c++\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = \"gnu17 gnu++20\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = codes.rambo.DeepLinkSecurity;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME:c99extidentifier)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t\tVERSION_INFO_PREFIX = \"\";\n\t\t\t};\n\t\t\tname = Dev_Release;\n\t\t};\n\t\tF4A277102BF50EA00011B626 /* Dev_Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDEVELOPMENT_ASSET_PATHS = VirtualCore/Source/Resources/Preview;\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tENABLE_MODULE_VERIFIER = NO;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_FRAMEWORK_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGES = \"objective-c objective-c++\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = \"gnu11 gnu++17\";\n\t\t\t\tOTHER_LDFLAGS = \"-Wl,-U,_OBJC_CLASS_$__VZVirtualMachineStartOptions\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(VB_BUNDLE_ID_PREFIX)codes.rambo.VirtualCore\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME:c99extidentifier)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Dev_Release;\n\t\t};\n\t\tF4A277112BF50EA00011B626 /* Dev_Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++20\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDEVELOPMENT_ASSET_PATHS = \"\";\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tENABLE_MODULE_VERIFIER = NO;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"\";\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_FRAMEWORK_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGES = \"objective-c objective-c++\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = \"gnu11 gnu++20\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(VB_BUNDLE_ID_PREFIX)codes.rambo.VirtualUI\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME:c99extidentifier)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Dev_Release;\n\t\t};\n\t\tF4A277122BF50EA00011B626 /* Dev_Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++20\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEVELOPMENT_TEAM = 8C7439RJLG;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = codes.rambo.VirtualWormholeTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = NO;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Dev_Release;\n\t\t};\n\t\tF4A277132BF50EA00011B626 /* Dev_Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tENABLE_MODULE_VERIFIER = NO;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"\";\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_FRAMEWORK_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGES = \"objective-c objective-c++\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = \"gnu11 gnu++17\";\n\t\t\t\tOTHER_LDFLAGS = \"\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(VB_BUNDLE_ID_PREFIX)codes.rambo.VirtualWormhole\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME:c99extidentifier)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Dev_Release;\n\t\t};\n\t\tF4B068A828882E9A003743BF /* Debug_Managed */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = F4C1224E280715F200D359E2 /* Main.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tARCHS = arm64;\n\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++17\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t};\n\t\t\tname = Debug_Managed;\n\t\t};\n\t\tF4B068A928882E9A003743BF /* Debug_Managed */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = F4B068B528882F5D003743BF /* AppTarget.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=macosx*]\" = \"Mac Developer\";\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEVELOPMENT_ASSET_PATHS = \"\\\"VirtualBuddy/Preview Content\\\"\";\n\t\t\t\tDEVELOPMENT_TEAM = 8C7439RJLG;\n\t\t\t\t\"DEVELOPMENT_TEAM[sdk=macosx*]\" = 8C7439RJLG;\n\t\t\t\tENABLE_HARDENED_RUNTIME = YES;\n\t\t\t\tENABLE_PREVIEWS = YES;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_FILE = VirtualBuddy/Info.plist;\n\t\t\t\tINFOPLIST_KEY_LSApplicationCategoryType = \"public.app-category.developer-tools\";\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"© 2023 Buddy Software LTD\";\n\t\t\t\tINFOPLIST_KEY_NSMicrophoneUsageDescription = \"Enable audio input for your virtual machines. Without this permission, virtual machines won't be able to record any audio.\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_APP_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tOTHER_LDFLAGS = \"\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(VB_BUNDLE_ID_PREFIX)codes.rambo.VirtualBuddy\";\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Debug_Managed;\n\t\t};\n\t\tF4B068AA28882E9A003743BF /* Debug_Managed */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = VirtualBuddyGuest/VirtualBuddyGuest.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEVELOPMENT_ASSET_PATHS = \"\\\"VirtualBuddyGuest/Preview Content\\\"\";\n\t\t\t\tDEVELOPMENT_TEAM = 8C7439RJLG;\n\t\t\t\tENABLE_HARDENED_RUNTIME = YES;\n\t\t\t\tENABLE_PREVIEWS = YES;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_FILE = \"$(DERIVED_FILE_DIR)/VBGenerated-Info.plist\";\n\t\t\t\tINFOPLIST_KEY_LSApplicationCategoryType = \"public.app-category.utilities\";\n\t\t\t\tINFOPLIST_KEY_LSUIElement = YES;\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"\";\n\t\t\t\tINFOPLIST_KEY_NSMainStoryboardFile = Main;\n\t\t\t\tINFOPLIST_KEY_NSMicrophoneUsageDescription = \"Enable audio input for your virtual machines. Without this permission, virtual machines won't be able to record any audio.\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_APP_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(VB_BUNDLE_ID_PREFIX)codes.rambo.VirtualBuddyGuest\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"VirtualBuddyGuest/Dashboard/Support/VirtualBuddyGuest-Bridging-Header.h\";\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Debug_Managed;\n\t\t};\n\t\tF4B068AB28882E9A003743BF /* Debug_Managed */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDEVELOPMENT_ASSET_PATHS = VirtualCore/Source/Resources/Preview;\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tENABLE_MODULE_VERIFIER = NO;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_FRAMEWORK_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGES = \"objective-c objective-c++\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = \"gnu11 gnu++17\";\n\t\t\t\tOTHER_LDFLAGS = \"-Wl,-U,_OBJC_CLASS_$__VZVirtualMachineStartOptions\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(VB_BUNDLE_ID_PREFIX)codes.rambo.VirtualCore\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME:c99extidentifier)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Debug_Managed;\n\t\t};\n\t\tF4B068AC28882E9A003743BF /* Debug_Managed */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++20\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDEVELOPMENT_ASSET_PATHS = \"\";\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tENABLE_MODULE_VERIFIER = NO;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"\";\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_FRAMEWORK_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGES = \"objective-c objective-c++\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = \"gnu11 gnu++20\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(VB_BUNDLE_ID_PREFIX)codes.rambo.VirtualUI\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME:c99extidentifier)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Debug_Managed;\n\t\t};\n\t\tF4B068AD28882E9A003743BF /* Debug_Managed */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tENABLE_MODULE_VERIFIER = NO;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"\";\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_FRAMEWORK_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGES = \"objective-c objective-c++\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = \"gnu11 gnu++17\";\n\t\t\t\tOTHER_LDFLAGS = \"\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(VB_BUNDLE_ID_PREFIX)codes.rambo.VirtualWormhole\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME:c99extidentifier)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Debug_Managed;\n\t\t};\n\t\tF4B068AE28882EA3003743BF /* Release_Managed */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = F4C1224E280715F200D359E2 /* Main.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tARCHS = arm64;\n\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++17\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t};\n\t\t\tname = Release_Managed;\n\t\t};\n\t\tF4B068AF28882EA3003743BF /* Release_Managed */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = F4B068B528882F5D003743BF /* AppTarget.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=macosx*]\" = \"Mac Developer\";\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEVELOPMENT_ASSET_PATHS = \"\\\"VirtualBuddy/Preview Content\\\"\";\n\t\t\t\tDEVELOPMENT_TEAM = 8C7439RJLG;\n\t\t\t\t\"DEVELOPMENT_TEAM[sdk=macosx*]\" = 8C7439RJLG;\n\t\t\t\tENABLE_HARDENED_RUNTIME = YES;\n\t\t\t\tENABLE_PREVIEWS = YES;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_FILE = VirtualBuddy/Info.plist;\n\t\t\t\tINFOPLIST_KEY_LSApplicationCategoryType = \"public.app-category.developer-tools\";\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"© 2023 Buddy Software LTD\";\n\t\t\t\tINFOPLIST_KEY_NSMicrophoneUsageDescription = \"Enable audio input for your virtual machines. Without this permission, virtual machines won't be able to record any audio.\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_APP_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tOTHER_LDFLAGS = \"\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(VB_BUNDLE_ID_PREFIX)codes.rambo.VirtualBuddy\";\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Release_Managed;\n\t\t};\n\t\tF4B068B028882EA3003743BF /* Release_Managed */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = VirtualBuddyGuest/VirtualBuddyGuest.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEVELOPMENT_ASSET_PATHS = \"\\\"VirtualBuddyGuest/Preview Content\\\"\";\n\t\t\t\tDEVELOPMENT_TEAM = 8C7439RJLG;\n\t\t\t\tENABLE_HARDENED_RUNTIME = YES;\n\t\t\t\tENABLE_PREVIEWS = YES;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_FILE = \"$(DERIVED_FILE_DIR)/VBGenerated-Info.plist\";\n\t\t\t\tINFOPLIST_KEY_LSApplicationCategoryType = \"public.app-category.utilities\";\n\t\t\t\tINFOPLIST_KEY_LSUIElement = YES;\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"\";\n\t\t\t\tINFOPLIST_KEY_NSMainStoryboardFile = Main;\n\t\t\t\tINFOPLIST_KEY_NSMicrophoneUsageDescription = \"Enable audio input for your virtual machines. Without this permission, virtual machines won't be able to record any audio.\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_APP_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(VB_BUNDLE_ID_PREFIX)codes.rambo.VirtualBuddyGuest\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"VirtualBuddyGuest/Dashboard/Support/VirtualBuddyGuest-Bridging-Header.h\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Release_Managed;\n\t\t};\n\t\tF4B068B128882EA3003743BF /* Release_Managed */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDEVELOPMENT_ASSET_PATHS = VirtualCore/Source/Resources/Preview;\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tENABLE_MODULE_VERIFIER = NO;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_FRAMEWORK_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGES = \"objective-c objective-c++\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = \"gnu11 gnu++17\";\n\t\t\t\tOTHER_LDFLAGS = \"-Wl,-U,_OBJC_CLASS_$__VZVirtualMachineStartOptions\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(VB_BUNDLE_ID_PREFIX)codes.rambo.VirtualCore\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME:c99extidentifier)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Release_Managed;\n\t\t};\n\t\tF4B068B228882EA3003743BF /* Release_Managed */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++20\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDEVELOPMENT_ASSET_PATHS = \"\";\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tENABLE_MODULE_VERIFIER = NO;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"\";\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_FRAMEWORK_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGES = \"objective-c objective-c++\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = \"gnu11 gnu++20\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(VB_BUNDLE_ID_PREFIX)codes.rambo.VirtualUI\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME:c99extidentifier)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Release_Managed;\n\t\t};\n\t\tF4B068B328882EA3003743BF /* Release_Managed */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tENABLE_MODULE_VERIFIER = NO;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"\";\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_FRAMEWORK_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGES = \"objective-c objective-c++\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = \"gnu11 gnu++17\";\n\t\t\t\tOTHER_LDFLAGS = \"\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(VB_BUNDLE_ID_PREFIX)codes.rambo.VirtualWormhole\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME:c99extidentifier)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Release_Managed;\n\t\t};\n\t\tF4BE9C5B27FF052100B648F8 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = F4C1224E280715F200D359E2 /* Main.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tARCHS = arm64;\n\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++17\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tF4BE9C5C27FF052100B648F8 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = F4C1224E280715F200D359E2 /* Main.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tARCHS = arm64;\n\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++17\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tF4BE9C5E27FF052100B648F8 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = F4B068B528882F5D003743BF /* AppTarget.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEVELOPMENT_ASSET_PATHS = \"\\\"VirtualBuddy/Preview Content\\\"\";\n\t\t\t\tDEVELOPMENT_TEAM = 8C7439RJLG;\n\t\t\t\tENABLE_HARDENED_RUNTIME = YES;\n\t\t\t\tENABLE_PREVIEWS = YES;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_FILE = VirtualBuddy/Info.plist;\n\t\t\t\tINFOPLIST_KEY_LSApplicationCategoryType = \"public.app-category.developer-tools\";\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"© 2023 Buddy Software LTD\";\n\t\t\t\tINFOPLIST_KEY_NSMicrophoneUsageDescription = \"Enable audio input for your virtual machines. Without this permission, virtual machines won't be able to record any audio.\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_APP_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tOTHER_LDFLAGS = \"\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(VB_BUNDLE_ID_PREFIX)codes.rambo.VirtualBuddy\";\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tF4BE9C5F27FF052100B648F8 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = F4B068B528882F5D003743BF /* AppTarget.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEVELOPMENT_ASSET_PATHS = \"\\\"VirtualBuddy/Preview Content\\\"\";\n\t\t\t\tDEVELOPMENT_TEAM = 8C7439RJLG;\n\t\t\t\tENABLE_HARDENED_RUNTIME = YES;\n\t\t\t\tENABLE_PREVIEWS = YES;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_FILE = VirtualBuddy/Info.plist;\n\t\t\t\tINFOPLIST_KEY_LSApplicationCategoryType = \"public.app-category.developer-tools\";\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"© 2023 Buddy Software LTD\";\n\t\t\t\tINFOPLIST_KEY_NSMicrophoneUsageDescription = \"Enable audio input for your virtual machines. Without this permission, virtual machines won't be able to record any audio.\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_APP_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tOTHER_LDFLAGS = \"\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(VB_BUNDLE_ID_PREFIX)codes.rambo.VirtualBuddy\";\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tF4BE9C6E27FF053A00B648F8 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDEVELOPMENT_ASSET_PATHS = VirtualCore/Source/Resources/Preview;\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tENABLE_MODULE_VERIFIER = NO;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_FRAMEWORK_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGES = \"objective-c objective-c++\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = \"gnu11 gnu++17\";\n\t\t\t\tOTHER_LDFLAGS = \"-Wl,-U,_OBJC_CLASS_$__VZVirtualMachineStartOptions\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(VB_BUNDLE_ID_PREFIX)codes.rambo.VirtualCore\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME:c99extidentifier)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tF4BE9C6F27FF053A00B648F8 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDEVELOPMENT_ASSET_PATHS = VirtualCore/Source/Resources/Preview;\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tENABLE_MODULE_VERIFIER = NO;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_FRAMEWORK_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGES = \"objective-c objective-c++\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = \"gnu11 gnu++17\";\n\t\t\t\tOTHER_LDFLAGS = \"-Wl,-U,_OBJC_CLASS_$__VZVirtualMachineStartOptions\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(VB_BUNDLE_ID_PREFIX)codes.rambo.VirtualCore\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME:c99extidentifier)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tF4C189E82848F59F00335EC7 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tENABLE_MODULE_VERIFIER = NO;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"\";\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_FRAMEWORK_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGES = \"objective-c objective-c++\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = \"gnu11 gnu++17\";\n\t\t\t\tOTHER_LDFLAGS = \"\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(VB_BUNDLE_ID_PREFIX)codes.rambo.VirtualWormhole\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME:c99extidentifier)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tF4C189EA2848F59F00335EC7 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tENABLE_MODULE_VERIFIER = NO;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"\";\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_FRAMEWORK_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGES = \"objective-c objective-c++\";\n\t\t\t\tMODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = \"gnu11 gnu++17\";\n\t\t\t\tOTHER_LDFLAGS = \"\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(VB_BUNDLE_ID_PREFIX)codes.rambo.VirtualWormhole\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME:c99extidentifier)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tF4C18A4F28491B8500335EC7 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = VirtualBuddyGuest/VirtualBuddyGuest.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEVELOPMENT_ASSET_PATHS = \"\\\"VirtualBuddyGuest/Preview Content\\\"\";\n\t\t\t\tDEVELOPMENT_TEAM = 8C7439RJLG;\n\t\t\t\tENABLE_HARDENED_RUNTIME = YES;\n\t\t\t\tENABLE_PREVIEWS = YES;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_FILE = \"$(DERIVED_FILE_DIR)/VBGenerated-Info.plist\";\n\t\t\t\tINFOPLIST_KEY_LSApplicationCategoryType = \"public.app-category.utilities\";\n\t\t\t\tINFOPLIST_KEY_LSUIElement = YES;\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"\";\n\t\t\t\tINFOPLIST_KEY_NSMainStoryboardFile = Main;\n\t\t\t\tINFOPLIST_KEY_NSMicrophoneUsageDescription = \"Enable audio input for your virtual machines. Without this permission, virtual machines won't be able to record any audio.\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_APP_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(VB_BUNDLE_ID_PREFIX)codes.rambo.VirtualBuddyGuest\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"VirtualBuddyGuest/Dashboard/Support/VirtualBuddyGuest-Bridging-Header.h\";\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tF4C18A5128491B8500335EC7 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = VirtualBuddyGuest/VirtualBuddyGuest.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tDEVELOPMENT_ASSET_PATHS = \"\\\"VirtualBuddyGuest/Preview Content\\\"\";\n\t\t\t\tDEVELOPMENT_TEAM = 8C7439RJLG;\n\t\t\t\tENABLE_HARDENED_RUNTIME = YES;\n\t\t\t\tENABLE_PREVIEWS = YES;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_FILE = \"$(DERIVED_FILE_DIR)/VBGenerated-Info.plist\";\n\t\t\t\tINFOPLIST_KEY_LSApplicationCategoryType = \"public.app-category.utilities\";\n\t\t\t\tINFOPLIST_KEY_LSUIElement = YES;\n\t\t\t\tINFOPLIST_KEY_NSHumanReadableCopyright = \"\";\n\t\t\t\tINFOPLIST_KEY_NSMainStoryboardFile = Main;\n\t\t\t\tINFOPLIST_KEY_NSMicrophoneUsageDescription = \"Enable audio input for your virtual machines. Without this permission, virtual machines won't be able to record any audio.\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = \"$(VB_APP_RUNPATH_SEARCH_PATHS)\";\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"$(VB_BUNDLE_ID_PREFIX)codes.rambo.VirtualBuddyGuest\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"VirtualBuddyGuest/Dashboard/Support/VirtualBuddyGuest-Bridging-Header.h\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tF4D305A429B8DB700006E748 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++20\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEVELOPMENT_TEAM = 8C7439RJLG;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = codes.rambo.VirtualWormholeTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = NO;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tF4D305A529B8DB700006E748 /* Debug_Managed */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++20\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEVELOPMENT_TEAM = 8C7439RJLG;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = codes.rambo.VirtualWormholeTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = NO;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Debug_Managed;\n\t\t};\n\t\tF4D305A629B8DB700006E748 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++20\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEVELOPMENT_TEAM = 8C7439RJLG;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = codes.rambo.VirtualWormholeTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = NO;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tF4D305A729B8DB700006E748 /* Release_Managed */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++20\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEVELOPMENT_TEAM = 8C7439RJLG;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = codes.rambo.VirtualWormholeTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = NO;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Release_Managed;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\tF41369B729918576002CE8D3 /* Build configuration list for PBXNativeTarget \"VirtualBuddyGuestHelper\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tF41369B829918576002CE8D3 /* Debug */,\n\t\t\t\tF41369B929918576002CE8D3 /* Debug_Managed */,\n\t\t\t\tF40A1E8A2C1869680033E47D /* Beta_Debug */,\n\t\t\t\tF41369BA29918576002CE8D3 /* Release */,\n\t\t\t\tF41369BB29918576002CE8D3 /* Release_Managed */,\n\t\t\t\tF40A1E932C18697B0033E47D /* Beta_Release */,\n\t\t\t\tF4A2770E2BF50EA00011B626 /* Dev_Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tF43B01212AD858FE00164CD1 /* Build configuration list for PBXNativeTarget \"DeepLinkSecurity\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tF43B011D2AD858FE00164CD1 /* Debug */,\n\t\t\t\tF43B011E2AD858FE00164CD1 /* Debug_Managed */,\n\t\t\t\tF40A1E8B2C1869680033E47D /* Beta_Debug */,\n\t\t\t\tF43B011F2AD858FE00164CD1 /* Release */,\n\t\t\t\tF43B01202AD858FE00164CD1 /* Release_Managed */,\n\t\t\t\tF40A1E942C18697B0033E47D /* Beta_Release */,\n\t\t\t\tF4A2770F2BF50EA00011B626 /* Dev_Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tF453C4502DF0BCE3007EAD5F /* Build configuration list for PBXAggregateTarget \"vctool\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tF453C4512DF0BCE3007EAD5F /* Debug */,\n\t\t\t\tF453C4522DF0BCE3007EAD5F /* Debug_Managed */,\n\t\t\t\tF453C4532DF0BCE3007EAD5F /* Beta_Debug */,\n\t\t\t\tF453C4542DF0BCE3007EAD5F /* Release */,\n\t\t\t\tF453C4552DF0BCE3007EAD5F /* Release_Managed */,\n\t\t\t\tF453C4562DF0BCE3007EAD5F /* Beta_Release */,\n\t\t\t\tF453C4572DF0BCE3007EAD5F /* Dev_Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tF498AD082884BF13006F1C00 /* Build configuration list for PBXNativeTarget \"VirtualUI\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tF498AD062884BF13006F1C00 /* Debug */,\n\t\t\t\tF4B068AC28882E9A003743BF /* Debug_Managed */,\n\t\t\t\tF40A1E8D2C1869680033E47D /* Beta_Debug */,\n\t\t\t\tF498AD072884BF13006F1C00 /* Release */,\n\t\t\t\tF4B068B228882EA3003743BF /* Release_Managed */,\n\t\t\t\tF40A1E962C18697B0033E47D /* Beta_Release */,\n\t\t\t\tF4A277112BF50EA00011B626 /* Dev_Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tF4BE9C4927FF052100B648F8 /* Build configuration list for PBXProject \"VirtualBuddy\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tF4BE9C5B27FF052100B648F8 /* Debug */,\n\t\t\t\tF4B068A828882E9A003743BF /* Debug_Managed */,\n\t\t\t\tF40A1E872C1869680033E47D /* Beta_Debug */,\n\t\t\t\tF4BE9C5C27FF052100B648F8 /* Release */,\n\t\t\t\tF4B068AE28882EA3003743BF /* Release_Managed */,\n\t\t\t\tF40A1E902C18697B0033E47D /* Beta_Release */,\n\t\t\t\tF4A2770B2BF50EA00011B626 /* Dev_Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tF4BE9C5D27FF052100B648F8 /* Build configuration list for PBXNativeTarget \"VirtualBuddy\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tF4BE9C5E27FF052100B648F8 /* Debug */,\n\t\t\t\tF4B068A928882E9A003743BF /* Debug_Managed */,\n\t\t\t\tF40A1E882C1869680033E47D /* Beta_Debug */,\n\t\t\t\tF4BE9C5F27FF052100B648F8 /* Release */,\n\t\t\t\tF4B068AF28882EA3003743BF /* Release_Managed */,\n\t\t\t\tF40A1E912C18697B0033E47D /* Beta_Release */,\n\t\t\t\tF4A2770C2BF50EA00011B626 /* Dev_Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tF4BE9C6D27FF053A00B648F8 /* Build configuration list for PBXNativeTarget \"VirtualCore\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tF4BE9C6E27FF053A00B648F8 /* Debug */,\n\t\t\t\tF4B068AB28882E9A003743BF /* Debug_Managed */,\n\t\t\t\tF40A1E8C2C1869680033E47D /* Beta_Debug */,\n\t\t\t\tF4BE9C6F27FF053A00B648F8 /* Release */,\n\t\t\t\tF4B068B128882EA3003743BF /* Release_Managed */,\n\t\t\t\tF40A1E952C18697B0033E47D /* Beta_Release */,\n\t\t\t\tF4A277102BF50EA00011B626 /* Dev_Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tF4C189EB2848F59F00335EC7 /* Build configuration list for PBXNativeTarget \"VirtualWormhole\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tF4C189E82848F59F00335EC7 /* Debug */,\n\t\t\t\tF4B068AD28882E9A003743BF /* Debug_Managed */,\n\t\t\t\tF40A1E8F2C1869680033E47D /* Beta_Debug */,\n\t\t\t\tF4C189EA2848F59F00335EC7 /* Release */,\n\t\t\t\tF4B068B328882EA3003743BF /* Release_Managed */,\n\t\t\t\tF40A1E982C18697B0033E47D /* Beta_Release */,\n\t\t\t\tF4A277132BF50EA00011B626 /* Dev_Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tF4C18A4E28491B8500335EC7 /* Build configuration list for PBXNativeTarget \"VirtualBuddyGuest\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tF4C18A4F28491B8500335EC7 /* Debug */,\n\t\t\t\tF4B068AA28882E9A003743BF /* Debug_Managed */,\n\t\t\t\tF40A1E892C1869680033E47D /* Beta_Debug */,\n\t\t\t\tF4C18A5128491B8500335EC7 /* Release */,\n\t\t\t\tF4B068B028882EA3003743BF /* Release_Managed */,\n\t\t\t\tF40A1E922C18697B0033E47D /* Beta_Release */,\n\t\t\t\tF4A2770D2BF50EA00011B626 /* Dev_Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tF4D305A329B8DB700006E748 /* Build configuration list for PBXNativeTarget \"VirtualWormholeTests\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tF4D305A429B8DB700006E748 /* Debug */,\n\t\t\t\tF4D305A529B8DB700006E748 /* Debug_Managed */,\n\t\t\t\tF40A1E8E2C1869680033E47D /* Beta_Debug */,\n\t\t\t\tF4D305A629B8DB700006E748 /* Release */,\n\t\t\t\tF4D305A729B8DB700006E748 /* Release_Managed */,\n\t\t\t\tF40A1E972C18697B0033E47D /* Beta_Release */,\n\t\t\t\tF4A277122BF50EA00011B626 /* Dev_Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\n/* Begin XCRemoteSwiftPackageReference section */\n\t\tF43B01452AD85A7D00164CD1 /* XCRemoteSwiftPackageReference \"URLQueryItemCoder\" */ = {\n\t\t\tisa = XCRemoteSwiftPackageReference;\n\t\t\trepositoryURL = \"https://github.com/kylehughes/URLQueryItemCoder.git\";\n\t\t\trequirement = {\n\t\t\t\tkind = upToNextMajorVersion;\n\t\t\t\tminimumVersion = 1.0.0;\n\t\t\t};\n\t\t};\n\t\tF453C3D82DF0A426007EAD5F /* XCRemoteSwiftPackageReference \"BuddyKit\" */ = {\n\t\t\tisa = XCRemoteSwiftPackageReference;\n\t\t\trepositoryURL = \"https://github.com/insidegui/BuddyKit\";\n\t\t\trequirement = {\n\t\t\t\tkind = upToNextMajorVersion;\n\t\t\t\tminimumVersion = 1.6.1;\n\t\t\t};\n\t\t};\n\t\tF453C4012DF0AEF5007EAD5F /* XCRemoteSwiftPackageReference \"swift-argument-parser\" */ = {\n\t\t\tisa = XCRemoteSwiftPackageReference;\n\t\t\trepositoryURL = \"https://github.com/apple/swift-argument-parser.git\";\n\t\t\trequirement = {\n\t\t\t\tkind = upToNextMajorVersion;\n\t\t\t\tminimumVersion = 1.5.1;\n\t\t\t};\n\t\t};\n\t\tF453C4482DF0B7F6007EAD5F /* XCRemoteSwiftPackageReference \"libfragmentzip\" */ = {\n\t\t\tisa = XCRemoteSwiftPackageReference;\n\t\t\trepositoryURL = \"https://github.com/insidegui/libfragmentzip.git\";\n\t\t\trequirement = {\n\t\t\t\tkind = upToNextMajorVersion;\n\t\t\t\tminimumVersion = 1.0.0;\n\t\t\t};\n\t\t};\n\t\tF4A7FB712BB7238A00E4C12A /* XCRemoteSwiftPackageReference \"swiftui-introspect\" */ = {\n\t\t\tisa = XCRemoteSwiftPackageReference;\n\t\t\trepositoryURL = \"https://github.com/siteline/swiftui-introspect\";\n\t\t\trequirement = {\n\t\t\t\tkind = exactVersion;\n\t\t\t\tversion = \"1.4.0-beta.2\";\n\t\t\t};\n\t\t};\n\t\tF4D0F71628674E4B004D5782 /* XCRemoteSwiftPackageReference \"Sparkle\" */ = {\n\t\t\tisa = XCRemoteSwiftPackageReference;\n\t\t\trepositoryURL = \"https://github.com/sparkle-project/Sparkle\";\n\t\t\trequirement = {\n\t\t\t\tkind = upToNextMajorVersion;\n\t\t\t\tminimumVersion = 2.0.0;\n\t\t\t};\n\t\t};\n\t\tF4E4F7242DF0A1CB00B3B8BA /* XCRemoteSwiftPackageReference \"BuddyKit\" */ = {\n\t\t\tisa = XCRemoteSwiftPackageReference;\n\t\t\trepositoryURL = \"https://github.com/insidegui/BuddyKit\";\n\t\t\trequirement = {\n\t\t\t\tkind = upToNextMajorVersion;\n\t\t\t\tminimumVersion = 1.6.1;\n\t\t\t};\n\t\t};\n/* End XCRemoteSwiftPackageReference section */\n\n/* Begin XCSwiftPackageProductDependency section */\n\t\tF43B01462AD85A7D00164CD1 /* URLQueryItemCoder */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tpackage = F43B01452AD85A7D00164CD1 /* XCRemoteSwiftPackageReference \"URLQueryItemCoder\" */;\n\t\t\tproductName = URLQueryItemCoder;\n\t\t};\n\t\tF453C3EC2DF0A4D1007EAD5F /* BuddyKit */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tpackage = F453C3D82DF0A426007EAD5F /* XCRemoteSwiftPackageReference \"BuddyKit\" */;\n\t\t\tproductName = BuddyKit;\n\t\t};\n\t\tF453C4112DF0B1ED007EAD5F /* BuddyKit */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tpackage = F453C3D82DF0A426007EAD5F /* XCRemoteSwiftPackageReference \"BuddyKit\" */;\n\t\t\tproductName = BuddyKit;\n\t\t};\n\t\tF453C42A2DF0B792007EAD5F /* ArgumentParser */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tpackage = F453C4012DF0AEF5007EAD5F /* XCRemoteSwiftPackageReference \"swift-argument-parser\" */;\n\t\t\tproductName = ArgumentParser;\n\t\t};\n\t\tF453C4492DF0B7F6007EAD5F /* FragmentZip */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tpackage = F453C4482DF0B7F6007EAD5F /* XCRemoteSwiftPackageReference \"libfragmentzip\" */;\n\t\t\tproductName = FragmentZip;\n\t\t};\n\t\tF453C45A2DF0C4BE007EAD5F /* BuddyKit */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tpackage = F453C3D82DF0A426007EAD5F /* XCRemoteSwiftPackageReference \"BuddyKit\" */;\n\t\t\tproductName = BuddyKit;\n\t\t};\n\t\tF453C4622DF0E609007EAD5F /* BuddyKit */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tpackage = F453C3D82DF0A426007EAD5F /* XCRemoteSwiftPackageReference \"BuddyKit\" */;\n\t\t\tproductName = BuddyKit;\n\t\t};\n\t\tF4A7FB722BB7238A00E4C12A /* SwiftUIIntrospect-Static */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tpackage = F4A7FB712BB7238A00E4C12A /* XCRemoteSwiftPackageReference \"swiftui-introspect\" */;\n\t\t\tproductName = \"SwiftUIIntrospect-Static\";\n\t\t};\n\t\tF4D0F71728674E4B004D5782 /* Sparkle */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tpackage = F4D0F71628674E4B004D5782 /* XCRemoteSwiftPackageReference \"Sparkle\" */;\n\t\t\tproductName = Sparkle;\n\t\t};\n\t\tF4E4F7252DF0A1CB00B3B8BA /* BuddyKit */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tpackage = F4E4F7242DF0A1CB00B3B8BA /* XCRemoteSwiftPackageReference \"BuddyKit\" */;\n\t\t\tproductName = BuddyKit;\n\t\t};\n/* End XCSwiftPackageProductDependency section */\n\t};\n\trootObject = F4BE9C4627FF052100B648F8 /* Project object */;\n}\n"
  },
  {
    "path": "VirtualBuddy.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "VirtualBuddy.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "VirtualBuddy.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved",
    "content": "{\n  \"originHash\" : \"3fd24967a48b127010a1cab40c41fd70176878f897a946d6404561fee4decff1\",\n  \"pins\" : [\n    {\n      \"identity\" : \"buddykit\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/insidegui/BuddyKit\",\n      \"state\" : {\n        \"revision\" : \"21d183e3c9c55ad23eb795739ffd73a1e354bca5\",\n        \"version\" : \"1.8.0\"\n      }\n    },\n    {\n      \"identity\" : \"libfragmentzip\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/insidegui/libfragmentzip.git\",\n      \"state\" : {\n        \"revision\" : \"9f72ba6706fe4a16413d463a79eae52c374f3c29\",\n        \"version\" : \"1.0.0\"\n      }\n    },\n    {\n      \"identity\" : \"sparkle\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/sparkle-project/Sparkle\",\n      \"state\" : {\n        \"revision\" : \"0ca3004e98712ea2b39dd881d28448630cce1c99\",\n        \"version\" : \"2.7.0\"\n      }\n    },\n    {\n      \"identity\" : \"swift-argument-parser\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/apple/swift-argument-parser\",\n      \"state\" : {\n        \"revision\" : \"011f0c765fb46d9cac61bca19be0527e99c98c8b\",\n        \"version\" : \"1.5.1\"\n      }\n    },\n    {\n      \"identity\" : \"swiftui-introspect\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/siteline/swiftui-introspect\",\n      \"state\" : {\n        \"revision\" : \"c2b0625d0ef385994e4c6bc36116c0e7bfa6dafa\",\n        \"version\" : \"1.4.0-beta.2\"\n      }\n    },\n    {\n      \"identity\" : \"urlqueryitemcoder\",\n      \"kind\" : \"remoteSourceControl\",\n      \"location\" : \"https://github.com/kylehughes/URLQueryItemCoder.git\",\n      \"state\" : {\n        \"revision\" : \"a828b3bb5b273adf85a3ca95d9705987cb2de7c7\",\n        \"version\" : \"1.0.0\"\n      }\n    }\n  ],\n  \"version\" : 3\n}\n"
  },
  {
    "path": "VirtualBuddy.xcodeproj/xcshareddata/xcdebugger/Breakpoints_v2.xcbkptlist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Bucket\n   uuid = \"F8D26811-F4BF-47A8-98F0-596926D6C20D\"\n   type = \"4\"\n   version = \"2.0\">\n</Bucket>\n"
  },
  {
    "path": "VirtualBuddy.xcodeproj/xcshareddata/xcschemes/DeepLinkSecurity.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1640\"\n   version = \"1.7\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"F43B01142AD858FE00164CD1\"\n               BuildableName = \"DeepLinkSecurity.framework\"\n               BlueprintName = \"DeepLinkSecurity\"\n               ReferencedContainer = \"container:VirtualBuddy.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      shouldAutocreateTestPlan = \"YES\">\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"F43B01142AD858FE00164CD1\"\n            BuildableName = \"DeepLinkSecurity.framework\"\n            BlueprintName = \"DeepLinkSecurity\"\n            ReferencedContainer = \"container:VirtualBuddy.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "VirtualBuddy.xcodeproj/xcshareddata/xcschemes/VirtualBuddy (Dev Release).xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1640\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"F4BE9C4D27FF052100B648F8\"\n               BuildableName = \"VirtualBuddy.app\"\n               BlueprintName = \"VirtualBuddy\"\n               ReferencedContainer = \"container:VirtualBuddy.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug_Managed\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <Testables>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug_Managed\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"F4BE9C4D27FF052100B648F8\"\n            BuildableName = \"VirtualBuddy.app\"\n            BlueprintName = \"VirtualBuddy\"\n            ReferencedContainer = \"container:VirtualBuddy.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n      <CommandLineArguments>\n         <CommandLineArgument\n            argument = \"-VBAPIEnvironment local\"\n            isEnabled = \"NO\">\n         </CommandLineArgument>\n         <CommandLineArgument\n            argument = \"-VBDisableMemoryLeakAssertions YES\"\n            isEnabled = \"NO\">\n         </CommandLineArgument>\n         <CommandLineArgument\n            argument = \"-WHDisablePayloadPropagation YES\"\n            isEnabled = \"NO\">\n         </CommandLineArgument>\n         <CommandLineArgument\n            argument = \"-WHVerbosePacketLogging YES\"\n            isEnabled = \"YES\">\n         </CommandLineArgument>\n      </CommandLineArguments>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Dev_Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"F4BE9C4D27FF052100B648F8\"\n            BuildableName = \"VirtualBuddy.app\"\n            BlueprintName = \"VirtualBuddy\"\n            ReferencedContainer = \"container:VirtualBuddy.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug_Managed\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Dev_Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "VirtualBuddy.xcodeproj/xcshareddata/xcschemes/VirtualBuddy (Managed - Beta).xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1640\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"F4BE9C4D27FF052100B648F8\"\n               BuildableName = \"VirtualBuddy.app\"\n               BlueprintName = \"VirtualBuddy\"\n               ReferencedContainer = \"container:VirtualBuddy.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Beta_Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <Testables>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Beta_Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"F4BE9C4D27FF052100B648F8\"\n            BuildableName = \"VirtualBuddy.app\"\n            BlueprintName = \"VirtualBuddy\"\n            ReferencedContainer = \"container:VirtualBuddy.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n      <CommandLineArguments>\n         <CommandLineArgument\n            argument = \"-VBAPIEnvironment local\"\n            isEnabled = \"NO\">\n         </CommandLineArgument>\n         <CommandLineArgument\n            argument = \"-VBDisableMemoryLeakAssertions YES\"\n            isEnabled = \"NO\">\n         </CommandLineArgument>\n         <CommandLineArgument\n            argument = \"-WHDisablePayloadPropagation YES\"\n            isEnabled = \"NO\">\n         </CommandLineArgument>\n         <CommandLineArgument\n            argument = \"-WHVerbosePacketLogging YES\"\n            isEnabled = \"YES\">\n         </CommandLineArgument>\n      </CommandLineArguments>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Beta_Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"F4BE9C4D27FF052100B648F8\"\n            BuildableName = \"VirtualBuddy.app\"\n            BlueprintName = \"VirtualBuddy\"\n            ReferencedContainer = \"container:VirtualBuddy.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Beta_Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Beta_Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "VirtualBuddy.xcodeproj/xcshareddata/xcschemes/VirtualBuddy (Managed).xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1640\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"F4BE9C4D27FF052100B648F8\"\n               BuildableName = \"VirtualBuddy.app\"\n               BlueprintName = \"VirtualBuddy\"\n               ReferencedContainer = \"container:VirtualBuddy.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug_Managed\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <Testables>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug_Managed\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"YES\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"F4BE9C4D27FF052100B648F8\"\n            BuildableName = \"VirtualBuddy.app\"\n            BlueprintName = \"VirtualBuddy\"\n            ReferencedContainer = \"container:VirtualBuddy.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n      <CommandLineArguments>\n         <CommandLineArgument\n            argument = \"-VerboseUILoggingEnabled YES\"\n            isEnabled = \"YES\">\n         </CommandLineArgument>\n         <CommandLineArgument\n            argument = \"-WHVerbosePacketLogging YES\"\n            isEnabled = \"YES\">\n         </CommandLineArgument>\n         <CommandLineArgument\n            argument = \"-VBAPIEnvironment local\"\n            isEnabled = \"NO\">\n         </CommandLineArgument>\n         <CommandLineArgument\n            argument = \"-VBDisableMemoryLeakAssertions YES\"\n            isEnabled = \"NO\">\n         </CommandLineArgument>\n         <CommandLineArgument\n            argument = \"-WHDisablePayloadPropagation YES\"\n            isEnabled = \"NO\">\n         </CommandLineArgument>\n         <CommandLineArgument\n            argument = \"-VBForceBuiltInSoftwareCatalog YES\"\n            isEnabled = \"NO\">\n         </CommandLineArgument>\n         <CommandLineArgument\n            argument = \"-VBSimulateInstall YES\"\n            isEnabled = \"NO\">\n         </CommandLineArgument>\n         <CommandLineArgument\n            argument = \"-VBSimulateDownload YES\"\n            isEnabled = \"NO\">\n         </CommandLineArgument>\n         <CommandLineArgument\n            argument = \"-VBSimulateSlowCatalogFetch YES\"\n            isEnabled = \"NO\">\n         </CommandLineArgument>\n         <CommandLineArgument\n            argument = \"-VBSimulateHostVersion 12.0\"\n            isEnabled = \"NO\">\n         </CommandLineArgument>\n         <CommandLineArgument\n            argument = \"-VBSimulateMobileDeviceVersion 1770.0.0\"\n            isEnabled = \"NO\">\n         </CommandLineArgument>\n         <CommandLineArgument\n            argument = \"-VBForceSigningStatusUnsigned YES\"\n            isEnabled = \"NO\">\n         </CommandLineArgument>\n         <CommandLineArgument\n            argument = \"-VBForceSigningStatusSigned YES\"\n            isEnabled = \"NO\">\n         </CommandLineArgument>\n         <CommandLineArgument\n            argument = \"-VBForceSigningStatusRequestFailed YES\"\n            isEnabled = \"NO\">\n         </CommandLineArgument>\n         <CommandLineArgument\n            argument = \"-VBTestImport &apos;/Users/insidegui/Library/Containers/com.utmapp.UTM/Data/Documents/macOS 26.utm&apos;\"\n            isEnabled = \"NO\">\n         </CommandLineArgument>\n         <CommandLineArgument\n            argument = \"-VBTestImportSkipConfirmation YES\"\n            isEnabled = \"NO\">\n         </CommandLineArgument>\n         <CommandLineArgument\n            argument = \"-VBAlwaysAttemptLegacyThumbnailMigration YES\"\n            isEnabled = \"NO\">\n         </CommandLineArgument>\n         <CommandLineArgument\n            argument = \"-VBSimulateGuestDiskImageGeneration YES\"\n            isEnabled = \"NO\">\n         </CommandLineArgument>\n         <CommandLineArgument\n            argument = \"-VBDelayGuestDiskImageGenerationBySeconds 20\"\n            isEnabled = \"NO\">\n         </CommandLineArgument>\n         <CommandLineArgument\n            argument = \"-VBSimulateGuestDiskImageGenerationError YES\"\n            isEnabled = \"NO\">\n         </CommandLineArgument>\n         <CommandLineArgument\n            argument = \"-VBOpenSettings YES\"\n            isEnabled = \"NO\">\n         </CommandLineArgument>\n         <CommandLineArgument\n            argument = \"-VBSimulateLibraryVolumeNotMounted YES\"\n            isEnabled = \"NO\">\n         </CommandLineArgument>\n         <CommandLineArgument\n            argument = \"-VBSimulateLibraryDirectoryMissing YES\"\n            isEnabled = \"NO\">\n         </CommandLineArgument>\n         <CommandLineArgument\n            argument = \"-VBSimulateLibraryEmpty YES\"\n            isEnabled = \"NO\">\n         </CommandLineArgument>\n         <CommandLineArgument\n            argument = \"-VBForceFirstLaunchExperience YES\"\n            isEnabled = \"NO\">\n         </CommandLineArgument>\n      </CommandLineArguments>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release_Managed\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"F4BE9C4D27FF052100B648F8\"\n            BuildableName = \"VirtualBuddy.app\"\n            BlueprintName = \"VirtualBuddy\"\n            ReferencedContainer = \"container:VirtualBuddy.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug_Managed\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release_Managed\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "VirtualBuddy.xcodeproj/xcshareddata/xcschemes/VirtualBuddy.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1640\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"F4BE9C4D27FF052100B648F8\"\n               BuildableName = \"VirtualBuddy.app\"\n               BlueprintName = \"VirtualBuddy\"\n               ReferencedContainer = \"container:VirtualBuddy.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <Testables>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"F4BE9C4D27FF052100B648F8\"\n            BuildableName = \"VirtualBuddy.app\"\n            BlueprintName = \"VirtualBuddy\"\n            ReferencedContainer = \"container:VirtualBuddy.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n      <CommandLineArguments>\n         <CommandLineArgument\n            argument = \"-VBAPIEnvironment development\"\n            isEnabled = \"NO\">\n         </CommandLineArgument>\n         <CommandLineArgument\n            argument = \"-VBSimulateNonAPFSVolume YES\"\n            isEnabled = \"NO\">\n         </CommandLineArgument>\n      </CommandLineArguments>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"F4BE9C4D27FF052100B648F8\"\n            BuildableName = \"VirtualBuddy.app\"\n            BlueprintName = \"VirtualBuddy\"\n            ReferencedContainer = \"container:VirtualBuddy.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "VirtualBuddy.xcodeproj/xcshareddata/xcschemes/VirtualBuddyGuest.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1640\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"F4C18A4128491B8500335EC7\"\n               BuildableName = \"VirtualBuddyGuest.app\"\n               BlueprintName = \"VirtualBuddyGuest\"\n               ReferencedContainer = \"container:VirtualBuddy.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <Testables>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"F4C18A4128491B8500335EC7\"\n            BuildableName = \"VirtualBuddyGuest.app\"\n            BlueprintName = \"VirtualBuddyGuest\"\n            ReferencedContainer = \"container:VirtualBuddy.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n      <CommandLineArguments>\n         <CommandLineArgument\n            argument = \"-ShowPanelOnLaunch YES\"\n            isEnabled = \"YES\">\n         </CommandLineArgument>\n         <CommandLineArgument\n            argument = \"-DisableInstall YES\"\n            isEnabled = \"YES\">\n         </CommandLineArgument>\n         <CommandLineArgument\n            argument = \"-WHLogPacketContents YES\"\n            isEnabled = \"YES\">\n         </CommandLineArgument>\n      </CommandLineArguments>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"F4C18A4128491B8500335EC7\"\n            BuildableName = \"VirtualBuddyGuest.app\"\n            BlueprintName = \"VirtualBuddyGuest\"\n            ReferencedContainer = \"container:VirtualBuddy.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "VirtualBuddy.xcodeproj/xcshareddata/xcschemes/VirtualCore.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1640\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"F4BE9C6427FF053A00B648F8\"\n               BuildableName = \"VirtualCore.framework\"\n               BlueprintName = \"VirtualCore\"\n               ReferencedContainer = \"container:VirtualBuddy.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <Testables>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"F4BE9C6427FF053A00B648F8\"\n            BuildableName = \"VirtualCore.framework\"\n            BlueprintName = \"VirtualCore\"\n            ReferencedContainer = \"container:VirtualBuddy.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "VirtualBuddy.xcodeproj/xcshareddata/xcschemes/VirtualUI.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1640\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"F498ACFD2884BF13006F1C00\"\n               BuildableName = \"VirtualUI.framework\"\n               BlueprintName = \"VirtualUI\"\n               ReferencedContainer = \"container:VirtualBuddy.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <Testables>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"F498ACFD2884BF13006F1C00\"\n            BuildableName = \"VirtualUI.framework\"\n            BlueprintName = \"VirtualUI\"\n            ReferencedContainer = \"container:VirtualBuddy.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "VirtualBuddy.xcodeproj/xcshareddata/xcschemes/VirtualWormhole.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1640\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"F4C189DF2848F59F00335EC7\"\n               BuildableName = \"VirtualWormhole.framework\"\n               BlueprintName = \"VirtualWormhole\"\n               ReferencedContainer = \"container:VirtualBuddy.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <Testables>\n         <TestableReference\n            skipped = \"NO\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"F4D3059B29B8DB700006E748\"\n               BuildableName = \"VirtualWormholeTests.xctest\"\n               BlueprintName = \"VirtualWormholeTests\"\n               ReferencedContainer = \"container:VirtualBuddy.xcodeproj\">\n            </BuildableReference>\n         </TestableReference>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"F4C189DF2848F59F00335EC7\"\n            BuildableName = \"VirtualWormhole.framework\"\n            BlueprintName = \"VirtualWormhole\"\n            ReferencedContainer = \"container:VirtualBuddy.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "VirtualBuddy.xcodeproj/xcshareddata/xcschemes/vctool.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1640\"\n   version = \"1.7\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\"\n      buildArchitectures = \"Automatic\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"F453C44F2DF0BCE3007EAD5F\"\n               BuildableName = \"vctool\"\n               BlueprintName = \"vctool\"\n               ReferencedContainer = \"container:VirtualBuddy.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      shouldAutocreateTestPlan = \"YES\">\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"F4BE9C4D27FF052100B648F8\"\n            BuildableName = \"VirtualBuddy.app\"\n            BlueprintName = \"VirtualBuddy\"\n            ReferencedContainer = \"container:VirtualBuddy.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n      <CommandLineArguments>\n         <CommandLineArgument\n            argument = \"--help\"\n            isEnabled = \"YES\">\n         </CommandLineArgument>\n      </CommandLineArguments>\n      <EnvironmentVariables>\n         <EnvironmentVariable\n            key = \"VB_TOOL\"\n            value = \"vctool\"\n            isEnabled = \"YES\">\n         </EnvironmentVariable>\n      </EnvironmentVariables>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"F453C44F2DF0BCE3007EAD5F\"\n            BuildableName = \"vctool\"\n            BlueprintName = \"vctool\"\n            ReferencedContainer = \"container:VirtualBuddy.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "VirtualBuddyGuest/Assets.xcassets/AccentColor.colorset/Contents.json",
    "content": "{\n  \"colors\" : [\n    {\n      \"color\" : {\n        \"color-space\" : \"display-p3\",\n        \"components\" : {\n          \"alpha\" : \"1.000\",\n          \"blue\" : \"0.000\",\n          \"green\" : \"0.551\",\n          \"red\" : \"1.000\"\n        }\n      },\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "VirtualBuddyGuest/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"icon_16x16.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"16x16\"\n    },\n    {\n      \"filename\" : \"icon_16x16@2x@2x.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"16x16\"\n    },\n    {\n      \"filename\" : \"icon_32x32.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"32x32\"\n    },\n    {\n      \"filename\" : \"icon_32x32@2x@2x.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"32x32\"\n    },\n    {\n      \"filename\" : \"icon_128x128.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"128x128\"\n    },\n    {\n      \"filename\" : \"icon_128x128@2x@2x.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"128x128\"\n    },\n    {\n      \"filename\" : \"icon_256x256.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"256x256\"\n    },\n    {\n      \"filename\" : \"icon_256x256@2x@2x.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"256x256\"\n    },\n    {\n      \"filename\" : \"icon_512x512.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"512x512\"\n    },\n    {\n      \"filename\" : \"icon_512x512@2x@2x.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"512x512\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "VirtualBuddyGuest/Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "VirtualBuddyGuest/Assets.xcassets/StatusItem.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"menubar.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"filename\" : \"menubar@2x.png\",\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  },\n  \"properties\" : {\n    \"template-rendering-intent\" : \"template\"\n  }\n}\n"
  },
  {
    "path": "VirtualBuddyGuest/Dashboard/GuestDashboard.swift",
    "content": "//\n//  ContentView.swift\n//  VirtualBuddyGuest\n//\n//  Created by Guilherme Rambo on 02/06/22.\n//\n\nimport SwiftUI\nimport VirtualWormhole\n\nstruct GuestDashboard: View {\n    @EnvironmentObject private var launchAtLoginManager: GuestLaunchAtLoginManager\n    @EnvironmentObject private var hostConnection: WormholeManager\n    @EnvironmentObject private var sharedFolders: GuestSharedFoldersManager\n\n    @State var activated = false\n\n    #if ENABLE_USERDEFAULTS_SYNC\n    @State private var showingDefaultsPopover = false\n    #endif\n\n    var body: some View {\n        VStack {\n            connectionState\n\n            sharedFoldersState\n\n            Spacer()\n\n            Form {\n                Toggle(\"Launch At Login\", isOn: launchAtLoginBinding)\n\n                #if ENABLE_USERDEFAULTS_SYNC\n                Button(\"Defaults Import…\") {\n                    showingDefaultsPopover.toggle()\n                }\n                .popover(isPresented: $showingDefaultsPopover) {\n                    GuestDefaultsImportView()\n                }\n                #endif\n            }\n\n            Spacer()\n        }\n            .padding()\n            .frame(minWidth: 300, maxWidth: .infinity, minHeight: 200, maxHeight: .infinity)\n    }\n\n    @ViewBuilder\n    private var connectionState: some View {\n        HStack(spacing: 4) {\n            Circle()\n                .foregroundColor(hostConnection.isConnected ? Color.green : Color.red)\n                .frame(width: 6)\n\n            if hostConnection.isConnected {\n                Text(\"Connected to VirtualBuddy\")\n            } else {\n                Text(\"Not Connected\")\n            }\n        }\n        .foregroundStyle(.secondary)\n        .font(.caption)\n    }\n\n    @ViewBuilder\n    private var sharedFoldersState: some View {\n        if let error = sharedFolders.error {\n            VStack {\n                Text(\"Failed to mount shared folders:\")\n                    .foregroundColor(.secondary)\n                Text(error.localizedDescription)\n                    .foregroundColor(.red)\n            }\n        } else {\n            HStack {\n                Text(\"Shared Folders:\")\n                Button(\"Reveal in Finder\") {\n                    sharedFolders.revealInFinder()\n                }\n            }\n        }\n    }\n\n    private var launchAtLoginBinding: Binding<Bool> {\n        .init {\n            launchAtLoginManager.isLaunchAtLoginEnabled\n        } set: { newValue in\n            Task {\n                do {\n                    try await launchAtLoginManager.setLaunchAtLoginEnabled(newValue)\n                } catch {\n                    await MainActor.run {\n                        _ = NSAlert(error: error).runModal()\n                    }\n                }\n            }\n        }\n    }\n}\n\n#if DEBUG\nstruct GuestDashboard_Previews: PreviewProvider {\n    static var previews: some View {\n        GuestDashboard()\n            .environmentObject(GuestLaunchAtLoginManager())\n            .environmentObject(WormholeManager.sharedGuest)\n            .environmentObject(GuestSharedFoldersManager())\n    }\n}\n\nfinal class MockHostConnectionStateProvider: HostConnectionStateProvider {\n    var isConnected: Bool = false\n}\n#endif\n"
  },
  {
    "path": "VirtualBuddyGuest/Dashboard/GuestDefaultsImportView.swift",
    "content": "#if ENABLE_USERDEFAULTS_SYNC\nimport SwiftUI\nimport VirtualCore\nimport VirtualUI\nimport VirtualWormhole\nimport Combine\n\nfinal class DefaultsImportViewModel: ObservableObject {\n\n    let connection: WormholeManager\n    let controller = DefaultsImportController()\n\n    @Published private(set) var domains = [DefaultsDomainDescriptor]()\n\n    init(connection: WormholeManager = .sharedGuest) {\n        self.connection = connection\n\n        controller.$sortedDomains.assign(to: &$domains)\n    }\n\n    private var _client: WHDefaultsImportClient?\n    private var client: WHDefaultsImportClient {\n        get throws {\n            if let _client { return _client }\n\n            let newClient = try connection.makeClient(WHDefaultsImportClient.self)\n\n            _client = newClient\n\n            return newClient\n        }\n    }\n\n    func importDomain(with id: DefaultsDomainDescriptor.ID) async throws {\n        let defaultsClient = try client\n\n        try await defaultsClient.importDomain(with: id)\n    }\n\n}\n\nstruct GuestDefaultsImportView: View {\n    @StateObject var viewModel = DefaultsImportViewModel()\n\n    var body: some View {\n        List {\n            ForEach(viewModel.domains) { domain in\n                DefaultsItemView(domain: domain)\n                    .environmentObject(viewModel)\n            }\n        }\n        .frame(minWidth: 200, maxWidth: .infinity, minHeight: 200, maxHeight: .infinity)\n    }\n}\n\nstruct DefaultsItemView: View {\n    @EnvironmentObject var viewModel: DefaultsImportViewModel\n\n    var domain: DefaultsDomainDescriptor\n\n    var body: some View {\n        HStack {\n            Image(nsImage: domain.target.iconImage())\n                .resizable()\n                .aspectRatio(contentMode: .fit)\n                .frame(height: 64)\n            VStack(alignment: .leading) {\n                Text(domain.target.name)\n                HStack {\n                    if isLoading {\n                        ProgressView()\n                            .controlSize(.small)\n                    } else {\n                        Button(\"Import\") {\n                            importDomain()\n                        }\n                    }\n                }\n                .controlSize(.small)\n            }\n        }\n    }\n\n    @State private var isLoading = false\n\n    private func importDomain() {\n        isLoading = true\n\n        Task {\n            do {\n                try await viewModel.importDomain(with: domain.id)\n            } catch {\n                NSAlert(error: error).runModal()\n            }\n\n            isLoading = false\n        }\n    }\n}\n\n#if DEBUG\nstruct GuestDefaultsImportView_Previews: PreviewProvider {\n    static var previews: some View {\n        GuestDefaultsImportView()\n    }\n}\n#endif\n\n#endif // ENABLE_USERDEFAULTS_SYNC\n"
  },
  {
    "path": "VirtualBuddyGuest/Dashboard/GuestSharedFoldersManager.swift",
    "content": "import Foundation\nimport VirtualCore\nimport OSLog\n\nfinal class GuestSharedFoldersManager: ObservableObject {\n\n    private lazy var logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: String(describing: Self.self))\n\n    private let queue = DispatchQueue(label: \"mount\", qos: .userInitiated)\n\n    @Published private(set) var error: Error?\n\n    func mount() async throws {\n        logger.notice(\"Mount shared folders\")\n\n        let alreadyMounted = await checkAlreadyMounted()\n\n        guard !alreadyMounted else {\n            logger.notice(\"Shared folders already mounted, skipping mount\")\n            return\n        }\n\n        do {\n            if !FileManager.default.fileExists(atPath: Self.defaultMountPointURL.path) {\n                logger.info(\"Shared folders mount point doesn't exist, creating at \\(Self.defaultMountPointURL.path, privacy: .public)\")\n\n                try FileManager.default.createDirectory(at: Self.defaultMountPointURL, withIntermediateDirectories: true, attributes: nil)\n            }\n\n            try await runMount(with: [\n                \"-t\",\n                \"virtiofs\",\n                VBSharedFolder.virtualBuddyShareName,\n                Self.defaultMountPointURL.path\n            ])\n        } catch {\n            logger.error(\"Mount shared folders failed with error: \\(error, privacy: .public)\")\n\n            await MainActor.run {\n                self.error = error\n            }\n\n            throw error\n        }\n    }\n\n    func revealInFinder() {\n        NSWorkspace.shared.selectFile(Self.defaultMountPointURL.path, inFileViewerRootedAtPath: Self.defaultMountPointURL.deletingLastPathComponent().path)\n    }\n\n    private static let defaultMountPointURL: URL = {\n        URL(fileURLWithPath: NSHomeDirectory())\n            .appendingPathComponent(\"Desktop\")\n            .appendingPathComponent(VBSharedFolder.virtualBuddyShareName)\n    }()\n\n    /// `true` if the shared folder is already mounted.\n    private func checkAlreadyMounted() async -> Bool {\n        guard let mountPoints = (try? await runMount()).flatMap({ $0.components(separatedBy: .newlines) }) else {\n            return false\n        }\n\n        logger.debug(\"Mount points: \\(mountPoints.joined(separator: \"\\n\"))\")\n\n        return mountPoints.contains { $0.contains(Self.defaultMountPointURL.path) }\n    }\n\n    @discardableResult\n    private func runMount(with arguments: [String] = []) async throws -> String {\n        try await withCheckedThrowingContinuation { continuation in\n            queue.async {\n                let proc = Process()\n                proc.executableURL = URL(fileURLWithPath: \"/sbin/mount\")\n                proc.arguments = arguments\n                let outPipe = Pipe()\n                let errPipe = Pipe()\n                proc.standardOutput = outPipe\n                proc.standardError = errPipe\n\n                do {\n                    try proc.run()\n                    proc.waitUntilExit()\n\n                    if proc.terminationStatus == 0 {\n                        let output = (try? outPipe.fileHandleForReading.readToEnd()).flatMap({ String(decoding: $0, as: UTF8.self) })\n                        continuation.resume(returning: output ?? \"\")\n                    } else {\n                        continuation.resume(throwing: CocoaError(.coderInvalidValue, userInfo: [NSLocalizedDescriptionKey: \"Mount command failed with code \\(proc.terminationStatus).\"]))\n                    }\n                } catch {\n                    continuation.resume(throwing: error)\n                }\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "VirtualBuddyGuest/Dashboard/HostConnectionStateProvider.swift",
    "content": "import Foundation\nimport VirtualWormhole\n\nprotocol HostConnectionStateProvider: ObservableObject {\n    var isConnected: Bool { get }\n}\n\nextension WormholeManager: HostConnectionStateProvider { }\n"
  },
  {
    "path": "VirtualBuddyGuest/Dashboard/Support/GuestLaunchAtLoginManager.swift",
    "content": "import Foundation\nimport ServiceManagement\nimport OSLog\n\nfinal class GuestLaunchAtLoginManager: ObservableObject {\n\n    private lazy var logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: String(describing: Self.self))\n\n    var isLaunchAtLoginEnabled: Bool { SMAppService.guestHelper.status == .enabled }\n\n    func setLaunchAtLoginEnabled(_ enabled: Bool) async throws {\n        logger.debug(\"Set launch at login enabled: \\(enabled, privacy: .public)\")\n\n        if enabled {\n            try SMAppService.guestHelper.register()\n        } else {\n            try await SMAppService.guestHelper.unregister()\n        }\n\n        await MainActor.run { objectWillChange.send() }\n    }\n\n    private var hasAutoEnabled: Bool {\n        get { UserDefaults.standard.bool(forKey: #function) }\n        set {\n            UserDefaults.standard.set(newValue, forKey: #function)\n            UserDefaults.standard.synchronize()\n        }\n    }\n\n    func autoEnableIfNeeded() {\n        guard !hasAutoEnabled else { return }\n        hasAutoEnabled = true\n\n        logger.debug(\"Attempting to auto-enable launch at login\")\n\n        Task {\n            do {\n                try await setLaunchAtLoginEnabled(true)\n\n                logger.debug(\"Successfully auto-enabled launch at login\")\n            } catch {\n                logger.error(\"Failed to auto-enable launch at login: \\(error, privacy: .public)\")\n            }\n        }\n    }\n\n}\n\n@available(macOS 13.0, *)\nprivate extension SMAppService {\n    static let guestHelper = SMAppService.loginItem(identifier: kGuestAppLaunchAtLoginHelperBundleID)\n}\n"
  },
  {
    "path": "VirtualBuddyGuest/Dashboard/Support/VirtualBuddyGuest-Bridging-Header.h",
    "content": "#define kGuestAppLaunchAtLoginHelperBundleID GUEST_LAUNCH_AT_LOGIN_HELPER_BUNDLE_ID\n"
  },
  {
    "path": "VirtualBuddyGuest/GuestAppDelegate.swift",
    "content": "import Cocoa\nimport SwiftUI\nimport VirtualUI\nimport VirtualWormhole\nimport OSLog\n\n@NSApplicationMain\nfinal class GuestAppDelegate: NSObject, NSApplicationDelegate {\n\n    private let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? \"Guest\", category: \"GuestAppDelegate\")\n\n    private lazy var launchAtLoginManager = GuestLaunchAtLoginManager()\n\n    private lazy var sharedFolders = GuestSharedFoldersManager()\n\n    private lazy var dashboardItem: StatusItemManager = {\n        StatusItemManager(\n            configuration: .default.id(\"dashboard\"),\n            statusItem: .button(label: { Image(\"StatusItem\") }),\n            content: GuestDashboard()\n                .environmentObject(self.launchAtLoginManager)\n                .environmentObject(WormholeManager.sharedGuest)\n                .environmentObject(self.sharedFolders)\n        )\n    }()\n\n    private var shouldShowPanelAfterLaunching: Bool {\n        get { !UserDefaults.standard.bool(forKey: \"shownPanelOnFirstLauch\") || UserDefaults.standard.bool(forKey: \"ShowPanelOnLaunch\") }\n        set {\n            UserDefaults.standard.set(!newValue, forKey: \"shownPanelOnFirstLauch\")\n            UserDefaults.standard.synchronize()\n        }\n    }\n\n    private let installer = GuestAppInstaller()\n\n    func applicationWillFinishLaunching(_ notification: Notification) {\n        do {\n            try installer.installIfNeeded()\n        } catch {\n            let alert = NSAlert(error: error)\n            alert.runModal()\n        }\n    }\n\n    func applicationDidFinishLaunching(_ notification: Notification) {\n        /// Skip regular app activation if installation is needed (i.e. running from disk image).\n        guard !installer.needsInstall else { return }\n\n        launchAtLoginManager.autoEnableIfNeeded()\n\n        WormholeManager.sharedGuest.activate()\n\n        Task {\n            try? await sharedFolders.mount()\n        }\n        \n        dashboardItem.install()\n\n        perform(#selector(showPanelForFirstLaunchIfNeeded), with: nil, afterDelay: 0.5)\n\n        NSWorkspace.shared.notificationCenter.addObserver(forName: NSWorkspace.willPowerOffNotification, object: nil, queue: nil) { _ in\n            self.logger.notice(\"Received power off notification.\")\n        }\n    }\n\n    func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {\n        showPanel()\n\n        return true\n    }\n\n    @objc private func showPanelForFirstLaunchIfNeeded() {\n        guard shouldShowPanelAfterLaunching else { return }\n        shouldShowPanelAfterLaunching = false\n\n        NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(showPanelForFirstLaunchIfNeeded), object: nil)\n\n        showPanel()\n    }\n\n    @objc private func showPanel() {\n        dashboardItem.showPanel()\n    }\n\n    private var startedDesktopPictureSendBeforeTermination = false\n\n    func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {\n        logger.debug(#function)\n\n        guard !startedDesktopPictureSendBeforeTermination else { return .terminateLater }\n        startedDesktopPictureSendBeforeTermination = true\n\n        Task {\n            logger.notice(\"Requesting WH send desktop picture\")\n\n            try? await Task.sleep(for: .milliseconds(500))\n\n            await WormholeManager.sharedGuest.sendDesktopPicture()\n\n            try? await Task.sleep(for: .seconds(500))\n\n            NSApp.reply(toApplicationShouldTerminate: true)\n        }\n\n        return .terminateLater\n    }\n\n}\n"
  },
  {
    "path": "VirtualBuddyGuest/GuestAppInstaller.swift",
    "content": "import Cocoa\nimport OSLog\n\nfinal class GuestAppInstaller {\n\n    private lazy var logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: String(describing: Self.self))\n\n    func installIfNeeded() throws {\n        do {\n            let needsUpdate = mountedGuestImageNeedsInstall\n\n            guard !needsUpdate else {\n                logger.notice(\"🚀 Relaunching from mounted image for update\")\n                try NSApplication.shared.relaunch(at: mountedImageAppURL.path)\n                return\n            }\n\n            guard needsInstall else {\n                logger.debug(\"Install not needed: running from supported path and updated not needed. Path: \\(Bundle.main.bundleURL.deletingLastPathComponent().path, privacy: .public)\")\n                return\n            }\n\n            let destURL = URL(fileURLWithPath: \"/Applications\")\n                .appendingPathComponent(Bundle.main.bundleURL.lastPathComponent)\n\n            logger.notice(\"Performing install (running from \\(Bundle.main.bundleURL.deletingLastPathComponent().path, privacy: .public), installing to \\(destURL.path, privacy: .public))\")\n\n            if FileManager.default.fileExists(atPath: destURL.path) {\n                logger.debug(\"Removing existing app at \\(destURL.path, privacy: .public)\")\n\n                if let runningApp = NSRunningApplication.runningApplications(withBundleIdentifier: Bundle.main.bundleIdentifier!).first(where: { $0.bundleURL == destURL }) {\n                    logger.debug(\"Terminating existing app instance\")\n\n                    if !runningApp.forceTerminate() {\n                        logger.error(\"Failed to terminate existing app instance\")\n                    }\n                }\n\n                try FileManager.default.removeItem(at: destURL)\n            }\n\n            try FileManager.default.copyItem(at: Bundle.main.bundleURL, to: destURL)\n\n            try NSApplication.shared.relaunch(at: destURL.path)\n        } catch {\n            logger.error(\"Install failed: \\(error, privacy: .public)\")\n\n            throw CocoaError(.coderInvalidValue, userInfo: [\n                NSLocalizedDescriptionKey: \"Failed to install the VirtualBuddyGuest app. This can occur if the Mac user account on the virtual machine can't write to /Applications.\",\n                NSUnderlyingErrorKey: error\n            ])\n        }\n    }\n\n    var needsInstall: Bool {\n        guard !UserDefaults.standard.bool(forKey: \"DisableInstall\") else { return false }\n        return !isRunningFromApplicationsDirectory\n    }\n\n    private var isRunningFromApplicationsDirectory: Bool {\n        let directories = NSSearchPathForDirectoriesInDomains(.applicationDirectory, .allDomainsMask, true)\n        for directory in directories {\n            if Bundle.main.bundlePath.hasPrefix(directory) { return true }\n        }\n        return false\n    }\n\n    private var mountedImageAppURL: URL { URL(fileURLWithPath: \"/Volumes/Guest/VirtualBuddyGuest.app\") }\n\n    /// `true` if there's a VirtualBuddyGuest image mounted at `/Volumes/Guest`\n    /// for which the following conditions are true:\n    /// 1 - The guest in the volume has a different `VBGuestBuildID` from this process\n    /// 2 - The guest in the volume has a `CFBundleVersion` that's **greater than or equal to** the `CFBundleVersion` of this process\n    private var mountedGuestImageNeedsInstall: Bool {\n        guard FileManager.default.fileExists(atPath: \"/Volumes/Guest\") else { return false }\n\n        logger.debug(\"Guest volume is mounted, checking app version\")\n\n        let imageURL = mountedImageAppURL\n\n        guard imageURL.path != Bundle.main.bundleURL.path else {\n            logger.debug(\"We're the mounted image app, skipping update checks.\")\n            return false\n        }\n\n        guard let imageBundle = Bundle(url: imageURL) else {\n            logger.error(\"Couldn't get bundle at \\(imageURL.path, privacy: .public)\")\n            return false\n        }\n\n        guard let imageBuildID = imageBundle.vbGuestBuildID else {\n            logger.error(\"Couldn't find VBGuestBuildID in image bundle\")\n            return false\n        }\n\n        guard let imageBundleVersion = imageBundle.bundleVersion else {\n            logger.error(\"Couldn't find CFBundleVersion in image bundle\")\n            return false\n        }\n\n        guard let currentBuildID = Bundle.main.vbGuestBuildID else {\n            logger.error(\"Couldn't find VBGuestBuildID in current bundle\")\n            return false\n        }\n\n        guard let currentBundleVersion = Bundle.main.bundleVersion else {\n            logger.error(\"Couldn't find CFBundleVersion in current bundle\")\n            return false\n        }\n\n        guard imageBuildID != currentBuildID else {\n            logger.debug(\"Image build ID is same as current build ID (\\(currentBuildID, privacy: .public)), update won't be performed\")\n            return false\n        }\n\n        guard imageBundleVersion >= currentBundleVersion else {\n            logger.debug(\"Image build ID differs from current build ID, but image has a lower CFBundleVersion (\\(imageBundleVersion, privacy: .public)), ignoring\")\n            return false\n        }\n\n        logger.notice(\"Mounted image qualifies for update with CFBundleVersion \\(imageBundleVersion, privacy: .public), VBGuestBuildID \\(imageBuildID, privacy: .public)\")\n\n        return true\n    }\n\n}\n\nextension Bundle {\n    var bundleVersion: Int? {\n        guard let str = infoDictionary?[kCFBundleVersionKey as String] as? String else { return nil }\n        return Int(str)\n    }\n    var vbGuestBuildID: String? {\n        infoDictionary?[\"VBGuestBuildID\"] as? String\n    }\n}\n\nextension NSApplication {\n    // Credit: Andy Kim (PFMoveApplication)\n    func relaunch(at path: String) throws {\n        let pid = ProcessInfo.processInfo.processIdentifier\n\n        let xattrScript = \"/usr/bin/xattr -d -r com.apple.quarantine \\(path)\"\n        let script = \"(while /bin/kill -0 \\(pid) >&/dev/null; do /bin/sleep 0.1; done; \\(xattrScript); /usr/bin/open \\(path)) &\"\n\n        let proc = Process()\n        proc.executableURL = URL(fileURLWithPath: \"/bin/sh\")\n        proc.arguments = [\n            \"-c\",\n            script\n        ]\n\n        try proc.run()\n\n        exit(0)\n    }\n}\n"
  },
  {
    "path": "VirtualBuddyGuest/Main.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"21507\" targetRuntime=\"MacOSX.Cocoa\" propertyAccessControl=\"none\" useAutolayout=\"YES\">\n    <dependencies>\n        <deployment identifier=\"macosx\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.CocoaPlugin\" version=\"21507\"/>\n    </dependencies>\n    <scenes>\n        <!--Application-->\n        <scene sceneID=\"JPo-4y-FX3\">\n            <objects>\n                <application id=\"hnw-xV-0zn\" sceneMemberID=\"viewController\">\n                    <menu key=\"mainMenu\" title=\"Main Menu\" systemMenu=\"main\" id=\"AYu-sK-qS6\">\n                        <items>\n                            <menuItem title=\"VirtualBuddyGuest\" id=\"1Xt-HY-uBw\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"VirtualBuddyGuest\" systemMenu=\"apple\" id=\"uQy-DD-JDr\">\n                                    <items>\n                                        <menuItem title=\"About VirtualBuddyGuest\" id=\"5kV-Vb-QxS\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"orderFrontStandardAboutPanel:\" target=\"Ady-hI-5gd\" id=\"Exp-CZ-Vem\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"VOq-y0-SEH\"/>\n                                        <menuItem title=\"Preferences…\" keyEquivalent=\",\" id=\"BOF-NM-1cW\"/>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"wFC-TO-SCJ\"/>\n                                        <menuItem title=\"Services\" id=\"NMo-om-nkz\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <menu key=\"submenu\" title=\"Services\" systemMenu=\"services\" id=\"hz9-B4-Xy5\"/>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"4je-JR-u6R\"/>\n                                        <menuItem title=\"Hide VirtualBuddyGuest\" keyEquivalent=\"h\" id=\"Olw-nP-bQN\">\n                                            <connections>\n                                                <action selector=\"hide:\" target=\"Ady-hI-5gd\" id=\"PnN-Uc-m68\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Hide Others\" keyEquivalent=\"h\" id=\"Vdr-fp-XzO\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                            <connections>\n                                                <action selector=\"hideOtherApplications:\" target=\"Ady-hI-5gd\" id=\"VT4-aY-XCT\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Show All\" id=\"Kd2-mp-pUS\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"unhideAllApplications:\" target=\"Ady-hI-5gd\" id=\"Dhg-Le-xox\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"kCx-OE-vgT\"/>\n                                        <menuItem title=\"Quit VirtualBuddyGuest\" keyEquivalent=\"q\" id=\"4sb-4s-VLi\">\n                                            <connections>\n                                                <action selector=\"terminate:\" target=\"Ady-hI-5gd\" id=\"Te7-pn-YzF\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"File\" id=\"dMs-cI-mzQ\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"File\" id=\"bib-Uj-vzu\">\n                                    <items>\n                                        <menuItem title=\"New\" keyEquivalent=\"n\" id=\"Was-JA-tGl\">\n                                            <connections>\n                                                <action selector=\"newDocument:\" target=\"Ady-hI-5gd\" id=\"4Si-XN-c54\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Open…\" keyEquivalent=\"o\" id=\"IAo-SY-fd9\">\n                                            <connections>\n                                                <action selector=\"openDocument:\" target=\"Ady-hI-5gd\" id=\"bVn-NM-KNZ\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Open Recent\" id=\"tXI-mr-wws\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <menu key=\"submenu\" title=\"Open Recent\" systemMenu=\"recentDocuments\" id=\"oas-Oc-fiZ\">\n                                                <items>\n                                                    <menuItem title=\"Clear Menu\" id=\"vNY-rz-j42\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"clearRecentDocuments:\" target=\"Ady-hI-5gd\" id=\"Daa-9d-B3U\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                </items>\n                                            </menu>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"m54-Is-iLE\"/>\n                                        <menuItem title=\"Close\" keyEquivalent=\"w\" id=\"DVo-aG-piG\">\n                                            <connections>\n                                                <action selector=\"performClose:\" target=\"Ady-hI-5gd\" id=\"HmO-Ls-i7Q\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Save…\" keyEquivalent=\"s\" id=\"pxx-59-PXV\">\n                                            <connections>\n                                                <action selector=\"saveDocument:\" target=\"Ady-hI-5gd\" id=\"teZ-XB-qJY\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Save As…\" keyEquivalent=\"S\" id=\"Bw7-FT-i3A\">\n                                            <connections>\n                                                <action selector=\"saveDocumentAs:\" target=\"Ady-hI-5gd\" id=\"mDf-zr-I0C\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Revert to Saved\" keyEquivalent=\"r\" id=\"KaW-ft-85H\">\n                                            <connections>\n                                                <action selector=\"revertDocumentToSaved:\" target=\"Ady-hI-5gd\" id=\"iJ3-Pv-kwq\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"aJh-i4-bef\"/>\n                                        <menuItem title=\"Page Setup…\" keyEquivalent=\"P\" id=\"qIS-W8-SiK\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\" shift=\"YES\" command=\"YES\"/>\n                                            <connections>\n                                                <action selector=\"runPageLayout:\" target=\"Ady-hI-5gd\" id=\"Din-rz-gC5\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Print…\" keyEquivalent=\"p\" id=\"aTl-1u-JFS\">\n                                            <connections>\n                                                <action selector=\"print:\" target=\"Ady-hI-5gd\" id=\"qaZ-4w-aoO\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Edit\" id=\"5QF-Oa-p0T\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Edit\" id=\"W48-6f-4Dl\">\n                                    <items>\n                                        <menuItem title=\"Undo\" keyEquivalent=\"z\" id=\"dRJ-4n-Yzg\">\n                                            <connections>\n                                                <action selector=\"undo:\" target=\"Ady-hI-5gd\" id=\"M6e-cu-g7V\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Redo\" keyEquivalent=\"Z\" id=\"6dh-zS-Vam\">\n                                            <connections>\n                                                <action selector=\"redo:\" target=\"Ady-hI-5gd\" id=\"oIA-Rs-6OD\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"WRV-NI-Exz\"/>\n                                        <menuItem title=\"Cut\" keyEquivalent=\"x\" id=\"uRl-iY-unG\">\n                                            <connections>\n                                                <action selector=\"cut:\" target=\"Ady-hI-5gd\" id=\"YJe-68-I9s\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Copy\" keyEquivalent=\"c\" id=\"x3v-GG-iWU\">\n                                            <connections>\n                                                <action selector=\"copy:\" target=\"Ady-hI-5gd\" id=\"G1f-GL-Joy\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Paste\" keyEquivalent=\"v\" id=\"gVA-U4-sdL\">\n                                            <connections>\n                                                <action selector=\"paste:\" target=\"Ady-hI-5gd\" id=\"UvS-8e-Qdg\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Paste and Match Style\" keyEquivalent=\"V\" id=\"WeT-3V-zwk\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                            <connections>\n                                                <action selector=\"pasteAsPlainText:\" target=\"Ady-hI-5gd\" id=\"cEh-KX-wJQ\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Delete\" id=\"pa3-QI-u2k\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"delete:\" target=\"Ady-hI-5gd\" id=\"0Mk-Ml-PaM\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Select All\" keyEquivalent=\"a\" id=\"Ruw-6m-B2m\">\n                                            <connections>\n                                                <action selector=\"selectAll:\" target=\"Ady-hI-5gd\" id=\"VNm-Mi-diN\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"uyl-h8-XO2\"/>\n                                        <menuItem title=\"Find\" id=\"4EN-yA-p0u\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <menu key=\"submenu\" title=\"Find\" id=\"1b7-l0-nxx\">\n                                                <items>\n                                                    <menuItem title=\"Find…\" tag=\"1\" keyEquivalent=\"f\" id=\"Xz5-n4-O0W\">\n                                                        <connections>\n                                                            <action selector=\"performFindPanelAction:\" target=\"Ady-hI-5gd\" id=\"cD7-Qs-BN4\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Find and Replace…\" tag=\"12\" keyEquivalent=\"f\" id=\"YEy-JH-Tfz\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                                        <connections>\n                                                            <action selector=\"performFindPanelAction:\" target=\"Ady-hI-5gd\" id=\"WD3-Gg-5AJ\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Find Next\" tag=\"2\" keyEquivalent=\"g\" id=\"q09-fT-Sye\">\n                                                        <connections>\n                                                            <action selector=\"performFindPanelAction:\" target=\"Ady-hI-5gd\" id=\"NDo-RZ-v9R\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Find Previous\" tag=\"3\" keyEquivalent=\"G\" id=\"OwM-mh-QMV\">\n                                                        <connections>\n                                                            <action selector=\"performFindPanelAction:\" target=\"Ady-hI-5gd\" id=\"HOh-sY-3ay\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Use Selection for Find\" tag=\"7\" keyEquivalent=\"e\" id=\"buJ-ug-pKt\">\n                                                        <connections>\n                                                            <action selector=\"performFindPanelAction:\" target=\"Ady-hI-5gd\" id=\"U76-nv-p5D\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Jump to Selection\" keyEquivalent=\"j\" id=\"S0p-oC-mLd\">\n                                                        <connections>\n                                                            <action selector=\"centerSelectionInVisibleArea:\" target=\"Ady-hI-5gd\" id=\"IOG-6D-g5B\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                </items>\n                                            </menu>\n                                        </menuItem>\n                                        <menuItem title=\"Spelling and Grammar\" id=\"Dv1-io-Yv7\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <menu key=\"submenu\" title=\"Spelling\" id=\"3IN-sU-3Bg\">\n                                                <items>\n                                                    <menuItem title=\"Show Spelling and Grammar\" keyEquivalent=\":\" id=\"HFo-cy-zxI\">\n                                                        <connections>\n                                                            <action selector=\"showGuessPanel:\" target=\"Ady-hI-5gd\" id=\"vFj-Ks-hy3\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Check Document Now\" keyEquivalent=\";\" id=\"hz2-CU-CR7\">\n                                                        <connections>\n                                                            <action selector=\"checkSpelling:\" target=\"Ady-hI-5gd\" id=\"fz7-VC-reM\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem isSeparatorItem=\"YES\" id=\"bNw-od-mp5\"/>\n                                                    <menuItem title=\"Check Spelling While Typing\" id=\"rbD-Rh-wIN\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"toggleContinuousSpellChecking:\" target=\"Ady-hI-5gd\" id=\"7w6-Qz-0kB\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Check Grammar With Spelling\" id=\"mK6-2p-4JG\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"toggleGrammarChecking:\" target=\"Ady-hI-5gd\" id=\"muD-Qn-j4w\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Correct Spelling Automatically\" id=\"78Y-hA-62v\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"toggleAutomaticSpellingCorrection:\" target=\"Ady-hI-5gd\" id=\"2lM-Qi-WAP\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                </items>\n                                            </menu>\n                                        </menuItem>\n                                        <menuItem title=\"Substitutions\" id=\"9ic-FL-obx\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <menu key=\"submenu\" title=\"Substitutions\" id=\"FeM-D8-WVr\">\n                                                <items>\n                                                    <menuItem title=\"Show Substitutions\" id=\"z6F-FW-3nz\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"orderFrontSubstitutionsPanel:\" target=\"Ady-hI-5gd\" id=\"oku-mr-iSq\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem isSeparatorItem=\"YES\" id=\"gPx-C9-uUO\"/>\n                                                    <menuItem title=\"Smart Copy/Paste\" id=\"9yt-4B-nSM\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"toggleSmartInsertDelete:\" target=\"Ady-hI-5gd\" id=\"3IJ-Se-DZD\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Smart Quotes\" id=\"hQb-2v-fYv\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"toggleAutomaticQuoteSubstitution:\" target=\"Ady-hI-5gd\" id=\"ptq-xd-QOA\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Smart Dashes\" id=\"rgM-f4-ycn\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"toggleAutomaticDashSubstitution:\" target=\"Ady-hI-5gd\" id=\"oCt-pO-9gS\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Smart Links\" id=\"cwL-P1-jid\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"toggleAutomaticLinkDetection:\" target=\"Ady-hI-5gd\" id=\"Gip-E3-Fov\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Data Detectors\" id=\"tRr-pd-1PS\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"toggleAutomaticDataDetection:\" target=\"Ady-hI-5gd\" id=\"R1I-Nq-Kbl\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Text Replacement\" id=\"HFQ-gK-NFA\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"toggleAutomaticTextReplacement:\" target=\"Ady-hI-5gd\" id=\"DvP-Fe-Py6\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                </items>\n                                            </menu>\n                                        </menuItem>\n                                        <menuItem title=\"Transformations\" id=\"2oI-Rn-ZJC\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <menu key=\"submenu\" title=\"Transformations\" id=\"c8a-y6-VQd\">\n                                                <items>\n                                                    <menuItem title=\"Make Upper Case\" id=\"vmV-6d-7jI\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"uppercaseWord:\" target=\"Ady-hI-5gd\" id=\"sPh-Tk-edu\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Make Lower Case\" id=\"d9M-CD-aMd\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"lowercaseWord:\" target=\"Ady-hI-5gd\" id=\"iUZ-b5-hil\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Capitalize\" id=\"UEZ-Bs-lqG\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"capitalizeWord:\" target=\"Ady-hI-5gd\" id=\"26H-TL-nsh\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                </items>\n                                            </menu>\n                                        </menuItem>\n                                        <menuItem title=\"Speech\" id=\"xrE-MZ-jX0\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <menu key=\"submenu\" title=\"Speech\" id=\"3rS-ZA-NoH\">\n                                                <items>\n                                                    <menuItem title=\"Start Speaking\" id=\"Ynk-f8-cLZ\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"startSpeaking:\" target=\"Ady-hI-5gd\" id=\"654-Ng-kyl\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Stop Speaking\" id=\"Oyz-dy-DGm\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"stopSpeaking:\" target=\"Ady-hI-5gd\" id=\"dX8-6p-jy9\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                </items>\n                                            </menu>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Format\" id=\"jxT-CU-nIS\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Format\" id=\"GEO-Iw-cKr\">\n                                    <items>\n                                        <menuItem title=\"Font\" id=\"Gi5-1S-RQB\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <menu key=\"submenu\" title=\"Font\" systemMenu=\"font\" id=\"aXa-aM-Jaq\">\n                                                <items>\n                                                    <menuItem title=\"Show Fonts\" keyEquivalent=\"t\" id=\"Q5e-8K-NDq\">\n                                                        <connections>\n                                                            <action selector=\"orderFrontFontPanel:\" target=\"YLy-65-1bz\" id=\"WHr-nq-2xA\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Bold\" tag=\"2\" keyEquivalent=\"b\" id=\"GB9-OM-e27\">\n                                                        <connections>\n                                                            <action selector=\"addFontTrait:\" target=\"YLy-65-1bz\" id=\"hqk-hr-sYV\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Italic\" tag=\"1\" keyEquivalent=\"i\" id=\"Vjx-xi-njq\">\n                                                        <connections>\n                                                            <action selector=\"addFontTrait:\" target=\"YLy-65-1bz\" id=\"IHV-OB-c03\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Underline\" keyEquivalent=\"u\" id=\"WRG-CD-K1S\">\n                                                        <connections>\n                                                            <action selector=\"underline:\" target=\"Ady-hI-5gd\" id=\"FYS-2b-JAY\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem isSeparatorItem=\"YES\" id=\"5gT-KC-WSO\"/>\n                                                    <menuItem title=\"Bigger\" tag=\"3\" keyEquivalent=\"+\" id=\"Ptp-SP-VEL\">\n                                                        <connections>\n                                                            <action selector=\"modifyFont:\" target=\"YLy-65-1bz\" id=\"Uc7-di-UnL\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Smaller\" tag=\"4\" keyEquivalent=\"-\" id=\"i1d-Er-qST\">\n                                                        <connections>\n                                                            <action selector=\"modifyFont:\" target=\"YLy-65-1bz\" id=\"HcX-Lf-eNd\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem isSeparatorItem=\"YES\" id=\"kx3-Dk-x3B\"/>\n                                                    <menuItem title=\"Kern\" id=\"jBQ-r6-VK2\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <menu key=\"submenu\" title=\"Kern\" id=\"tlD-Oa-oAM\">\n                                                            <items>\n                                                                <menuItem title=\"Use Default\" id=\"GUa-eO-cwY\">\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"useStandardKerning:\" target=\"Ady-hI-5gd\" id=\"6dk-9l-Ckg\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                                <menuItem title=\"Use None\" id=\"cDB-IK-hbR\">\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"turnOffKerning:\" target=\"Ady-hI-5gd\" id=\"U8a-gz-Maa\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                                <menuItem title=\"Tighten\" id=\"46P-cB-AYj\">\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"tightenKerning:\" target=\"Ady-hI-5gd\" id=\"hr7-Nz-8ro\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                                <menuItem title=\"Loosen\" id=\"ogc-rX-tC1\">\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"loosenKerning:\" target=\"Ady-hI-5gd\" id=\"8i4-f9-FKE\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                            </items>\n                                                        </menu>\n                                                    </menuItem>\n                                                    <menuItem title=\"Ligatures\" id=\"o6e-r0-MWq\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <menu key=\"submenu\" title=\"Ligatures\" id=\"w0m-vy-SC9\">\n                                                            <items>\n                                                                <menuItem title=\"Use Default\" id=\"agt-UL-0e3\">\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"useStandardLigatures:\" target=\"Ady-hI-5gd\" id=\"7uR-wd-Dx6\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                                <menuItem title=\"Use None\" id=\"J7y-lM-qPV\">\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"turnOffLigatures:\" target=\"Ady-hI-5gd\" id=\"iX2-gA-Ilz\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                                <menuItem title=\"Use All\" id=\"xQD-1f-W4t\">\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"useAllLigatures:\" target=\"Ady-hI-5gd\" id=\"KcB-kA-TuK\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                            </items>\n                                                        </menu>\n                                                    </menuItem>\n                                                    <menuItem title=\"Baseline\" id=\"OaQ-X3-Vso\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <menu key=\"submenu\" title=\"Baseline\" id=\"ijk-EB-dga\">\n                                                            <items>\n                                                                <menuItem title=\"Use Default\" id=\"3Om-Ey-2VK\">\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"unscript:\" target=\"Ady-hI-5gd\" id=\"0vZ-95-Ywn\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                                <menuItem title=\"Superscript\" id=\"Rqc-34-cIF\">\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"superscript:\" target=\"Ady-hI-5gd\" id=\"3qV-fo-wpU\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                                <menuItem title=\"Subscript\" id=\"I0S-gh-46l\">\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"subscript:\" target=\"Ady-hI-5gd\" id=\"Q6W-4W-IGz\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                                <menuItem title=\"Raise\" id=\"2h7-ER-AoG\">\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"raiseBaseline:\" target=\"Ady-hI-5gd\" id=\"4sk-31-7Q9\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                                <menuItem title=\"Lower\" id=\"1tx-W0-xDw\">\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"lowerBaseline:\" target=\"Ady-hI-5gd\" id=\"OF1-bc-KW4\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                            </items>\n                                                        </menu>\n                                                    </menuItem>\n                                                    <menuItem isSeparatorItem=\"YES\" id=\"Ndw-q3-faq\"/>\n                                                    <menuItem title=\"Show Colors\" keyEquivalent=\"C\" id=\"bgn-CT-cEk\">\n                                                        <connections>\n                                                            <action selector=\"orderFrontColorPanel:\" target=\"Ady-hI-5gd\" id=\"mSX-Xz-DV3\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem isSeparatorItem=\"YES\" id=\"iMs-zA-UFJ\"/>\n                                                    <menuItem title=\"Copy Style\" keyEquivalent=\"c\" id=\"5Vv-lz-BsD\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                                        <connections>\n                                                            <action selector=\"copyFont:\" target=\"Ady-hI-5gd\" id=\"GJO-xA-L4q\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Paste Style\" keyEquivalent=\"v\" id=\"vKC-jM-MkH\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                                        <connections>\n                                                            <action selector=\"pasteFont:\" target=\"Ady-hI-5gd\" id=\"JfD-CL-leO\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                </items>\n                                            </menu>\n                                        </menuItem>\n                                        <menuItem title=\"Text\" id=\"Fal-I4-PZk\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <menu key=\"submenu\" title=\"Text\" id=\"d9c-me-L2H\">\n                                                <items>\n                                                    <menuItem title=\"Align Left\" keyEquivalent=\"{\" id=\"ZM1-6Q-yy1\">\n                                                        <connections>\n                                                            <action selector=\"alignLeft:\" target=\"Ady-hI-5gd\" id=\"zUv-R1-uAa\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Center\" keyEquivalent=\"|\" id=\"VIY-Ag-zcb\">\n                                                        <connections>\n                                                            <action selector=\"alignCenter:\" target=\"Ady-hI-5gd\" id=\"spX-mk-kcS\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Justify\" id=\"J5U-5w-g23\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"alignJustified:\" target=\"Ady-hI-5gd\" id=\"ljL-7U-jND\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Align Right\" keyEquivalent=\"}\" id=\"wb2-vD-lq4\">\n                                                        <connections>\n                                                            <action selector=\"alignRight:\" target=\"Ady-hI-5gd\" id=\"r48-bG-YeY\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem isSeparatorItem=\"YES\" id=\"4s2-GY-VfK\"/>\n                                                    <menuItem title=\"Writing Direction\" id=\"H1b-Si-o9J\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <menu key=\"submenu\" title=\"Writing Direction\" id=\"8mr-sm-Yjd\">\n                                                            <items>\n                                                                <menuItem title=\"Paragraph\" enabled=\"NO\" id=\"ZvO-Gk-QUH\">\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                </menuItem>\n                                                                <menuItem id=\"YGs-j5-SAR\">\n                                                                    <string key=\"title\">\tDefault</string>\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"makeBaseWritingDirectionNatural:\" target=\"Ady-hI-5gd\" id=\"qtV-5e-UBP\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                                <menuItem id=\"Lbh-J2-qVU\">\n                                                                    <string key=\"title\">\tLeft to Right</string>\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"makeBaseWritingDirectionLeftToRight:\" target=\"Ady-hI-5gd\" id=\"S0X-9S-QSf\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                                <menuItem id=\"jFq-tB-4Kx\">\n                                                                    <string key=\"title\">\tRight to Left</string>\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"makeBaseWritingDirectionRightToLeft:\" target=\"Ady-hI-5gd\" id=\"5fk-qB-AqJ\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                                <menuItem isSeparatorItem=\"YES\" id=\"swp-gr-a21\"/>\n                                                                <menuItem title=\"Selection\" enabled=\"NO\" id=\"cqv-fj-IhA\">\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                </menuItem>\n                                                                <menuItem id=\"Nop-cj-93Q\">\n                                                                    <string key=\"title\">\tDefault</string>\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"makeTextWritingDirectionNatural:\" target=\"Ady-hI-5gd\" id=\"lPI-Se-ZHp\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                                <menuItem id=\"BgM-ve-c93\">\n                                                                    <string key=\"title\">\tLeft to Right</string>\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"makeTextWritingDirectionLeftToRight:\" target=\"Ady-hI-5gd\" id=\"caW-Bv-w94\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                                <menuItem id=\"RB4-Sm-HuC\">\n                                                                    <string key=\"title\">\tRight to Left</string>\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"makeTextWritingDirectionRightToLeft:\" target=\"Ady-hI-5gd\" id=\"EXD-6r-ZUu\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                            </items>\n                                                        </menu>\n                                                    </menuItem>\n                                                    <menuItem isSeparatorItem=\"YES\" id=\"fKy-g9-1gm\"/>\n                                                    <menuItem title=\"Show Ruler\" id=\"vLm-3I-IUL\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"toggleRuler:\" target=\"Ady-hI-5gd\" id=\"FOx-HJ-KwY\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Copy Ruler\" keyEquivalent=\"c\" id=\"MkV-Pr-PK5\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\" control=\"YES\" command=\"YES\"/>\n                                                        <connections>\n                                                            <action selector=\"copyRuler:\" target=\"Ady-hI-5gd\" id=\"71i-fW-3W2\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Paste Ruler\" keyEquivalent=\"v\" id=\"LVM-kO-fVI\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\" control=\"YES\" command=\"YES\"/>\n                                                        <connections>\n                                                            <action selector=\"pasteRuler:\" target=\"Ady-hI-5gd\" id=\"cSh-wd-qM2\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                </items>\n                                            </menu>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"View\" id=\"H8h-7b-M4v\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"View\" id=\"HyV-fh-RgO\">\n                                    <items>\n                                        <menuItem title=\"Show Toolbar\" keyEquivalent=\"t\" id=\"snW-S8-Cw5\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                            <connections>\n                                                <action selector=\"toggleToolbarShown:\" target=\"Ady-hI-5gd\" id=\"BXY-wc-z0C\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Customize Toolbar…\" id=\"1UK-8n-QPP\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"runToolbarCustomizationPalette:\" target=\"Ady-hI-5gd\" id=\"pQI-g3-MTW\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"hB3-LF-h0Y\"/>\n                                        <menuItem title=\"Show Sidebar\" keyEquivalent=\"s\" id=\"kIP-vf-haE\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\" control=\"YES\" command=\"YES\"/>\n                                            <connections>\n                                                <action selector=\"toggleSidebar:\" target=\"Ady-hI-5gd\" id=\"iwa-gc-5KM\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Enter Full Screen\" keyEquivalent=\"f\" id=\"4J7-dP-txa\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\" control=\"YES\" command=\"YES\"/>\n                                            <connections>\n                                                <action selector=\"toggleFullScreen:\" target=\"Ady-hI-5gd\" id=\"dU3-MA-1Rq\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Window\" id=\"aUF-d1-5bR\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Window\" systemMenu=\"window\" id=\"Td7-aD-5lo\">\n                                    <items>\n                                        <menuItem title=\"Minimize\" keyEquivalent=\"m\" id=\"OY7-WF-poV\">\n                                            <connections>\n                                                <action selector=\"performMiniaturize:\" target=\"Ady-hI-5gd\" id=\"VwT-WD-YPe\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Zoom\" id=\"R4o-n2-Eq4\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"performZoom:\" target=\"Ady-hI-5gd\" id=\"DIl-cC-cCs\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"eu3-7i-yIM\"/>\n                                        <menuItem title=\"Bring All to Front\" id=\"LE2-aR-0XJ\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"arrangeInFront:\" target=\"Ady-hI-5gd\" id=\"DRN-fu-gQh\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Help\" id=\"wpr-3q-Mcd\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Help\" systemMenu=\"help\" id=\"F2S-fz-NVQ\">\n                                    <items>\n                                        <menuItem title=\"VirtualBuddyGuest Help\" keyEquivalent=\"?\" id=\"FKE-Sm-Kum\">\n                                            <connections>\n                                                <action selector=\"showHelp:\" target=\"Ady-hI-5gd\" id=\"y7X-2Q-9no\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                        </items>\n                    </menu>\n                    <connections>\n                        <outlet property=\"delegate\" destination=\"Voe-Tx-rLC\" id=\"PrD-fu-P6m\"/>\n                    </connections>\n                </application>\n                <customObject id=\"Voe-Tx-rLC\" customClass=\"GuestAppDelegate\" customModule=\"VirtualBuddyGuest\" customModuleProvider=\"target\"/>\n                <customObject id=\"YLy-65-1bz\" customClass=\"NSFontManager\"/>\n                <customObject id=\"Ady-hI-5gd\" userLabel=\"First Responder\" customClass=\"NSResponder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"75\" y=\"0.0\"/>\n        </scene>\n    </scenes>\n</document>\n"
  },
  {
    "path": "VirtualBuddyGuest/Preview Content/Preview Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "VirtualBuddyGuest/VirtualBuddyGuest.entitlements",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict/>\n</plist>\n"
  },
  {
    "path": "VirtualBuddyGuestHelper/Assets.xcassets/AccentColor.colorset/Contents.json",
    "content": "{\n  \"colors\" : [\n    {\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "VirtualBuddyGuestHelper/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"16x16\"\n    },\n    {\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"16x16\"\n    },\n    {\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"32x32\"\n    },\n    {\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"32x32\"\n    },\n    {\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"128x128\"\n    },\n    {\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"128x128\"\n    },\n    {\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"256x256\"\n    },\n    {\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"256x256\"\n    },\n    {\n      \"idiom\" : \"mac\",\n      \"scale\" : \"1x\",\n      \"size\" : \"512x512\"\n    },\n    {\n      \"idiom\" : \"mac\",\n      \"scale\" : \"2x\",\n      \"size\" : \"512x512\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "VirtualBuddyGuestHelper/Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "VirtualBuddyGuestHelper/Base.lproj/Main.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"21507\" targetRuntime=\"MacOSX.Cocoa\" propertyAccessControl=\"none\" useAutolayout=\"YES\">\n    <dependencies>\n        <deployment identifier=\"macosx\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.CocoaPlugin\" version=\"21507\"/>\n    </dependencies>\n    <scenes>\n        <!--Application-->\n        <scene sceneID=\"JPo-4y-FX3\">\n            <objects>\n                <application id=\"hnw-xV-0zn\" sceneMemberID=\"viewController\">\n                    <menu key=\"mainMenu\" title=\"Main Menu\" systemMenu=\"main\" id=\"AYu-sK-qS6\">\n                        <items>\n                            <menuItem title=\"VirtualBuddyGuestHelper\" id=\"1Xt-HY-uBw\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"VirtualBuddyGuestHelper\" systemMenu=\"apple\" id=\"uQy-DD-JDr\">\n                                    <items>\n                                        <menuItem title=\"About VirtualBuddyGuestHelper\" id=\"5kV-Vb-QxS\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"orderFrontStandardAboutPanel:\" target=\"Ady-hI-5gd\" id=\"Exp-CZ-Vem\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"VOq-y0-SEH\"/>\n                                        <menuItem title=\"Preferences…\" keyEquivalent=\",\" id=\"BOF-NM-1cW\"/>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"wFC-TO-SCJ\"/>\n                                        <menuItem title=\"Services\" id=\"NMo-om-nkz\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <menu key=\"submenu\" title=\"Services\" systemMenu=\"services\" id=\"hz9-B4-Xy5\"/>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"4je-JR-u6R\"/>\n                                        <menuItem title=\"Hide VirtualBuddyGuestHelper\" keyEquivalent=\"h\" id=\"Olw-nP-bQN\">\n                                            <connections>\n                                                <action selector=\"hide:\" target=\"Ady-hI-5gd\" id=\"PnN-Uc-m68\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Hide Others\" keyEquivalent=\"h\" id=\"Vdr-fp-XzO\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                            <connections>\n                                                <action selector=\"hideOtherApplications:\" target=\"Ady-hI-5gd\" id=\"VT4-aY-XCT\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Show All\" id=\"Kd2-mp-pUS\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"unhideAllApplications:\" target=\"Ady-hI-5gd\" id=\"Dhg-Le-xox\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"kCx-OE-vgT\"/>\n                                        <menuItem title=\"Quit VirtualBuddyGuestHelper\" keyEquivalent=\"q\" id=\"4sb-4s-VLi\">\n                                            <connections>\n                                                <action selector=\"terminate:\" target=\"Ady-hI-5gd\" id=\"Te7-pn-YzF\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"File\" id=\"dMs-cI-mzQ\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"File\" id=\"bib-Uj-vzu\">\n                                    <items>\n                                        <menuItem title=\"New\" keyEquivalent=\"n\" id=\"Was-JA-tGl\">\n                                            <connections>\n                                                <action selector=\"newDocument:\" target=\"Ady-hI-5gd\" id=\"4Si-XN-c54\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Open…\" keyEquivalent=\"o\" id=\"IAo-SY-fd9\">\n                                            <connections>\n                                                <action selector=\"openDocument:\" target=\"Ady-hI-5gd\" id=\"bVn-NM-KNZ\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Open Recent\" id=\"tXI-mr-wws\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <menu key=\"submenu\" title=\"Open Recent\" systemMenu=\"recentDocuments\" id=\"oas-Oc-fiZ\">\n                                                <items>\n                                                    <menuItem title=\"Clear Menu\" id=\"vNY-rz-j42\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"clearRecentDocuments:\" target=\"Ady-hI-5gd\" id=\"Daa-9d-B3U\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                </items>\n                                            </menu>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"m54-Is-iLE\"/>\n                                        <menuItem title=\"Close\" keyEquivalent=\"w\" id=\"DVo-aG-piG\">\n                                            <connections>\n                                                <action selector=\"performClose:\" target=\"Ady-hI-5gd\" id=\"HmO-Ls-i7Q\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Save…\" keyEquivalent=\"s\" id=\"pxx-59-PXV\">\n                                            <connections>\n                                                <action selector=\"saveDocument:\" target=\"Ady-hI-5gd\" id=\"teZ-XB-qJY\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Save As…\" keyEquivalent=\"S\" id=\"Bw7-FT-i3A\">\n                                            <connections>\n                                                <action selector=\"saveDocumentAs:\" target=\"Ady-hI-5gd\" id=\"mDf-zr-I0C\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Revert to Saved\" keyEquivalent=\"r\" id=\"KaW-ft-85H\">\n                                            <connections>\n                                                <action selector=\"revertDocumentToSaved:\" target=\"Ady-hI-5gd\" id=\"iJ3-Pv-kwq\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"aJh-i4-bef\"/>\n                                        <menuItem title=\"Page Setup…\" keyEquivalent=\"P\" id=\"qIS-W8-SiK\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\" shift=\"YES\" command=\"YES\"/>\n                                            <connections>\n                                                <action selector=\"runPageLayout:\" target=\"Ady-hI-5gd\" id=\"Din-rz-gC5\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Print…\" keyEquivalent=\"p\" id=\"aTl-1u-JFS\">\n                                            <connections>\n                                                <action selector=\"print:\" target=\"Ady-hI-5gd\" id=\"qaZ-4w-aoO\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Edit\" id=\"5QF-Oa-p0T\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Edit\" id=\"W48-6f-4Dl\">\n                                    <items>\n                                        <menuItem title=\"Undo\" keyEquivalent=\"z\" id=\"dRJ-4n-Yzg\">\n                                            <connections>\n                                                <action selector=\"undo:\" target=\"Ady-hI-5gd\" id=\"M6e-cu-g7V\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Redo\" keyEquivalent=\"Z\" id=\"6dh-zS-Vam\">\n                                            <connections>\n                                                <action selector=\"redo:\" target=\"Ady-hI-5gd\" id=\"oIA-Rs-6OD\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"WRV-NI-Exz\"/>\n                                        <menuItem title=\"Cut\" keyEquivalent=\"x\" id=\"uRl-iY-unG\">\n                                            <connections>\n                                                <action selector=\"cut:\" target=\"Ady-hI-5gd\" id=\"YJe-68-I9s\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Copy\" keyEquivalent=\"c\" id=\"x3v-GG-iWU\">\n                                            <connections>\n                                                <action selector=\"copy:\" target=\"Ady-hI-5gd\" id=\"G1f-GL-Joy\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Paste\" keyEquivalent=\"v\" id=\"gVA-U4-sdL\">\n                                            <connections>\n                                                <action selector=\"paste:\" target=\"Ady-hI-5gd\" id=\"UvS-8e-Qdg\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Paste and Match Style\" keyEquivalent=\"V\" id=\"WeT-3V-zwk\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                            <connections>\n                                                <action selector=\"pasteAsPlainText:\" target=\"Ady-hI-5gd\" id=\"cEh-KX-wJQ\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Delete\" id=\"pa3-QI-u2k\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"delete:\" target=\"Ady-hI-5gd\" id=\"0Mk-Ml-PaM\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Select All\" keyEquivalent=\"a\" id=\"Ruw-6m-B2m\">\n                                            <connections>\n                                                <action selector=\"selectAll:\" target=\"Ady-hI-5gd\" id=\"VNm-Mi-diN\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"uyl-h8-XO2\"/>\n                                        <menuItem title=\"Find\" id=\"4EN-yA-p0u\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <menu key=\"submenu\" title=\"Find\" id=\"1b7-l0-nxx\">\n                                                <items>\n                                                    <menuItem title=\"Find…\" tag=\"1\" keyEquivalent=\"f\" id=\"Xz5-n4-O0W\">\n                                                        <connections>\n                                                            <action selector=\"performFindPanelAction:\" target=\"Ady-hI-5gd\" id=\"cD7-Qs-BN4\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Find and Replace…\" tag=\"12\" keyEquivalent=\"f\" id=\"YEy-JH-Tfz\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                                        <connections>\n                                                            <action selector=\"performFindPanelAction:\" target=\"Ady-hI-5gd\" id=\"WD3-Gg-5AJ\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Find Next\" tag=\"2\" keyEquivalent=\"g\" id=\"q09-fT-Sye\">\n                                                        <connections>\n                                                            <action selector=\"performFindPanelAction:\" target=\"Ady-hI-5gd\" id=\"NDo-RZ-v9R\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Find Previous\" tag=\"3\" keyEquivalent=\"G\" id=\"OwM-mh-QMV\">\n                                                        <connections>\n                                                            <action selector=\"performFindPanelAction:\" target=\"Ady-hI-5gd\" id=\"HOh-sY-3ay\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Use Selection for Find\" tag=\"7\" keyEquivalent=\"e\" id=\"buJ-ug-pKt\">\n                                                        <connections>\n                                                            <action selector=\"performFindPanelAction:\" target=\"Ady-hI-5gd\" id=\"U76-nv-p5D\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Jump to Selection\" keyEquivalent=\"j\" id=\"S0p-oC-mLd\">\n                                                        <connections>\n                                                            <action selector=\"centerSelectionInVisibleArea:\" target=\"Ady-hI-5gd\" id=\"IOG-6D-g5B\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                </items>\n                                            </menu>\n                                        </menuItem>\n                                        <menuItem title=\"Spelling and Grammar\" id=\"Dv1-io-Yv7\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <menu key=\"submenu\" title=\"Spelling\" id=\"3IN-sU-3Bg\">\n                                                <items>\n                                                    <menuItem title=\"Show Spelling and Grammar\" keyEquivalent=\":\" id=\"HFo-cy-zxI\">\n                                                        <connections>\n                                                            <action selector=\"showGuessPanel:\" target=\"Ady-hI-5gd\" id=\"vFj-Ks-hy3\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Check Document Now\" keyEquivalent=\";\" id=\"hz2-CU-CR7\">\n                                                        <connections>\n                                                            <action selector=\"checkSpelling:\" target=\"Ady-hI-5gd\" id=\"fz7-VC-reM\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem isSeparatorItem=\"YES\" id=\"bNw-od-mp5\"/>\n                                                    <menuItem title=\"Check Spelling While Typing\" id=\"rbD-Rh-wIN\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"toggleContinuousSpellChecking:\" target=\"Ady-hI-5gd\" id=\"7w6-Qz-0kB\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Check Grammar With Spelling\" id=\"mK6-2p-4JG\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"toggleGrammarChecking:\" target=\"Ady-hI-5gd\" id=\"muD-Qn-j4w\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Correct Spelling Automatically\" id=\"78Y-hA-62v\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"toggleAutomaticSpellingCorrection:\" target=\"Ady-hI-5gd\" id=\"2lM-Qi-WAP\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                </items>\n                                            </menu>\n                                        </menuItem>\n                                        <menuItem title=\"Substitutions\" id=\"9ic-FL-obx\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <menu key=\"submenu\" title=\"Substitutions\" id=\"FeM-D8-WVr\">\n                                                <items>\n                                                    <menuItem title=\"Show Substitutions\" id=\"z6F-FW-3nz\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"orderFrontSubstitutionsPanel:\" target=\"Ady-hI-5gd\" id=\"oku-mr-iSq\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem isSeparatorItem=\"YES\" id=\"gPx-C9-uUO\"/>\n                                                    <menuItem title=\"Smart Copy/Paste\" id=\"9yt-4B-nSM\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"toggleSmartInsertDelete:\" target=\"Ady-hI-5gd\" id=\"3IJ-Se-DZD\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Smart Quotes\" id=\"hQb-2v-fYv\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"toggleAutomaticQuoteSubstitution:\" target=\"Ady-hI-5gd\" id=\"ptq-xd-QOA\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Smart Dashes\" id=\"rgM-f4-ycn\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"toggleAutomaticDashSubstitution:\" target=\"Ady-hI-5gd\" id=\"oCt-pO-9gS\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Smart Links\" id=\"cwL-P1-jid\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"toggleAutomaticLinkDetection:\" target=\"Ady-hI-5gd\" id=\"Gip-E3-Fov\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Data Detectors\" id=\"tRr-pd-1PS\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"toggleAutomaticDataDetection:\" target=\"Ady-hI-5gd\" id=\"R1I-Nq-Kbl\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Text Replacement\" id=\"HFQ-gK-NFA\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"toggleAutomaticTextReplacement:\" target=\"Ady-hI-5gd\" id=\"DvP-Fe-Py6\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                </items>\n                                            </menu>\n                                        </menuItem>\n                                        <menuItem title=\"Transformations\" id=\"2oI-Rn-ZJC\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <menu key=\"submenu\" title=\"Transformations\" id=\"c8a-y6-VQd\">\n                                                <items>\n                                                    <menuItem title=\"Make Upper Case\" id=\"vmV-6d-7jI\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"uppercaseWord:\" target=\"Ady-hI-5gd\" id=\"sPh-Tk-edu\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Make Lower Case\" id=\"d9M-CD-aMd\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"lowercaseWord:\" target=\"Ady-hI-5gd\" id=\"iUZ-b5-hil\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Capitalize\" id=\"UEZ-Bs-lqG\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"capitalizeWord:\" target=\"Ady-hI-5gd\" id=\"26H-TL-nsh\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                </items>\n                                            </menu>\n                                        </menuItem>\n                                        <menuItem title=\"Speech\" id=\"xrE-MZ-jX0\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <menu key=\"submenu\" title=\"Speech\" id=\"3rS-ZA-NoH\">\n                                                <items>\n                                                    <menuItem title=\"Start Speaking\" id=\"Ynk-f8-cLZ\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"startSpeaking:\" target=\"Ady-hI-5gd\" id=\"654-Ng-kyl\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Stop Speaking\" id=\"Oyz-dy-DGm\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"stopSpeaking:\" target=\"Ady-hI-5gd\" id=\"dX8-6p-jy9\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                </items>\n                                            </menu>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Format\" id=\"jxT-CU-nIS\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Format\" id=\"GEO-Iw-cKr\">\n                                    <items>\n                                        <menuItem title=\"Font\" id=\"Gi5-1S-RQB\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <menu key=\"submenu\" title=\"Font\" systemMenu=\"font\" id=\"aXa-aM-Jaq\">\n                                                <items>\n                                                    <menuItem title=\"Show Fonts\" keyEquivalent=\"t\" id=\"Q5e-8K-NDq\">\n                                                        <connections>\n                                                            <action selector=\"orderFrontFontPanel:\" target=\"YLy-65-1bz\" id=\"WHr-nq-2xA\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Bold\" tag=\"2\" keyEquivalent=\"b\" id=\"GB9-OM-e27\">\n                                                        <connections>\n                                                            <action selector=\"addFontTrait:\" target=\"YLy-65-1bz\" id=\"hqk-hr-sYV\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Italic\" tag=\"1\" keyEquivalent=\"i\" id=\"Vjx-xi-njq\">\n                                                        <connections>\n                                                            <action selector=\"addFontTrait:\" target=\"YLy-65-1bz\" id=\"IHV-OB-c03\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Underline\" keyEquivalent=\"u\" id=\"WRG-CD-K1S\">\n                                                        <connections>\n                                                            <action selector=\"underline:\" target=\"Ady-hI-5gd\" id=\"FYS-2b-JAY\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem isSeparatorItem=\"YES\" id=\"5gT-KC-WSO\"/>\n                                                    <menuItem title=\"Bigger\" tag=\"3\" keyEquivalent=\"+\" id=\"Ptp-SP-VEL\">\n                                                        <connections>\n                                                            <action selector=\"modifyFont:\" target=\"YLy-65-1bz\" id=\"Uc7-di-UnL\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Smaller\" tag=\"4\" keyEquivalent=\"-\" id=\"i1d-Er-qST\">\n                                                        <connections>\n                                                            <action selector=\"modifyFont:\" target=\"YLy-65-1bz\" id=\"HcX-Lf-eNd\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem isSeparatorItem=\"YES\" id=\"kx3-Dk-x3B\"/>\n                                                    <menuItem title=\"Kern\" id=\"jBQ-r6-VK2\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <menu key=\"submenu\" title=\"Kern\" id=\"tlD-Oa-oAM\">\n                                                            <items>\n                                                                <menuItem title=\"Use Default\" id=\"GUa-eO-cwY\">\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"useStandardKerning:\" target=\"Ady-hI-5gd\" id=\"6dk-9l-Ckg\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                                <menuItem title=\"Use None\" id=\"cDB-IK-hbR\">\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"turnOffKerning:\" target=\"Ady-hI-5gd\" id=\"U8a-gz-Maa\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                                <menuItem title=\"Tighten\" id=\"46P-cB-AYj\">\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"tightenKerning:\" target=\"Ady-hI-5gd\" id=\"hr7-Nz-8ro\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                                <menuItem title=\"Loosen\" id=\"ogc-rX-tC1\">\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"loosenKerning:\" target=\"Ady-hI-5gd\" id=\"8i4-f9-FKE\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                            </items>\n                                                        </menu>\n                                                    </menuItem>\n                                                    <menuItem title=\"Ligatures\" id=\"o6e-r0-MWq\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <menu key=\"submenu\" title=\"Ligatures\" id=\"w0m-vy-SC9\">\n                                                            <items>\n                                                                <menuItem title=\"Use Default\" id=\"agt-UL-0e3\">\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"useStandardLigatures:\" target=\"Ady-hI-5gd\" id=\"7uR-wd-Dx6\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                                <menuItem title=\"Use None\" id=\"J7y-lM-qPV\">\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"turnOffLigatures:\" target=\"Ady-hI-5gd\" id=\"iX2-gA-Ilz\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                                <menuItem title=\"Use All\" id=\"xQD-1f-W4t\">\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"useAllLigatures:\" target=\"Ady-hI-5gd\" id=\"KcB-kA-TuK\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                            </items>\n                                                        </menu>\n                                                    </menuItem>\n                                                    <menuItem title=\"Baseline\" id=\"OaQ-X3-Vso\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <menu key=\"submenu\" title=\"Baseline\" id=\"ijk-EB-dga\">\n                                                            <items>\n                                                                <menuItem title=\"Use Default\" id=\"3Om-Ey-2VK\">\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"unscript:\" target=\"Ady-hI-5gd\" id=\"0vZ-95-Ywn\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                                <menuItem title=\"Superscript\" id=\"Rqc-34-cIF\">\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"superscript:\" target=\"Ady-hI-5gd\" id=\"3qV-fo-wpU\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                                <menuItem title=\"Subscript\" id=\"I0S-gh-46l\">\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"subscript:\" target=\"Ady-hI-5gd\" id=\"Q6W-4W-IGz\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                                <menuItem title=\"Raise\" id=\"2h7-ER-AoG\">\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"raiseBaseline:\" target=\"Ady-hI-5gd\" id=\"4sk-31-7Q9\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                                <menuItem title=\"Lower\" id=\"1tx-W0-xDw\">\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"lowerBaseline:\" target=\"Ady-hI-5gd\" id=\"OF1-bc-KW4\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                            </items>\n                                                        </menu>\n                                                    </menuItem>\n                                                    <menuItem isSeparatorItem=\"YES\" id=\"Ndw-q3-faq\"/>\n                                                    <menuItem title=\"Show Colors\" keyEquivalent=\"C\" id=\"bgn-CT-cEk\">\n                                                        <connections>\n                                                            <action selector=\"orderFrontColorPanel:\" target=\"Ady-hI-5gd\" id=\"mSX-Xz-DV3\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem isSeparatorItem=\"YES\" id=\"iMs-zA-UFJ\"/>\n                                                    <menuItem title=\"Copy Style\" keyEquivalent=\"c\" id=\"5Vv-lz-BsD\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                                        <connections>\n                                                            <action selector=\"copyFont:\" target=\"Ady-hI-5gd\" id=\"GJO-xA-L4q\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Paste Style\" keyEquivalent=\"v\" id=\"vKC-jM-MkH\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                                        <connections>\n                                                            <action selector=\"pasteFont:\" target=\"Ady-hI-5gd\" id=\"JfD-CL-leO\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                </items>\n                                            </menu>\n                                        </menuItem>\n                                        <menuItem title=\"Text\" id=\"Fal-I4-PZk\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <menu key=\"submenu\" title=\"Text\" id=\"d9c-me-L2H\">\n                                                <items>\n                                                    <menuItem title=\"Align Left\" keyEquivalent=\"{\" id=\"ZM1-6Q-yy1\">\n                                                        <connections>\n                                                            <action selector=\"alignLeft:\" target=\"Ady-hI-5gd\" id=\"zUv-R1-uAa\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Center\" keyEquivalent=\"|\" id=\"VIY-Ag-zcb\">\n                                                        <connections>\n                                                            <action selector=\"alignCenter:\" target=\"Ady-hI-5gd\" id=\"spX-mk-kcS\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Justify\" id=\"J5U-5w-g23\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"alignJustified:\" target=\"Ady-hI-5gd\" id=\"ljL-7U-jND\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Align Right\" keyEquivalent=\"}\" id=\"wb2-vD-lq4\">\n                                                        <connections>\n                                                            <action selector=\"alignRight:\" target=\"Ady-hI-5gd\" id=\"r48-bG-YeY\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem isSeparatorItem=\"YES\" id=\"4s2-GY-VfK\"/>\n                                                    <menuItem title=\"Writing Direction\" id=\"H1b-Si-o9J\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <menu key=\"submenu\" title=\"Writing Direction\" id=\"8mr-sm-Yjd\">\n                                                            <items>\n                                                                <menuItem title=\"Paragraph\" enabled=\"NO\" id=\"ZvO-Gk-QUH\">\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                </menuItem>\n                                                                <menuItem id=\"YGs-j5-SAR\">\n                                                                    <string key=\"title\">\tDefault</string>\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"makeBaseWritingDirectionNatural:\" target=\"Ady-hI-5gd\" id=\"qtV-5e-UBP\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                                <menuItem id=\"Lbh-J2-qVU\">\n                                                                    <string key=\"title\">\tLeft to Right</string>\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"makeBaseWritingDirectionLeftToRight:\" target=\"Ady-hI-5gd\" id=\"S0X-9S-QSf\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                                <menuItem id=\"jFq-tB-4Kx\">\n                                                                    <string key=\"title\">\tRight to Left</string>\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"makeBaseWritingDirectionRightToLeft:\" target=\"Ady-hI-5gd\" id=\"5fk-qB-AqJ\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                                <menuItem isSeparatorItem=\"YES\" id=\"swp-gr-a21\"/>\n                                                                <menuItem title=\"Selection\" enabled=\"NO\" id=\"cqv-fj-IhA\">\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                </menuItem>\n                                                                <menuItem id=\"Nop-cj-93Q\">\n                                                                    <string key=\"title\">\tDefault</string>\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"makeTextWritingDirectionNatural:\" target=\"Ady-hI-5gd\" id=\"lPI-Se-ZHp\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                                <menuItem id=\"BgM-ve-c93\">\n                                                                    <string key=\"title\">\tLeft to Right</string>\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"makeTextWritingDirectionLeftToRight:\" target=\"Ady-hI-5gd\" id=\"caW-Bv-w94\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                                <menuItem id=\"RB4-Sm-HuC\">\n                                                                    <string key=\"title\">\tRight to Left</string>\n                                                                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                                    <connections>\n                                                                        <action selector=\"makeTextWritingDirectionRightToLeft:\" target=\"Ady-hI-5gd\" id=\"EXD-6r-ZUu\"/>\n                                                                    </connections>\n                                                                </menuItem>\n                                                            </items>\n                                                        </menu>\n                                                    </menuItem>\n                                                    <menuItem isSeparatorItem=\"YES\" id=\"fKy-g9-1gm\"/>\n                                                    <menuItem title=\"Show Ruler\" id=\"vLm-3I-IUL\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                                        <connections>\n                                                            <action selector=\"toggleRuler:\" target=\"Ady-hI-5gd\" id=\"FOx-HJ-KwY\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Copy Ruler\" keyEquivalent=\"c\" id=\"MkV-Pr-PK5\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\" control=\"YES\" command=\"YES\"/>\n                                                        <connections>\n                                                            <action selector=\"copyRuler:\" target=\"Ady-hI-5gd\" id=\"71i-fW-3W2\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                    <menuItem title=\"Paste Ruler\" keyEquivalent=\"v\" id=\"LVM-kO-fVI\">\n                                                        <modifierMask key=\"keyEquivalentModifierMask\" control=\"YES\" command=\"YES\"/>\n                                                        <connections>\n                                                            <action selector=\"pasteRuler:\" target=\"Ady-hI-5gd\" id=\"cSh-wd-qM2\"/>\n                                                        </connections>\n                                                    </menuItem>\n                                                </items>\n                                            </menu>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"View\" id=\"H8h-7b-M4v\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"View\" id=\"HyV-fh-RgO\">\n                                    <items>\n                                        <menuItem title=\"Show Toolbar\" keyEquivalent=\"t\" id=\"snW-S8-Cw5\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                            <connections>\n                                                <action selector=\"toggleToolbarShown:\" target=\"Ady-hI-5gd\" id=\"BXY-wc-z0C\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Customize Toolbar…\" id=\"1UK-8n-QPP\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"runToolbarCustomizationPalette:\" target=\"Ady-hI-5gd\" id=\"pQI-g3-MTW\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"hB3-LF-h0Y\"/>\n                                        <menuItem title=\"Show Sidebar\" keyEquivalent=\"s\" id=\"kIP-vf-haE\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\" control=\"YES\" command=\"YES\"/>\n                                            <connections>\n                                                <action selector=\"toggleSidebar:\" target=\"Ady-hI-5gd\" id=\"iwa-gc-5KM\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Enter Full Screen\" keyEquivalent=\"f\" id=\"4J7-dP-txa\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\" control=\"YES\" command=\"YES\"/>\n                                            <connections>\n                                                <action selector=\"toggleFullScreen:\" target=\"Ady-hI-5gd\" id=\"dU3-MA-1Rq\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Window\" id=\"aUF-d1-5bR\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Window\" systemMenu=\"window\" id=\"Td7-aD-5lo\">\n                                    <items>\n                                        <menuItem title=\"Minimize\" keyEquivalent=\"m\" id=\"OY7-WF-poV\">\n                                            <connections>\n                                                <action selector=\"performMiniaturize:\" target=\"Ady-hI-5gd\" id=\"VwT-WD-YPe\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Zoom\" id=\"R4o-n2-Eq4\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"performZoom:\" target=\"Ady-hI-5gd\" id=\"DIl-cC-cCs\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"eu3-7i-yIM\"/>\n                                        <menuItem title=\"Bring All to Front\" id=\"LE2-aR-0XJ\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"arrangeInFront:\" target=\"Ady-hI-5gd\" id=\"DRN-fu-gQh\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Help\" id=\"wpr-3q-Mcd\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Help\" systemMenu=\"help\" id=\"F2S-fz-NVQ\">\n                                    <items>\n                                        <menuItem title=\"VirtualBuddyGuestHelper Help\" keyEquivalent=\"?\" id=\"FKE-Sm-Kum\">\n                                            <connections>\n                                                <action selector=\"showHelp:\" target=\"Ady-hI-5gd\" id=\"y7X-2Q-9no\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                        </items>\n                    </menu>\n                    <connections>\n                        <outlet property=\"delegate\" destination=\"Voe-Tx-rLC\" id=\"PrD-fu-P6m\"/>\n                    </connections>\n                </application>\n                <customObject id=\"Voe-Tx-rLC\" customClass=\"GuestHelperAppDelegate\" customModule=\"VirtualBuddyGuestHelper\" customModuleProvider=\"target\"/>\n                <customObject id=\"YLy-65-1bz\" customClass=\"NSFontManager\"/>\n                <customObject id=\"Ady-hI-5gd\" userLabel=\"First Responder\" customClass=\"NSResponder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"75\" y=\"0.0\"/>\n        </scene>\n    </scenes>\n</document>\n"
  },
  {
    "path": "VirtualBuddyGuestHelper/GuestHelperAppDelegate.swift",
    "content": "import Cocoa\nimport os.log\n\n@main\nfinal class GuestHelperAppDelegate: NSObject, NSApplicationDelegate {\n\n    private let log = OSLog(subsystem: \"codes.rambo.VirtualBuddyGuestHelper\", category: String(describing: GuestHelperAppDelegate.self))\n\n    func applicationDidFinishLaunching(_ aNotification: Notification) {\n        let config = NSWorkspace.OpenConfiguration()\n        config.activates = false\n        config.addsToRecentItems = false\n        config.promptsUserIfNeeded = false\n\n        NSWorkspace.shared.openApplication(\n            at: Bundle.main.mainAppBundleURL,\n            configuration: config) { _, error in\n                if let error = error {\n                    os_log(\"Failed to launch main app: %{public}@\", log: self.log, type: .fault, String(describing: error))\n                } else {\n                    os_log(\"Main app launched successfully\", log: self.log, type: .info)\n                }\n\n                DispatchQueue.main.async { NSApp?.terminate(nil) }\n            }\n    }\n\n}\n\nextension Bundle {\n\n    var mainAppBundleURL: URL {\n        bundleURL\n            .deletingLastPathComponent() // VirtualBuddyGuestHelper.app\n            .deletingLastPathComponent() // LoginItems\n            .deletingLastPathComponent() // Library\n            .deletingLastPathComponent() // Contents\n    }\n\n}\n"
  },
  {
    "path": "VirtualBuddyGuestHelper/VirtualBuddyGuestHelper.entitlements",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>com.apple.security.app-sandbox</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "VirtualCore/Source/Definitions/Logging.swift",
    "content": "//\n//  Logging.swift\n//  VirtualCore\n//\n//  Created by Guilherme Rambo on 05/06/22.\n//\n\nimport Foundation\nimport OSLog\n\nextension Error {\n    var log: String { String(describing: self) }\n}\n\npublic extension Logger {\n    init<T>(for type: T.Type, label: String? = nil) {\n        let suffix = label.flatMap { \"(\\($0))\" } ?? \"\"\n        self.init(subsystem: VirtualCoreConstants.subsystemName, category: \"\\(String(describing: type))\\(suffix)\")\n    }\n}\n\nextension Logger {\n    func assert(_ message: String) {\n        fault(\"\\(message, privacy: .public)\")\n        assertionFailure(message)\n    }\n}\n"
  },
  {
    "path": "VirtualCore/Source/Definitions/PreviewSupport.swift",
    "content": "#if DEBUG\n\nimport Foundation\nimport Virtualization\n\nlet previewLibraryDirName = \"PreviewLibrary\"\n\npublic extension VBVirtualMachine {\n    static func previewMachine(named name: String) -> VBVirtualMachine {\n        try! VBVirtualMachine(bundleURL: Bundle.virtualCore.url(forResource: name, withExtension: VBVirtualMachine.bundleExtension, subdirectory: previewLibraryDirName)!)\n    }\n    static let preview = VBVirtualMachine.previewMachine(named: \"PreviewMac\")\n    static let previewBlurHash = VBVirtualMachine.previewMachine(named: \"PreviewMacBlurHash\")\n    static let previewNoArtwork = VBVirtualMachine.previewMachine(named: \"PreviewMacNoArtwork\")\n\n    static let previewLinux = VBVirtualMachine.previewMachine(named: \"PreviewLinux\")\n    static let previewLinuxBlurHash = VBVirtualMachine.previewMachine(named: \"PreviewLinuxBlurHash\")\n    static let previewLinuxNoArtwork = VBVirtualMachine.previewMachine(named: \"PreviewLinuxNoArtwork\")\n}\n\nextension Bundle {\n    func requiredPreviewDirectoryURL(named name: String) -> URL {\n        guard let url = Bundle.virtualCore.resourceURL?.appending(path: name, directoryHint: .isDirectory) else {\n            fatalError(\"Couldn't get resources URL for VirtualCore bundle\")\n        }\n        precondition(FileManager.default.fileExists(atPath: url.path), \"Missing \\(name) directory in VirtualCore resources\")\n        return url\n    }\n}\n\npublic extension VBSettingsContainer {\n    static let preview: VBSettingsContainer = {\n        let libraryURL = Bundle.virtualCore.requiredPreviewDirectoryURL(named: previewLibraryDirName)\n        let container = VBSettingsContainer()\n        container.settings.libraryURL = libraryURL\n        return container\n    }()\n}\n\npublic extension VMLibraryController {\n    static let preview: VMLibraryController = {\n        VMLibraryController(settingsContainer: .preview)\n    }()\n}\n\npublic extension VMSavedStatesController {\n    static var preview: VMSavedStatesController {\n        fatalError(\"VMSavedStatesController.preview needs to be reimplemented with new VMSavedStatesController requirements\")\n//        VMSavedStatesController(directoryURL: Bundle.virtualCore.requiredPreviewDirectoryURL(named: \"\\(previewLibraryDirName)/_SavedStates\"))\n    }\n}\n\n@MainActor\npublic extension VMController {\n    static let preview = VMController(with: .preview, library: .preview)\n}\n\npublic extension VBMacConfiguration {\n    \n    static let preview: VBMacConfiguration = {\n        var c = VBMacConfiguration.default\n        \n        c.hardware.storageDevices.append(.init(isBootVolume: false, isEnabled: true, isReadOnly: false, isUSBMassStorageDevice: false, backing: .managedImage(VBManagedDiskImage(filename: \"New Device\", size: VBManagedDiskImage.minimumExtraDiskImageSize))))\n        c.hardware.storageDevices.append(.init(isBootVolume: false, isEnabled: true, isReadOnly: false, isUSBMassStorageDevice: false, backing: .managedImage(VBManagedDiskImage(filename: \"Fake Managed Disk\", size: VBManagedDiskImage.minimumExtraDiskImageSize, format: .raw))))\n//        c.hardware.storageDevices.append(.init(isBootVolume: false, isEnabled: true, isReadOnly: false, isUSBMassStorageDevice: false, backing: .customImage(Bundle.virtualCore.url(forResource: \"Fake Custom Path Disk\", withExtension: \"dmg\", subdirectory: \"Preview.vbvm\")!)))\n        \n        c.sharedFolders = [\n            .init(id: UUID(uuidString: \"821BA195-D687-4B61-8412-0C6BA6C99074\")!, url: URL(fileURLWithPath: \"/Users/insidegui/Desktop\"), isReadOnly: true),\n            .init(id: UUID(uuidString: \"821BA195-D687-4B61-8412-0C6BA6C99075\")!, url: URL(fileURLWithPath: \"/Users/insidegui/Downloads\"), isReadOnly: false),\n            .init(id: UUID(uuidString: \"821BA195-D687-4B61-8412-0C6BA6C99076\")!, url: URL(fileURLWithPath: \"/Volumes/Rambo/Movies\"), isEnabled: false, isReadOnly: false),\n            .init(id: UUID(uuidString: \"821BA195-D687-4B61-8412-0C6BA6C99077\")!, url: URL(fileURLWithPath: \"/Some/Invalid/Path\"), isEnabled: true, isReadOnly: false),\n            .init(id: UUID(uuidString: \"821BA195-D687-4B61-8412-0C6BA6C99078\")!, url: URL(fileURLWithPath: \"/Users/insidegui/Music\"), isEnabled: true, isReadOnly: true),\n            .init(id: UUID(uuidString: \"821BA195-D687-4B61-8412-0C6BA6C99079\")!, url: URL(fileURLWithPath: \"/Users/insidegui/Developer\"), isEnabled: true, isReadOnly: true),\n        ]\n        \n        return c\n    }()\n    \n    static var networkPreviewNAT: VBMacConfiguration {\n        var config = VBMacConfiguration.preview\n        config.hardware.networkDevices = [VBNetworkDevice(id: \"Default\", name: \"Default\", kind: .NAT, macAddress: \"0A:82:7F:CE:C0:58\")]\n        return config\n    }\n    \n    static var networkPreviewBridge: VBMacConfiguration {\n        var config = VBMacConfiguration.preview\n        config.hardware.networkDevices = [VBNetworkDevice(id: VBNetworkDevice.defaultBridgeInterfaceID ?? \"ERROR\", name: \"Bridge\", kind: .bridge, macAddress: \"0A:82:7F:CE:C0:58\")]\n        return config\n    }\n    \n    static var networkPreviewNone: VBMacConfiguration {\n        var config = VBMacConfiguration.preview\n        config.hardware.networkDevices = []\n        return config\n    }\n    \n    var removingSharedFolders: Self {\n        var mSelf = self\n        mSelf.sharedFolders = []\n        return mSelf\n    }\n    \n    var linuxVirtualMachine: Self {\n        var mSelf = self\n        mSelf.systemType = .linux\n        return mSelf\n    }\n    \n}\n\npublic extension VZVirtualMachine {\n    /// A dummy `VZVirtualMachine` instance for previews where an instance is needed but nothing  is actually done with it.\n    static let preview: VZVirtualMachine = {\n        let config = VZVirtualMachineConfiguration()\n        /// Sneaky little swizzle to get around validation exception.\n        /// This is fine® because it's just for previews.\n        if let method = class_getInstanceMethod(VZVirtualMachineConfiguration.self, #selector(VZVirtualMachineConfiguration.validate)) {\n            let impBlock: @convention(block) () -> Bool = { return true }\n            method_setImplementation(method, imp_implementationWithBlock(impBlock))\n        }\n        return VZVirtualMachine(configuration: config)\n    }()\n}\n\npublic extension SoftwareCatalog {\n    static let previewMac = try! VBAPIClient.fetchBuiltInCatalog(for: .mac)\n    static let previewLinux = try! VBAPIClient.fetchBuiltInCatalog(for: .linux)\n}\n\npublic extension ResolvedCatalog {\n    static let previewMac = ResolvedCatalog(environment: .current.guest(platform: .mac), catalog: .previewMac)\n    static let previewLinux = ResolvedCatalog(environment: .current.guest(platform: .linux), catalog: .previewLinux)\n}\n\npublic extension ResolvedCatalogGroup {\n    static let previewMac = ResolvedCatalog.previewMac.groups[0]\n    static let previewLinux = ResolvedCatalog.previewLinux.groups[0]\n}\n\npublic extension ResolvedRestoreImage {\n    static let previewMac = ResolvedCatalog.previewMac.groups[0].restoreImages[0]\n    static let previewLinux = ResolvedCatalog.previewLinux.groups[0].restoreImages[0]\n}\n#endif\n"
  },
  {
    "path": "VirtualCore/Source/Definitions/VirtualCoreConstants.swift",
    "content": "//\n//  VirtualCoreConstants.swift\n//  VirtualCore\n//\n//  Created by Guilherme Rambo on 02/06/22.\n//\n\nimport Foundation\nimport OSLog\n@_exported import BuddyKit\n\nstruct VirtualCoreConstants {\n    static let subsystemName = \"codes.rambo.VirtualCore\"\n}\n\nprivate final class _VirtualCoreStub { }\n\npublic extension Bundle {\n    static let virtualCore = Bundle(for: _VirtualCoreStub.self)\n}\n"
  },
  {
    "path": "VirtualCore/Source/GuestSupport/CreateGuestImage.sh",
    "content": "#!/bin/sh\n\n: '\nThis script is used by VirtualBuddy to dynamically generate a disk image that can be mounted in a virtual machine,\ncontaining the current version of the VirtualBuddyGuest app embedded into VirtualBuddy itself.\n\nImages are stored in ~/Library/Application Support/VirtualBuddy/_GuestImage.\n\nAlongside the images, the app stores a digest of the entire contents of the Guest app bundle,\nso that it can be automatically updated whenever something changes in the Guest app.\n'\n\nGUEST_APP_PATH=\"$1\"\nGUEST_APP_DIGEST=\"$2\"\nGUEST_SIZE_ALLOCATION=\"$3\"\nGUEST_DMG_SUFFIX=\"$4\"\n\nif [ -z \"$GUEST_APP_PATH\" ]; then\n    echo \"Shell script invocation error: missing GUEST_APP_PATH value as first argument\" 1>&2\n    exit 7\nfi\n\nif [ -z \"$GUEST_APP_DIGEST\" ]; then\n    echo \"Shell script invocation error: missing GUEST_APP_DIGEST value as second argument\" 1>&2\n    exit 7\nfi\n\nif [ -z \"$GUEST_SIZE_ALLOCATION\" ]; then\n    echo \"Shell script invocation error: missing GUEST_SIZE_ALLOCATION value as third argument\" 1>&2\n    exit 7\nfi\n\nif [ ! -d \"$GUEST_APP_PATH\" ]; then\n    echo \"Shell script invocation error: guest app bundle doesn't exist at $GUEST_APP_PATH\" 1>&2\n    exit 7\nfi\n\nVBROOT=\"$HOME/Library/Application Support/VirtualBuddy\"\nGUEST_DMG_DEST_PATH=\"$VBROOT/_GuestImage\"\n\nGUEST_DMG_NAME=\"VirtualBuddyGuest$GUEST_DMG_SUFFIX\"\n\nGUEST_STAGING_PATH=\"$GUEST_DMG_DEST_PATH/staging\"\nGUEST_TEMP_MOUNT_PATH=\"$GUEST_STAGING_PATH/VirtualBuddyGuest$GUEST_DMG_SUFFIX\"\nGUEST_TEMP_DMG_PATH=\"$GUEST_STAGING_PATH/$GUEST_DMG_NAME.dmg\"\n\nGUEST_TEMP_DIGEST_PATH=\"$GUEST_TEMP_MOUNT_PATH/.$GUEST_DMG_NAME.digest\"\n\n# Make sure the temporary mount point exists\n\nmkdir -p \"$GUEST_TEMP_MOUNT_PATH\" 2>/dev/null || echo \"\"\n\n# Unmount and remove any leftovers from previous script invocation\n\nhdiutil detach -force \"$GUEST_TEMP_MOUNT_PATH\" 2>/dev/null || echo \"\"\n\nrm \"$GUEST_TEMP_DMG_PATH\" 2>/dev/null || echo \"\"\n\n# Create blank disk image\n\nhdiutil create -layout MBRSPUD -size $GUEST_SIZE_ALLOCATION -fs HFS+ -volname Guest \"$GUEST_TEMP_DMG_PATH\" || \\\n    { echo \"Failed to create VirtualBuddyGuest disk image: hdiutil exit code $?\" 1>&2; exit 1; }\n\n# Mount image at staging location\n\nhdiutil attach -imagekey diskimage-class=CRawDiskImage -noverify \"$GUEST_TEMP_DMG_PATH\" -mountpoint \"$GUEST_TEMP_MOUNT_PATH\" || \\\n    { echo \"Failed to mount empty VirtualBuddyGuest disk image: hdiutil exit code $?\" 1>&2; exit 1; }\n\n# Write digest to temporary mount\n\necho \"$GUEST_APP_DIGEST\" > \"$GUEST_TEMP_DIGEST_PATH\"\n\n# Copy VirtualBuddyGuest.app into the temporary mount\n\ncp -R \"$GUEST_APP_PATH\" \"$GUEST_TEMP_MOUNT_PATH/\" || \\\n    { echo \"Failed to copy VirtualBuddyGuest.app into disk image: exit code $?\" 1>&2; exit 1; }\n\n# Copy the digest to its final destination\n\nyes | cp -rf \"$GUEST_TEMP_DIGEST_PATH\" \"$GUEST_DMG_DEST_PATH\" || \\\n    { echo \"Failed to copy guest digest: exit code $?\" 1>&2; } # Failure to copy digest is non-fatal\n\n# Eject the disk image\n\nhdiutil detach -force \"$GUEST_TEMP_MOUNT_PATH\" || \\\n    { echo \"Failed to eject VirtualBuddyGuest disk image: exit code $?\" 1>&2; exit 1; }\n\n# Remove any extended attributes from the disk image\n\nxattr -cr \"$GUEST_TEMP_DMG_PATH\" 2>/dev/null || echo \"\"\n\n# Copy the finalized disk image to its final destination\n\nyes | cp -rf \"$GUEST_TEMP_DMG_PATH\" \"$GUEST_DMG_DEST_PATH\" || \\\n    { echo \"Failed to copy finalized disk image: exit code $?\" 1>&2; exit 1; }\n\n# Cleanup\n\nrm \"$GUEST_TEMP_DMG_PATH\" 2>/dev/null || echo \"\"\nrm -Rf \"$GUEST_TEMP_MOUNT_PATH\" 2>/dev/null || echo \"\"\nrm -Rf \"$GUEST_STAGING_PATH\" 2>/dev/null || echo \"\"\n\necho \"OK\"\n"
  },
  {
    "path": "VirtualCore/Source/GuestSupport/GuestAdditionsDiskImage.swift",
    "content": "//\n//  GuestAdditionsDiskImage.swift\n//  VirtualCore\n//\n//  Created by Guilherme Rambo on 07/03/23.\n//\n\nimport Foundation\nimport Virtualization\nimport CryptoKit\nimport UniformTypeIdentifiers\nimport OSLog\nimport Combine\n\npublic final class GuestAdditionsDiskImage: ObservableObject {\n\n    private lazy var logger = Logger(subsystem: VirtualCoreConstants.subsystemName, category: String(describing: Self.self))\n\n    public static let current = GuestAdditionsDiskImage()\n\n    public enum State: CustomStringConvertible {\n        case ready\n        case installing\n        case installFailed(Error)\n\n        public var description: String {\n            switch self {\n            case .ready: \"Ready\"\n            case .installing: \"Installing\"\n            case .installFailed(let error): \"Failed: \\(error)\"\n            }\n        }\n    }\n\n    @MainActor\n    @Published public private(set) var state = State.ready\n\n    public func installIfNeeded() async throws {\n        #if DEBUG\n        if await simulateInstall() { return }\n        #endif\n\n        do {\n            logger.debug(#function)\n\n            func performInstall(with digest: String) async throws {\n                await MainActor.run { state = .installing }\n\n                try await writeGuestImage(with: digest)\n\n                await MainActor.run { state = .ready }\n            }\n\n            let embeddedDigest = try computeEmbeddedGuestDigest()\n\n            if let currentlyInstalledGuestImageDigest {\n                logger.debug(\"Embedded guest app digest: \\(embeddedDigest, privacy: .public) / Library guest app digest: \\(currentlyInstalledGuestImageDigest, privacy: .public)\")\n\n                guard embeddedDigest != currentlyInstalledGuestImageDigest else {\n                    logger.debug(\"Guest digests match, skipping guest image generation\")\n\n                    await MainActor.run { state = .ready }\n\n                    return\n                }\n\n                logger.debug(\"Guest digests don't match, generating new guest image with embedded guest\")\n\n                try await performInstall(with: embeddedDigest)\n            } else {\n                logger.debug(\"No digest for currently installed image, assuming not installed. Embedded guest app digest: \\(embeddedDigest, privacy: .public)\")\n\n                try await performInstall(with: embeddedDigest)\n            }\n        } catch {\n            logger.error(\"Guest disk image installation failed. \\(error, privacy: .public)\")\n\n            await MainActor.run { state = .installFailed(error) }\n\n            throw error\n        }\n    }\n\n    // MARK: File Paths\n\n    private var embeddedGuestAppURL: URL {\n        get throws {\n            guard let url = Bundle.main.sharedSupportURL?.appendingPathComponent(\"VirtualBuddyGuest.app\") else {\n                throw Failure(\"Couldn't get VirtualBuddyGuest.app URL within main app bundle\")\n            }\n\n            guard FileManager.default.fileExists(atPath: url.path) else {\n                throw Failure(\"VirtualBuddyGuest.app doesn't exist at \\(url.path)\")\n            }\n\n            return url\n        }\n    }\n\n    private var generatorScriptURL: URL {\n        get throws {\n            guard let url = Bundle.virtualCore.url(forResource: \"CreateGuestImage\", withExtension: \"sh\") else {\n                throw Failure(\"Couldn't get CreateGuestImage.sh URL within VirtualCore bundle\")\n            }\n\n            guard FileManager.default.fileExists(atPath: url.path) else {\n                throw Failure(\"CreateGuestImage.sj doesn't exist at \\(url.path)\")\n            }\n\n            return url\n        }\n    }\n\n    private var _imageBaseName: String { \"VirtualBuddyGuest\" }\n\n    private var imageName: String {\n        if let suffix = VBBuildType.current.guestAdditionsImageSuffix {\n            _imageBaseName + suffix\n        } else {\n            _imageBaseName\n        }\n    }\n\n    static let imagesRootURL: URL = URL.defaultVirtualBuddyLibraryURL.appendingPathComponent(\"_GuestImage\")\n\n    private var imagesRootURL: URL { Self.imagesRootURL }\n\n    private var installedImageDigestURL: URL {\n        imagesRootURL\n            .appendingPathComponent(\".\" + imageName)\n            .appendingPathExtension(\"digest\")\n    }\n\n    public var installedImageURL: URL {\n        imagesRootURL\n            .appendingPathComponent(imageName)\n            .appendingPathExtension(\"dmg\")\n    }\n\n    // MARK: Digest\n\n    private var currentlyInstalledGuestImageDigest: String? {\n        guard FileManager.default.fileExists(atPath: installedImageDigestURL.path) else {\n            return nil\n        }\n        do {\n            return try String(contentsOf: installedImageDigestURL)\n                .trimmingCharacters(in: .whitespacesAndNewlines)\n        } catch {\n            logger.error(\"Failed to read installed image digest at \\(self.installedImageDigestURL.path): \\(error, privacy: .public)\")\n\n            return nil\n        }\n    }\n\n    private func computeEmbeddedGuestDigest() throws -> String {\n        let url = try embeddedGuestAppURL\n        guard let enumerator = FileManager.default.enumerator(at: url, includingPropertiesForKeys: [.contentTypeKey]) else {\n            throw Failure(\"Couldn't instantiate file enumerator for computing guest app bundle digest\")\n        }\n\n        var hash = SHA256()\n\n        while let url = enumerator.nextObject() as? URL {\n            guard let values = try? url.resourceValues(forKeys: [.contentTypeKey]),\n                  let contentType = values.contentType\n            else { continue }\n\n            guard contentType.conforms(to: .executable),\n                  !contentType.conforms(to: .directory)\n            else { continue }\n\n            #if DEBUG\n            logger.debug(\"Computing hash for \\(url.lastPathComponent)\")\n            #endif\n\n            do {\n                let data = try Data(contentsOf: url, options: .mappedIfSafe)\n\n                hash.update(data: data)\n            } catch {\n                logger.warning(\"Couldn't compute hash for \\(url.lastPathComponent): \\(error, privacy: .public)\")\n            }\n        }\n\n        let digest = hash.finalize()\n        let hashStr = digest.map { String(format: \"%02x\", $0) }.joined()\n\n        return hashStr\n    }\n\n    // MARK: Installation\n\n    private func writeGuestImage(with digest: String) async throws {\n        let scriptPath = try generatorScriptURL.path\n        let guestURL = try embeddedGuestAppURL\n        let guestPath = guestURL.path\n        let size = computeImageSizeInMB(guestAppURL: guestURL)\n\n        var args: [String] = [\n            scriptPath,\n            guestPath,\n            digest,\n            \"\\(size)MB\"\n        ]\n\n        if let suffix = VBBuildType.current.guestAdditionsImageSuffix {\n            args.append(suffix)\n        }\n\n        let p = Process()\n        p.executableURL = URL(fileURLWithPath: \"/bin/sh\")\n        p.arguments = args\n        let outPipe = Pipe()\n        let errPipe = Pipe()\n        p.standardOutput = outPipe\n        p.standardError = errPipe\n\n        try p.run()\n        p.waitUntilExit()\n\n        let outData = try outPipe.fileHandleForReading.readToEnd()\n        let errData = try errPipe.fileHandleForReading.readToEnd()\n\n        #if DEBUG\n        if let outData, !outData.isEmpty {\n            logger.debug(\"#### Generator script output (stdout): ####\")\n            logger.debug(\"\\(String(decoding: outData, as: UTF8.self), privacy: .public)\")\n        }\n        if let errData, !errData.isEmpty {\n            logger.debug(\"#### Generator script output (stderr): ####\")\n            logger.debug(\"\\(String(decoding: errData, as: UTF8.self), privacy: .public)\")\n        }\n        #endif\n\n        guard p.terminationStatus == 0 else {\n            if let message = errData.flatMap({ String(decoding: $0, as: UTF8.self) }) {\n                throw Failure(message)\n            } else {\n                throw Failure(\"Guest additions disk image generator failed with exit code \\(p.terminationStatus)\")\n            }\n        }\n\n        logger.notice(\"Guest additions disk image generated at \\(self.installedImageURL.path, privacy: .public)\")\n    }\n\n}\n\n// MARK: - Virtualization Extensions\n\nextension VZVirtioBlockDeviceConfiguration {\n\n    static var guestAdditionsDisk: VZVirtioBlockDeviceConfiguration? {\n        get throws {\n            let guestImageURL = GuestAdditionsDiskImage.current.installedImageURL\n\n            guard FileManager.default.fileExists(atPath: guestImageURL.path) else { return nil }\n\n            let guestAttachment = try VZDiskImageStorageDeviceAttachment(url: guestImageURL, readOnly: true)\n\n            return VZVirtioBlockDeviceConfiguration(attachment: guestAttachment)\n        }\n    }\n\n}\n\n// MARK: - Image Size Calculation\n\nprivate extension GuestAdditionsDiskImage {\n    /// Fallback size in case image size can't be calculated.\n    static let defaultImageSizeInMB = 32\n\n    /// Just being paranoid in case size computation goes haywire and ends up computing a huge image size.\n    static let maxImageSizeInMB = 128\n\n    /// Increase image size slightly when compared to guest app size to account for extra space needed for disk image.\n    static let imageSizeMultiplier: Double = 1.1\n\n    func computeImageSizeInMB(guestAppURL: URL) -> Int {\n        do {\n            guard let enumerator = FileManager.default.enumerator(at: guestAppURL, includingPropertiesForKeys: [.totalFileAllocatedSizeKey, .contentTypeKey], options: [], errorHandler: { url, error in\n                self.logger.warning(\"Error enumerating guest app contents at \\(url.lastPathComponent, privacy: .public) - \\(error, privacy: .public)\")\n                return true\n            }) else {\n                throw Failure(\"Failed to create directory enumerator.\")\n            }\n\n            var totalSize: Int = 0\n\n            while let file = enumerator.nextObject() as? URL {\n                let values = try file.resourceValues(forKeys: [.contentTypeKey, .totalFileAllocatedSizeKey])\n\n                guard let type = values.contentType else {\n                    throw Failure(\"Content type not available for \\(file.lastPathComponent)\")\n                }\n\n                guard !type.conforms(to: .directory) else { continue }\n\n                guard let size = values.totalFileAllocatedSize else {\n                    throw Failure(\"File size not available for \\(file.lastPathComponent)\")\n                }\n\n                totalSize += size\n            }\n\n            let totalSizeMB = Int(ceil(Double(totalSize) * Self.imageSizeMultiplier)) / 1000 / 1000\n\n            logger.info(\"Calculated guest disk image size: \\(totalSizeMB, privacy: .public)MB\")\n\n            guard totalSizeMB <= Self.maxImageSizeInMB else {\n                assertionFailure(\"\\(#function) calculated a size that's larger than the maximum allowed size. Calculated size in MB: \\(totalSizeMB), max size in MB: \\(Self.maxImageSizeInMB)\")\n                return Self.maxImageSizeInMB\n            }\n\n            return totalSizeMB\n        } catch {\n            logger.fault(\"Error computing total guest disk image size. \\(error, privacy: .public)\")\n            return Self.defaultImageSizeInMB\n        }\n    }\n\n}\n\nextension VBBuildType {\n    var guestAdditionsImageSuffix: String? {\n        switch self {\n        case .debug: \"_Debug\"\n        case .betaDebug: \"_Beta_Debug\"\n        case .release: nil\n        case .betaRelease: \"_Beta\"\n        case .devRelease: \"_Dev\"\n        }\n    }\n}\n\n// MARK: - Debug Simulation\n\n#if DEBUG\nprivate extension GuestAdditionsDiskImage {\n    func simulateInstall() async -> Bool {\n        guard UserDefaults.standard.bool(forKey: \"VBSimulateGuestDiskImageGeneration\") else {\n            return false\n        }\n\n        logger.debug(\"Guest disk image will not be generated because VBSimulateGuestDiskImageGeneration is enabled.\")\n\n        await MainActor.run {\n            state = .installing\n        }\n\n        let delaySeconds = UserDefaults.standard.integer(forKey: \"VBDelayGuestDiskImageGenerationBySeconds\")\n        if delaySeconds > 0 {\n            logger.debug(\"Simulating guest disk image install with custom delay of \\(delaySeconds) seconds\")\n\n            try? await Task.sleep(for: .seconds(delaySeconds))\n        } else {\n            logger.debug(\"Simulating guest disk image install with default delay\")\n\n            try? await Task.sleep(for: .seconds(3))\n        }\n\n        guard !UserDefaults.standard.bool(forKey: \"VBSimulateGuestDiskImageGenerationError\") else {\n            logger.debug(\"Simulating guest disk image install error.\")\n            await MainActor.run {\n                state = .installFailed(\"This is a simulated error for debugging.\")\n            }\n            return true\n        }\n\n        logger.debug(\"Simulated guest disk image install completed\")\n\n        await MainActor.run {\n            state = .ready\n        }\n\n        return true\n    }\n}\n#endif\n"
  },
  {
    "path": "VirtualCore/Source/Headers/VirtualizationPrivate.h",
    "content": "//\n//  VirtualizationPrivate.h\n//  VirtualBuddy\n//\n//  Created by Guilherme Rambo on 07/04/22.\n//\n\n#import <Virtualization/Virtualization.h>\n\nNS_ASSUME_NONNULL_BEGIN\n\n// Classes defined here are no longer SPI in macOS 14\n#if !defined(MAC_OS_VERSION_14_0)\n\n__attribute__((weak_import))\n@interface _VZFramebuffer: NSObject\n\n- (void)takeScreenshotWithCompletionHandler:(void(^)(NSImage *__nullable screenshot, NSError *__nullable error))completion;\n\n@end\n\n@interface _VZGraphicsDevice: NSObject\n\n- (NSInteger)type;\n- (NSArray <_VZFramebuffer *> *)framebuffers;\n\n@end\n\n#endif\n\n@interface _VZMultiTouchDeviceConfiguration: NSObject <NSCopying>\n@end\n\n@interface _VZAppleTouchScreenConfiguration: _VZMultiTouchDeviceConfiguration\n@end\n\n@interface _VZUSBTouchScreenConfiguration: _VZMultiTouchDeviceConfiguration\n@end\n\n__attribute__((weak_import))\n@interface _VZVirtualMachineStartOptions: NSObject <NSSecureCoding>\n\n@property (assign) BOOL forceDFU;\n@property (assign) BOOL stopInIBootStage1;\n@property (assign) BOOL stopInIBootStage2;\n@property (assign) BOOL bootMacOSRecovery;\n\n@end\n\n@interface VZMacOSVirtualMachineStartOptions (VZPrivate)\n\n@property (assign, setter=_setForceDFU:) BOOL _forceDFU;\n\n@end\n\n@interface VZMacAuxiliaryStorage (Private)\n\n- (NSDictionary <NSString *, id> *)_allNVRAMVariablesWithError:(NSError **)outError;\n- (NSDictionary <NSString *, id> *)_allNVRAMVariablesInPartition:(NSUInteger)partition error:(NSError **)outError;\n- (id __nullable)_valueForNVRAMVariableNamed:(NSString *)name error:(NSError **)arg2;\n- (BOOL)_removeNVRAMVariableNamed:(NSString *)name error:(NSError **)arg2;\n- (BOOL)_setValue:(id)arg1 forNVRAMVariableNamed:(NSString *)name error:(NSError **)arg3;\n\n@end\n\n@interface VZVirtualMachineConfiguration (Private)\n\n@property (strong, setter=_setMultiTouchDevices:) NSArray <_VZMultiTouchDeviceConfiguration *> *_multiTouchDevices;\n\n@end\n\n@interface VZVirtualMachine (Private)\n\n#if !defined(MAC_OS_VERSION_13_0) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_VERSION_13_0\n- (void)_startWithOptions:(_VZVirtualMachineStartOptions *__nullable)options\n        completionHandler:(void (^__nonnull)(NSError * _Nullable errorOrNil))completionHandler;\n#endif\n\n- (id)_USBDevices;\n- (BOOL)_canAttachUSBDevices;\n- (BOOL)_canDetachUSBDevices;\n- (BOOL)_canAttachUSBDevice:(id)arg1;\n- (BOOL)_canDetachUSBDevice:(id)arg1;\n- (BOOL)_attachUSBDevice:(id)arg1 error:(void *)arg2;\n- (BOOL)_detachUSBDevice:(id)arg1 error:(void *)arg2;\n- (void)_getUSBControllerLocationIDWithCompletionHandler:(void(^)(id val))arg1;\n\n#if !defined(MAC_OS_VERSION_14_0)\n@property (nonatomic, readonly) NSArray <_VZGraphicsDevice *> *_graphicsDevices;\n#endif\n\n@end\n\n@interface VZMacPlatformConfiguration (Private)\n\n@property (nonatomic, assign, setter=_setProductionModeEnabled:) BOOL _isProductionModeEnabled;\n\n- (id __nullable)_platform;\n\n@end\n\n@interface VZVirtualMachineView (Private)\n\n- (void)_setDelegate:(id)delegate;\n\n@end\n\n@interface VZGraphicsDisplay (Private)\n\n- (void)_takeScreenshotWithCompletionHandler:(void(^__nonnull)(id __nullable image, id __nullable error))completion NS_SWIFT_UI_ACTOR API_AVAILABLE(macos(14.0));\n\n@end\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "VirtualCore/Source/Import/UTM/UTMAppleConfiguration.swift",
    "content": "/*\nCode in this file is based on source code from the UTM project (https://github.com/utmapp/UTM)\n\nApache License\nVersion 2.0, January 2004\nhttp://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n\"License\" shall mean the terms and conditions for use, reproduction,\nand distribution as defined by Sections 1 through 9 of this document.\n\n\"Licensor\" shall mean the copyright owner or entity authorized by\nthe copyright owner that is granting the License.\n\n\"Legal Entity\" shall mean the union of the acting entity and all\nother entities that control, are controlled by, or are under common\ncontrol with that entity. For the purposes of this definition,\n\"control\" means (i) the power, direct or indirect, to cause the\ndirection or management of such entity, whether by contract or\notherwise, or (ii) ownership of fifty percent (50%) or more of the\noutstanding shares, or (iii) beneficial ownership of such entity.\n\n\"You\" (or \"Your\") shall mean an individual or Legal Entity\nexercising permissions granted by this License.\n\n\"Source\" form shall mean the preferred form for making modifications,\nincluding but not limited to software source code, documentation\nsource, and configuration files.\n\n\"Object\" form shall mean any form resulting from mechanical\ntransformation or translation of a Source form, including but\nnot limited to compiled object code, generated documentation,\nand conversions to other media types.\n\n\"Work\" shall mean the work of authorship, whether in Source or\nObject form, made available under the License, as indicated by a\ncopyright notice that is included in or attached to the work\n(an example is provided in the Appendix below).\n\n\"Derivative Works\" shall mean any work, whether in Source or Object\nform, that is based on (or derived from) the Work and for which the\neditorial revisions, annotations, elaborations, or other modifications\nrepresent, as a whole, an original work of authorship. For the purposes\nof this License, Derivative Works shall not include works that remain\nseparable from, or merely link (or bind by name) to the interfaces of,\nthe Work and Derivative Works thereof.\n\n\"Contribution\" shall mean any work of authorship, including\nthe original version of the Work and any modifications or additions\nto that Work or Derivative Works thereof, that is intentionally\nsubmitted to Licensor for inclusion in the Work by the copyright owner\nor by an individual or Legal Entity authorized to submit on behalf of\nthe copyright owner. For the purposes of this definition, \"submitted\"\nmeans any form of electronic, verbal, or written communication sent\nto the Licensor or its representatives, including but not limited to\ncommunication on electronic mailing lists, source code control systems,\nand issue tracking systems that are managed by, or on behalf of, the\nLicensor for the purpose of discussing and improving the Work, but\nexcluding communication that is conspicuously marked or otherwise\ndesignated in writing by the copyright owner as \"Not a Contribution.\"\n\n\"Contributor\" shall mean Licensor and any individual or Legal Entity\non behalf of whom a Contribution has been received by Licensor and\nsubsequently incorporated within the Work.\n\n2. Grant of Copyright License. Subject to the terms and conditions of\nthis License, each Contributor hereby grants to You a perpetual,\nworldwide, non-exclusive, no-charge, royalty-free, irrevocable\ncopyright license to reproduce, prepare Derivative Works of,\npublicly display, publicly perform, sublicense, and distribute the\nWork and such Derivative Works in Source or Object form.\n\n3. Grant of Patent License. Subject to the terms and conditions of\nthis License, each Contributor hereby grants to You a perpetual,\nworldwide, non-exclusive, no-charge, royalty-free, irrevocable\n(except as stated in this section) patent license to make, have made,\nuse, offer to sell, sell, import, and otherwise transfer the Work,\nwhere such license applies only to those patent claims licensable\nby such Contributor that are necessarily infringed by their\nContribution(s) alone or by combination of their Contribution(s)\nwith the Work to which such Contribution(s) was submitted. If You\ninstitute patent litigation against any entity (including a\ncross-claim or counterclaim in a lawsuit) alleging that the Work\nor a Contribution incorporated within the Work constitutes direct\nor contributory patent infringement, then any patent licenses\ngranted to You under this License for that Work shall terminate\nas of the date such litigation is filed.\n\n4. Redistribution. You may reproduce and distribute copies of the\nWork or Derivative Works thereof in any medium, with or without\nmodifications, and in Source or Object form, provided that You\nmeet the following conditions:\n\n(a) You must give any other recipients of the Work or\nDerivative Works a copy of this License; and\n\n(b) You must cause any modified files to carry prominent notices\nstating that You changed the files; and\n\n(c) You must retain, in the Source form of any Derivative Works\nthat You distribute, all copyright, patent, trademark, and\nattribution notices from the Source form of the Work,\nexcluding those notices that do not pertain to any part of\nthe Derivative Works; and\n\n(d) If the Work includes a \"NOTICE\" text file as part of its\ndistribution, then any Derivative Works that You distribute must\ninclude a readable copy of the attribution notices contained\nwithin such NOTICE file, excluding those notices that do not\npertain to any part of the Derivative Works, in at least one\nof the following places: within a NOTICE text file distributed\nas part of the Derivative Works; within the Source form or\ndocumentation, if provided along with the Derivative Works; or,\nwithin a display generated by the Derivative Works, if and\nwherever such third-party notices normally appear. The contents\nof the NOTICE file are for informational purposes only and\ndo not modify the License. You may add Your own attribution\nnotices within Derivative Works that You distribute, alongside\nor as an addendum to the NOTICE text from the Work, provided\nthat such additional attribution notices cannot be construed\nas modifying the License.\n\nYou may add Your own copyright statement to Your modifications and\nmay provide additional or different license terms and conditions\nfor use, reproduction, or distribution of Your modifications, or\nfor any such Derivative Works as a whole, provided Your use,\nreproduction, and distribution of the Work otherwise complies with\nthe conditions stated in this License.\n\n5. Submission of Contributions. Unless You explicitly state otherwise,\nany Contribution intentionally submitted for inclusion in the Work\nby You to the Licensor shall be under the terms and conditions of\nthis License, without any additional terms or conditions.\nNotwithstanding the above, nothing herein shall supersede or modify\nthe terms of any separate license agreement you may have executed\nwith Licensor regarding such Contributions.\n\n6. Trademarks. This License does not grant permission to use the trade\nnames, trademarks, service marks, or product names of the Licensor,\nexcept as required for reasonable and customary use in describing the\norigin of the Work and reproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty. Unless required by applicable law or\nagreed to in writing, Licensor provides the Work (and each\nContributor provides its Contributions) on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\nimplied, including, without limitation, any warranties or conditions\nof TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\nPARTICULAR PURPOSE. You are solely responsible for determining the\nappropriateness of using or redistributing the Work and assume any\nrisks associated with Your exercise of permissions under this License.\n\n8. Limitation of Liability. In no event and under no legal theory,\nwhether in tort (including negligence), contract, or otherwise,\nunless required by applicable law (such as deliberate and grossly\nnegligent acts) or agreed to in writing, shall any Contributor be\nliable to You for damages, including any direct, indirect, special,\nincidental, or consequential damages of any character arising as a\nresult of this License or out of the use or inability to use the\nWork (including but not limited to damages for loss of goodwill,\nwork stoppage, computer failure or malfunction, or any and all\nother commercial damages or losses), even if such Contributor\nhas been advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability. While redistributing\nthe Work or Derivative Works thereof, You may choose to offer,\nand charge a fee for, acceptance of support, warranty, indemnity,\nor other liability obligations and/or rights consistent with this\nLicense. However, in accepting such obligations, You may act only\non Your own behalf and on Your sole responsibility, not on behalf\nof any other Contributor, and only if You agree to indemnify,\ndefend, and hold each Contributor harmless for any liability\nincurred by, or claims asserted against, such Contributor by reason\nof your accepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work.\n\nTo apply the Apache License to your work, attach the following\nboilerplate notice, with the fields enclosed by brackets \"[]\"\nreplaced with your own identifying information. (Don't include\nthe brackets!)  The text should be enclosed in the appropriate\ncomment syntax for the file format. We also recommend that a\nfile or class name and description of purpose be included on the\nsame \"printed page\" as the copyright notice for easier\nidentification within third-party archives.\n\nCopyright [yyyy] [name of copyright owner]\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n */\nimport Foundation\nimport BuddyFoundation\n\nextension PropertyListDecoder {\n    static let utm = PropertyListDecoder()\n}\n\nextension UTMAppleConfiguration {\n    init(path: FilePath) throws {\n        let data: Data = try path.read()\n        let stub = try PropertyListDecoder.utm.decode(UTMConfigurationStub.self, from: data)\n\n        guard stub.backend == \"Apple\" else {\n            throw \"Only UTM virtual machines based on Apple Virtualization are supported.\"\n        }\n\n        self = try PropertyListDecoder.utm.decode(UTMAppleConfiguration.self, from: data)\n    }\n}\n\n// MARK: - UTM Data Structures\n\nstruct UTMConfigurationStub: Decodable {\n    var backend: String?\n    var configurationVersion: Int?\n\n    enum CodingKeys: String, CodingKey {\n        case backend = \"Backend\"\n        case configurationVersion = \"ConfigurationVersion\"\n    }\n}\n\nstruct UTMAppleConfiguration: Decodable {\n    var backend: String?\n    var configurationVersion: Int?\n    var information: UTMConfigurationInfo\n    var system: UTMAppleConfigurationSystem\n    var virtualization: UTMAppleConfigurationVirtualization\n    @DecodableDefault.EmptyList var sharedDirectories: [UTMAppleConfigurationSharedDirectory]\n    var displays: [UTMAppleConfigurationDisplay]\n    var drives: [UTMAppleConfigurationDrive]\n    var networks: [UTMAppleConfigurationNetwork]\n\n    private enum CodingKeys: String, CodingKey {\n        case backend = \"Backend\"\n        case configurationVersion = \"ConfigurationVersion\"\n        case information       = \"Information\"\n        case system            = \"System\"\n        case virtualization    = \"Virtualization\"\n        case sharedDirectories = \"SharedDirectory\"\n        case displays          = \"Display\"\n        case drives            = \"Drive\"\n        case networks          = \"Network\"\n    }\n}\n\nstruct UTMConfigurationInfo: Decodable {\n    var uuid: String\n    var name: String\n\n    private enum CodingKeys: String, CodingKey {\n        case uuid = \"UUID\"\n        case name = \"Name\"\n    }\n}\n\nstruct UTMAppleConfigurationSystem: Decodable {\n    var architecture: String\n    var cpuCount: Int\n    var memorySize: Int\n    var boot: UTMAppleConfigurationBoot\n    var macPlatform: UTMAppleConfigurationMacPlatform?\n    var genericPlatform: UTMAppleConfigurationGenericPlatform?\n\n    private enum CodingKeys: String, CodingKey {\n        case architecture    = \"Architecture\"\n        case cpuCount        = \"CPUCount\"\n        case memorySize      = \"MemorySize\"\n        case boot            = \"Boot\"\n        case macPlatform     = \"MacPlatform\"\n        case genericPlatform = \"GenericPlatform\"\n    }\n}\n\nstruct UTMAppleConfigurationBoot: Decodable {\n    enum OperatingSystem: String, Codable {\n        case none  = \"None\"\n        case linux = \"Linux\"\n        case macOS = \"macOS\"\n    }\n    var operatingSystem: OperatingSystem\n    var linuxKernelPath: String?\n    var linuxCommandLine: String?\n    var linuxInitialRamdiskPath: String?\n    var efiVariableStoragePath: String?\n    var hasUefiBoot: Bool\n\n    private enum CodingKeys: String, CodingKey {\n        case operatingSystem      = \"OperatingSystem\"\n        case linuxKernelPath      = \"LinuxKernelPath\"\n        case linuxCommandLine     = \"LinuxCommandLine\"\n        case linuxInitialRamdiskPath = \"LinuxInitialRamdiskPath\"\n        case efiVariableStoragePath   = \"EfiVariableStoragePath\"\n        case hasUefiBoot          = \"UEFIBoot\"\n    }\n}\n\nstruct UTMAppleConfigurationMacPlatform: Decodable {\n    var hardwareModel: Data\n    var machineIdentifier: Data\n    var auxiliaryStoragePath: String?\n\n    private enum CodingKeys: String, CodingKey {\n        case hardwareModel        = \"HardwareModel\"\n        case machineIdentifier    = \"MachineIdentifier\"\n        case auxiliaryStoragePath = \"AuxiliaryStoragePath\"\n    }\n}\n\nstruct UTMAppleConfigurationGenericPlatform: Decodable {\n    var machineIdentifier: Data?\n\n    private enum CodingKeys: String, CodingKey {\n        case machineIdentifier\n    }\n}\n\nstruct UTMAppleConfigurationSharedDirectory: Decodable {\n    var bookmark: Data\n    var isReadOnly: Bool\n\n    private enum CodingKeys: String, CodingKey {\n        case bookmark   = \"Bookmark\"\n        case isReadOnly = \"ReadOnly\"\n    }\n}\n\nstruct UTMAppleConfigurationDisplay: Decodable {\n    var widthInPixels: Int\n    var heightInPixels: Int\n    var pixelsPerInch: Int\n    var isDynamicResolution: Bool\n\n    private enum CodingKeys: String, CodingKey {\n        case widthInPixels       = \"WidthPixels\"\n        case heightInPixels      = \"HeightPixels\"\n        case pixelsPerInch       = \"PixelsPerInch\"\n        case isDynamicResolution = \"DynamicResolution\"\n    }\n}\n\nstruct UTMAppleConfigurationDrive: Decodable {\n    var imageName: String?\n    var isReadOnly: Bool\n    var isNvme: Bool\n    var identifier: String\n\n    private enum CodingKeys: String, CodingKey {\n        case imageName   = \"ImageName\"\n        case isReadOnly  = \"ReadOnly\"\n        case isNvme      = \"Nvme\"\n        case identifier  = \"Identifier\"\n    }\n}\n\nstruct UTMAppleConfigurationNetwork: Decodable {\n    enum NetworkMode: String, Codable {\n        case shared  = \"Shared\"\n        case bridged = \"Bridged\"\n    }\n    var mode: NetworkMode\n    var macAddress: String\n    var bridgeInterface: String?\n\n    private enum CodingKeys: String, CodingKey {\n        case mode            = \"Mode\"\n        case macAddress      = \"MacAddress\"\n        case bridgeInterface = \"BridgeInterface\"\n    }\n}\n\nstruct UTMAppleConfigurationVirtualization: Decodable {\n    enum PointerDevice: String, Codable {\n        case disabled = \"Disabled\"\n        case mouse    = \"Mouse\"\n        case trackpad = \"Trackpad\"\n    }\n\n    enum KeyboardDevice: String, Codable {\n        case disabled = \"Disabled\"\n        case generic  = \"Generic\"\n        case mac      = \"Mac\"\n    }\n\n    var hasAudio: Bool\n    var hasBalloon: Bool\n    var hasEntropy: Bool\n    var keyboard: KeyboardDevice\n    var pointer: PointerDevice\n    var hasRosetta: Bool?\n    var hasClipboardSharing: Bool\n\n    private enum CodingKeys: String, CodingKey {\n        case hasAudio            = \"Audio\"\n        case hasBalloon          = \"Balloon\"\n        case hasEntropy          = \"Entropy\"\n        case keyboard            = \"Keyboard\"\n        case pointer             = \"Pointer\"\n        case hasTrackpad         = \"Trackpad\"\n        case rosetta             = \"Rosetta\"\n        case hasClipboardSharing = \"ClipboardSharing\"\n    }\n\n    init(from decoder: Decoder) throws {\n        let c = try decoder.container(keyedBy: CodingKeys.self)\n        hasAudio            = try c.decode(Bool.self, forKey: .hasAudio)\n        hasBalloon          = try c.decode(Bool.self, forKey: .hasBalloon)\n        hasEntropy          = try c.decode(Bool.self, forKey: .hasEntropy)\n\n        if let kbBool = try? c.decode(Bool.self, forKey: .keyboard) {\n            keyboard = kbBool ? .generic : .disabled\n        } else {\n            keyboard = try c.decode(KeyboardDevice.self, forKey: .keyboard)\n        }\n\n        if let ptBool = try? c.decode(Bool.self, forKey: .pointer) {\n            let tp = try c.decodeIfPresent(Bool.self, forKey: .hasTrackpad) ?? false\n            pointer = tp ? .trackpad : (ptBool ? .mouse : .disabled)\n        } else {\n            pointer = try c.decode(PointerDevice.self, forKey: .pointer)\n        }\n\n        hasRosetta           = try c.decodeIfPresent(Bool.self, forKey: .rosetta)\n        hasClipboardSharing  = try c.decode(Bool.self, forKey: .hasClipboardSharing)\n    }\n}\n"
  },
  {
    "path": "VirtualCore/Source/Import/UTM/UTMImporter.swift",
    "content": "import Foundation\nimport BuddyFoundation\nimport UniformTypeIdentifiers\nimport OSLog\n\nprivate let logger = Logger(subsystem: VirtualCoreConstants.subsystemName, category: \"UTMImporter\")\n\nextension UTType {\n    static let utmBundle = UTType(importedAs: \"com.utmapp.utm\")\n}\n\nstruct UTMImporter: VMImporter {\n    let appName = \"UTM\"\n    let fileType = UTType.utmBundle\n\n    @discardableResult\n    func importVirtualMachine(from path: FilePath, into library: VMLibraryController) async throws -> VBVirtualMachine {\n        let errorPrefix = \"This \\(appName) virtual machine has a configuration that’s not supported by VirtualBuddy.\"\n\n        let configPath = path + \"config.plist\"\n        try configPath.isFile.require(\"\\(errorPrefix) Missing a config.plist file.\")\n\n        logger.debug(\"Config file path is \\(configPath.string.quoted)\")\n\n        let dataPath = path + \"Data\"\n        try dataPath.isDirectory.require(\"\\(errorPrefix) Missing a \\\"Data\\\" directory.\")\n\n        logger.debug(\"Data path is \\(dataPath.string.quoted)\")\n\n        let config = try UTMAppleConfiguration(path: configPath)\n\n        logger.debug(\"Loaded UTM configuration with backend \\(String(optional: config.backend?.quoted), privacy: .public), version \\(String(optional: config.configurationVersion), privacy: .public)\")\n\n        let isMac = config.system.boot.operatingSystem != .linux\n\n        try isMac.require(\"VirtualBuddy can only import Mac virtual machines from \\(appName).\")\n\n        let drives = config.drives\n        try (!drives.isEmpty).require(\"\\(errorPrefix) No storage devices available. Needs at least a boot drive.\")\n\n        var model = try createBundle(forImportedVMPath: path, library: library)\n\n        func createStorageDevice(for drive: UTMAppleConfigurationDrive, isBootDrive: Bool) throws -> VBStorageDevice {\n            let imageName = try drive.imageName.require(\"\\(errorPrefix) Boot drive is missing an image name.\")\n\n            let imageSource = dataPath + imageName\n\n            logger.debug(\"Image source is \\(imageSource)\")\n\n            try imageSource.isFile.require(\"\\(errorPrefix) Storage device image not found at \\(imageSource.string.quoted).\")\n\n            var image = if isBootDrive {\n                VBManagedDiskImage.managedBootImage\n            } else {\n                VBManagedDiskImage(filename: imageSource.lastComponentWithoutExtension, size: 0)\n            }\n\n            image.size = UInt64(imageSource.fileSize ?? 0)\n\n            /// ``VBManagedDiskImage/filename`` is the name without a file extension, which is respected by ``VBManagedDiskImage/managedBootImage``, but in UTM configs the image name includes the file extension.\n            /// When importing from UTM, the file extension is removed from the image name and re-added here.\n            let libraryImagePath = (FilePath(model.bundleURL) + image.filename).appendingExtension(image.format.fileExtension)\n\n            logger.debug(\"Copying disk to VirtualBuddy bundle disk path \\(libraryImagePath)\")\n\n            try imageSource.copy(libraryImagePath)\n\n            return VBStorageDevice(utmDrive: drive, image: image, isBootDrive: isBootDrive)\n        }\n\n        if isMac {\n            logger.debug(\"Detected Mac platform, gathering platform data files\")\n\n            let platform = try config.system.macPlatform.require(\"\\(errorPrefix) Mac virtual machine is missing a Mac platform configuration.\")\n\n            let auxStorageName = platform.auxiliaryStoragePath ?? \"AuxiliaryStorage\"\n            let auxStorageSource = dataPath + FilePath(auxStorageName)\n\n            try auxStorageSource.isFile.require(\"\\(errorPrefix) Mac platform file \\(auxStorageName.quoted) was not found in \\(dataPath.lastComponent.quoted) directory.\")\n\n            try auxStorageSource.copy(FilePath(model.auxiliaryStorageURL))\n\n            try platform.hardwareModel.write(to: model.hardwareModelURL)\n            logger.debug(\"Wrote hardware model to \\(model.hardwareModelURL.path)\")\n\n            try platform.machineIdentifier.write(to: model.machineIdentifierURL)\n            logger.debug(\"Wrote machine identifier to \\(model.machineIdentifierURL.path)\")\n        }\n\n        model.metadata = VBVirtualMachine.Metadata(utm: config)\n        model.configuration = VBMacConfiguration(utm: config)\n\n        for (index, drive) in drives.enumerated() {\n            logger.debug(\"Processing drive #\\(index)\")\n\n            let device = try createStorageDevice(for: drive, isBootDrive: index == 0)\n\n            model.configuration.hardware.storageDevices.append(device)\n        }\n\n        return model\n    }\n}\n\nprivate extension VBVirtualMachine.Metadata {\n    init(utm: UTMAppleConfiguration) {\n        self.init(\n            uuid: UUID(uuidString: utm.information.uuid) ?? UUID(),\n            version: VBVirtualMachine.Metadata.currentVersion,\n            installFinished: true,\n            firstBootDate: nil,\n            lastBootDate: nil,\n            backgroundHash: .virtualBuddyBackground,\n            remoteInstallImageURL: nil,\n            installImageURL: nil\n        )\n    }\n}\n\nprivate extension VBMacConfiguration {\n    init(utm: UTMAppleConfiguration) {\n        self.init(\n            systemType: utm.system.boot.operatingSystem == .linux ? .linux : .mac,\n            hardware: VBMacDevice(utm: utm),\n            sharedFolders: [],\n            guestAdditionsEnabled: true,\n            rosettaSharingEnabled: utm.virtualization.hasRosetta == true,\n            captureSystemKeys: true\n        )\n    }\n}\n\nprivate extension VBMacDevice {\n    private static let utmBytesInMib = UInt64(1048576)\n\n    init(utm: UTMAppleConfiguration) {\n        self.init(\n            cpuCount: utm.system.cpuCount,\n            memorySize: UInt64(utm.system.memorySize) * Self.utmBytesInMib,\n            pointingDevice: utm.virtualization.pointer == .trackpad ? .trackpad : .mouse,\n            keyboardDevice: utm.virtualization.keyboard == .mac ? .mac : .generic,\n            displayDevices: utm.displays.map(VBDisplayDevice.init(utm:)),\n            networkDevices: utm.networks.map(VBNetworkDevice.init(utm:)),\n            soundDevices: [VBSoundDevice(utm: utm.virtualization)].compactMap { $0 },\n            storageDevices: []\n        )\n    }\n}\n\nprivate extension VBDisplayDevice {\n    init(utm: UTMAppleConfigurationDisplay) {\n        self.init(\n            id: UUID(),\n            name: \"Default\",\n            width: utm.widthInPixels,\n            height: utm.heightInPixels,\n            pixelsPerInch: utm.pixelsPerInch,\n            automaticallyReconfiguresDisplay: utm.isDynamicResolution\n        )\n    }\n}\n\nprivate extension VBNetworkDevice {\n    init(utm: UTMAppleConfigurationNetwork) {\n        let kind: Kind = utm.mode == .bridged ? .bridge : .NAT\n\n        let id: String = if kind == .bridge {\n            if let interface = utm.bridgeInterface {\n                interface\n            } else {\n                Self.automaticBridgeID\n            }\n        } else {\n            Self.defaultID\n        }\n\n        self.init(\n            id: id,\n            name: \"Default\",\n            kind: kind,\n            macAddress: utm.macAddress\n        )\n    }\n}\n\nprivate extension VBSoundDevice {\n    init?(utm: UTMAppleConfigurationVirtualization) {\n        guard utm.hasAudio else { return nil }\n\n        self.init(enableOutput: utm.hasAudio, enableInput: utm.hasAudio)\n    }\n}\n\nprivate extension VBStorageDevice {\n    init(utmDrive: UTMAppleConfigurationDrive, image: VBManagedDiskImage, isBootDrive: Bool) {\n        self.init(\n            id: utmDrive.identifier,\n            isBootVolume: isBootDrive,\n            isEnabled: true,\n            isReadOnly: utmDrive.isReadOnly,\n            isUSBMassStorageDevice: false,\n            backing: .managedImage(image)\n        )\n    }\n}\n"
  },
  {
    "path": "VirtualCore/Source/Import/VMImporter+Helpers.swift",
    "content": "import Foundation\nimport BuddyFoundation\n\nextension VMImporter {\n    func createBundle(forImportedVMPath path: FilePath, library: VMLibraryController) throws -> VBVirtualMachine {\n        let name = path.lastComponentWithoutExtension\n\n        let vmURL = library.libraryURL\n            .appendingPathComponent(name)\n            .appendingPathExtension(VBVirtualMachine.bundleExtension)\n\n        guard !vmURL.isReadableDirectory else {\n            throw \"You already have a virtual machine named \\(name.quoted). If you’d like to import this virtual machine from \\(appName), please rename it first.\"\n        }\n\n        let model = try VBVirtualMachine(bundleURL: vmURL)\n\n        return model\n    }\n}\n"
  },
  {
    "path": "VirtualCore/Source/Import/VMImporter.swift",
    "content": "import Foundation\nimport BuddyFoundation\nimport UniformTypeIdentifiers\n\n/// Adopted by types that can import virtual machines from other apps.\n@MainActor\npublic protocol VMImporter {\n    /// The name of the app that this importer can import VM bundles from.\n    var appName: String { get }\n\n    /// The UTI for the virtual machine bundle that can be imported by this importer.\n    var fileType: UTType { get }\n\n    /// Performs all actions required to convert the virtual machine from the other app into VirtualBuddy, including copying it into the user's library.\n    ///\n    /// - note: Importers don't need to call `saveMetadata` on the VM model, this is done automatically after a successful import.\n    @discardableResult\n    func importVirtualMachine(from path: FilePath, into library: VMLibraryController) async throws -> VBVirtualMachine\n}\n"
  },
  {
    "path": "VirtualCore/Source/Import/VMImporterRegistry.swift",
    "content": "import Foundation\nimport BuddyFoundation\nimport OSLog\nimport UniformTypeIdentifiers\n\nprivate let logger = Logger(subsystem: VirtualCoreConstants.subsystemName, category: \"VMImporterRegistry\")\n\n/// Keeps track of available importers and helps find the importer to use for a given external VM bundle.\n@MainActor\npublic struct VMImporterRegistry {\n    public static let `default` = VMImporterRegistry()\n\n    private let importers: [VMImporter] = [\n        UTMImporter()\n    ]\n\n    public var supportedFileTypes: Set<UTType> {\n        Set(importers.map(\\.fileType))\n    }\n\n    /// Returns the importer that can handle the file at the specified path.\n    public func importer(for filePath: FilePath) -> VMImporter? {\n        logger.debug(\"Look up importer for \\(filePath)\")\n\n        guard let uti = filePath.contentType else {\n            logger.error(\"Couldn't determine UTI for importing \\(filePath)\")\n            return nil\n        }\n\n        guard let importer = importers.first(where: { uti.conforms(to: $0.fileType) }) else {\n            logger.notice(\"No importer found for type \\(uti.identifier, privacy: .public)\")\n            return nil\n        }\n\n        logger.notice(\"Matched importer \\(importer.appName.quoted, privacy: .public) for type \\(uti.identifier, privacy: .public)\")\n\n        return importer\n    }\n}\n"
  },
  {
    "path": "VirtualCore/Source/Models/BlurHashToken.swift",
    "content": "import Foundation\n\n/// Combination of blur hash number of components with the blur hash string itself.\n///\n/// This is used when a blur hash must be stored and it's possible that the number of components\n/// will change between different sources of the blur hash, ensuring that clients rendering the blur hash\n/// image always use the correct number of components.\npublic struct BlurHashToken: Hashable, Codable, Sendable, ProvidesEmptyPlaceholder {\n    public var value: String\n    public var size: Int\n\n    public init(value: String, size: Int = .vbBlurHashSize) {\n        self.value = value\n        self.size = size\n    }\n\n    public static let empty = BlurHashToken.virtualBuddyBackground\n}\n\npublic extension Int {\n    /// The size of blur hash used by VirtualBuddy.\n    static let vbBlurHashSize = 4\n}\n\npublic extension BlurHashToken {\n    /// Hardcoded VirtualBuddy orange background blur hash.\n    static let virtualBuddyBackground = BlurHashToken(\n        value: \"U4H09BEfIY$%U7ocVcM$8%R*M}f~zwIXcArd\",\n        size: 4\n    )\n\n    /// Hardcoded VirtualBuddy background blur hash for Linux VMs.\n    static let virtualBuddyBackgroundLinux = BlurHashToken(\n        value: \"UsLn]CG1I;t19uxAR$jJOXjEj=ayn%fkjubI\",\n        size: 4\n    )\n}\n"
  },
  {
    "path": "VirtualCore/Source/Models/Configuration/ConfigurationModels+Summary.swift",
    "content": "//\n//  ConfigurationModels+Summary.swift\n//  VirtualCore\n//\n//  Created by Guilherme Rambo on 19/07/22.\n//\n\nimport Foundation\n\npublic extension VBMacConfiguration {\n\n    var generalSummary: String {\n        \"\\(hardware.cpuCount) CPUs / \\(hardware.memorySize / 1024 / 1024 / 1024) GB\"\n    }\n\n    var storageSummary: String {\n        if hardware.storageDevices.count > 1 {\n            return \"\\(hardware.storageDevices.count) Devices\"\n        } else {\n            return \"Boot Only\"\n        }\n    }\n\n    var displaySummary: String {\n        guard let display = hardware.displayDevices.first else { return \"No Displays\" }\n        return \"\\(display.width)x\\(display.height)x\\(display.pixelsPerInch)\"\n    }\n\n    var soundSummary: String {\n        guard let sound = hardware.soundDevices.first else { return \"No Sound\" }\n        return sound.enableInput ? \"Input / Output\" : \"Output Only\"\n    }\n\n    var sharingSummary: String {\n        let foldersSum: String\n        if sharedFolders.count > 1 {\n            foldersSum = \"\\(sharedFolders.count) Folders\"\n        } else if sharedFolders.isEmpty {\n            foldersSum = \"\"\n        } else {\n            foldersSum = \"One Folder\"\n        }\n\n        return foldersSum.isEmpty ? \"None\" : foldersSum\n    }\n\n    var networkSummary: String {\n        guard let network = hardware.networkDevices.first else { return \"No Network\" }\n        return network.kind.name\n    }\n    \n    var pointingDeviceSummary: String { hardware.pointingDevice.kind.name }\n\n    var keyboardDeviceSummary: String { hardware.keyboardDevice.kind.name }\n\n    var guestAppSummary: String {\n        guestAdditionsEnabled ? \"Enabled\" : \"Disabled\"\n    }\n\n}\n"
  },
  {
    "path": "VirtualCore/Source/Models/Configuration/ConfigurationModels+Validation.swift",
    "content": "//\n//  ConfigurationModels+Validation.swift\n//  VirtualCore\n//\n//  Created by Guilherme Rambo on 19/07/22.\n//\n\nimport Cocoa\nimport UniformTypeIdentifiers\nimport Virtualization\n\npublic extension VBMacConfiguration {\n    \n    func validate(for model: VBVirtualMachine, skipVirtualizationConfig: Bool) async -> SupportState {\n        var tempModel = model\n        tempModel.configuration = self\n\n        guard !skipVirtualizationConfig else {\n            return hostSupportState\n        }\n\n        do {\n            let config = try await VMInstance.makeConfiguration(for: tempModel)\n\n            try config.validate()\n            \n            return hostSupportState\n        } catch {\n            return hostSupportState.merged(with: .unsupported([error.localizedDescription]))\n        }\n    }\n    \n}\n\npublic extension VBMacConfiguration {\n    /// The state of this configuration for the current host, used to indicate\n    /// possible issues the user may have with it, or to prevent unsupported\n    /// configurations from being saved.\n    var hostSupportState: SupportState {\n        var warnings = [String]()\n        var errors = [String]()\n        \n        if !hardware.pointingDevice.kind.isSupportedByHost {\n            errors.append(\"\\(hardware.pointingDevice.kind.name) requires macOS 13 or later.\")\n        }\n        if hasSharedFolders {\n            if VBMacConfiguration.isFileSharingSupported {\n                warnings.append(VBMacConfiguration.fileSharingNotice)\n            } else {\n                errors.append(VBMacConfiguration.fileSharingNotice)\n            }\n        }\n        if hardware.networkDevices.contains(where: { $0.kind == .bridge }), !VBNetworkDevice.appSupportsBridgedNetworking {\n            errors.append(VBNetworkDevice.bridgeUnsupportedMessage)\n        }\n        \n        return SupportState(errors: errors, warnings: warnings)\n    }\n\n    static let isFileSharingSupported = true\n\n    static let rosettaSupported: Bool = {\n        VZLinuxRosettaDirectoryShare.availability != VZLinuxRosettaAvailability.notSupported\n    }()\n\n    static func rosettaInstalled() -> Bool {\n        VZLinuxRosettaDirectoryShare.availability == VZLinuxRosettaAvailability.installed\n    }\n\n    static let fileSharingNotice: String = {\n        let tip = \"For older versions, you can use the standard macOS file sharing feature in System Preferences > Sharing.\"\n\n        if isFileSharingSupported {\n            return \"File sharing requires the virtual machine to be running macOS 13 or later. \\(tip)\"\n        } else {\n            return \"File sharing requires both the host Mac and the virtual machine to be running macOS 13 or later. \\(tip)\"\n        }\n    }()\n\n    static func rosettaSharingNotice() -> String? {\n        if rosettaSupported {\n            if rosettaInstalled() {\n                return nil\n            } else {\n                return \"Rosetta is not installed. Run `softwareupdate --install-rosetta` to install Rosetta.\"\n            }\n        } else {\n            return \"Rosetta for Linux requires the host Mac to be running macOS 13 or later.\"\n        }\n    }\n}\n\npublic extension VBMacConfiguration.SupportState {\n    var errors: [String] {\n        guard case .unsupported(let errors) = self else {\n            return []\n        }\n        return errors\n    }\n    \n    var warnings: [String] {\n        guard case .warnings(let warnings) = self else {\n            return []\n        }\n        return warnings\n    }\n    \n    var allowsSaving: Bool { errors.isEmpty }\n    \n    init(errors: [String] = [], warnings: [String] = []) {\n        if errors.isEmpty {\n            if warnings.isEmpty {\n                self = .supported\n            } else {\n                self = .warnings(warnings)\n            }\n        } else {\n            self = .unsupported(errors)\n        }\n    }\n    \n    func merged(with other: Self) -> Self {\n        Self.init(errors: errors + other.errors, warnings: warnings + other.warnings)\n    }\n}\n\npublic extension VBNetworkDevice {\n    static var appSupportsBridgedNetworking: Bool {\n        NSApplication.shared.hasEntitlement(\"com.apple.vm.networking\")\n    }\n    \n    static let bridgeUnsupportedMessage = \"Bridged network devices are not available in this build of the app.\"\n}\n\npublic extension VBDisplayDevice {\n    static var automaticallyReconfiguresDisplaySupportedByHost: Bool {\n        if #available(macOS 14.0, *) {\n            return true\n        } else {\n            return false\n        }\n    }\n}\n\npublic extension VBPointingDevice.Kind {\n    var warning: String? {\n        guard self == .trackpad else { return nil }\n        return \"Trackpad is only recognized by VMs running macOS 13 and later.\"\n    }\n    \n    var error: String? {\n        guard !isSupportedByHost else { return nil }\n        return \"Trackpad requires both host and VM to be on macOS 13 or later.\"\n    }\n\n    var isSupportedByHost: Bool { true }\n}\n\npublic extension VBKeyboardDevice.Kind {\n    var warning: String? {\n        guard self != .generic else { return nil }\n        return \"Mac keyboard is only recognized by VMs running macOS 13 and later.\"\n    }\n\n    var error: String? {\n        guard !isSupportedByHost else { return nil }\n        return \"Mac keyboard requires macOS 14 or later on host and macOS 13 or later on VM.\"\n    }\n\n    var isSupportedByHost: Bool {\n        switch self {\n        case .generic:\n            return true\n        case .mac:\n            if #available(macOS 14.0, *) {\n                return true\n            } else {\n                return false\n            }\n        }\n    }\n}\n\npublic extension VBGuestType {\n    var isSupportedByHost: Bool { true }\n\n    static let supportedByHost: [VBGuestType] = {\n        allCases.filter(\\.isSupportedByHost)\n    }()\n\n    var supportsVirtualTrackpad: Bool { self == .mac }\n\n    var supportsKeyboardCustomization: Bool { self == .mac }\n\n    var supportsDisplayPPI: Bool { self == .mac }\n\n    var supportsRosettaMount: Bool { self == .linux }\n\n    var supportsStateRestoration: Bool { self == .mac }\n\n    var supportedRestoreImageTypes: Set<UTType> {\n        switch self {\n        case .mac: return [.ipsw]\n        case .linux: return [.iso, .img]\n        }\n    }\n\n    var supportsGuestApp: Bool { self == .mac }\n\n}\n\npublic extension VBVirtualMachine {\n    var supportsStateRestoration: Bool { configuration.systemType.supportsStateRestoration }\n}\n\npublic extension UTType {\n    static let ipsw = UTType(filenameExtension: \"ipsw\")!\n    static let iso = UTType(filenameExtension: \"iso\")!\n    static let img = UTType(filenameExtension: \"img\")!\n}\n"
  },
  {
    "path": "VirtualCore/Source/Models/Configuration/ConfigurationModels.swift",
    "content": "//\n//  ConfigurationModels.swift\n//  VirtualCore\n//\n//  Created by Guilherme Rambo on 17/07/22.\n//\n\nimport Foundation\nimport SystemConfiguration\n\n/**\n ## Note to contributors:\n\n Care must be taken when changing any of the structs in this file that conform to `Codable`,\n since users may have VMs configured using older versions of the structs. Failure to decode the configuration\n after updates to how it's stored can result in data loss.\n\n In order to keep backwards-compatibility for new properties without having to make everything optional,\n the `@DecodableDefault` property wrapper can be used.\n */\n\npublic enum VBGuestType: String, Identifiable, Codable, CaseIterable, ProvidesEmptyPlaceholder {\n    public var id: RawValue { rawValue }\n    \n    case mac\n    case linux\n\n    public static var empty: VBGuestType { .mac }\n}\n\npublic struct VBMacConfiguration: Hashable, Codable {\n    \n    public enum SupportState: Hashable {\n        case supported\n        case warnings([String])\n        case unsupported([String])\n    }\n\n    public static let currentVersion = 0\n    @DecodableDefault.Zero public var version = VBMacConfiguration.currentVersion\n\n    @DecodableDefault.FirstCase\n    public var systemType: VBGuestType = .mac\n\n    public var hardware = VBMacDevice.default\n    public var sharedFolders = [VBSharedFolder]()\n    @DecodableDefault.True\n    public var guestAdditionsEnabled = true\n\n    @DecodableDefault.False\n    public var rosettaSharingEnabled = false\n\n    @DecodableDefault.True public var captureSystemKeys = true\n\n    public var hasSharedFolders: Bool { !sharedFolders.filter(\\.isEnabled).isEmpty }\n\n}\n\n// MARK: - Hardware Configuration\n\n/// Configures a disk image that's managed by VirtualBuddy, as opposed to a disk image that the user provides.\n/// **Read the note at the top of this file before modifying this**\npublic struct VBManagedDiskImage: Identifiable, Hashable, Codable {\n    public init(id: String = UUID().uuidString, filename: String, size: UInt64, format: VBManagedDiskImage.Format = .sparse) {\n        self.id = id\n        self.filename = filename\n        self.size = size\n        self.format = format\n    }\n    \n    public static let defaultBootDiskImageSize: UInt64 = 64 * .storageGigabyte\n    public static let minimumBootDiskImageSize: UInt64 = 2 * .storageGigabyte\n    public static let maximumBootDiskImageSize: UInt64 = 8 * .storageTerabyte\n\n    public static let minimumExtraDiskImageSize: UInt64 = 1 * .storageGigabyte\n    public static let maximumExtraDiskImageSize: UInt64 = 512 * .storageGigabyte\n    \n    public enum Format: Int, Codable {\n        case raw\n        case dmg\n        case sparse\n        case asif\n\n        /// The default format for the boot disk image in the current environment.\n        static var defaultBootDisk: Format {\n            if #available(macOS 26, *), VBSettings.current.bootDiskImagesUseASIF {\n                .asif\n            } else {\n                .raw\n            }\n        }\n\n        var fileExtension: String {\n            switch self {\n            case .raw: \"img\"\n            case .dmg: \"dmg\"\n            case .sparse: \"sparseimage\"\n            case .asif: \"asif\"\n            }\n        }\n\n        public var isSupported: Bool {\n            switch self {\n            case .raw, .dmg, .sparse:\n                true\n            case .asif:\n                if #available(macOS 26, *) {\n                    true\n                } else {\n                    false\n                }\n            }\n        }\n    }\n    \n    public var id: String = UUID().uuidString\n    public var filename: String\n    public var size: UInt64\n    public var format: Format = .sparse\n\n    // Not a stored property because Format.defaultBootDisk can change based on user preferences.\n    public static var managedBootImage: VBManagedDiskImage {\n        VBManagedDiskImage(\n            id: \"__BOOT__\",\n            filename: \"Disk\",\n            size: Self.defaultBootDiskImageSize,\n            format: .defaultBootDisk\n        )\n    }\n    \n    public static var template: VBManagedDiskImage {\n        VBManagedDiskImage(\n            filename: RandomNameGenerator.shared.newName(),\n            size: VBManagedDiskImage.minimumExtraDiskImageSize,\n            format: .raw\n        )\n    }\n}\n\n/// Configures a storage device.\n/// **Read the note at the top of this file before modifying this**\npublic struct VBStorageDevice: Identifiable, Hashable, Codable {\n    public init(id: String = UUID().uuidString, isBootVolume: Bool, isEnabled: Bool = true, isReadOnly: Bool, isUSBMassStorageDevice: Bool, backing: VBStorageDevice.BackingStore) {\n        self.id = id\n        self.isBootVolume = isBootVolume\n        self.isEnabled = isEnabled\n        self.isReadOnly = isReadOnly\n        self.isUSBMassStorageDevice = isUSBMassStorageDevice\n        self.backing = backing\n    }\n    \n    /// The underlying storage for the device, which currently can be either a custom disk image,\n    /// or a disk image managed by VirtualBuddy.\n    public enum BackingStore: Hashable, Codable {\n        /// Image created and managed by VirtualBuddy.\n        case managedImage(VBManagedDiskImage)\n        /// Arbitrary image provided by the user, file must exist on disk at the same location\n        /// if the image is to be used again in the future.\n        case customImage(URL)\n    }\n    \n    public var id: String = UUID().uuidString\n    /// `true` for the initial boot volume (Disk.img) that's created by VirtualBuddy.\n    public internal(set) var isBootVolume: Bool\n    /// Setting to `false` disables the storage device without removing it from the VM.\n    @DecodableDefault.True\n    public var isEnabled: Bool\n    /// `true` if this storage device represents a clone created for a virtual machine save state.\n    @DecodableDefault.False\n    public var isSavedStateClone: Bool\n    /// `true` when the device can't be written to by the VM.\n    public var isReadOnly: Bool\n    /// `true` when the device represents an external USB mass storage device in the guest OS.\n    public var isUSBMassStorageDevice: Bool\n    /// The underlying storage for the storage device, which can currently be a disk image managed\n    /// by VirtualBuddy, or a custom image provided by the user.\n    public var backing: BackingStore\n    \n    public static var defaultBootDevice: VBStorageDevice {\n        VBStorageDevice(\n            isBootVolume: true,\n            isReadOnly: false,\n            isUSBMassStorageDevice: false,\n            backing: .managedImage(.managedBootImage)\n        )\n    }\n\n    public static var template: VBStorageDevice {\n        let name = RandomNameGenerator.shared.newName()\n        \n        let image = VBManagedDiskImage(\n            filename: name,\n            size: VBManagedDiskImage.minimumExtraDiskImageSize,\n            format: .sparse\n        )\n        \n        return VBStorageDevice(\n            isBootVolume: false,\n            isReadOnly: false,\n            isUSBMassStorageDevice: false,\n            backing: .managedImage(image)\n        )\n    }\n    \n    public var displayName: String {\n        guard !isBootVolume else { return \"Boot\" }\n        \n        switch backing {\n        case .customImage(let url):\n            return url.deletingPathExtension().lastPathComponent\n        case .managedImage(let image):\n            return image.filename\n        }\n    }\n}\n\n/// Configures a display device.\n/// **Read the note at the top of this file before modifying this**\npublic struct VBDisplayDevice: Identifiable, Hashable, Codable {\n    public init(id: UUID = UUID(), name: String = \"Default\", width: Int = 1920, height: Int = 1080, pixelsPerInch: Int = 144, automaticallyReconfiguresDisplay: Bool = false) {\n        self.id = id\n        self.name = name\n        self.width = width\n        self.height = height\n        self.pixelsPerInch = pixelsPerInch\n        self.automaticallyReconfiguresDisplay = automaticallyReconfiguresDisplay\n    }\n    \n    public var id = UUID()\n    public var name = \"Default\"\n    public var width = 1920\n    public var height = 1080\n    public var pixelsPerInch = 144\n    @DecodableDefault.False public var automaticallyReconfiguresDisplay = false\n}\n\n/// Configures a network device.\n/// **Read the note at the top of this file before modifying this**\npublic struct VBNetworkDevice: Identifiable, Hashable, Codable {\n    public static let defaultID = \"Default\"\n    public static let automaticBridgeID = \"Automatic Bridge\"\n\n    public init(id: String = VBNetworkDevice.defaultID, name: String = \"Default\", kind: VBNetworkDevice.Kind = Kind.NAT, macAddress: String = VZMACAddress.randomLocallyAdministered().string.uppercased()) {\n        self.id = id\n        self.name = name\n        self.kind = kind\n        self.macAddress = macAddress\n    }\n    \n    public enum Kind: Int, Identifiable, CaseIterable, Codable {\n        public var id: RawValue { rawValue }\n\n        case NAT\n        case bridge\n        \n        public var name: String {\n            switch self {\n            case .NAT: return \"NAT\"\n            case .bridge: return \"Bridge\"\n            }\n        }\n    }\n\n    public var id = VBNetworkDevice.defaultID\n    public var name = \"Default\"\n    public var kind = Kind.NAT\n    public var macAddress = VZMACAddress.randomLocallyAdministered().string.uppercased()\n}\n\n/// Configures a pointing device, such as a mouse or trackpad.\n/// **Read the note at the top of this file before modifying this**\npublic struct VBPointingDevice: Hashable, Codable {\n    public enum Kind: Int, Identifiable, CaseIterable, Codable {\n        public var id: RawValue { rawValue }\n\n        case mouse\n        case trackpad\n        \n        public var name: String {\n            switch self {\n            case .mouse: return \"Mouse\"\n            case .trackpad: return \"Trackpad\"\n            }\n        }\n    }\n\n    public var kind = Kind.mouse\n}\n\n/// Configures a keyboard device.\n/// **Read the note at the top of this file before modifying this**\npublic struct VBKeyboardDevice: Hashable, Codable, ProvidesEmptyPlaceholder {\n    public enum Kind: Int, Identifiable, CaseIterable, Codable {\n        public var id: RawValue { rawValue }\n\n        case generic\n        case mac\n\n        public var name: String {\n            switch self {\n            case .generic: return \"Generic\"\n            case .mac: return \"Mac\"\n            }\n        }\n    }\n\n    public var kind = Kind.generic\n\n    public static var empty: VBKeyboardDevice { VBKeyboardDevice(kind: .generic) }\n}\n\n/// Configures sound input/output.\n/// **Read the note at the top of this file before modifying this**\npublic struct VBSoundDevice: Identifiable, Hashable, Codable {\n    public var id = UUID()\n    public var name = \"Default\"\n    public var enableOutput = true\n    public var enableInput = true\n}\n\n/// Describes a Mac VM with its associated hardware configuration.\n/// **Read the note at the top of this file before modifying this**\npublic struct VBMacDevice: Hashable, Codable {\n    public init(cpuCount: Int, memorySize: UInt64, pointingDevice: VBPointingDevice, keyboardDevice: VBKeyboardDevice, displayDevices: [VBDisplayDevice], networkDevices: [VBNetworkDevice], soundDevices: [VBSoundDevice], storageDevices: [VBStorageDevice], NVRAM: [VBNVRAMVariable] = [VBNVRAMVariable]()) {\n        self.cpuCount = cpuCount\n        self.memorySize = memorySize\n        self.pointingDevice = pointingDevice\n        self.keyboardDevice = keyboardDevice\n        self.displayDevices = displayDevices\n        self.networkDevices = networkDevices\n        self.soundDevices = soundDevices\n        self.storageDevices = storageDevices\n        self.NVRAM = NVRAM\n    }\n    \n    public var cpuCount: Int\n    public var memorySize: UInt64\n    public var pointingDevice: VBPointingDevice\n    @DecodableDefault.EmptyPlaceholder\n    public var keyboardDevice: VBKeyboardDevice\n    public var displayDevices: [VBDisplayDevice]\n    public var networkDevices: [VBNetworkDevice]\n    public var soundDevices: [VBSoundDevice]\n    public var NVRAM = [VBNVRAMVariable]()\n    \n    public var storageDevices: [VBStorageDevice] {\n        /// Special handling for migration from previous versions.\n        /// Ensures all VMs have the boot storage device set if no storage devices are\n        /// present in the loaded configuration.\n        get { _storageDevices ?? [.defaultBootDevice] }\n        set { _storageDevices = newValue }\n    }\n    private var _storageDevices: [VBStorageDevice]? = nil\n    \n    mutating func addMissingBootDeviceIfNeeded() {\n        guard _storageDevices == nil else { return }\n        _storageDevices = [.defaultBootDevice]\n    }\n}\n\n// MARK: - Sharing And Other Features\n\n/// Configures a folder that's shared between the host and the guest.\n/// **Read the note at the top of this file before modifying this**\npublic struct VBSharedFolder: Identifiable, Hashable, Codable {\n    /// The name the VirtualBuddy share will have in the guest OS.\n    ///\n    /// This is the name that must be used with the `mount` command, like so:\n    /// ```\n    /// mkdir -p ~/Desktop/VirtualBuddyShared && mount -t virtiofs VirtualBuddyShared ~/Desktop/VirtualBuddyShared\n    /// ```\n    public static let virtualBuddyShareName = \"VirtualBuddyShared\"\n\n    public static let rosettaShareName = \"Rosetta\"\n\n    public init(id: UUID = UUID(), url: URL, isEnabled: Bool = true, isReadOnly: Bool = false, customMountPointName: String? = nil) {\n        self.id = id\n        self.url = url\n        self.isEnabled = isEnabled\n        self.isReadOnly = isReadOnly\n        self.customMountPointName = customMountPointName\n    }\n\n    public var id = UUID()\n    public var name: String { url.lastPathComponent }\n    public var url: URL\n    @DecodableDefault.True\n    public var isEnabled = true\n    public var isReadOnly = false\n    \n    /// A custom name for the folder when mounted in the guest OS.\n    public var customMountPointName: String? = nil\n    /// The default name for the folder when mounted in the guest OS\n    var mountPointName: String { url.lastPathComponent }\n    /// The effective name this folder will have when mounted in the guest OS.\n    public var effectiveMountPointName: String { customMountPointName ?? mountPointName }\n}\n\npublic extension VBMacConfiguration {\n    func hasSharedFolder(with url: URL) -> Bool {\n        sharedFolders.contains(where: { $0.url.path == url.path })\n    }\n\n    @discardableResult\n    mutating func addSharedFolder(with url: URL) throws -> VBSharedFolder {\n        guard url.isReadableDirectory else {\n            throw Failure(\"VirtualBuddy couldn't access the selected location, or it is not a directory.\")\n        }\n\n        guard !hasSharedFolder(with: url) else {\n            throw Failure(\"That directory is already in the shared folders.\")\n        }\n\n        /// Figure out how many \"Folder\", \"Folder 1\", \"Folder 2\", and so on we have in the shared folders collection.\n        let conflictingMountPointCount = sharedFolders.filter { $0.mountPointName.trimmingCharacters(in: .decimalDigits.union(.whitespacesAndNewlines)).hasPrefix(url.lastPathComponent) }.count\n        \n        let customMountPointName: String?\n        if conflictingMountPointCount > 0 {\n            customMountPointName = \"\\(url.lastPathComponent) \\(conflictingMountPointCount + 1)\"\n        } else {\n            customMountPointName = nil\n        }\n        \n        let folder = VBSharedFolder(url: url, customMountPointName: customMountPointName)\n\n        sharedFolders.append(folder)\n\n        return folder\n    }\n\n    mutating func removeSharedFolders(with identifiers: Set<VBSharedFolder.ID>) {\n        sharedFolders.removeAll(where: { identifiers.contains($0.id) })\n    }\n    \n    func hasSharedFolders(inVolume volumeURL: URL) -> Bool {\n        sharedFolders.contains(where: { $0.externalVolumeURL == volumeURL })\n    }\n}\n\npublic extension VBSharedFolder {\n    var shortName: String {\n        if url.path.hasPrefix(NSHomeDirectory()) {\n            return url.lastPathComponent\n        } else {\n            return url.path\n        }\n    }\n\n    var shortNameForDialogs: String { url.lastPathComponent }\n\n    var externalVolumeURL: URL? { url.externalVolumeURL }\n\n    var errorMessage: String? {\n        guard !url.isReadableDirectory else { return nil }\n        if let externalVolumeURL, !externalVolumeURL.isReadableDirectory {\n            return \"This directory is in a removable volume that's not currently available.\"\n        } else {\n            return \"This directory doesn't exist, or VirtualBuddy can't read it right now.\"\n        }\n    }\n\n    var isAvailable: Bool { url.isReadableDirectory }\n}\n\npublic extension URL {\n    var isReadableDirectory: Bool {\n        var isDir = ObjCBool(false)\n        guard FileManager.default.fileExists(atPath: path, isDirectory: &isDir), isDir.boolValue else {\n            return false\n        }\n        return true\n    }\n}\n\n\n\n// MARK: - Default Devices\n\npublic extension VBMacConfiguration {\n    static var `default`: VBMacConfiguration { .init() }\n\n    func guestType(_ type: VBGuestType) -> Self {\n        var mSelf = self\n        mSelf.systemType = type\n        return mSelf\n    }\n}\n\npublic extension VBMacDevice {\n    static var `default`: VBMacDevice {\n        VBMacDevice(\n            cpuCount: .vb_suggestedVirtualCPUCount,\n            memorySize: .vb_suggestedMemorySize,\n            pointingDevice: .default,\n            keyboardDevice: .default,\n            displayDevices: [.default],\n            networkDevices: [.default],\n            soundDevices: [.default],\n            storageDevices: [.defaultBootDevice]\n        )\n    }\n}\n\npublic extension VBPointingDevice {\n    static var `default`: VBPointingDevice { .init() }\n\n    static let mouse = VBPointingDevice(kind: .mouse)\n    static let trackpad = VBPointingDevice(kind: .mouse)\n}\n\npublic extension VBKeyboardDevice {\n    static var `default`: VBKeyboardDevice { .empty }\n\n    static let generic = VBKeyboardDevice(kind: .generic)\n    static let mac = VBKeyboardDevice(kind: .mac)\n}\n\npublic extension VBNetworkDevice {\n    static var `default`: VBNetworkDevice { .init() }\n}\n\npublic extension VBSoundDevice {\n    static var `default`: VBSoundDevice { .init() }\n}\n\npublic extension VBDisplayDevice {\n    static var `default`: VBDisplayDevice { .matchHost }\n\n    static var fallback: VBDisplayDevice { .init() }\n\n    static var matchHost: VBDisplayDevice {\n        guard let screen = NSScreen.main else { return .fallback }\n\n        let resolution = screen.dpi\n        guard let size = screen.deviceDescription[.size] as? NSSize else { return .fallback }\n\n        let pointHeight = size.height - screen.safeAreaInsets.top\n\n        return VBDisplayDevice(\n            id: UUID(),\n            name: ProcessInfo.processInfo.vb_hostName,\n            width: Int(size.width * screen.backingScaleFactor),\n            height: Int(pointHeight * screen.backingScaleFactor),\n            pixelsPerInch: Int(resolution.width)\n        )\n    }\n\n    static var sizeToFit: VBDisplayDevice {\n        guard let screen = NSScreen.main,\n              let size = screen.deviceDescription[.size] as? NSSize else { return .fallback }\n\n        let reference = VZMacGraphicsDisplayConfiguration(for: screen, sizeInPoints: size)\n\n        return VBDisplayDevice(\n            id: UUID(),\n            name: ProcessInfo.processInfo.vb_hostName,\n            width: reference.widthInPixels,\n            height: reference.heightInPixels,\n            pixelsPerInch: reference.pixelsPerInch\n        )\n    }\n}\n\n// MARK: - Presets\n\npublic struct VBDisplayPreset: Identifiable, Hashable {\n    public var id: String { name }\n    public var name: String\n    public var defaultForGuestType: VBGuestType? = nil\n    public var limitToGuestType: VBGuestType? = nil\n    public var device: VBDisplayDevice\n    public var warning: String? = nil\n    public var isAvailable: () -> Bool = { true }\n\n    public func hash(into hasher: inout Hasher) { hasher.combine(id) }\n\n    public static func ==(lhs: Self, rhs: Self) -> Bool { lhs.id == rhs.id }\n}\n\npublic extension VBDisplayPreset {\n    static let fullHD = VBDisplayPreset(\n        name: \"Full HD\",\n        defaultForGuestType: .linux,\n        device: VBDisplayDevice(\n            name: \"1920x1080@144\",\n            width: 1920,\n            height: 1080,\n            pixelsPerInch: 144\n        )\n    )\n\n    static let fourK = VBDisplayPreset(\n        name: \"4K\",\n        device: .init(\n            name: \"3840x2160\",\n            width: 3840,\n            height: 2160\n        )\n    )\n\n    static let four5K = VBDisplayPreset(\n        name: \"4.5K\",\n        limitToGuestType: .linux,\n        device: .init(\n            name: \"4480x2520@72\",\n            width: 4480,\n            height: 2520,\n            pixelsPerInch: 72\n        )\n    )\n\n    static let four5KRetina = VBDisplayPreset(\n        name: \"4.5K Retina\",\n        defaultForGuestType: .mac,\n        limitToGuestType: .mac,\n        device: .init(\n            name: \"4480x2520@218\",\n            width: 4480,\n            height: 2520,\n            pixelsPerInch: 218\n        )\n    )\n\n    static let matchMainDisplay = VBDisplayPreset(\n        name: \"Match \\\"\\(ProcessInfo.processInfo.vb_mainDisplayName)\\\"\",\n        device: .matchHost,\n        warning: \"If things look small in the VM after boot, go to System Preferences and select a HiDPI scaled reslution for the display.\",\n        isAvailable: {\n            /// This preset is only relevant for displays with a notch.\n            ProcessInfo.processInfo.vb_mainDisplayHasNotch\n        })\n\n    static let sizeToFitMainDisplay = VBDisplayPreset(\n        name: \"Size to fit in \\\"\\(ProcessInfo.processInfo.vb_mainDisplayName)\\\"\",\n        device: .sizeToFit\n    )\n\n    static let presets: [VBDisplayPreset] = [\n        .fullHD,\n        .fourK,\n        .four5K,\n        .four5KRetina,\n        .matchMainDisplay,\n        .sizeToFitMainDisplay,\n    ]\n    \n    static func availablePresets(for guestType: VBGuestType) -> [VBDisplayPreset] {\n        presets.filter {\n            $0.isAvailable()\n            && $0.limitToGuestType == nil || $0.limitToGuestType == guestType\n        }\n    }\n\n    static func defaultPreset(for guestType: VBGuestType) -> VBDisplayPreset {\n        presets.first(where: { $0.isAvailable() && $0.defaultForGuestType == guestType }) ?? presets[0]\n    }\n}\n\npublic struct VBNetworkDeviceInterface: Identifiable, Hashable {\n    public var id: String\n    public var name: String\n}\n\nextension VBNetworkDeviceInterface {\n    init(_ interface: VZBridgedNetworkInterface) {\n        self.id = interface.identifier\n        self.name = interface.localizedDisplayName ?? interface.identifier\n    }\n}\n\npublic extension VBNetworkDeviceInterface {\n    static let automatic = VBNetworkDeviceInterface(id: VBNetworkDevice.automaticBridgeID, name: \"Automatic\")\n}\n\npublic extension VBNetworkDevice {\n    static var defaultBridgeInterfaceID: String? {\n        VZBridgedNetworkInterface.networkInterfaces.first?.identifier\n    }\n    \n    static var bridgeInterfaces: [VBNetworkDeviceInterface] {\n        VZBridgedNetworkInterface.networkInterfaces.map(VBNetworkDeviceInterface.init)\n    }\n}\n\n// MARK: - Helpers\n\npublic extension UInt64 {\n    static let storageGigabyte = UInt64(1024 * 1024 * 1024)\n    static let storageMegabyte = UInt64(1024 * 1024)\n    static let storageTerabyte = storageGigabyte * 1024\n}\n\npublic extension VBStorageDevice {\n    static func validationError(for name: String) -> String? {\n        guard !name.isEmpty else {\n            return \"Name can't be empty.\"\n        }\n        do {\n            try VZVirtioBlockDeviceConfiguration.validateBlockDeviceIdentifier(name)\n            return nil\n        } catch {\n            return error.localizedDescription\n        }\n    }\n\n    static var hostSupportsUSBMassStorage: Bool { true }\n\n    func diskImageExists(for container: VBStorageDeviceContainer) -> Bool {\n        let url = container.diskImageURL(for: self)\n        return FileManager.default.fileExists(atPath: url.path)\n    }\n}\n\npublic extension VBNetworkDevice {\n    static func validateMAC(_ address: String) -> Bool {\n        VZMACAddress(string: address) != nil\n    }\n}\n\npublic extension VBMacDevice {\n    static let minimumCPUCount: Int = VZVirtualMachineConfiguration.minimumAllowedCPUCount\n\n    static let maximumCPUCount: Int = {\n        min(ProcessInfo.processInfo.processorCount, VZVirtualMachineConfiguration.maximumAllowedCPUCount)\n    }()\n\n    static let virtualCPUCountRange: ClosedRange<Int> = {\n        minimumCPUCount...maximumCPUCount\n    }()\n\n    static let minimumMemorySizeInGigabytes = 2\n\n    static let maximumMemorySizeInGigabytes: Int = {\n        let value = Swift.min(ProcessInfo.processInfo.physicalMemory, VZVirtualMachineConfiguration.maximumAllowedMemorySize)\n        return Int(value / 1024 / 1024 / 1024)\n    }()\n\n    static let memorySizeRangeInGigabytes: ClosedRange<Int> = {\n        minimumMemorySizeInGigabytes...maximumMemorySizeInGigabytes\n    }()\n}\n\npublic extension VBDisplayDevice {\n\n    static let minimumDisplayWidth = 800\n    \n    static let minimumDisplayHeight = 600\n\n    static var maximumDisplayWidth = 6016\n\n    static var maximumDisplayHeight = 3384\n\n    static let displayWidthRange: ClosedRange<Int> = {\n        minimumDisplayWidth...maximumDisplayWidth\n    }()\n\n    static let displayHeightRange: ClosedRange<Int> = {\n        minimumDisplayHeight...maximumDisplayHeight\n    }()\n\n    static let minimumDisplayPPI = 72\n\n    static let maximumDisplayPPI = 218\n\n    static let displayPPIRange: ClosedRange<Int> = {\n        minimumDisplayPPI...maximumDisplayPPI\n    }()\n\n}\n\nextension Int {\n\n    static let vb_suggestedVirtualCPUCount: Int = {\n        let totalAvailableCPUs = ProcessInfo.processInfo.processorCount\n\n        var virtualCPUCount = totalAvailableCPUs <= 1 ? 1 : totalAvailableCPUs / 2\n        virtualCPUCount = Swift.max(virtualCPUCount, VZVirtualMachineConfiguration.minimumAllowedCPUCount)\n        virtualCPUCount = Swift.min(virtualCPUCount, VZVirtualMachineConfiguration.maximumAllowedCPUCount)\n\n        return virtualCPUCount\n    }()\n\n}\n\nextension UInt64 {\n\n    static let vb_suggestedMemorySize: UInt64 = {\n        let hostMemory = ProcessInfo.processInfo.physicalMemory\n        var memorySize = hostMemory / 2\n        memorySize = Swift.max(memorySize, VZVirtualMachineConfiguration.minimumAllowedMemorySize)\n        memorySize = Swift.min(memorySize, VZVirtualMachineConfiguration.maximumAllowedMemorySize)\n\n        return memorySize\n    }()\n\n}\n\npublic extension ProcessInfo {\n    var vb_hostName: String {\n        SCDynamicStoreCopyComputerName(nil, nil) as? String ?? \"This Mac\"\n    }\n    \n    var vb_mainDisplayName: String {\n        guard let screen = NSScreen.main else { return \"\\(vb_hostName)\" }\n        return screen.localizedName\n    }\n    \n    var vb_mainDisplayHasNotch: Bool { NSScreen.main?.auxiliaryTopLeftArea != nil }\n}\n\npublic extension NSScreen {\n    var dpi: CGSize {\n        (deviceDescription[NSDeviceDescriptionKey.resolution] as? CGSize) ?? CGSize(width: 72.0, height: 72.0)\n    }\n}\n"
  },
  {
    "path": "VirtualCore/Source/Models/Configuration/DecodableDefault.swift",
    "content": "import Foundation\n\npublic protocol DecodableDefaultSource {\n    associatedtype Value: Decodable\n    static var defaultValue: Value { get }\n}\n\npublic enum DecodableDefault {}\n\npublic extension DecodableDefault {\n    @propertyWrapper\n    struct Wrapper<Source: DecodableDefaultSource> {\n        public typealias Value = Source.Value\n        public var wrappedValue = Source.defaultValue\n        \n        public init(wrappedValue: Value = Source.defaultValue) {\n            self.wrappedValue = wrappedValue\n        }\n    }\n}\n\nextension DecodableDefault.Wrapper: Decodable {\n    public init(from decoder: Decoder) throws {\n        let container = try decoder.singleValueContainer()\n        wrappedValue = try container.decode(Value.self)\n    }\n}\n\npublic extension KeyedDecodingContainer {\n    func decode<T>(_ type: DecodableDefault.Wrapper<T>.Type,\n                   forKey key: Key) throws -> DecodableDefault.Wrapper<T> {\n        try decodeIfPresent(type, forKey: key) ?? .init()\n    }\n}\n\npublic protocol ProvidesEmptyPlaceholder: Codable {\n    static var empty: Self { get }\n}\n\npublic extension DecodableDefault {\n    typealias Source = DecodableDefaultSource\n    typealias List = Decodable & ExpressibleByArrayLiteral\n    typealias Map = Decodable & ExpressibleByDictionaryLiteral\n    typealias Enum = Decodable & CaseIterable\n\n    enum Sources {\n        public enum Zero: Source {\n            public static var defaultValue: Int { 0 }\n        }\n        \n        public enum True: Source {\n            public static var defaultValue: Bool { true }\n        }\n\n        public enum False: Source {\n            public static var defaultValue: Bool { false }\n        }\n\n        public enum EmptyString: Source {\n            public static var defaultValue: String { \"\" }\n        }\n\n        public enum EmptyList<T: List>: Source {\n            public static var defaultValue: T { [] }\n        }\n\n        public enum EmptyMap<T: Map>: Source {\n            public static var defaultValue: T { [:] }\n        }\n        \n        public enum EmptyPlaceholder<T: ProvidesEmptyPlaceholder>: Source {\n            public static var defaultValue: T { .empty }\n        }\n        \n        public enum FirstCase<T: Enum>: Source {\n            public static var defaultValue: T { T.allCases.first! }\n        }\n    }\n}\n\npublic extension DecodableDefault {\n    typealias Zero = Wrapper<Sources.Zero>\n    typealias True = Wrapper<Sources.True>\n    typealias False = Wrapper<Sources.False>\n    typealias EmptyString = Wrapper<Sources.EmptyString>\n    typealias EmptyList<T: List> = Wrapper<Sources.EmptyList<T>>\n    typealias EmptyMap<T: Map> = Wrapper<Sources.EmptyMap<T>>\n    typealias EmptyPlaceholder<T: ProvidesEmptyPlaceholder> = Wrapper<Sources.EmptyPlaceholder<T>>\n    typealias FirstCase<T: Enum> = Wrapper<Sources.FirstCase<T>>\n}\n\nextension DecodableDefault.Wrapper: Equatable where Value: Equatable {}\nextension DecodableDefault.Wrapper: Hashable where Value: Hashable {}\n\nextension DecodableDefault.Wrapper: Encodable where Value: Encodable {\n    public func encode(to encoder: Encoder) throws {\n        var container = encoder.singleValueContainer()\n        try container.encode(wrappedValue)\n    }\n}\n"
  },
  {
    "path": "VirtualCore/Source/Models/Configuration/VBMacDevice+Storage.swift",
    "content": "//\n//  VBMacDevice+Storage.swift\n//  VirtualCore\n//\n//  Created by Guilherme Rambo on 20/07/22.\n//\n\nimport Foundation\n\npublic extension VBMacDevice {\n\n    mutating func addOrUpdate(_ storage: VBStorageDevice) {\n        if let idx = storageDevices.firstIndex(where: { $0.id == storage.id }) {\n            storageDevices[idx] = storage\n        } else {\n            storageDevices.append(storage)\n        }\n    }\n\n}\n"
  },
  {
    "path": "VirtualCore/Source/Models/SavedState/VBSavedStateMetadata+Clone.swift",
    "content": "import Foundation\n\nextension VBSavedStateMetadata {\n    static func createStorageDeviceClones(packageURL: URL, model: VBVirtualMachine) async throws -> [VBStorageDevice] {\n        let inputDevices = model.configuration.hardware.storageDevices\n        var outputDevices = [VBStorageDevice]()\n\n        for var device in inputDevices {\n            guard case .managedImage = device.backing else {\n                /// Custom images are arbirary, may be anywhere on disk or external storage.\n                /// Such images are managed by the user, not the app, and thus are not cloned when saving state.\n                outputDevices.append(device)\n                continue\n            }\n\n            let inputURL: URL = model.diskImageURL(for: device)\n            let cloneURL: URL = packageURL.appending(path: inputURL.lastPathComponent)\n\n            try FileManager.default.copyItem(at: inputURL, to: cloneURL)\n\n            device.isSavedStateClone = true\n\n            outputDevices.append(device)\n        }\n\n        return outputDevices\n    }\n\n    mutating func createStorageDeviceClones(packageURL: URL, model: VBVirtualMachine) async throws {\n        storageDevices = try await Self.createStorageDeviceClones(packageURL: packageURL, model: model)\n    }\n}\n"
  },
  {
    "path": "VirtualCore/Source/Models/SavedState/VBSavedStateMetadata.swift",
    "content": "import Foundation\nimport BuddyFoundation\n\npublic struct VBSavedStateMetadata: Identifiable, Hashable, Codable {\n    public var id: UUID\n    public var vmUUID: UUID\n    public var date: Date\n    public var appVersion: SoftwareVersion\n    public var appBuild: Int\n    public var hostECID: UInt64?\n\n    /// Copy of ``VBMacDevice/storageDevices`` as those existed at the time the snapshot was taken.\n    /// The ``VBStorageDevice/isSavedStateClone`` property is set to `true` once the state has been saved.\n    /// Only managed disk images are cloned alongside saved states, custom user-provided images are referenced from their original locations.\n    @DecodableDefault.EmptyList\n    public var storageDevices: [VBStorageDevice]\n\n    init(id: UUID, vmUUID: UUID, date: Date, appVersion: SoftwareVersion, appBuild: Int, hostECID: UInt64? = nil, storageDevices: [VBStorageDevice]) {\n        self.id = id\n        self.vmUUID = vmUUID\n        self.date = date\n        self.appVersion = appVersion\n        self.appBuild = appBuild\n        self.hostECID = hostECID\n        self.storageDevices = storageDevices\n    }\n}\n\n// MARK: - Saved State Metadata Creation\n\npublic extension VBSavedStateMetadata {\n    init(model: VBVirtualMachine) {\n        let ecid = ProcessInfo.processInfo.machineECID\n        \n        assert(ecid != nil, \"Failed to get host machine ECID\")\n\n        self.init(\n            id: UUID(),\n            vmUUID: model.metadata.uuid,\n            date: .now,\n            appVersion: Bundle.main.vbVersion,\n            appBuild: Bundle.main.vbBuild,\n            hostECID: ecid,\n            storageDevices: model.configuration.hardware.storageDevices // will be modified once package is saved\n        )\n    }\n}\n\n// MARK: - Directory Helpers\n\n@MainActor\npublic extension VBVirtualMachine {\n    func savedStatesDirectoryURL(in library: VMLibraryController) -> URL {\n        library.savedStateDirectoryURL(for: self)\n    }\n\n    func savedStatesDirectoryURLCreatingIfNeeded(in library: VMLibraryController) throws -> URL {\n        try library.savedStateDirectoryURLCreatingIfNeeded(for: self)\n    }\n\n    /// Convenience for ``VMLibraryController/createSavedStatePackage(for:)``.\n    func createSavedStatePackage(in library: VMLibraryController, snapshotName name: String) throws -> VBSavedStatePackage {\n        try library.createSavedStatePackage(for: self, snapshotName: name)\n    }\n}\n"
  },
  {
    "path": "VirtualCore/Source/Models/SavedState/VBSavedStatePackage+VM.swift",
    "content": "import Foundation\n\nextension VBSavedStatePackage: VBStorageDeviceContainer {\n    public var bundleURL: URL { url }\n    public var storageDevices: [VBStorageDevice] { metadata.storageDevices }\n}\n"
  },
  {
    "path": "VirtualCore/Source/Models/SavedState/VBSavedStatePackage.swift",
    "content": "import Foundation\nimport UniformTypeIdentifiers\nimport OSLog\n\npublic extension UTType {\n    static let virtualBuddySavedState = UTType(\n        exportedAs: \"codes.rambo.VirtualBuddy.SavedState\",\n        conformingTo: .bundle\n    )\n}\n\n/// Represents a `vbst` file on disk, encapsulating all operations related to saved state packages.\npublic final class VBSavedStatePackage: Identifiable, Hashable {\n    public var id: UUID { metadata.id }\n\n    static let dataFilename = \"State.vzvmsave\"\n    static let infoFilename = \"Info.plist\"\n    static let screenshotFilename = \"Screenshot.heic\"\n    static let thumbnailFilename = \"Thumbnail.heic\"\n    static let fileExtension = \"vbst\"\n\n    public let url: URL\n    public let dataFileURL: URL\n    public let infoFileURL: URL\n    public let screenshotFileURL: URL\n    public let thumbnailFileURL: URL\n    private let manager: FileManager\n    private let logger: Logger\n    public var metadata: VBSavedStateMetadata {\n        didSet { saveMetadata(oldValue) }\n    }\n\n    /// Creates a new package on disk for the given virtual machine, initializing the saved state package accordingly.\n    public convenience init(creatingPackageInDirectoryAt baseURL: URL, model: VBVirtualMachine, snapshotName: String) throws {\n        let url = baseURL.appendingPathComponent(snapshotName, conformingTo: .virtualBuddySavedState)\n        let createdURL = try url.creatingDirectoryIfNeeded()\n\n        try self.init(url: createdURL, metadata: VBSavedStateMetadata(model: model))\n\n        save()\n    }\n\n    /// Initializes a saved state package from an existing package on disk.\n    public convenience init(url: URL) throws {\n        try self.init(url: url, metadata: nil)\n    }\n\n    private init(url: URL, metadata: VBSavedStateMetadata?) throws {\n        guard FileManager.default.fileExists(atPath: url.path) else {\n            throw Failure(\"Saved state package doesn't exist at \\(url.path)\")\n        }\n        let infoURL = url.appending(path: Self.infoFilename)\n        self.logger = Logger(subsystem: VirtualCoreConstants.subsystemName, category: \"VBSavedStatePackage(\\(url.deletingPathExtension().lastPathComponent))\")\n        self.manager = FileManager()\n        self.url = url\n        self.dataFileURL = url.appending(path: Self.dataFilename)\n        self.infoFileURL = infoURL\n        self.screenshotFileURL = url.appending(path: Self.screenshotFilename)\n        self.thumbnailFileURL = url.appending(path: Self.thumbnailFilename)\n\n        if FileManager.default.fileExists(atPath: infoURL.path) {\n            let data = try Data(contentsOf: infoURL)\n            self.metadata = try PropertyListDecoder.virtualBuddy.decode(VBSavedStateMetadata.self, from: data)\n        } else {\n            guard let inputMetadata = metadata else {\n                throw Failure(\"Initializing VBSavedStatePackage with new package requires metadata to be provided\")\n            }\n            self.metadata = inputMetadata\n        }\n    }\n\n    public var thumbnail: NSImage? { NSImage(contentsOf: thumbnailFileURL) }\n\n    public var screenshot: NSImage? {\n        get { NSImage(contentsOf: screenshotFileURL) }\n        set {\n            guard let newValue else {\n                try? manager.removeItem(at: screenshotFileURL)\n                try? manager.removeItem(at: thumbnailFileURL)\n                return\n            }\n\n            do {\n                try newValue.vb_encodeHEIC(to: screenshotFileURL)\n                try newValue.vb_createThumbnail(at: thumbnailFileURL)\n            } catch {\n                logger.error(\"Error saving new screenshot/thumbnail: \\(error, privacy: .public)\")\n            }\n        }\n    }\n\n    public func save() {\n        logger.debug(#function)\n        \n        saveMetadata(nil)\n    }\n\n    public func createStorageDeviceClones(model: VBVirtualMachine) async throws {\n        try await metadata.createStorageDeviceClones(packageURL: url, model: model)\n    }\n\n    public func delete() throws {\n        logger.debug(#function)\n\n        try manager.removeItem(at: url)\n    }\n\n    /// Whether this saved state needs to be migrated by cloning existing storage devices\n    /// because it was created in a version of VirtualBuddy that didn't do this.\n    public var needsStorageCloneMigration: Bool { metadata.storageDevices.isEmpty }\n\n    private func saveMetadata(_ oldValue: VBSavedStateMetadata?) {\n        guard metadata != oldValue else { return }\n\n        do {\n            let encoded = try PropertyListEncoder.virtualBuddy.encode(metadata)\n            try encoded.write(to: infoFileURL)\n        } catch {\n            logger.error(\"Error saving updated info: \\(error, privacy: .public)\")\n\n            assertionFailure(\"Error saving updated info: \\(error)\")\n        }\n    }\n\n    public func hash(into hasher: inout Hasher) {\n        hasher.combine(metadata)\n    }\n\n    public static func ==(lhs: VBSavedStatePackage, rhs: VBSavedStatePackage) -> Bool { lhs.metadata == rhs.metadata }\n}\n\n// MARK: - Validation\n\nextension VBSavedStatePackage {\n    func validate(for model: VBVirtualMachine) throws {\n        if let stateHostECID = metadata.hostECID,\n           let currentHostECID = ProcessInfo.processInfo.machineECID\n        {\n            guard stateHostECID == currentHostECID else {\n                throw Failure(\"This saved state is not for the current host. Saved states are paired to the host machine and can't be restored on a different host.\")\n            }\n        }\n\n        guard metadata.vmUUID == model.metadata.uuid else {\n            throw Failure(\"This saved state is not for this virtual machine. Saved states can only be restored on the virtual machine that saved the state.\")\n        }\n    }\n}\n\npublic extension VBSavedStateMetadata {\n    init(packageAt url: URL) throws {\n        let infoURL = url.appendingPathComponent(VBSavedStatePackage.infoFilename)\n        self = try PropertyListDecoder.virtualBuddy.decode(Self.self, from: Data(contentsOf: infoURL))\n    }\n}\n"
  },
  {
    "path": "VirtualCore/Source/Models/SavedState/VMLibraryController+SavedState.swift",
    "content": "import Foundation\n\n@MainActor\npublic extension VMLibraryController {\n\n    nonisolated static let savedStateDirectoryName = \"_SavedState\"\n\n    var savedStatesDirectoryURL: URL {\n        self.libraryURL.appending(path: Self.savedStateDirectoryName, directoryHint: .isDirectory)\n    }\n\n    func savedStatesLibraryURLCreatingIfNeeded() throws -> URL {\n        try savedStatesDirectoryURL.creatingDirectoryIfNeeded()\n    }\n\n    func savedStateDirectoryURL(for model: VBVirtualMachine) -> URL {\n        savedStatesDirectoryURL\n            .appending(path: model.metadata.uuid.uuidString, directoryHint: .isDirectory)\n    }\n\n    func savedStateDirectoryURLCreatingIfNeeded(for model: VBVirtualMachine) throws -> URL {\n        try savedStateDirectoryURL(for: model)\n            .creatingDirectoryIfNeeded()\n    }\n\n    func createSavedStatePackage(for model: VBVirtualMachine, snapshotName name: String) throws -> VBSavedStatePackage {\n        let baseURL = try model.savedStatesDirectoryURLCreatingIfNeeded(in: self)\n\n        return try VBSavedStatePackage(creatingPackageInDirectoryAt: baseURL, model: model, snapshotName: name)\n    }\n\n    func virtualMachine(with uuid: UUID) throws -> VBVirtualMachine {\n        guard let model = virtualMachines.first(where: { $0.metadata.uuid == uuid }) else {\n            throw Failure(\"Virtual machine not found with UUID \\(uuid)\")\n        }\n        return model\n    }\n\n    func virtualMachineURL(forSavedStatePackageURL url: URL) throws -> URL {\n        try virtualMachine(forSavedStatePackageURL: url).bundleURL\n    }\n\n    func virtualMachine(forSavedStatePackageURL url: URL) throws -> VBVirtualMachine {\n        let metadata = try VBSavedStateMetadata(packageAt: url)\n        let model = try virtualMachine(forSavedStateMetadata: metadata)\n        return model\n    }\n\n    func virtualMachine(forSavedStateMetadata metadata: VBSavedStateMetadata) throws -> VBVirtualMachine {\n        try virtualMachine(with: metadata.vmUUID)\n    }\n}\n"
  },
  {
    "path": "VirtualCore/Source/Models/VBError.swift",
    "content": "//\n//  VBError.swift\n//  VirtualCore\n//\n//  Created by Guilherme Rambo on 10/04/22.\n//\n\nimport Foundation\n\npublic struct VBError: LocalizedError {\n    \n    public var errorDescription: String?\n    \n    init(_ desc: String) { self.errorDescription = desc }\n    \n}\n"
  },
  {
    "path": "VirtualCore/Source/Models/VBNVRAMVariable.swift",
    "content": "//\n//  VBNVRAMVariable.swift\n//  VirtualCore\n//\n//  Created by Guilherme Rambo on 11/04/22.\n//\n\nimport Foundation\n\npublic struct VBNVRAMVariable: Identifiable, Hashable, Codable {\n    public var id: String { name }\n    public let name: String\n    public var value: String?\n    \n    public init(name: String, value: String?) {\n        self.name = name\n        self.value = value\n    }\n}\n"
  },
  {
    "path": "VirtualCore/Source/Models/VBStorageDeviceContainer.swift",
    "content": "import Foundation\n\n/// Adopted by ``VMVirtualMachine`` and ``VBSavedStatePackage``\n/// in order to help resolve storage devices when bootstrapping a VM.\npublic protocol VBStorageDeviceContainer {\n    var bundleURL: URL { get }\n    var storageDevices: [VBStorageDevice] { get }\n    var bootDevice: VBStorageDevice { get throws }\n    var bootDiskImage: VBManagedDiskImage { get throws }\n    var allowDiskImageCreation: Bool { get }\n    func diskImageURL(for image: VBManagedDiskImage) -> URL\n    func diskImageURL(for device: VBStorageDevice) -> URL\n}\n\n// MARK: - Default Implementations\n\n/// These default implementations take care of resolving disk images for both ``VBVirtualMachine`` and ``VBSavedStatePackage``.\n/// Which one is used will be determine in `VirtualMachineConfigurationHelper` when bootstrapping the VM.\npublic extension VBStorageDeviceContainer {\n    var allowDiskImageCreation: Bool { false }\n\n    var bootDevice: VBStorageDevice {\n        get throws {\n            guard let device = storageDevices.first(where: { $0.isBootVolume }) else {\n                throw Failure(\"The virtual machine lacks a bootable storage device.\")\n            }\n\n            return device\n        }\n    }\n\n    var bootDiskImage: VBManagedDiskImage {\n        get throws {\n            let device = try bootDevice\n\n            guard case .managedImage(let image) = device.backing else {\n                throw Failure(\"The boot device must use a disk image managed by VirtualBuddy\")\n            }\n\n            return image\n        }\n    }\n\n    func diskImageURL(for image: VBManagedDiskImage) -> URL {\n        bundleURL\n            .appendingPathComponent(image.filename)\n            .appendingPathExtension(image.format.fileExtension)\n    }\n\n    func diskImageURL(for device: VBStorageDevice) -> URL {\n        switch device.backing {\n        case .managedImage(let image):\n            return diskImageURL(for: image)\n        case .customImage(let customURL):\n            return customURL\n        }\n    }\n}\n"
  },
  {
    "path": "VirtualCore/Source/Models/VBVirtualMachine+Metadata.swift",
    "content": "//\n//  VBVirtualMachine+Metadata.swift\n//  VirtualCore\n//\n//  Created by Guilherme Rambo on 24/06/22.\n//\n\nimport Cocoa\n\npublic extension VBVirtualMachine {\n\n    func metadataDirectoryCreatingIfNeeded() throws -> URL {\n        try metadataDirectoryURL.creatingDirectoryIfNeeded()\n    }\n\n    func write(_ data: Data, forMetadataFileNamed name: String) throws {\n        let baseURL = try metadataDirectoryCreatingIfNeeded()\n\n        let fileURL = baseURL.appendingPathComponent(name)\n\n        try data.write(to: fileURL, options: .atomic)\n    }\n\n    func deleteMetadataFile(named name: String) throws {\n        let fileURL = metadataDirectoryURL.appendingPathComponent(name)\n\n        guard FileManager.default.fileExists(atPath: fileURL.path) else { return }\n\n        try FileManager.default.removeItem(at: fileURL)\n    }\n\n    func metadataFileURL(_ fileName: String) -> URL {\n        let fileURL = metadataDirectoryURL.appendingPathComponent(fileName)\n\n        return fileURL\n    }\n\n    func metadataContents(_ fileName: String) -> Data? {\n        let fileURL = metadataFileURL(fileName)\n\n        guard FileManager.default.fileExists(atPath: fileURL.path) else { return nil }\n\n        return try? Data(contentsOf: fileURL)\n    }\n\n}\n\nextension URL {\n    func creatingDirectoryIfNeeded() throws -> Self {\n        if !FileManager.default.fileExists(atPath: path) {\n            try FileManager.default.createDirectory(at: self, withIntermediateDirectories: true)\n        }\n        return self\n    }\n}\n\nextension URL {\n    /// `true` if URL points to a file contained within a VirtualBuddy VM bundle metadata directory.\n    var isVirtualBuddyDataDirectoryFile: Bool {\n        deletingLastPathComponent().lastPathComponent == VBVirtualMachine.metadataDirectoryName\n    }\n\n    var virtualMachineBundleParent: URL? {\n        var current = self\n        while current.pathExtension != VBVirtualMachine.bundleExtension {\n            current = current.deletingLastPathComponent()\n            guard current.path != \"/\" else { return nil }\n        }\n        return current\n    }\n}\n"
  },
  {
    "path": "VirtualCore/Source/Models/VBVirtualMachine+Screenshot.swift",
    "content": "import Cocoa\n\npublic extension VBVirtualMachine {\n\n    var screenshot: NSImage? {\n        guard let imageData = metadataContents(VBVirtualMachine.screenshotFileName) ?? metadataContents(VBVirtualMachine._legacyScreenshotFileName) else { return nil }\n        return NSImage(data: imageData)\n    }\n\n    var thumbnail: NSImage? {\n        guard let imageData = metadataContents(VBVirtualMachine.thumbnailFileName) ?? metadataContents(VBVirtualMachine._legacyThumbnailFileName) else { return nil }\n        return NSImage(data: imageData)\n    }\n\n    func thumbnailImage() -> NSImage? {\n        let thumbnailURL = metadataFileURL(Self.thumbnailFileName)\n        \n        if let existingImage = NSImage(contentsOf: thumbnailURL) {\n            return existingImage\n        }\n        \n        return try? screenshot?.vb_createThumbnail(at: thumbnailURL)\n    }\n\n    func invalidateScreenshot() throws {\n        try deleteMetadataFile(named: Self.screenshotFileName)\n    }\n\n    func invalidateThumbnail() throws {\n        try deleteMetadataFile(named: Self.thumbnailFileName)\n    }\n\n}\n"
  },
  {
    "path": "VirtualCore/Source/Models/VBVirtualMachine.swift",
    "content": "import Foundation\nimport UniformTypeIdentifiers\nimport Combine\n\npublic typealias VoidSubject = PassthroughSubject<(), Never>\npublic typealias BoolSubject = PassthroughSubject<Bool, Never>\n\npublic struct VBVirtualMachine: Identifiable, VBStorageDeviceContainer {\n\n    public struct Metadata: Hashable, Codable {\n        public static let currentVersion = 1\n        @DecodableDefault.EmptyPlaceholder\n        public var uuid = UUID()\n        public var version = Self.currentVersion\n        public var installFinished: Bool = false\n        public var firstBootDate: Date? = nil\n        public var lastBootDate: Date? = nil\n        @DecodableDefault.EmptyPlaceholder\n        public var backgroundHash: BlurHashToken = .virtualBuddyBackground\n        /// If this VM was imported from some other app, contains the name of the ``VMImporter`` that was used.\n        public var importedFromAppName: String? = nil\n\n        /// The original remote URL that was specified for downloading the restore image (if downloaded from a remote source).\n        public private(set) var remoteInstallImageURL: URL? = nil\n\n        /// The original local file URL that was specified (or set after a successful download from ``remoteInstallImageURL``).\n        public private(set) var installImageURL: URL? = nil\n\n        /**\n         Usage of the same property for both local and remote restore image URLs has been the source of recurring bugs in the past.\n         Example: https://github.com/insidegui/VirtualBuddy/pull/395\n         \n         To keep this struct backwards-compatible and still have better safeguards against regressions, ``remoteInstallImageURL`` and ``installImageURL``\n         are only settable by the struct itself. To update the metadata, clients must use this method, which will automatically set the correct property by inspecting the URL.\n         */\n        public mutating func updateInstallImageURL(_ url: URL) {\n            if url.isFileURL {\n                installImageURL = url\n            } else {\n                remoteInstallImageURL = url\n            }\n        }\n\n        /// If Linux VM is using fallback VirtualBuddy orange background hash, updates it to use the Linux-specific one.\n        fileprivate mutating func setLinuxBackgroundHashIfNeeded() {\n            guard backgroundHash == .virtualBuddyBackground else { return }\n            backgroundHash = .virtualBuddyBackgroundLinux\n        }\n    }\n\n    public var id: String { bundleURL.absoluteString }\n    public internal(set) var bundleURL: URL\n    public var name: String { bundleURL.deletingPathExtension().lastPathComponent }\n\n    public internal(set) var uuid: UUID {\n        get { metadata.uuid }\n        set { metadata.uuid = newValue }\n    }\n\n    private var _configuration: VBMacConfiguration?\n    private var _metadata: Metadata?\n    private var _installRestoreData: Data?\n\n    public var configuration: VBMacConfiguration {\n        /// Masking private `_configuration` since it's initialized dynamically from a file.\n        get { _configuration ?? .default }\n        set { _configuration = newValue }\n    }\n\n    public var metadata: Metadata {\n        /// Masking private `_metadata` since it's initialized dynamically from a file.\n        get { _metadata ?? .init() }\n        set { _metadata = newValue }\n    }\n\n    public var installRestoreData: Data? {\n        /// Masking private `_installRestoreData` since it's initialized dynamically from a file.\n        get { _installRestoreData }\n        set { _installRestoreData = newValue }\n    }\n    \n}\n\npublic extension VBVirtualMachine {\n    static let bundleExtension = \"vbvm\"\n    static let screenshotFileName = \"Screenshot.heic\"\n    static let thumbnailFileName = \"Thumbnail.heic\"\n    \n    /// Only used for migrations.\n    static let _legacyScreenshotFileName = \"Screenshot.tiff\"\n    /// Only used for migrations.\n    static let _legacyThumbnailFileName = \"Thumbnail.jpg\"\n}\n\nextension VBVirtualMachine {\n\n    static let metadataFilename = \"Metadata.plist\"\n    static let configurationFilename = \"Config.plist\"\n    static let installRestoreFilename = \"Install.plist\"\n\n    public var storageDevices: [VBStorageDevice] { configuration.hardware.storageDevices }\n\n    var auxiliaryStorageURL: URL {\n        bundleURL.appendingPathComponent(\"AuxiliaryStorage\")\n    }\n    \n    var machineIdentifierURL: URL {\n        bundleURL.appendingPathComponent(\"MachineIdentifier\")\n    }\n    \n    var hardwareModelURL: URL {\n        bundleURL.appendingPathComponent(\"HardwareModel\")\n    }\n\n    public var metadataDirectoryURL: URL { Self.metadataDirectoryURL(for: bundleURL) }\n\n    static let metadataDirectoryName = \".vbdata\"\n\n    static func metadataDirectoryURL(for bundleURL: URL) -> URL {\n        bundleURL.appendingPathComponent(metadataDirectoryName)\n    }\n\n    public var needsInstall: Bool {\n        guard configuration.systemType == .mac else { return false }\n        return !metadata.installFinished || !FileManager.default.fileExists(atPath: hardwareModelURL.path)\n    }\n\n    /// Conforming to ``VBStorageDeviceContainer``, which defaults this to `false`.\n    /// When restoring from a saved state, disk image creation is not allowed, but when bootstrapping\n    /// a virtual machine, disk image creation is allowed.\n    public var allowDiskImageCreation: Bool { true }\n\n}\n\npublic extension UTType {\n    static let virtualBuddyVM = UTType(exportedAs: \"codes.rambo.VirtualBuddy.VM\", conformingTo: .bundle)\n}\n\npublic extension VBVirtualMachine {\n\n    struct BundleDirectoryMissingError: Error { }\n\n    init(bundleURL: URL, isNewInstall: Bool = false, createIfNeeded: Bool = true) throws {\n        /// If we're not allowed to create the bundle and its metadata directory doesn't exist, throw a specific error type that's caught in ``VMLibraryController``.\n        /// This is to prevent the app from creating a dummy VM bundle after a VM is deleted from the library.\n        if !createIfNeeded {\n            let metaDirectory = bundleURL.appending(path: Self.metadataDirectoryName, directoryHint: .isDirectory)\n            guard metaDirectory.isReadableDirectory else {\n                throw BundleDirectoryMissingError()\n            }\n        }\n\n        if !FileManager.default.fileExists(atPath: bundleURL.path) {\n            #if DEBUG\n            guard !ProcessInfo.isSwiftUIPreview else {\n                fatalError(\"Missing SwiftUI preview VM at \\(bundleURL.path)\")\n            }\n            #endif\n            \n            try FileManager.default.createDirectory(at: bundleURL, withIntermediateDirectories: true)\n        }\n\n        self.bundleURL = bundleURL\n        var (metadata, config, installRestore) = try loadMetadata()\n\n        if isNewInstall {\n            config.hardware.displayDevices = [\n                VBDisplayPreset.defaultPreset(for: .mac).device\n            ]\n        }\n\n        /// Migration from previous versions that didn't have a configuration file\n        /// describing the storage devices.\n        config.hardware.addMissingBootDeviceIfNeeded()\n\n        /// Migration from previous versions that didn't have dedicated fallback artwork for Linux.\n        if config.systemType == .linux {\n            metadata?.setLinuxBackgroundHashIfNeeded()\n        }\n\n        self.configuration = config\n\n        if let metadata {\n            self.metadata = metadata\n        } else {\n            /// Migration from previous versions that didn't have a metadata file.\n            self.metadata = Metadata(installFinished: !isNewInstall, firstBootDate: .now, lastBootDate: .now)\n        }\n\n        self.installRestoreData = installRestore\n    }\n\n    @available(macOS 13, *)\n    init(creatingLinuxMachineAt bundleURL: URL) throws {\n        guard !FileManager.default.fileExists(atPath: bundleURL.path) else { fatalError() }\n        try FileManager.default.createDirectory(at: bundleURL, withIntermediateDirectories: true)\n        self.bundleURL = bundleURL\n\n        var hardware = VBMacDevice.default\n        hardware.displayDevices = [\n            VBDisplayPreset.defaultPreset(for: .linux).device\n        ]\n        self.configuration = .init(systemType: .linux, hardware: hardware)\n\n        self.metadata = Metadata(installFinished: false, firstBootDate: .now, lastBootDate: .now)\n        metadata.setLinuxBackgroundHashIfNeeded()\n\n        try saveMetadata()\n    }\n\n    func saveMetadata() throws {\n        #if DEBUG\n        guard !ProcessInfo.isSwiftUIPreview else { return }\n        #endif\n        \n        let configData = try PropertyListEncoder.virtualBuddy.encode(configuration)\n        try write(configData, forMetadataFileNamed: Self.configurationFilename)\n\n        let metaData = try PropertyListEncoder.virtualBuddy.encode(metadata)\n        try write(metaData, forMetadataFileNamed: Self.metadataFilename)\n\n        try saveInstallData()\n    }\n\n    func saveInstallData() throws {\n        if let installRestoreData {\n            try write(installRestoreData, forMetadataFileNamed: Self.installRestoreFilename)\n        } else {\n            try? deleteMetadataFile(named: Self.installRestoreFilename)\n        }\n    }\n\n    func loadMetadata() throws -> (Metadata?, VBMacConfiguration, Data?) {\n        let metadata: Metadata?\n        let config: VBMacConfiguration\n        let installRestore: Data?\n\n        if let data = metadataContents(Self.configurationFilename) {\n            config = try PropertyListDecoder.virtualBuddy.decode(VBMacConfiguration.self, from: data)\n        } else {\n            /// Linux guests don't go through this code path, so it should be safe to assume Mac here (famous last words).\n            config = .default.guestType(.mac)\n        }\n\n        if let data = metadataContents(Self.metadataFilename) {\n            metadata = try PropertyListDecoder.virtualBuddy.decode(Metadata.self, from: data)\n        } else {\n            metadata = nil\n        }\n\n        if let data = metadataContents(Self.installRestoreFilename) {\n            installRestore = data\n        } else {\n            installRestore = nil\n        }\n\n        return (metadata, config, installRestore)\n    }\n\n    mutating func reloadMetadata() {\n        #if DEBUG\n        guard !ProcessInfo.isSwiftUIPreview else { return }\n        #endif\n        \n        guard let (metadata, config, installRestore) = try? loadMetadata() else {\n            assertionFailure(\"Failed to reload metadata\")\n            return\n        }\n\n        self.metadata = metadata ?? .init()\n        self.configuration = config\n        self.installRestoreData = installRestore\n    }\n\n}\n\nextension URL {\n    var creationDate: Date {\n        get { (try? resourceValues(forKeys: [.creationDateKey]))?.creationDate ?? .distantPast }\n        set {\n            var values = URLResourceValues()\n            values.creationDate = newValue\n            try? setResourceValues(values)\n        }\n    }\n}\n\npublic extension PropertyListEncoder {\n    static let virtualBuddy = PropertyListEncoder()\n}\n\npublic extension PropertyListDecoder {\n    static let virtualBuddy = PropertyListDecoder()\n}\n\nextension UUID: ProvidesEmptyPlaceholder {\n    public static var empty: UUID { UUID() }\n}\n"
  },
  {
    "path": "VirtualCore/Source/ReleaseTrains/AppUpdateChannel.swift",
    "content": "//\n//  AppUpdateChannel.swift\n//  VirtualCore\n//\n//  Created by Guilherme Rambo on 25/06/22.\n//\n\nimport Foundation\n\npublic struct AppUpdateChannel: Identifiable, Hashable, CustomStringConvertible, Sendable {\n    public var id: String { name.lowercased() }\n    public let name: String\n    public let appCastURL: URL\n}\n\npublic extension AppUpdateChannel {\n    static let release = AppUpdateChannel(\n        name: \"Release\",\n        appCastURL: URL(string: \"https://su.virtualbuddy.app/appcast.xml?channel=release\")!\n    )\n\n    static let beta = AppUpdateChannel(\n        name: \"Beta\",\n        appCastURL: URL(string: \"https://su.virtualbuddy.app/appcast.xml?channel=beta\")!\n    )\n\n    static let channelsByID: [AppUpdateChannel.ID: AppUpdateChannel] = [\n        AppUpdateChannel.release.id: AppUpdateChannel.release,\n        AppUpdateChannel.beta.id: AppUpdateChannel.beta\n    ]\n\n    static func defaultChannel(for buildType: VBBuildType) -> AppUpdateChannel {\n        switch buildType {\n        case .debug, .devRelease, .release:\n            return .release\n        case .betaDebug, .betaRelease:\n            return .beta\n        }\n    }\n}\n\npublic extension AppUpdateChannel {\n    var description: String { id }\n}\n"
  },
  {
    "path": "VirtualCore/Source/ReleaseTrains/VBBuildType.swift",
    "content": "import Foundation\n\n/// Represents the type of app build, such as beta vs. release.\npublic enum VBBuildType: String, CaseIterable, CustomStringConvertible {\n    case debug\n    case betaDebug\n    case release\n    case betaRelease\n    case devRelease\n}\n\npublic extension VBBuildType {\n    /// The current build type according to compile-time flags.\n    static let current: VBBuildType = {\n        #if BUILDING_DEV_RELEASE\n        return .devRelease\n        #elseif BETA && DEBUG\n        return .betaDebug\n        #elseif BETA\n        return .betaRelease\n        #elseif DEBUG\n        return .debug\n        #else\n        return .release\n        #endif\n    }()\n\n    /// A user-facing name for the build type, or `nil` if it's a regular release build.\n    var name: String? {\n        switch self {\n        case .debug:\n            return \"Debug\"\n        case .betaDebug:\n            return \"Beta Debug\"\n        case .release:\n            return nil\n        case .betaRelease:\n            return \"Beta\"\n        case .devRelease:\n            return \"Dev\"\n        }\n    }\n\n    var description: String { name ?? \"Release\" }\n}\n\npublic extension Bundle {\n    var vbBuildType: VBBuildType { .current }\n\n    /// The build description, including build type, version, and build number.\n    /// Example: \"Beta 2.0 - 123\"\n    var vbBuildDescription: String {\n        if let typeDescription = vbBuildType.name {\n            return \"\\(typeDescription) \\(vbShortVersionString) - \\(vbBuild)\"\n        } else {\n            return \"\\(vbShortVersionString) - \\(vbBuild)\"\n        }\n    }\n\n    /// The full version description such as \"VirtualBuddy (Beta 2.0 - 123)\", or just \"VirtualBuddy\" for release builds.\n    var vbFullVersionDescription: String {\n        let appName = \"VirtualBuddy\"\n        if vbBuildType.name != nil {\n            return \"\\(appName) (\\(vbBuildDescription))\"\n        } else {\n            return appName\n        }\n    }\n}\n"
  },
  {
    "path": "VirtualCore/Source/Resources/Preview/PreviewLinux.vbvm/Disk.img",
    "content": "Fake\n"
  },
  {
    "path": "VirtualCore/Source/Resources/Preview/PreviewLinuxBlurHash.vbvm/Disk.img",
    "content": "Fake\n"
  },
  {
    "path": "VirtualCore/Source/Resources/Preview/PreviewLinuxNoArtwork.vbvm/Disk.img",
    "content": "Fake\n"
  },
  {
    "path": "VirtualCore/Source/Resources/Preview/PreviewMac.vbvm/Disk.img",
    "content": "Fake\n"
  },
  {
    "path": "VirtualCore/Source/Resources/Preview/PreviewMacBlurHash.vbvm/Disk.img",
    "content": "Fake\n"
  },
  {
    "path": "VirtualCore/Source/Resources/Preview/PreviewMacNoArtwork.vbvm/Disk.img",
    "content": "Fake\n"
  },
  {
    "path": "VirtualCore/Source/Resources/Preview/PreviewSavedStates/Save-2024-03-27_16;06;24.vbst/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>appBuild</key>\n\t<integer>110</integer>\n\t<key>appVersion</key>\n\t<string>1.4.1</string>\n\t<key>date</key>\n\t<date>2024-03-27T19:06:24Z</date>\n\t<key>hostECID</key>\n\t<integer>2939957236449310</integer>\n\t<key>id</key>\n\t<string>65D33ECA-ACF2-4241-9F45-00417FAFF473</string>\n\t<key>vmUUID</key>\n\t<string>96830330-D195-4211-BB4B-7D898177B12C</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "VirtualCore/Source/Resources/Preview/PreviewSavedStates/Save-2024-03-27_16;07;04.vbst/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>appBuild</key>\n\t<integer>110</integer>\n\t<key>appVersion</key>\n\t<string>1.4.1</string>\n\t<key>date</key>\n\t<date>2024-03-27T19:07:04Z</date>\n\t<key>hostECID</key>\n\t<integer>2939957236449310</integer>\n\t<key>id</key>\n\t<string>85CCCE19-1B97-45B5-A2E0-DF8A1F2DFA07</string>\n\t<key>vmUUID</key>\n\t<string>96830330-D195-4211-BB4B-7D898177B12C</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "VirtualCore/Source/Resources/Preview/PreviewSavedStates/Save-2024-03-27_16;08;06.vbst/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>appBuild</key>\n\t<integer>110</integer>\n\t<key>appVersion</key>\n\t<string>1.4.1</string>\n\t<key>date</key>\n\t<date>2024-03-27T19:08:06Z</date>\n\t<key>hostECID</key>\n\t<integer>2939957236449310</integer>\n\t<key>id</key>\n\t<string>3C248913-3420-4688-96FB-5B32E91026E4</string>\n\t<key>vmUUID</key>\n\t<string>96830330-D195-4211-BB4B-7D898177B12C</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "VirtualCore/Source/Resources/Preview/PreviewSavedStates/Save-2024-03-27_16;08;28.vbst/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>appBuild</key>\n\t<integer>110</integer>\n\t<key>appVersion</key>\n\t<string>1.4.1</string>\n\t<key>date</key>\n\t<date>2024-03-27T19:08:28Z</date>\n\t<key>hostECID</key>\n\t<integer>2939957236449310</integer>\n\t<key>id</key>\n\t<string>FC0182EC-BC5C-4817-ABFF-8DD1B9E49735</string>\n\t<key>vmUUID</key>\n\t<string>96830330-D195-4211-BB4B-7D898177B12C</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "VirtualCore/Source/Resources/Preview/PreviewSavedStates/Save-2024-03-27_16;08;51.vbst/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>appBuild</key>\n\t<integer>110</integer>\n\t<key>appVersion</key>\n\t<string>1.4.1</string>\n\t<key>date</key>\n\t<date>2024-03-27T19:08:51Z</date>\n\t<key>hostECID</key>\n\t<integer>2939957236449310</integer>\n\t<key>id</key>\n\t<string>F29733F1-0056-4C90-A9E5-04F42C127086</string>\n\t<key>vmUUID</key>\n\t<string>96830330-D195-4211-BB4B-7D898177B12C</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "VirtualCore/Source/Resources/Preview/_Downloads/UniversalMac_13.3.1_22E261_Restore.ipsw",
    "content": "dummy"
  },
  {
    "path": "VirtualCore/Source/Resources/Preview/_Downloads/UniversalMac_14.0_23A344_Restore.ipsw",
    "content": "dummy"
  },
  {
    "path": "VirtualCore/Source/Resources/Preview/_Downloads/UniversalMac_14.5_23F79_Restore.ipsw",
    "content": "dummy"
  },
  {
    "path": "VirtualCore/Source/Resources/Preview/_Downloads/UniversalMac_15.3_24D60_Restore.ipsw",
    "content": "dummy"
  },
  {
    "path": "VirtualCore/Source/Resources/Preview/_Downloads/UniversalMac_15.5_24F74_Restore.ipsw",
    "content": "dummy"
  },
  {
    "path": "VirtualCore/Source/Resources/VirtualCore.xcassets/Adjectives.dataset/Adjectives.txt",
    "content": "Able\nAbove\nAbsent\nAbsolute\nAbstract\nAbundant\nAcademic\nAcceptable\nAccepted\nAccessible\nAccurate\nAccused\nActive\nActual\nAcute\nAdded\nAdditional\nAdequate\nAdjacent\nAdministrative\nAdorable\nAdvanced\nAdverse\nAdvisory\nAesthetic\nAfraid\nAggregate\nAggressive\nAgreeable\nAgreed\nAgricultural\nAlert\nAlive\nAlleged\nAllied\nAlone\nAlright\nAlternative\nAmateur\nAmazing\nAmbitious\nAmused\nAncient\nAngry\nAnnoyed\nAnnual\nAnonymous\nAnxious\nAppalling\nApparent\nApplicable\nAppropriate\nArbitrary\nArchitectural\nArmed\nArrogant\nArtificial\nArtistic\nAshamed\nAsleep\nAssistant\nAssociated\nAtomic\nAttractive\nAutomatic\nAutonomous\nAvailable\nAverage\nAwake\nAware\nAwful\nAwkward\nBack\nBad\nBalanced\nBare\nBasic\nBeautiful\nBeneficial\nBetter\nBewildered\nBig\nBinding\nBiological\nBitter\nBizarre\nBlack\nBlank\nBlind\nBlonde\nBloody\nBlue\nBlushing\nBoiling\nBold\nBored\nBoring\nBottom\nBrainy\nBrave\nBreakable\nBreezy\nBrief\nBright\nBrilliant\nBroad\nBroken\nBrown\nBumpy\nBurning\nBusy\nCalm\nCapable\nCareful\nCasual\nCausal\nCautious\nCentral\nCertain\nChanging\nCharacteristic\nCharming\nCheap\nCheerful\nChemical\nChief\nChilly\nChosen\nChronic\nChubby\nCircular\nCivic\nCivil\nCivilian\nClassic\nClassical\nClean\nClear\nClever\nClinical\nClose\nClosed\nCloudy\nClumsy\nCoastal\nCognitive\nCoherent\nCold\nCollective\nColonial\nColorful\nColossal\nColored\nColorful\nCombative\nCombined\nComfortable\nComing\nCommercial\nCommon\nCompact\nComparable\nComparative\nCompatible\nCompetent\nCompetitive\nComplete\nComplex\nComplicated\nComprehensive\nCompulsory\nConceptual\nConcerned\nConcrete\nCondemned\nConfident\nConfidential\nConfused\nConscious\nConservation\nConservative\nConsiderable\nConsistent\nConstant\nConstitutional\nContemporary\nContent\nContinental\nContinued\nContinuing\nContinuous\nControlled\nControversial\nConvenient\nConventional\nConvinced\nConvincing\nCooing\nCool\nCooperative\nCorporate\nCorrect\nCorresponding\nCostly\nCourageous\nCrazy\nCreative\nCreepy\nCriminal\nCritical\nCrooked\nCrowded\nCrucial\nCrude\nCruel\nCuddly\nCultural\nCurious\nCurly\nCurrent\nCurved\nCute\nDaily\nDamaged\nDamp\nDangerous\nDark\nDead\nDeaf\nDeafening\nDear\nDecent\nDecisive\nDeep\nDefeated\nDefensive\nDefiant\nDefinite\nDeliberate\nDelicate\nDelicious\nDelighted\nDelightful\nDemocratic\nDependent\nDepressed\nDesirable\nDesperate\nDetailed\nDetermined\nDeveloped\nDeveloping\nDevoted\nDifferent\nDifficult\nDigital\nDiplomatic\nDirect\nDirty\nDisabled\nDisappointed\nDisastrous\nDisciplinary\nDisgusted\nDistant\nDistinct\nDistinctive\nDistinguished\nDisturbed\nDisturbing\nDiverse\nDivine\nDizzy\nDomestic\nDominant\nDouble\nDoubtful\nDrab\nDramatic\nDreadful\nDriving\nDrunk\nDry\nDual\nDue\nDull\nDusty\nDying\nDynamic\nEager\nEarly\nEasy\nEconomic\nEducational\nEerie\nEffective\nEfficient\nElaborate\nElated\nElderly\nEldest\nElectoral\nElectric\nElectrical\nElectronic\nElegant\nEligible\nEmbarrassed\nEmbarrassing\nEmotional\nEmpirical\nEmpty\nEnchanting\nEncouraging\nEndless\nEnergetic\nEnormous\nEnthusiastic\nEntire\nEntitled\nEnvious\nEnvironmental\nEqual\nEquivalent\nEssential\nEstablished\nEstimated\nEthical\nEventual\nEveryday\nEvident\nEvil\nEvolutionary\nExact\nExcellent\nExceptional\nExcess\nExcessive\nExcited\nExciting\nExclusive\nExisting\nExotic\nExpected\nExpensive\nExperienced\nExperimental\nExplicit\nExtended\nExtensive\nExternal\nExtra\nExtraordinary\nExtreme\nExuberant\nFaint\nFair\nFaithful\nFamiliar\nFamous\nFancy\nFantastic\nFar\nFascinating\nFashionable\nFast\nFatal\nFavourable\nFavourite\nFederal\nFellow\nFemale\nFew\nFierce\nFilthy\nFinal\nFinancial\nFine\nFirm\nFiscal\nFit\nFixed\nFlaky\nFlat\nFlexible\nFluffy\nFluttering\nFlying\nFollowing\nFond\nFoolish\nForeign\nFormal\nFormidable\nForthcoming\nFortunate\nForward\nFragile\nFrail\nFrantic\nFree\nFrench\nFrequent\nFresh\nFriendly\nFrightened\nFront\nFrozen\nFull\nFull-time\nFun\nFunctional\nFundamental\nFunny\nFurious\nFuture\nFuzzy\nGastric\nGeneral\nGenerous\nGenetic\nGentle\nGenuine\nGeographical\nGiant\nGigantic\nGiven\nGlad\nGlamorous\nGleaming\nGlobal\nGlorious\nGolden\nGood\nGorgeous\nGothic\nGoverning\nGraceful\nGradual\nGrand\nGrateful\nGreasy\nGreat\nGreek\nGreen\nGrey\nGrieving\nGrim\nGross\nGrotesque\nGrowing\nGrubby\nGrumpy\nGuilty\nHandicapped\nHandsome\nHappy\nHard\nHarsh\nHead\nHealthy\nHeavy\nHelpful\nHelpless\nHidden\nHigh\nHigh-pitched\nHilarious\nHissing\nHistoric\nHistorical\nHollow\nHoly\nHomeless\nHomely\nHon\nHonest\nHorizontal\nHorrible\nHostile\nHot\nHuge\nHuman\nHungry\nHurt\nHushed\nHusky\nIcy\nIdeal\nIdentical\nIdeological\nIll\nIllegal\nImaginative\nImmediate\nImmense\nImperial\nImplicit\nImportant\nImpossible\nImpressed\nImpressive\nImproved\nInadequate\nInappropriate\nInclined\nIncreased\nIncreasing\nIncredible\nIndependent\nIndirect\nIndividual\nIndustrial\nInevitable\nInfluential\nInformal\nInherent\nInitial\nInjured\nInland\nInner\nInnocent\nInnovative\nInquisitive\nInstant\nInstitutional\nInsufficient\nIntact\nIntegral\nIntegrated\nIntellectual\nIntelligent\nIntense\nIntensive\nInterested\nInteresting\nInterim\nInterior\nIntermediate\nInternal\nInternational\nIntimate\nInvisible\nInvolved\nIrrelevant\nIsolated\nItchy\nJealous\nJittery\nJoint\nJolly\nJoyous\nJudicial\nJuicy\nJunior\nJust\nKeen\nKey\nKind\nKnown\nLabour\nLarge\nLarge-scale\nLate\nLatin\nLazy\nLeading\nLeft\nLegal\nLegislative\nLegitimate\nLengthy\nLesser\nLevel\nLexical\nLiable\nLiberal\nLight\nLike\nLikely\nLimited\nLinear\nLinguistic\nLiquid\nLiterary\nLittle\nLive\nLively\nLiving\nLocal\nLogical\nLonely\nLong\nLong-term\nLoose\nLost\nLoud\nLovely\nLow\nLoyal\nLtd\nLucky\nMad\nMagenta\nMagic\nMagnetic\nMagnificent\nMain\nMajor\nMale\nMammoth\nManagerial\nManaging\nManual\nMany\nMarginal\nMarine\nMarked\nMarried\nMarvellous\nMass\nMassive\nMathematical\nMature\nMaximum\nMean\nMeaningful\nMechanical\nMedical\nMedieval\nMelodic\nMelted\nMental\nMere\nMetropolitan\nMid\nMiddle\nMiddle-class\nMighty\nMild\nMilitary\nMiniature\nMinimal\nMinimum\nMinisterial\nMinor\nMiserable\nMisleading\nMissing\nMisty\nMixed\nMoaning\nMobile\nModerate\nModern\nModest\nMolecular\nMonetary\nMonthly\nMoral\nMotionless\nMuddy\nMultiple\nMushy\nMusical\nMute\nMutual\nMysterious\nNaked\nNarrow\nNasty\nNative\nNatural\nNaughty\nNaval\nNear\nNearby\nNeat\nNecessary\nNegative\nNeighbouring\nNervous\nNet\nNeutral\nNew\nNice\nNineteenth-century\nNoble\nNoisy\nNormal\nNorthern\nNosy\nNotable\nNovel\nNuclear\nNumerous\nNursing\nNutritious\nNutty\nObedient\nObjective\nObliged\nObnoxious\nObvious\nOccasional\nOccupational\nOdd\nOfficial\nOk\nOkay\nOld\nOld-fashioned\nOlympic\nOnly\nOpen\nOperational\nOpposite\nOptimistic\nOral\nOrange\nOrdinary\nOrganic\nOrganisational\nOriginal\nOrthodox\nOther\nOutdoor\nOuter\nOutrageous\nOutside\nOutstanding\nOverall\nOverseas\nOverwhelming\nPainful\nPanicky\nParallel\nParental\nParliamentary\nPart-time\nPartial\nParticular\nPassing\nPassive\nPast\nPatient\nPayable\nPeaceful\nPeculiar\nPerfect\nPermanent\nPersistent\nPersonal\nPetite\nPhilosophical\nPhysical\nPink\nPlain\nPlanned\nPlastic\nPleasant\nPleased\nPoised\nPolite\nPoor\nPopular\nPositive\nPossible\nPost-war\nPotential\nPowerful\nPractical\nPrecious\nPrecise\nPreferred\nPregnant\nPreliminary\nPremier\nPrepared\nPresent\nPresidential\nPretty\nPrevious\nPrickly\nPrimary\nPrime\nPrimitive\nPrincipal\nPrinted\nPrior\nPrivate\nProbable\nProductive\nProfessional\nProfitable\nProfound\nProgressive\nProminent\nPromising\nProper\nProposed\nProspective\nProtective\nProtestant\nProud\nProvincial\nPsychiatric\nPsychological\nPublic\nPuny\nPure\nPurple\nPurring\nPuzzled\nQuaint\nQualified\nQuick\nQuickest\nQuiet\nRacial\nRadical\nRainy\nRandom\nRapid\nRare\nRaspy\nRational\nRatty\nRaw\nReady\nReal\nRealistic\nRear\nReasonable\nRecent\nRed\nReduced\nRedundant\nRegional\nRegistered\nRegular\nRegulatory\nRelated\nRelative\nRelaxed\nRelevant\nReliable\nRelieved\nReligious\nReluctant\nRemaining\nRemarkable\nRemote\nRenewed\nRepresentative\nRepulsive\nRequired\nResident\nResidential\nResonant\nRespectable\nRespective\nResponsible\nResulting\nRetail\nRetired\nRevolutionary\nRich\nRidiculous\nRight\nRigid\nRipe\nRising\nRival\nRoasted\nRobust\nRolling\nRomantic\nRotten\nRough\nRound\nRoyal\nRubber\nRude\nRuling\nRunning\nRural\nSacred\nSad\nSafe\nSalty\nSatisfactory\nSatisfied\nScared\nScary\nScattered\nScientific\nScornful\nScrawny\nScreeching\nSecondary\nSecret\nSecure\nSelect\nSelected\nSelective\nSelfish\nSemantic\nSenior\nSensible\nSensitive\nSeparate\nSerious\nSevere\nShaggy\nShaky\nShallow\nShared\nSharp\nSheer\nShiny\nShivering\nShocked\nShort\nShort-term\nShrill\nShy\nSick\nSignificant\nSilent\nSilky\nSilly\nSimilar\nSimple\nSingle\nSkilled\nSkinny\nSleepy\nSlight\nSlim\nSlimy\nSlippery\nSlow\nSmall\nSmart\nSmiling\nSmoggy\nSmooth\nSo-called\nSocial\nSoft\nSolar\nSole\nSolid\nSophisticated\nSore\nSorry\nSound\nSour\nSouthern\nSoviet\nSpare\nSparkling\nSpatial\nSpecial\nSpecific\nSpecified\nSpectacular\nSpicy\nSpiritual\nSplendid\nSpontaneous\nSporting\nSpotless\nSpotty\nSquare\nSquealing\nStable\nStale\nStandard\nStatic\nStatistical\nStatutory\nSteady\nSteep\nSticky\nStiff\nStill\nStingy\nStormy\nStraight\nStraightforward\nStrange\nStrategic\nStrict\nStriking\nStriped\nStrong\nStructural\nStuck\nSubjective\nSubsequent\nSubstantial\nSubtle\nSuccessful\nSuccessive\nSudden\nSufficient\nSuitable\nSunny\nSuper\nSuperb\nSuperior\nSupporting\nSupposed\nSupreme\nSure\nSurprised\nSurprising\nSurrounding\nSurviving\nSuspicious\nSweet\nSwift\nSymbolic\nSympathetic\nSystematic\nTall\nTame\nTan\nTart\nTasteless\nTasty\nTechnical\nTechnological\nTeenage\nTemporary\nTender\nTense\nTerrible\nTerritorial\nTesty\nThen\nTheoretical\nThick\nThin\nThirsty\nThorough\nThoughtful\nThoughtless\nThundering\nTight\nTiny\nTired\nTop\nTory\nTotal\nTough\nToxic\nTraditional\nTragic\nTremendous\nTricky\nTropical\nTroubled\nTypical\nUgliest\nUltimate\nUnable\nUnacceptable\nUnaware\nUncertain\nUnchanged\nUncomfortable\nUnconscious\nUnderground\nUnderlying\nUnemployed\nUneven\nUnexpected\nUnfair\nUnfortunate\nUnhappy\nUniform\nUninterested\nUnique\nUnited\nUniversal\nUnknown\nUnlikely\nUnnecessary\nUnpleasant\nUnsightly\nUnusual\nUnwilling\nUpper\nUpset\nUptight\nUrban\nUrgent\nUsed\nUseful\nUseless\nUsual\nVague\nValid\nValuable\nVariable\nVaried\nVarious\nVarying\nVast\nVerbal\nVertical\nVery\nVictorious\nVideo-taped\nViolent\nVisible\nVisiting\nVisual\nVital\nVivacious\nVivid\nVocational\nVoiceless\nVoluntary\nVulnerable\nWandering\nWarm\nWasteful\nWatery\nWeak\nWealthy\nWeary\nWee\nWeekly\nWeird\nWelcome\nWell\nWell-known\nWelsh\nWet\nWhispering\nWhite\nWhole\nWicked\nWide\nWide-eyed\nWidespread\nWild\nWilling\nWise\nWitty\nWonderful\nWooden\nWorking\nWorldwide\nWorried\nWorrying\nWorthwhile\nWorthy\nWritten\nWrong\nYellow\nYoung\nYummy\nZany\nZealous"
  },
  {
    "path": "VirtualCore/Source/Resources/VirtualCore.xcassets/Adjectives.dataset/Contents.json",
    "content": "{\n  \"data\" : [\n    {\n      \"filename\" : \"Adjectives.txt\",\n      \"idiom\" : \"universal\",\n      \"universal-type-identifier\" : \"public.plain-text\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "VirtualCore/Source/Resources/VirtualCore.xcassets/Animals.dataset/Animals.txt",
    "content": "Aardvark\nAlbatross\nAlligator\nAlpaca\nAnt\nAnteater\nAntelope\nApe\nArmadillo\nDonkey\nBaboon\nBadger\nBarracuda\nBat\nBear\nBeaver\nBee\nBison\nBoar\nBuffalo\nButterfly\nCamel\nCapybara\nCaribou\nCassowary\nCat\nCaterpillar\nCattle\nChamois\nCheetah\nChicken\nChimpanzee\nChinchilla\nChough\nClam\nCobra\nCockroach\nCod\nCormorant\nCoyote\nCrab\nCrane\nCrocodile\nCrow\nCurlew\nDeer\nDinosaur\nDog\nDogfish\nDolphin\nDotterel\nDove\nDragonfly\nDuck\nDugong\nDunlin\nEagle\nEchidna\nEel\nEland\nElephant\nElk\nEmu\nFalcon\nFerret\nFinch\nFish\nFlamingo\nFly\nFox\nFrog\nGaur\nGazelle\nGerbil\nGiraffe\nGnat\nGnu\nGoat\nGoldfinch\nGoldfish\nGoose\nGorilla\nGoshawk\nGrasshopper\nGrouse\nGuanaco\nGull\nHamster\nHare\nHawk\nHedgehog\nHeron\nHerring\nHippopotamus\nHornet\nHorse\nHuman\nHummingbird\nHyena\nIbex\nIbis\nJackal\nJaguar\nJay\nJellyfish\nKangaroo\nKingfisher\nKoala\nKookabura\nKouprey\nKudu\nLapwing\nLark\nLemur\nLeopard\nLion\nLlama\nLobster\nLocust\nLoris\nLouse\nLyrebird\nMagpie\nMallard\nManatee\nMandrill\nMantis\nMarten\nMeerkat\nMink\nMole\nMongoose\nMonkey\nMoose\nMosquito\nMouse\nMule\nNarwhal\nNewt\nNightingale\nOctopus\nOkapi\nOpossum\nOryx\nOstrich\nOtter\nOwl\nOyster\nPanther\nParrot\nPartridge\nPeafowl\nPelican\nPenguin\nPheasant\nPig\nPigeon\nPony\nPorcupine\nPorpoise\nQuail\nQuelea\nQuetzal\nRabbit\nRaccoon\nRail\nRam\nRat\nRaven\nRed deer\nRed panda\nReindeer\nRhinoceros\nRook\nSalamander\nSalmon\nSand Dollar\nSandpiper\nSardine\nScorpion\nSeahorse\nSeal\nShark\nSheep\nShrew\nSkunk\nSnail\nSnake\nSparrow\nSpider\nSpoonbill\nSquid\nSquirrel\nStarling\nStingray\nStinkbug\nStork\nSwallow\nSwan\nTapir\nTarsier\nTermite\nTiger\nToad\nTrout\nTurkey\nTurtle\nViper\nVulture\nWallaby\nWalrus\nWasp\nWeasel\nWhale\nWildcat\nWolf\nWolverine\nWombat\nWoodcock\nWoodpecker\nWorm\nWren\nYak\nZebra"
  },
  {
    "path": "VirtualCore/Source/Resources/VirtualCore.xcassets/Animals.dataset/Contents.json",
    "content": "{\n  \"data\" : [\n    {\n      \"filename\" : \"Animals.txt\",\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "VirtualCore/Source/Resources/VirtualCore.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "VirtualCore/Source/Restore/Download/DownloadBackend.swift",
    "content": "import Foundation\nimport Combine\n\npublic enum DownloadState: Hashable {\n    case idle\n    case preCheck(_ message: String)\n    case downloading(_ progress: Double?, _ eta: Double?)\n    case failed(_ error: String)\n    case done(_ localURL: URL)\n}\n\npublic protocol DownloadBackend: AnyObject {\n    init(library: VMLibraryController, cookie: String?)\n    var statePublisher: AnyPublisher<DownloadState, Never> { get }\n    @MainActor func startDownload(with url: URL)\n    @MainActor func cancelDownload()\n}\n"
  },
  {
    "path": "VirtualCore/Source/Restore/Download/SimulatedDownloadBackend.swift",
    "content": "import Foundation\nimport Combine\n\n#if DEBUG\npublic final class SimulatedDownloadBackend: NSObject, DownloadBackend {\n    public static let localFileURL = Bundle.virtualCore.url(forResource: \"FakeRestoreImage\", withExtension: \"ipsw\")!\n\n    public init(library: VMLibraryController, cookie: String?) {\n        super.init()\n    }\n\n    public var statePublisher: AnyPublisher<DownloadState, Never> { stateSubject.eraseToAnyPublisher() }\n\n    private let stateSubject = PassthroughSubject<DownloadState, Never>()\n\n    private var timer: Timer?\n\n    private var progress: Double = 0\n\n    public func startDownload(with url: URL) {\n        let timer = Timer(timeInterval: 0.1, repeats: true) { [weak self] _ in\n            guard let self else { return }\n\n            progress += 0.01\n\n            if progress >= 1.0 {\n                stateSubject.send(.done(Self.localFileURL))\n            } else {\n                stateSubject.send(.downloading(progress, progress >= 0.15 ? 100 - progress * 100 : 0))\n            }\n        }\n        self.timer = timer\n\n        /// Schedule timer manually so that it's not blocked by modal dialogs or event tracking.\n        RunLoop.main.add(timer, forMode: .common)\n    }\n\n    public func cancelDownload() {\n        stateSubject.send(.failed(\"Cancelled.\"))\n    }\n}\n#endif // DEBUG\n"
  },
  {
    "path": "VirtualCore/Source/Restore/Download/URLSessionDownloadBackend.swift",
    "content": "//\n//  URLSessionDownloadBackend.swift\n//  VirtualCore\n//\n//  Created by Guilherme Rambo on 07/06/22.\n//\n\nimport Foundation\nimport Combine\nimport OSLog\nimport BuddyFoundation\n\npublic final class URLSessionDownloadBackend: NSObject, ObservableObject, DownloadBackend {\n\n    private let logger = Logger(subsystem: VirtualCoreConstants.subsystemName, category: \"URLSessionDownloadBackend\")\n\n    let library: VMLibraryController\n    public var cookie: String?\n\n    public init(library: VMLibraryController, cookie: String?) {\n        self.library = library\n        self.cookie = cookie\n    }\n\n    private var downloadTask: URLSessionDownloadTask?\n\n    private var isInFailedState: Bool {\n        guard case .failed = state else { return false }\n        return true\n    }\n\n    public private(set) lazy var statePublisher: AnyPublisher<DownloadState, Never> = $state.eraseToAnyPublisher()\n\n    @Published\n    public private(set) var state = DownloadState.idle\n\n    private lazy var session = makeSession()\n\n    private func makeSession() -> URLSession {\n        let config = URLSessionConfiguration.default\n        config.httpMaximumConnectionsPerHost = 16\n        return URLSession(configuration: config, delegate: self, delegateQueue: .main)\n    }\n\n    private var destinationURL: URL?\n\n    @MainActor\n    public func startDownload(with url: URL) {\n        logger.debug(\"Start download from \\(url.absoluteString.quoted)\")\n\n        session = makeSession()\n\n        resetProgress()\n\n        state = .downloading(nil, nil)\n\n        let filename = url.lastPathComponent\n\n        self.destinationURL = VBSettings.current.downloadsDirectoryURL.appendingPathComponent(filename)\n\n        var request = URLRequest(url: url)\n\n        request.setValue(cookie, forHTTPHeaderField: \"Cookie\")\n\n        downloadTask = session.downloadTask(with: request)\n        downloadTask?.delegate = self\n        downloadTask?.resume()\n    }\n\n    @MainActor\n    public func cancelDownload() {\n        downloadTask?.cancel()\n        downloadTask = nil\n\n        session.finishTasksAndInvalidate()\n    }\n\n    private let minElapsedProgressForETA: Double = 0.01\n    private var elapsedTime: Double = 0\n    private var ppsObservations: [Double] = []\n    private let ppsObservationsLimit = 500\n    private var ppsAverage: Double {\n        guard !ppsObservations.isEmpty else { return -1 }\n        return ppsObservations.reduce(Double(0), +) / Double(ppsObservations.count)\n    }\n\n    private var pps: Double = -1\n\n    private var eta: Double = -1\n\n    private var lastProgressDate = Date()\n\n    private var progress: Double = 0\n\n    private func resetProgress() {\n        elapsedTime = 0\n        eta = -1\n        pps = -1\n        ppsObservations = []\n    }\n\n}\n\nextension URLSessionDownloadBackend: URLSessionDownloadDelegate, URLSessionDelegate {\n\n    public func urlSession(_ session: URLSession, task: URLSessionTask, willPerformHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest) async -> URLRequest? {\n        logger.debug(\"Will perform HTTP redirection \\(response)\")\n\n        if request.url?.absoluteString.lowercased().contains(\"unauthorized\") == true {\n            DispatchQueue.main.async {\n                self.state = .failed(\"The download failed due to missing authentication credentials.\")\n            }\n            return nil\n        } else {\n            if let newCookie = response.value(forHTTPHeaderField: \"Set-Cookie\"), let firstItem = newCookie.components(separatedBy: \";\").first {\n                var newRequest = request\n                let newCookieValue = (newRequest.value(forHTTPHeaderField: \"Cookie\") ?? \"\") + \"; \" + firstItem\n                newRequest.setValue(newCookieValue, forHTTPHeaderField: \"Cookie\")\n                return newRequest\n            } else {\n                return request\n            }\n        }\n    }\n\n    public func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {\n        guard !isInFailedState else { return }\n\n        logger.notice(\"Download finished to \\(location.path.quoted)\")\n\n        if let response = downloadTask.response as? HTTPURLResponse {\n            guard 200..<300 ~= response.statusCode else {\n                logger.error(\"Download failed with HTTP \\(response.statusCode)\")\n\n                state = .failed(\"HTTP error \\(response.statusCode). Please check the download link.\")\n                return\n            }\n        } else {\n            logger.fault(\"Download task finished without a valid response!\")\n        }\n\n        guard let destinationURL = destinationURL else {\n            state = .failed(\"Missing destination URL.\")\n            assertionFailure(\"WAT\")\n            return\n        }\n\n        do {\n            if FileManager.default.fileExists(atPath: destinationURL.path) {\n                try FileManager.default.removeItem(at: destinationURL)\n            }\n\n            try FileManager.default.moveItem(at: location, to: destinationURL)\n\n            DispatchQueue.main.async { self.state = .done(destinationURL) }\n        } catch {\n            DispatchQueue.main.async { self.state = .failed(\"Failed to move downloaded file: \\(error.localizedDescription)\") }\n        }\n    }\n\n    public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {\n        if let error {\n            logger.error(\"Download failed - \\(error, privacy: .public)\")\n        } else {\n            logger.notice(\"Download completed\")\n        }\n\n        // Successful completion is handled in `urlSession:downloadTask:didFinishDownloadingTo`.\n        guard let error = error else { return }\n        state = .failed(error.localizedDescription)\n    }\n\n    public func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {\n        DispatchQueue.main.async { [self] in\n            let interval = Date().timeIntervalSince(lastProgressDate)\n            lastProgressDate = Date()\n\n            let percent = Double(totalBytesWritten) / Double(totalBytesExpectedToWrite)\n\n            updateProgress(with: percent, interval: interval)\n        }\n    }\n\n    private func updateProgress(with progress: Double, interval: Double) {\n        let currentPPS = progress / elapsedTime\n\n        if currentPPS.isFinite && !currentPPS.isZero && !currentPPS.isNaN {\n            ppsObservations.append(currentPPS)\n            if ppsObservations.count >= ppsObservationsLimit {\n                ppsObservations.removeFirst()\n            }\n        }\n\n        elapsedTime += interval\n\n        if self.progress > self.minElapsedProgressForETA {\n            if pps < 0 {\n                pps = progress / elapsedTime\n            }\n\n            eta = (1/ppsAverage) - elapsedTime\n\n            self.state = .downloading(progress, eta)\n        } else {\n            self.state = .downloading(progress, nil)\n        }\n\n        self.progress = progress\n    }\n\n}\n"
  },
  {
    "path": "VirtualCore/Source/Restore/Installation/RestoreBackend.swift",
    "content": "import Foundation\nimport Virtualization\nimport BuddyKit\n\n@MainActor\npublic protocol RestoreBackend: AnyObject {\n    init(model: VBVirtualMachine, restoringFromImageAt restoreImageFileURL: URL)\n    var progress: Progress { get }\n    func install() async throws\n    func cancel() async\n}\n"
  },
  {
    "path": "VirtualCore/Source/Restore/Installation/SimulatedRestoreBackend.swift",
    "content": "import Foundation\nimport Virtualization\nimport BuddyKit\nimport Combine\n\n#if DEBUG\npublic final class SimulatedRestoreBackend: NSObject, RestoreBackend, @unchecked Sendable {\n    public init(model: VBVirtualMachine, restoringFromImageAt restoreImageFileURL: URL) {\n        super.init()\n    }\n\n    public let progress = Progress(totalUnitCount: 100)\n\n    private var timer: Timer?\n\n    private var cancellable: AnyCancellable?\n\n    public func install() async throws {\n        await withCheckedContinuation { continuation in\n            cancellable = Timer.publish(every: 0.1, on: .main, in: .common)\n                .autoconnect()\n                .sink { [weak self] _ in\n                    guard let self else { return }\n\n                    let count = progress.completedUnitCount + 1\n                    progress.completedUnitCount = count\n\n                    if count >= progress.totalUnitCount {\n                        MainActor.assumeIsolated {\n                            self.timer?.invalidate()\n                            self.cancellable = nil\n                        }\n\n                        continuation.resume()\n                    }\n                }\n        }\n    }\n\n    public func cancel() async {\n        \n    }\n}\n#endif // DEBUG\n"
  },
  {
    "path": "VirtualCore/Source/Restore/Installation/VirtualizationRestoreBackend.swift",
    "content": "import Foundation\nimport Virtualization\nimport BuddyKit\nimport OSLog\nimport Combine\n\npublic final class VirtualizationRestoreBackend: RestoreBackend {\n    private let logger = Logger(subsystem: VirtualCoreConstants.subsystemName, category: \"VirtualizationRestoreBackend\")\n\n    public let model: VBVirtualMachine\n    public let restoreImageFileURL: URL\n\n    public init(model: VBVirtualMachine, restoringFromImageAt restoreImageFileURL: URL) {\n        self.model = model\n        self.restoreImageFileURL = restoreImageFileURL\n    }\n\n    private var cancellables = Set<AnyCancellable>()\n\n    public let progress = Progress()\n\n    private var _installer: VZMacOSInstaller?\n\n    private let virtualMachineSubject = PassthroughSubject<VZVirtualMachine?, Never>()\n    public var virtualMachine: AnyPublisher<VZVirtualMachine?, Never> { virtualMachineSubject.eraseToAnyPublisher() }\n\n    public func install() async throws {\n        let installModel = model.forInstallation()\n\n        let config = try await VMInstance.makeConfiguration(for: installModel, installImageURL: restoreImageFileURL)\n\n        let vm = VZVirtualMachine(configuration: config)\n\n        await MainActor.run {\n            virtualMachineSubject.send(vm)\n        }\n\n        let installer = VZMacOSInstaller(virtualMachine: vm, restoringFromImageAt: restoreImageFileURL)\n\n        _installer = installer\n\n        createInternalProgressObservations(with: installer)\n\n        defer {\n            cleanup()\n        }\n\n        try Task.checkCancellation()\n\n        try await installer.install()\n    }\n\n    public func cancel() async {\n        logger.warning(\"Installation cancelled by client.\")\n\n        progress.cancel()\n\n        if let _installer, _installer.virtualMachine.canStop {\n            do {\n                logger.info(\"Stopping installation VM...\")\n\n                try await _installer.virtualMachine.stop()\n\n                logger.info(\"Installation VM stopped.\")\n            } catch {\n                logger.error(\"Error forcing installation VM stop - \\(error, privacy: .public)\")\n            }\n        }\n\n        cleanup()\n    }\n\n    private func cleanup() {\n        logger.debug(\"Cleaning up installation.\")\n\n        cancellables.removeAll()\n        _installer = nil\n        virtualMachineSubject.send(nil)\n    }\n\n    private func createInternalProgressObservations(with installer: VZMacOSInstaller) {\n        installer.progress\n            .publisher(for: \\.totalUnitCount, options: [.initial, .new])\n            .sink { [weak self] value in\n                self?.progress.totalUnitCount = value\n            }\n            .store(in: &cancellables)\n\n        installer.progress\n            .publisher(for: \\.completedUnitCount, options: [.initial, .new])\n            .sink { [weak self] value in\n                self?.progress.completedUnitCount = value\n            }\n            .store(in: &cancellables)\n    }\n}\n\nextension VBVirtualMachine {\n    /// Returns a copy of the model configured for use during installation.\n    func forInstallation() -> Self {\n        var mself = self\n\n        /// Use a fixed 1080p display resolution for installation.\n        mself.configuration.hardware.displayDevices = [VBDisplayPreset.fullHD.device]\n\n        return mself\n    }\n}\n"
  },
  {
    "path": "VirtualCore/Source/Restore Images/Models/VBRestoreImageInfo.swift",
    "content": "//\n//  VBRestoreImageInfo.swift\n//  VirtualCore\n//\n//  Created by Guilherme Rambo on 07/06/22.\n//\n\nimport Foundation\nimport BuddyFoundation\n\npublic struct VBGuestReleaseChannel: Hashable, Identifiable, Codable {\n    public struct Authentication: Hashable, Identifiable, Codable {\n        public var id: String { name }\n        public var name: String\n        public var url: URL\n        public var note: String\n    }\n\n    public var id: String\n    public var name: String\n    public var note: String\n    public var icon: String\n    public var authentication: Authentication?\n\n    public init(id: String, name: String, note: String, icon: String, authentication: Authentication? = nil) {\n        self.id = id\n        self.name = name\n        self.note = note\n        self.icon = icon\n        self.authentication = authentication\n    }\n}\n\npublic struct VBGuestReleaseGroup: Hashable, Identifiable, Codable {\n    public var id: String\n    public var name: String\n    public var majorVersion: SoftwareVersion\n    public var minHostVersion: SoftwareVersion\n\n    public init(id: String, name: String, majorVersion: SoftwareVersion, minHostVersion: SoftwareVersion) {\n        self.id = id\n        self.name = name\n        self.majorVersion = majorVersion\n        self.minHostVersion = minHostVersion\n    }\n}\n\npublic struct VBRestoreImageInfo: Hashable, Identifiable, Codable {\n    public var id: String { build }\n    public var group: VBGuestReleaseGroup\n    public var channel: VBGuestReleaseChannel\n    public var name: String\n    public var build: String\n    public var url: URL\n    @DecodableDefault.False\n    public var needsCookie: Bool\n\n    public init(group: VBGuestReleaseGroup, channel: VBGuestReleaseChannel, name: String, build: String, url: URL, needsCookie: Bool) {\n        self.group = group\n        self.channel = channel\n        self.name = name\n        self.build = build\n        self.url = url\n        self.needsCookie = needsCookie\n    }\n}\n\npublic extension VBRestoreImageInfo {\n\n    var authenticationRequirement: VBGuestReleaseChannel.Authentication? {\n        guard needsCookie else { return nil }\n\n        return channel.authentication\n    }\n\n}\n\npublic extension VBGuestReleaseChannel.Authentication {\n\n    func satisfiedCookieHeaderValue(with cookies: [HTTPCookie]) -> String? {\n        let targetCookieNames = Set([\"myacinfo\", \"aidshd\", \"DSESSIONID\", \"PHPSESSID\", \"dawsp\", \"aasp\"])\n        guard Set(cookies.map(\\.name)).intersection(targetCookieNames) == targetCookieNames else { return nil }\n\n        let formattedCookies = cookies.map({ \"\\($0.name)=\\($0.value)\" }).joined(separator: \"; \")\n\n        return formattedCookies\n    }\n\n}\n"
  },
  {
    "path": "VirtualCore/Source/Restore Images/Models/VBRestoreImagesResponse.swift",
    "content": "//\n//  VBRestoreImagesResponse.swift\n//  VirtualCore\n//\n//  Created by Guilherme Rambo on 07/06/22.\n//\n\nimport Foundation\n\nstruct VBRestoreImagesResponse: Decodable {\n    let success: Bool\n    let error: String?\n    let channels: [VBGuestReleaseChannel]\n    let groups: [VBGuestReleaseGroup]\n    let images: [VBRestoreImageInfo]\n}\n\n"
  },
  {
    "path": "VirtualCore/Source/Restore Images/VBAPIClient.swift",
    "content": "//\n//  VBAPIClient.swift\n//  VirtualCore\n//\n//  Created by Guilherme Rambo on 07/06/22.\n//\n\nimport Foundation\nimport OSLog\n\npublic final class VBAPIClient {\n\n    private let logger = Logger(for: VBAPIClient.self)\n\n    public struct Environment: Hashable {\n        public var baseURL: URL\n        public var apiKey: String\n\n        #if DEBUG\n        public static let local = Environment(\n            baseURL: URL(string: \"https://virtualbuddy.ngrok.io/v2\")!,\n            apiKey: \"15A25D48-4A34-4EE4-A293-C22B0DE1B54E\"\n        )\n\n        public static let development = Environment(\n            baseURL: URL(string: \"https://virtualbuddy-api-dev.bestbuddyapps3496.workers.dev/v2\")!,\n            apiKey: \"15A25D48-4A34-4EE4-A293-C22B0DE1B54E\"\n        )\n        #endif\n\n        public static let production = Environment(\n            baseURL: URL(string: \"https://api.virtualbuddy.app/v2\")!,\n            apiKey: \"15A25D48-4A34-4EE4-A293-C22B0DE1B54E\"\n        )\n\n        public static var current: Environment {\n            #if DEBUG\n            if let override = UserDefaults.standard.string(forKey: \"VBAPIEnvironment\") {\n                if override == \"development\" {\n                    return .development\n                } else if override == \"local\" {\n                    return .local\n                } else {\n                    assertionFailure(\"Unknown API environment: \\(override)\")\n                    return .production\n                }\n            } else {\n                return .production\n            }\n            #else\n            return .production\n            #endif\n        }\n    }\n\n    public let environment: Environment\n\n    public init(environment: Environment = .current) {\n        self.environment = environment\n    }\n\n    private func request(for endpoint: String, query: [String: String] = [:], cachePolicy: URLRequest.CachePolicy = .useProtocolCachePolicy) throws -> URLRequest {\n        let url = environment.baseURL\n            .appendingPathComponent(endpoint)\n\n        var components = URLComponents(url: url, resolvingAgainstBaseURL: false)!\n\n        components.queryItems = [\n            URLQueryItem(name: \"apiKey\", value: environment.apiKey)\n        ]\n\n        for (key, value) in query {\n            components.queryItems?.append(URLQueryItem(name: key, value: value))\n        }\n\n        var request = try URLRequest(url: components.url.require(\"Failed to construct request URL.\"))\n\n        request.cachePolicy = cachePolicy\n\n        return request\n    }\n\n    private static let decoder = JSONDecoder()\n\n    @MainActor\n    public func fetchRestoreImages(for guest: VBGuestType, skipCache: Bool) async throws -> SoftwareCatalog {\n        let catalog = try await performCatalogFetch(for: guest, skipCache: skipCache)\n\n        SoftwareCatalog.setCurrent(catalog, for: guest)\n\n        return catalog\n    }\n\n    @MainActor\n    func performCatalogFetch(for guest: VBGuestType, skipCache: Bool) async throws -> SoftwareCatalog {\n        #if DEBUG\n        guard !ProcessInfo.isSwiftUIPreview, !UserDefaults.standard.bool(forKey: \"VBForceBuiltInSoftwareCatalog\") else {\n            logger.debug(\"Forcing built-in catalog\")\n            return try Self.fetchBuiltInCatalog(for: guest)\n        }\n        #endif\n\n        do {\n            let remoteCatalog = try await fetchRemoteCatalog(for: guest, skipCache: skipCache)\n\n            logger.debug(\"Fetched remote catalog with \\(remoteCatalog.restoreImages.count, privacy: .public) restore images\")\n\n            return remoteCatalog\n        } catch {\n            logger.error(\"Remote catalog fetch failed: \\(error, privacy: .public), using local fallback\")\n\n            do {\n                let builtInCatalog = try Self.fetchBuiltInCatalog(for: guest)\n\n                logger.debug(\"Fetched built-in catalog with \\(builtInCatalog.restoreImages.count, privacy: .public) restore images\")\n\n                return builtInCatalog\n            } catch {\n                logger.fault(\"Built-in catalog load failed: \\(error, privacy: .public)\")\n                assertionFailure(\"Built-in catalog load failed: \\(error)\")\n                throw error\n            }\n        }\n    }\n\n    func fetchRemoteCatalog(for guest: VBGuestType, skipCache: Bool) async throws -> SoftwareCatalog {\n        let req = try request(\n            for: guest.restoreImagesAPIPath,\n            cachePolicy: skipCache ? .reloadIgnoringLocalAndRemoteCacheData : .useProtocolCachePolicy\n        )\n\n        logger.debug(\"Fetching remote catalog from \\(req.url?.host() ?? \"<nil>\") (skipCache? \\(skipCache, privacy: .public))\")\n\n        let (data, res) = try await URLSession.shared.data(for: req)\n\n        let code = (res as! HTTPURLResponse).statusCode\n\n        guard code == 200 else {\n            throw \"HTTP \\(code)\"\n        }\n\n        let response = try Self.decoder.decode(SoftwareCatalog.self, from: data)\n\n        return response\n    }\n\n    static func fetchBuiltInCatalog(for guest: VBGuestType) throws -> SoftwareCatalog {\n        let fileName = switch guest {\n        case .mac:\n            \"ipsws_v2\"\n        case .linux:\n            \"linux_v2\"\n        }\n\n        guard let url = Bundle.virtualCore.url(forResource: fileName, withExtension: \"json\", subdirectory: \"SoftwareCatalog\") else {\n            throw \"\\(fileName) not found in VirtualCore SoftwareCatalog resources\"\n        }\n\n        let data = try Data(contentsOf: url)\n\n        return try decoder.decode(SoftwareCatalog.self, from: data)\n    }\n\n    @MainActor\n    public func signingStatus(for ipswURL: URL) async -> BuildSigningStatus {\n        #if DEBUG\n        let simulatedStatus: BuildSigningStatus? = if UserDefaults.standard.bool(forKey: \"VBForceSigningStatusUnsigned\") {\n            .unsigned(\"This version of macOS is not being signed by Apple for virtual machines, so it can’t be installed.\")\n        } else if UserDefaults.standard.bool(forKey: \"VBForceSigningStatusSigned\") {\n            .signed\n        } else if UserDefaults.standard.bool(forKey: \"VBForceSigningStatusRequestFailed\") {\n            .checkFailed(\"Simulated request failure.\")\n        } else if ProcessInfo.isSwiftUIPreview {\n            .signed\n        } else {\n            nil\n        }\n\n        if let simulatedStatus {\n            try? await Task.sleep(for: .milliseconds(200))\n            return simulatedStatus\n        }\n        #endif // DEBUG\n\n        do {\n            let req = try request(for: \"tss\", query: [\"ipsw\": ipswURL.absoluteString])\n\n            logger.debug(\"Fetching signing status for \\(ipswURL.absoluteString.quoted)\")\n\n            let (data, res) = try await URLSession.shared.data(for: req)\n\n            let code = (res as! HTTPURLResponse).statusCode\n\n            guard code == 200 else {\n                throw \"HTTP \\(code)\"\n            }\n\n            let response = try Self.decoder.decode(BuildSigningStatusResponse.self, from: data)\n\n            logger.info(\"TSS check request succeeded with response:\\n\\(String(decoding: data, as: UTF8.self))\")\n\n            if response.isSigned {\n                return .signed\n            } else {\n                assert(response.message != nil, \"Expected API to return message for unsigned TSS response.\")\n                return .unsigned(response.message ?? \"This build is not signed.\")\n            }\n        } catch {\n            logger.error(\"Signing status fetch failed - \\(error, privacy: .public)\")\n\n            return .checkFailed(error.localizedDescription)\n        }\n    }\n\n}\n\nprivate extension VBGuestType {\n    var restoreImagesAPIPath: String {\n        switch self {\n        case .mac:\n            return \"/restore/mac\"\n        case .linux:\n            return \"/restore/linux\"\n        }\n    }\n}\n\n/// Vended by ``VBAPIClient/signingStatus(for:)``.\npublic enum BuildSigningStatus {\n    case signed\n    case unsigned(_ message: String)\n    case checkFailed(_ error: String)\n}\n\n/// TSS signing status check result.\nprivate struct BuildSigningStatusResponse: Decodable {\n    /// Whether the build is signed by Apple.\n    public private(set) var isSigned: Bool\n\n    /// User-facing message when `isSigned` is `false`.\n    public private(set) var message: String?\n}\n"
  },
  {
    "path": "VirtualCore/Source/Settings/VBSettings+CatalogDownload.swift",
    "content": "import Foundation\nimport BuddyKit\nimport OSLog\n\nprivate let logger = Logger(subsystem: VirtualCoreConstants.subsystemName, category: \"VBSettings+CatalogDownload\")\n\npublic extension VBSettings {\n    private static let fallbackURL = FileManager.default.temporaryDirectory\n\n    var downloadsDirectoryURL: URL {\n        do {\n            let baseURL = libraryURL.appendingPathComponent(\"_Downloads\")\n\n            if !FileManager.default.fileExists(atPath: baseURL.path) {\n                logger.debug(\"Creating downloads directory at \\(baseURL.path)\")\n\n                try FileManager.default.createDirectory(at: baseURL, withIntermediateDirectories: true)\n            }\n\n            return baseURL\n        } catch {\n            logger.fault(\"Error getting downloads base URL - \\(error, privacy: .public)\")\n\n            return Self.fallbackURL\n        }\n    }\n}\n\nextension VBSettings: CatalogDownloadsProvider {\n    public func localFileURL(for restoreImage: RestoreImage) -> URL? {\n        do {\n            let files = try FilePath(downloadsDirectoryURL).children().map(\\.url.vb_restoreImageStub)\n\n            guard let stub = files.vb_elementMatchingDownloadableCatalogContent(at: restoreImage.url) else { return nil }\n\n            logger.debug(\"Found download matching \\(restoreImage.name.quoted) - \\(stub)\")\n\n            /// Take this opportunity to set the extended attribute if it hasn't been set yet.\n            stub.url.vb_addSoftwareCatalogExtendedAttributeIfNeeded(for: restoreImage)\n\n            return stub.url\n        } catch {\n            logger.fault(\"Error enumerating downloads directory - \\(error, privacy: .public)\")\n\n            return nil\n        }\n    }\n}\n\nextension URL {\n    private static let logger = Logger(subsystem: VirtualCoreConstants.subsystemName, category: \"URL+SoftwareCatalogAttribute\")\n\n    func vb_addSoftwareCatalogExtendedAttributeIfNeeded(for restoreImage: RestoreImage) {\n        guard vb_softwareCatalogData == nil else { return }\n\n        Self.logger.debug(\"Adding software catalog extended attribute for \\(restoreImage.build) to \\(lastPathComponent.quoted)\")\n\n        vb_softwareCatalogData = VirtualBuddyCatalogData(\n            build: restoreImage.build,\n            filename: restoreImage.url.lastPathComponent\n        )\n    }\n}\n"
  },
  {
    "path": "VirtualCore/Source/Settings/VBSettings.swift",
    "content": "//\n//  VBSettings.swift\n//  VirtualCore\n//\n//  Created by Guilherme Rambo on 05/06/22.\n//\n\nimport Foundation\nimport OSLog\n\nprivate let logger = Logger(subsystem: VirtualCoreConstants.subsystemName, category: String(describing: VBSettings.self))\n\npublic struct VBSettings: Hashable, Sendable {\n\n    public static var current: VBSettings { VBSettingsContainer.current.settings }\n\n    public static let updateChannelDidChangeNotification = Notification.Name(\"VBSettingsUpdateChannelDidChangeNotification\")\n\n    public static let currentVersion = 3\n\n    public var version: Int = Self.currentVersion\n    public var libraryURL: URL {\n        didSet {\n            guard libraryURL != oldValue else { return }\n            isLibraryInRemovableVolume = libraryURL.isInRemovableVolume ?? false\n        }\n    }\n    public var updateChannel: AppUpdateChannel {\n        didSet {\n            guard updateChannel != oldValue else { return }\n            NotificationCenter.default.post(name: Self.updateChannelDidChangeNotification, object: updateChannel)\n        }\n    }\n    public var enableTSSCheck: Bool\n\n    /// Show desktop picture from guest VM in thumbnails instead of the blur hash version.\n    /// Currently not exposed in the UI.\n    public var showDesktopPictureThumbnails: Bool\n\n    /// Enables using the new ASIF format for boot disk images (requires macOS 26+ host).\n    public var bootDiskImagesUseASIF: Bool\n\n    /// Updated when user changes the library directory.\n    /// This helps ``VMLibraryController`` determine whether the library is in a removable volume\n    /// so that it can better handle the case where the app is launched before the volume is mounted.\n    public var isLibraryInRemovableVolume: Bool\n\n}\n\nextension VBSettings {\n\n    static let defaultUpdateChannel: AppUpdateChannel = .release\n    static let defaultEnableTSSCheck = true\n    static let defaultShowDesktopPictureThumbnails = false\n    static let defaultBootDiskImagesUseASIF: Bool = {\n        if #available(macOS 26, *) {\n            true\n        } else {\n            false\n        }\n    }()\n\n    init() {\n        self.libraryURL = .defaultVirtualBuddyLibraryURL\n        self.updateChannel = Self.defaultUpdateChannel\n        self.enableTSSCheck = Self.defaultEnableTSSCheck\n        self.showDesktopPictureThumbnails = Self.defaultShowDesktopPictureThumbnails\n        self.bootDiskImagesUseASIF = Self.defaultBootDiskImagesUseASIF\n        self.isLibraryInRemovableVolume = false\n    }\n\n    private struct Keys {\n        static let version = \"version\"\n        static let libraryPath: String = {\n            #if DEBUG\n            return ProcessInfo.isSwiftUIPreview ? \"libraryPath-preview\" : \"libraryPath\"\n            #else\n            return \"libraryPath\"\n            #endif\n        }()\n        static let updateChannel = \"updateChannel\"\n        static let enableTSSCheck = \"enableTSSCheck\"\n        static let showDesktopPictureThumbnails = \"showDesktopPictureThumbnails\"\n        static let bootDiskImagesUseASIF = \"bootDiskImagesUseASIF\"\n        static let isLibraryInRemovableVolume = \"isLibraryInRemovableVolume\"\n    }\n\n    init(with defaults: UserDefaults) throws {\n        defaults.register(defaults: [\n            Keys.enableTSSCheck: Self.defaultEnableTSSCheck\n        ])\n\n        self.version = defaults.integer(forKey: Keys.version)\n        self.enableTSSCheck = defaults.bool(forKey: Keys.enableTSSCheck)\n        self.showDesktopPictureThumbnails = defaults.bool(forKey: Keys.showDesktopPictureThumbnails)\n        self.bootDiskImagesUseASIF = defaults.bool(forKey: Keys.bootDiskImagesUseASIF)\n        self.isLibraryInRemovableVolume = defaults.bool(forKey: Keys.isLibraryInRemovableVolume)\n\n        if let path = defaults.string(forKey: Keys.libraryPath) {\n            self.libraryURL = URL(fileURLWithPath: path)\n        } else {\n            if version == 2 || version == 1 {\n                /// Default library folder changed in VBSettings version 3,\n                /// so if we're decoding settings from a previous release without a\n                /// library defined, use the old default, which may have user VMs in it.\n                /// \n                /// There's a high chance this never gets hit because the legacy default folder is likely\n                /// to be in user defaults, even if the user never changed it.\n                self.libraryURL = ._legacyDefaultVirtualBuddyLibraryURLForMigrationOnly\n            } else {\n                self.libraryURL = .defaultVirtualBuddyLibraryURL\n            }\n        }\n\n        if let appUpdateChannelID = defaults.string(forKey: Keys.updateChannel) {\n            logger.debug(\"Found channel \\(appUpdateChannelID, privacy: .public) in user defaults\")\n\n            let restoredChannel = AppUpdateChannel.channelsByID[appUpdateChannelID] ?? .release\n            let defaultChannel = AppUpdateChannel.defaultChannel(for: .current)\n\n            if restoredChannel == .release, defaultChannel != .release {\n                logger.debug(\"Settings specify release channel, but current build is for \\(defaultChannel, privacy: .public), setting channel to \\(defaultChannel, privacy: .public)\")\n\n                self.updateChannel = defaultChannel\n            } else {\n                self.updateChannel = restoredChannel\n            }\n        } else {\n            let defaultChannel = AppUpdateChannel.defaultChannel(for: .current)\n\n            logger.debug(\"No channel set in preferences, using default channel \\(defaultChannel, privacy: .public) for build type \\(VBBuildType.current, privacy: .public)\")\n\n            self.updateChannel = defaultChannel\n        }\n    }\n\n    func write(to defaults: UserDefaults) throws {\n        defaults.set(version, forKey: Keys.version)\n        defaults.set(libraryURL.path, forKey: Keys.libraryPath)\n        defaults.set(updateChannel.id, forKey: Keys.updateChannel)\n        defaults.set(enableTSSCheck, forKey: Keys.enableTSSCheck)\n        defaults.set(showDesktopPictureThumbnails, forKey: Keys.showDesktopPictureThumbnails)\n        defaults.set(bootDiskImagesUseASIF, forKey: Keys.bootDiskImagesUseASIF)\n        defaults.set(isLibraryInRemovableVolume, forKey: Keys.isLibraryInRemovableVolume)\n    }\n\n}\n\npublic extension URL {\n    static let _legacyDefaultVirtualBuddyLibraryURLForMigrationOnly: URL = {\n        do {\n            let baseURL = try FileManager.default.url(\n                for: .documentDirectory,\n                in: .userDomainMask,\n                appropriateFor: nil,\n                create: false\n            )\n\n            return baseURL\n                .appendingPathComponent(\"VirtualBuddy\")\n        } catch {\n            fatalError(\"VirtualBuddy is unable to write to your user's documents directory, this is bad!\")\n        }\n    }()\n\n    static let defaultVirtualBuddyLibraryURL: URL = {\n        do {\n            let baseURL = try FileManager.default.url(\n                for: .applicationSupportDirectory,\n                in: .userDomainMask,\n                appropriateFor: nil,\n                create: true\n            )\n\n            let url = baseURL.appendingPathComponent(\"VirtualBuddy\")\n\n            try FileManager.default.createDirectory(at: url, withIntermediateDirectories: true)\n\n            return url\n        } catch {\n            fatalError(\"VirtualBuddy is unable to write to your user's Library/Application Support directory, this is bad!\")\n        }\n    }()\n}\n"
  },
  {
    "path": "VirtualCore/Source/Settings/VBSettingsContainer.swift",
    "content": "//\n//  VBSettingsContainer.swift\n//  VirtualCore\n//\n//  Created by Guilherme Rambo on 05/06/22.\n//\n\nimport Foundation\nimport Combine\nimport OSLog\n\npublic final class VBSettingsContainer: ObservableObject {\n\n    private lazy var logger = Logger(for: Self.self)\n\n    public static let current = VBSettingsContainer()\n\n    @Published public var settings = VBSettings()\n\n    public let defaults: UserDefaults\n\n    public init(with defaults: UserDefaults = .standard) {\n        self.defaults = defaults\n\n        read()\n        bind()\n    }\n\n    private lazy var cancellables = Set<AnyCancellable>()\n\n}\n\n// MARK: - Internal Extensions\n\nprivate extension VBSettingsContainer {\n\n    func bind() {\n        $settings\n            .removeDuplicates()\n            .throttle(for: 0.2, scheduler: DispatchQueue.main, latest: true)\n            .sink { [weak self] settings in\n                self?.write(settings)\n            }\n            .store(in: &cancellables)\n    }\n\n    func read() {\n        logger.debug(#function)\n\n        do {\n            self.settings = try VBSettings(with: defaults)\n        } catch {\n            logger.assert(\"Failed to read settings from defaults: \\(error.log)\")\n        }\n    }\n\n    func write(_ newSettings: VBSettings) {\n        logger.debug(#function)\n\n        do {\n            try newSettings.write(to: defaults)\n        } catch {\n            logger.assert(\"Failed to write settings into defaults: \\(error.log)\")\n        }\n    }\n\n}\n"
  },
  {
    "path": "VirtualCore/Source/Utilities/Bundle+Version.swift",
    "content": "import Foundation\nimport BuddyFoundation\n\npublic extension Bundle {\n    var vbShortVersionString: String {\n        infoDictionary?[\"CFBundleShortVersionString\"] as? String ?? \"0.0\"\n    }\n\n    var vbVersion: SoftwareVersion {\n        SoftwareVersion(string: vbShortVersionString) ?? SoftwareVersion.empty\n    }\n\n    var vbBuild: Int {\n        let str = infoDictionary?[\"CFBundleVersion\"] as? String ?? \"0\"\n        return Int(str) ?? 0\n    }\n}\n"
  },
  {
    "path": "VirtualCore/Source/Utilities/LogStreamer.swift",
    "content": "import Foundation\nimport OSLog\nimport Combine\n\npublic struct LogEntry: Identifiable, Hashable, Codable, CustomStringConvertible {\n    public enum Level: String, Codable {\n        case debug = \"Debug\"\n        case trace = \"Trace\"\n        case notice = \"Notice\"\n        case info = \"Info\"\n        case `default` = \"Default\"\n        case warning = \"Warning\"\n        case error = \"Error\"\n        case fault = \"Fault\"\n        case critical = \"Critical\"\n    }\n\n    public var id = UUID()\n    public var date: Date = .now\n    public var level: Level { _level ?? .default }\n    public let traceID: UInt64\n    public let message: String\n\n    private var _level: Level? = nil\n\n    public enum CodingKeys: String, CodingKey {\n        case traceID = \"traceID\"\n        case message = \"eventMessage\"\n        case _level = \"messageType\"\n    }\n}\n\npublic extension LogEntry {\n    private static let timeFormatter: DateFormatter = {\n        let f = DateFormatter()\n        f.dateFormat = \"HH:mm:ss.sss\"\n        return f\n    }()\n\n    var formattedTime: String { Self.timeFormatter.string(from: date) }\n\n    var description: String {\n        \"[\\(formattedTime)] (\\(level.rawValue)) - \\(message)\"\n    }\n}\n\npublic final class LogStreamer: ObservableObject {\n\n    private lazy var logger = Logger(for: Self.self)\n\n    private var logProcess: Process?\n\n    @Published public private(set) var events = [LogEntry]()\n\n    public let onEvent = PassthroughSubject<LogEntry, Never>()\n\n    public enum Predicate: CustomStringConvertible {\n        case library(String)\n        case subsystem(String)\n        case process(String)\n        case custom(String)\n\n        public var description: String {\n            switch self {\n            case .library(let name):\n                return \"library = '\\(name)'\"\n            case .subsystem(let name):\n                return \"subsystem = '\\(name)'\"\n            case .process(let name):\n                return \"process = '\\(name)'\"\n            case .custom(let str):\n                return str\n            }\n        }\n    }\n\n    public let predicate: Predicate\n    public let throttleInterval: Double\n\n    public init(predicate: Predicate, throttleInterval: Double = 0.5) {\n        self.predicate = predicate\n        self.throttleInterval = throttleInterval\n    }\n\n    private var eventsCancellable: Cancellable?\n\n    public func activate() {\n        logger.debug(#function)\n\n        eventsCancellable = onEvent\n            .throttle(for: .init(throttleInterval), scheduler: RunLoop.main, latest: true)\n            .sink(receiveValue: { [weak self] newEvent in\n                guard let self = self else { return }\n                self.events.insert(newEvent, at: 0)\n            })\n\n        let p = Process()\n        p.executableURL = URL(fileURLWithPath: \"/usr/bin/log\")\n        p.arguments = [\n            \"stream\",\n            \"--level\",\n            \"debug\",\n            \"--style\",\n            \"ndjson\",\n            \"--predicate\",\n            \"\\(predicate)\"\n        ]\n\n        let outPipe = Pipe()\n        p.standardError = Pipe()\n        p.standardOutput = outPipe\n\n        do {\n            try p.run()\n\n            self.logProcess = p\n\n            startStreaming(with: outPipe.fileHandleForReading)\n        } catch {\n            logger.fault(\"Failed to launch log process: \\(String(describing: error), privacy: .public)\")\n        }\n    }\n\n    private var streamTask: Task<Void, Never>?\n\n    private func startStreaming(with fileHandle: FileHandle) {\n        streamTask = Task {\n            do {\n                for try await line in fileHandle.bytes.lines {\n                    await onTaskProduceEvent(for: line)\n                }\n            } catch {\n                logger.error(\"AsyncSequence error: \\(String(describing: error), privacy: .public)\")\n            }\n        }\n\n    }\n\n    private func onTaskProduceEvent(for line: String) async {\n        guard !line.contains(\"Filtering the log data using\") else { return }\n\n        do {\n            let entry = try JSONDecoder().decode(LogEntry.self, from: Data(line.utf8))\n\n            await MainActor.run {\n                onEvent.send(entry)\n            }\n        } catch {\n            logger.error(\"Error decoding log entry \\(line, privacy: .public): \\(String(describing: error), privacy: .public)\")\n        }\n    }\n\n    public func invalidate() {\n        logger.debug(#function)\n\n        streamTask?.cancel()\n        streamTask = nil\n\n        logProcess?.terminate()\n        logProcess = nil\n    }\n\n    deinit { invalidate() }\n\n}\n\n#if DEBUG\npublic extension LogStreamer {\n    static let previewSubsystemName = \"codes.rambo.LogStreamer.PreviewSubsystem\"\n    static let previewLogger = Logger(subsystem: previewSubsystemName, category: previewSubsystemName)\n    static var preview: LogStreamer {\n        DispatchQueue.main.async {\n            LogStreamer.previewLogger.debug(\"This is a debug message\")\n            LogStreamer.previewLogger.info(\"This is an info message\")\n            LogStreamer.previewLogger.notice(\"This is a notice message\")\n            LogStreamer.previewLogger.trace(\"This is a trace message\")\n            LogStreamer.previewLogger.log(\"This is a log message\")\n            LogStreamer.previewLogger.warning(\"This is a warning message\")\n            LogStreamer.previewLogger.error(\"This is an error message\")\n            LogStreamer.previewLogger.fault(\"This is a fault message\")\n            LogStreamer.previewLogger.critical(\"This is a critical message\")\n        }\n        return LogStreamer(predicate: .subsystem(Self.previewSubsystemName))\n    }\n}\n\n#endif\n"
  },
  {
    "path": "VirtualCore/Source/Utilities/PreventTerminationAssertion.swift",
    "content": "import Cocoa\nimport BuddyKit\nimport OSLog\n\n/// An assertion that prevents the app from being terminated during its lifetime.\npublic final class PreventTerminationAssertion: NSObject {\n    public typealias ShouldTerminateHandler = @MainActor (PreventTerminationAssertion) -> NSApplication.TerminateReply\n    public typealias InvalidationHandler = (PreventTerminationAssertion) -> ()\n\n    @Lock public private(set) var isValid: Bool = true\n\n    private let logger: Logger\n\n    public let id: String\n    public let reason: String\n    private let shouldTerminateHandler: ShouldTerminateHandler?\n    private let invalidationHandler: InvalidationHandler?\n\n    fileprivate init(id: String = UUID().uuidString,\n                     reason: String,\n                     shouldTerminate: ShouldTerminateHandler? = nil,\n                     invalidationHandler: InvalidationHandler? = nil)\n    {\n        self.id = id\n        self.reason = reason\n        self.shouldTerminateHandler = shouldTerminate\n        self.invalidationHandler = invalidationHandler\n        self.logger = Logger(subsystem: VirtualCoreConstants.subsystemName, category: \"PreventTerminationAssertion(\\(reason.quoted))\")\n\n        super.init()\n    }\n\n    @MainActor\n    public func handleShouldTerminate() -> NSApplication.TerminateReply? {\n        guard let shouldTerminateHandler else { return nil }\n        return shouldTerminateHandler(self)\n    }\n\n    public func invalidate() {\n        logger.debug(#function)\n\n        assert(isValid, \"Attempt to invalidate \\(description) multiple times!\")\n\n        isValid = false\n\n        invalidationHandler?(self)\n    }\n\n    public override var description: String { \"#\\(id.shortID) (\\(reason.quoted))\" }\n\n    deinit {\n        if isValid {\n            invalidate()\n        }\n    }\n}\n\nprivate let logger = Logger(subsystem: VirtualCoreConstants.subsystemName, category: \"NSApplication+PreventTermination\")\n\npublic extension NSApplication {\n    \n    var assertionsPreventingAppTermination: [PreventTerminationAssertion] {\n        Self.preventTerminationAssertions.dictionaryRepresentation().values.filter(\\.isValid)\n    }\n    \n    var isTerminationBeingPreventedByAssertion: Bool { !assertionsPreventingAppTermination.isEmpty }\n    \n    /// Prevents the app from being terminated for the lifetime of the returned object, or until ``PreventTerminationAssertion/invalidate()`` is called on it.\n    /// - Parameters:\n    ///   - id: An arbitrary identifier. Uses a random UUID by default.\n    ///   - reason: A user-facing string describing the reason why app termination is being prevented. This is used in the default UI alert if a custom `shouldTerminate` block is not provided.\n    ///   - shouldTerminate: A closure that can be used to present a custom confirmation alert and return the appropriate response for the app termination request.\n    ///   - invalidationHandler: A closure that's called when the assertion gets invalidated.\n    /// - Returns: The assertion preventing app termination. Callers **must** retain a reference to this object until you want to invalidate the assertion.\n    ///\n    /// If multiple ``PreventTerminationAssertion`` objects are valid, the app will only terminate once all of them have been invalidated,\n    /// and only if `.terminateLater` was returned from `applicationShouldTerminate`, implemented in the app delegate.\n    func preventTermination(id: String = UUID().uuidString,\n                            reason: String,\n                            shouldTerminate: PreventTerminationAssertion.ShouldTerminateHandler? = nil,\n                            invalidationHandler: PreventTerminationAssertion.InvalidationHandler? = nil) -> PreventTerminationAssertion\n    {\n        let assertion = PreventTerminationAssertion(id: id, reason: reason, shouldTerminate: shouldTerminate) { assertion in\n            invalidationHandler?(assertion)\n            self.handleAssertionInvalidated(assertion)\n        }\n        \n        Self.preventTerminationAssertions.setObject(assertion, forKey: assertion.id as NSString)\n        \n        logger.info(\"Activated prevent termination assertion: \\(assertion, privacy: .public)\")\n        \n        return assertion\n    }\n\n    /// Instructs the app to terminate as soon as the last ``PreventTerminationAssertion`` gets invalidated.\n    @MainActor\n    var shouldTerminateWhenLastAssertionInvalidated: Bool {\n        get { Self.shouldTerminateWhenLastAssertionInvalidated }\n        set { Self.shouldTerminateWhenLastAssertionInvalidated = newValue }\n    }\n\n}\n\n// MARK: - Assertion Management\n\nprivate extension NSApplication {\n    /// How long after the last assertion gets invalidated before the app will terminate automatically.\n    /// If a new assertion is created before this time delay, then termination will wait until that new assertion gets invalidated before terminating.\n    static let delayInSecondsBeforeAutomaticTermination: TimeInterval = 1\n\n    static let preventTerminationAssertions = NSMapTable<NSString, PreventTerminationAssertion>(keyOptions: [.strongMemory, .objectPersonality], valueOptions: [.weakMemory])\n\n    @Lock static var shouldTerminateWhenLastAssertionInvalidated = false\n\n    func handleAssertionInvalidated(_ assertion: PreventTerminationAssertion) {\n        Self.preventTerminationAssertions.removeObject(forKey: assertion.id as NSString)\n\n        guard !isTerminationBeingPreventedByAssertion else { return }\n\n        logger.info(\"All prevent termination assertions invalidated\")\n\n        guard shouldTerminateWhenLastAssertionInvalidated else { return }\n\n        DispatchQueue.main.asyncAfter(deadline: .now() + Self.delayInSecondsBeforeAutomaticTermination) { [self] in\n            guard !isTerminationBeingPreventedByAssertion else {\n                logger.info(\"New assertion prevents scheduled termination, waiting for it before terminating.\")\n                return\n            }\n\n            logger.info(\"Termination requested when all assertions invalidated, terminating now.\")\n\n            NSApp.reply(toApplicationShouldTerminate: true)\n        }\n    }\n}\n\n// MARK: - Convenience\n\nextension NSApplication.TerminateReply: @retroactive CustomStringConvertible {\n    public var description: String {\n        switch self {\n        case .terminateCancel: \"cancel\"\n        case .terminateNow: \"now\"\n        case .terminateLater: \"later\"\n        @unknown default: \"unknown(\\(rawValue))\"\n        }\n    }\n}\n"
  },
  {
    "path": "VirtualCore/Source/Utilities/ProcessInfo+ECID.swift",
    "content": "import Foundation\nimport IOKit\n\nextension ProcessInfo {\n\n    var machineECID: UInt64? {\n        let entry = IORegistryEntryFromPath(kIOMainPortDefault, \"IODeviceTree:/chosen\")\n        guard entry != MACH_PORT_NULL else { return nil }\n\n        guard let ecidData = IORegistryEntrySearchCFProperty(entry, \"IODeviceTree:/chosen\", \"unique-chip-id\" as CFString, kCFAllocatorDefault, 0) as? Data else { return nil }\n\n        guard ecidData.count > 0 else { return nil }\n\n        return ecidData.withUnsafeBytes { (buffer: UnsafeRawBufferPointer) -> UInt64? in\n            guard let base = buffer.baseAddress else { return nil }\n            return UnsafeRawPointer(base).load(as: UInt64.self)\n        }\n    }\n\n}\n"
  },
  {
    "path": "VirtualCore/Source/Utilities/VBMemoryLeakDebugAssertions.swift",
    "content": "import Foundation\n\npublic final class VBMemoryLeakDebugAssertions {\n    @inlinable\n    public static func vb_objectShouldBeReleasedSoon(_ object: AnyObject, after interval: TimeInterval = 0.5) {\n        #if DEBUG\n        _vb_objectShouldBeReleasedSoon(object, after: interval)\n        #endif\n    }\n\n    @inlinable\n    public static func vb_objectIsBeingReleased(_ object: AnyObject) {\n        #if DEBUG\n        _vb_objectIsBeingReleased(object)\n        #endif\n    }\n\n    #if DEBUG\n    public static let _disableFlag = \"VBDisableMemoryLeakAssertions\"\n\n    public static var _vb_debugAssertionsEnabled: Bool { !UserDefaults.standard.bool(forKey: _disableFlag) }\n\n    public static var _releasedObjects = Set<String>()\n\n    @inlinable\n    public static func _objectID(_ object: AnyObject) -> String {\n        String(describing: Unmanaged.passUnretained(object).toOpaque())\n    }\n\n    @inlinable\n    public static func _vb_objectShouldBeReleasedSoon(_ object: AnyObject, after interval: TimeInterval) {\n        guard _vb_debugAssertionsEnabled else { return }\n\n        let id = _objectID(object)\n        let className = String(describing: type(of: object))\n\n        let description: String\n\n        if let window = object as? NSWindow {\n            let title = window.title\n            description = \"#\\(id) (\\(className) - \\\"\\(title)\\\")\"\n        } else {\n            description = \"#\\(id) (\\(className) - \\\"\\(String(describing: object))\\\")\"\n        }\n\n        DispatchQueue.main.asyncAfter(deadline: .now() + interval) {\n            assert(_releasedObjects.contains(id), \"💦 POSSIBLE LEAK: \\(description) was not released when expected (set \\(_disableFlag) defaults flag to disable this)\")\n        }\n    }\n\n    @inlinable\n    public static func _vb_objectIsBeingReleased(_ object: AnyObject) {\n        guard _vb_debugAssertionsEnabled else { return }\n\n        _releasedObjects.insert(_objectID(object))\n    }\n    #endif\n}\n"
  },
  {
    "path": "VirtualCore/Source/Utilities/VolumeUtils.swift",
    "content": "//\n//  VolumeUtils.swift\n//  VirtualCore\n//\n//  Created by Guilherme Rambo on 20/07/22.\n//\n\nimport Foundation\n\npublic extension VBSettingsContainer {\n\n    /// Returns `true` if the VirtualBuddy library seems to be in an APFS volume.\n    var isLibraryInAPFSVolume: Bool { settings.libraryURL.hasAPFSIdentifier }\n\n    /// Returns `true` if the volume where the VirtualBuddy library resides has enough\n    /// free space to fit the given amount of bytes.\n    /// If checking the free disk space fails, this falls back to returning `true`.\n    func libraryVolumeCanFit(_ size: UInt64) -> Bool {\n        guard let freeSize = settings.libraryURL.freeDiskSpaceOnVolume else { return true }\n        return Int64(freeSize) - Int64(size) > 0\n    }\n\n}\n\npublic extension VMLibraryController {\n    /// Whether this library is stored in an APFS volume.\n    var isInAPFSVolume: Bool { libraryURL.hasAPFSIdentifier }\n}\n\npublic extension URL {\n\n    /// Checks if the item at the URL contains an APFS content identifier, as a way to check for\n    /// whether the containing volume is an APFS volume.\n    var hasAPFSIdentifier: Bool {\n        #if DEBUG\n        guard !UserDefaults.standard.bool(forKey: \"VBSimulateNonAPFSVolume\") else { return false }\n        #endif\n\n        guard let values = try? resourceValues(forKeys: [.fileContentIdentifierKey]),\n              values.fileContentIdentifier != nil\n        else {\n            return false\n        }\n\n        return true\n    }\n\n    /// The free disk space in the volume that contains this URL.\n    var freeDiskSpaceOnVolume: UInt64? {\n        do {\n            let attrs = try FileManager.default.attributesOfFileSystem(forPath: path)\n            guard let freeSize = attrs[.systemFreeSize] as? UInt64 else { return nil }\n            return freeSize\n        } catch {\n            return nil\n        }\n    }\n\n    /// User-friendly name for the volume that contains this URL.\n    var containingVolumeName: String? {\n        guard let volumeURL = (try? resourceValues(forKeys: [.volumeURLKey]))?.volume else { return nil }\n\n        guard let values = try? volumeURL.resourceValues(forKeys: [.volumeLocalizedNameKey, .volumeNameKey]) else { return nil }\n\n        if let localizedName = values.volumeLocalizedName {\n            return localizedName\n        } else if let name = values.volumeName {\n            return name\n        } else {\n            return volumeURL.lastPathComponent\n        }\n    }\n\n    /// `true` if the URL is contained in a volume that's not internal.\n    var residesInExternalVolume: Bool {\n        (try? resourceValues(forKeys: [.volumeIsInternalKey]))?.volumeIsInternal == false\n    }\n\n    /// Returns the URL for the external volume that contains this URL.\n    /// Returns `nil` if the URL resides in internal storage, or if the volume couldn't be determined.\n    var externalVolumeURL: URL? {\n        guard residesInExternalVolume else { return nil }\n        return try? resourceValues(forKeys: [.volumeURLKey]).volume\n    }\n    \n}\n"
  },
  {
    "path": "VirtualCore/Source/Utilities/WeakReference.swift",
    "content": "import Foundation\n\n/// Wraps a reference type as a weak reference.\n/// Useful as value type in collections when holding a strong reference to the objects is undesirable.\npublic struct WeakReference<Object: AnyObject> {\n    public private(set) weak var object: Object?\n\n    public init(_ object: Object) {\n        self.object = object\n    }\n}\n\nextension WeakReference: Equatable where Object: Equatable { }\nextension WeakReference: Hashable where Object: Hashable { }\n"
  },
  {
    "path": "VirtualCore/Source/VirtualCatalog/LegacyCatalog.swift",
    "content": "import Foundation\nimport BuddyFoundation\n\npublic struct LegacyCatalog: Decodable {\n    public let channels: [LegacyCatalogChannel]\n    public let groups: [LegacyCatalogGroup]\n    public let images: [LegacyRestoreImage]\n}\n\npublic struct LegacyCatalogChannel: Hashable, Identifiable, Codable {\n    public struct Authentication: Hashable, Identifiable, Codable {\n        public var id: String { name }\n        public var name: String\n        public var url: URL\n        public var note: String\n    }\n\n    public var id: String\n    public var name: String\n    public var note: String\n    public var icon: String\n}\n\npublic struct LegacyCatalogGroup: Hashable, Identifiable, Codable {\n    public var id: String\n    public var name: String\n    public var majorVersion: SoftwareVersion\n    public var minHostVersion: SoftwareVersion\n}\n\npublic struct LegacyRestoreImage: Hashable, Identifiable, Codable {\n    public var id: String { build }\n    public var group: LegacyCatalogGroup\n    public var channel: LegacyCatalogChannel\n    public var name: String\n    public var build: String\n    public var url: URL\n    public var needsCookie: Bool?\n}\n\npublic extension LegacyCatalog {\n    private static let decoder = JSONDecoder()\n\n    init(data: Data) throws {\n        self = try Self.decoder.decode(Self.self, from: data)\n    }\n\n    init(contentsOf url: URL) throws {\n        let data = try Data(contentsOf: url)\n        try self.init(data: data)\n    }\n}\n"
  },
  {
    "path": "VirtualCore/Source/VirtualCatalog/MobileDeviceFramework.swift",
    "content": "import Foundation\nimport BuddyFoundation\n\n/// A representation of the MobileDevice framework and its properties.\npublic struct MobileDeviceFramework: Sendable {\n    /// The version of the MobileDevice framework.\n    public let version: SoftwareVersion\n\n    /// The MobileDevice framework in the current system, or `nil` if it couldn't be found\n    /// or its properties couldn't be parsed.\n    public static var current: MobileDeviceFramework? {\n        MobileDeviceFramework()\n    }\n}\n\nprivate extension MobileDeviceFramework {\n    init?() {\n        #if DEBUG\n        if let simulatedVersion = UserDefaults.standard.string(forKey: \"VBSimulateMobileDeviceVersion\") {\n            self.init(version: SoftwareVersion(string: simulatedVersion)!)\n            return\n        }\n        #endif\n        \n        let path = \"/System/Library/PrivateFrameworks/MobileDevice.framework\"\n        guard let bundle = Bundle(path: path) else {\n            assertionFailure(\"MobileDevice.framework not found at \\(path)\")\n            return nil\n        }\n\n        guard let info = bundle.infoDictionary else {\n            assertionFailure(\"MobileDevice.framework is missing an info dictionary\")\n            return nil\n        }\n\n        guard let rawVersion = info[kCFBundleVersionKey as String] as? String else {\n            assertionFailure(\"MobileDevice.framework has missing or invalid CFBundleVersion\")\n            return nil\n        }\n\n        guard let version = SoftwareVersion(string: rawVersion) else {\n            assertionFailure(\"MobileDevice.framework has invalid CFBundleVersion: \\(rawVersion)\")\n            return nil\n        }\n\n        self.init(version: version)\n    }\n}\n"
  },
  {
    "path": "VirtualCore/Source/VirtualCatalog/README.md",
    "content": "# VirtualCatalog\n\nThis directory includes models and associated logic used by VirtualBuddy to inspect and resolve downloadable restore images for virtual machines.\n\nThe catalog itself is a JSON file stored in the VirtualBuddy GitHub repository.\n\nThere are two JSON files for the current version of the catalog:\n\n- `ipsws_v2.json`: for macOS virtual machines\n- `linux_v2.json`: for Linux virtual machines\n\nIn order to avoid hitting GitHub too frequently and provide server-side logic, I run a small function on Cloudflare that the app talks to via HTTP.\n\nThe server fetches the appropriate JSON file from the repository based on the request path, performs any compatibility conversions depending on the client version, then returns the catalog in the response, caching it on the CDN for a short period.\n\n## Adding New Versions of macOS\n\nTo add a new version of macOS to the catalog, use `vctool`, which is included as part of VirtualBuddy.\n\nIf you have installed VirtualBuddy in `/Applications`, then `vctool` will be available at `/Applications/VirtualBuddy.app/Contents/MacOS/vctool`.\n\nYou may symlink `vctool` to a directory in your `PATH` or just add the corresponding `VirtualBuddy.app/Contents/MacOS` directory to your `PATH` to make running the tool more convenient.\n\nYou will also need to clone the VirtualBuddy repository itself so that you can open a pull request with the updated catalog after running `vctool`.\n\nTo add a new release to the catalog, use the `vctool catalog image add` command:\n\n```bash\nUSAGE: vctool catalog image add --ipsw <ipsw> --channel <channel> --name <name> --output <output> [--force]\n\nOPTIONS:\n  -i, --ipsw <ipsw>       URL to the IPSW file.\n  -c, --channel <channel> ID of the release channel (devbeta or regular).\n  -n, --name <name>       User-facing name for the release (ex: \"macOS 15.0 Developer Beta 4\").\n  -o, --output <output>   Path to the software catalog JSON file that will be updated.\n  -f, --force             Replace existing build if it already exists in the catalog.\n  -h, --help              Show help information.\n```\n\nAssuming you have cloned the VirtualBuddy repository in `~/Developer/VirtualBuddy`, here's an example of how you could add a new image to the catalog:\n\n```bash\nvctool catalog image add \\\n    -i 'https://updates.cdn-apple.com/2025SpringFCS/fullrestores/082-44534/CE6C1054-99A3-4F67-A823-3EE9E6510CDE/UniversalMac_15.5_24F74_Restore.ipsw' \\\n    -c regular \\\n    -n 'macOS 15.5' \\\n    -o ~/Developer/VirtualBuddy/data/ipsws_v2.json\n``` \n\nIf successful, the `ipsws_v2.json` file in your clone of the VirtualBuddy repository will have the new macOS release added to it.\n\nYou may then create a new branch in your fork and submit a pull request.\n\nShortly after a pull request that updates the software catalog is merged, the updated catalog is picked up by the server, making any new OS releases available in the app.\n\n## Updating Existing Versions\n\nIf an OS build that's already in the catalog needs to be updated, just use the same command but add the `--force` flag so that it doesn't complain about the version already being in the catalog. Any changes made to the title, channel, or other properties will be updated.\n\n## ⚠️ Important Note About New Major Versions\n\nAdding new major OS versions (ex: macOS ~16~26) requires additional work because a corresponding release group has to be added, and that requires the availability of image assets that follow a certain specification used by the app.\n\nFor that reason, adding major new OS versions is currently reserved to the maintainer of the repository (@insidegui)."
  },
  {
    "path": "VirtualCore/Source/VirtualCatalog/ResolvedCatalog.swift",
    "content": "import Foundation\nimport BuddyFoundation\nimport OSLog\n\nprivate let logger = Logger(subsystem: VirtualCoreConstants.subsystemName, category: \"ResolvedCatalog\")\n\npublic protocol ResolvedCatalogModel: Identifiable, Hashable, Sendable { }\n\n/// Represents a ``SoftwareCatalog`` that's been processed in order to resolve\n/// all channel and group members, as well as the supported features and requirement sets for the current environment.\npublic struct ResolvedCatalog: Hashable, Sendable {\n    /// The resolved release groups.\n    public var groups: [ResolvedCatalogGroup]\n}\n\npublic struct ResolvedCatalogGroup: ResolvedCatalogModel {\n    public var id: CatalogGroup.ID { group.id }\n    public var group: CatalogGroup\n    public var restoreImages: [ResolvedRestoreImage]\n\n    public var name: String { group.name }\n    public var majorVersion: SoftwareVersion { group.majorVersion }\n    public var image: CatalogGraphic { group.image }\n    public var darkImage: CatalogGraphic { group.darkImage ?? group.image }\n\n    public init(group: CatalogGroup, restoreImages: [ResolvedRestoreImage]) {\n        self.group = group\n        self.restoreImages = restoreImages\n    }\n}\n\npublic struct ResolvedRestoreImage: ResolvedCatalogModel, DownloadableCatalogContent {\n    public var id: RestoreImage.ID { image.id }\n    public var image: RestoreImage\n    public var channel: CatalogChannel\n    public var features: [ResolvedVirtualizationFeature]\n    public var requirements: ResolvedRequirementSet\n    public var deviceSupportVersion: CatalogDeviceSupportVersion?\n    public var status: ResolvedFeatureStatus\n    public var localFileURL: URL?\n\n    public var name: String { image.name }\n    public var build: String { image.build }\n    public var version: SoftwareVersion { image.version }\n    public var mobileDeviceMinVersion: SoftwareVersion { image.mobileDeviceMinVersion }\n    public var url: URL { image.url }\n    public var downloadSize: Int64 { Int64(image.downloadSize ?? 0) }\n    public var isDownloaded: Bool { localFileURL != nil }\n\n    public init(image: RestoreImage, channel: CatalogChannel, features: [ResolvedVirtualizationFeature], requirements: ResolvedRequirementSet, status: ResolvedFeatureStatus, localFileURL: URL?, deviceSupportVersion: CatalogDeviceSupportVersion?) {\n        self.image = image\n        self.channel = channel\n        self.features = features\n        self.requirements = requirements\n        self.status = status\n        self.localFileURL = localFileURL\n        self.deviceSupportVersion = deviceSupportVersion\n    }\n}\n\n/// The status of a feature or requirement set for the current environment.\npublic enum ResolvedFeatureStatus: Hashable, Sendable {\n    /// The feature is fully supported.\n    case supported\n    /// The feature is partially supported.\n    case warning(title: String?, message: String)\n    /// The feature is not supported.\n    case unsupported(title: String?, message: String)\n\n    var title: String? {\n        switch self {\n        case .supported: return nil\n        case .warning(let title, _), .unsupported(let title, _): return title\n        }\n    }\n\n    var message: String? {\n        switch self {\n        case .supported: return nil\n        case .warning(_, let message), .unsupported(_, let message): return message\n        }\n    }\n}\n\n/// A feature that's been resolved for the current environment, indicating whether it is supported.\npublic struct ResolvedVirtualizationFeature: ResolvedCatalogModel {\n    public var feature: VirtualizationFeature\n    public var status: ResolvedFeatureStatus\n    public var platform: CatalogGuestPlatform\n\n    public var id: VirtualizationFeature.ID { feature.id }\n    public var minVersionGuest: SoftwareVersion { feature.minVersionGuest }\n    public var minVersionHost: SoftwareVersion { feature.minVersionHost }\n    public var name: String { feature.name }\n    public var unsupportedPlatform: Bool { feature.unsupportedPlatform }\n}\n\n/// A requirement set that's been resolved for the current environment.\npublic struct ResolvedRequirementSet: ResolvedCatalogModel {\n    public var id: RequirementSet.ID { requirements.id }\n    public var requirements: RequirementSet\n    public var status: ResolvedFeatureStatus\n\n    public init(requirements: RequirementSet, status: ResolvedFeatureStatus) {\n        self.requirements = requirements\n        self.status = status\n    }\n}\n\n// MARK: - Catalog Resolution\n\n/// Represents a guest platform such as Mac or Linux.\n/// Not an enum just in case more platforms are added in the future (iOS? 🤞🏻)\npublic struct CatalogGuestPlatform: ResolvedCatalogModel, RawRepresentable, CaseIterable, CustomStringConvertible {\n    public typealias RawValue = String\n    public var rawValue: String { id }\n\n    public var id: String\n    public var name: String\n\n    public static let mac = CatalogGuestPlatform(id: \"mac\", name: \"Mac\")\n    public static let linux = CatalogGuestPlatform(id: \"linux\", name: \"Linux\")\n    public static let unknown = CatalogGuestPlatform(id: \"_unknown\", name: \"Unknown\")\n\n    public static let allCases: [CatalogGuestPlatform] = [.mac, .linux]\n\n    public init(rawValue: String) {\n        if let platform = Self.allCases.first(where: { $0.rawValue.caseInsensitiveCompare(rawValue) == .orderedSame }) {\n            self = platform\n        } else {\n            assertionFailure(\"Unsupported platform \\\"\\(rawValue)\\\"\")\n            self = .unknown\n        }\n    }\n\n    init(id: String, name: String) {\n        self.id = id\n        self.name = name\n    }\n\n    public var description: String { id }\n}\n\n/// Protocol adopted by types that can provide local file URLs to a ``CatalogResolutionEnvironment``.\n///\n/// This type is used when resolving download status for restore images.\npublic protocol CatalogDownloadsProvider: Sendable {\n    /// Returns the local file URL matching the remote restore image.\n    ///\n    /// Implementations must return `nil` if a local file matching the remote restore image has not been downloaded yet.\n    func localFileURL(for restoreImage: RestoreImage) -> URL?\n}\n\n/// Properties used when resolving a catalog for a given environment.\n/// These are used to assess the support status for different features/requirements.\npublic struct CatalogResolutionEnvironment: Sendable {\n    /// The host OS version.\n    public var hostVersion: SoftwareVersion\n    /// The guest OS version.\n    public var guestVersion: SoftwareVersion\n    /// The guest OS platform.\n    public var guestPlatform: CatalogGuestPlatform\n    /// The version of the host app.\n    public var appVersion: SoftwareVersion\n    /// The version of the MobileDevice framework on the host.\n    public var mobileDeviceVersion: SoftwareVersion\n    /// The number of CPU cores configured for the VM.\n    /// Can be set to nil if performing resolution before configuration,\n    /// in which case the requirement set will be considered as satisfied.\n    public var cpuCoreCount: Int?\n    /// The number of CPU cores configured for the VM.\n    /// Can be set to nil if performing resolution before configuration,\n    /// in which case the requirement set will be considered as satisfied.\n    public var memorySizeMB: Int?\n    /// Information about catalog content that's already been downloaded by the host.\n    public var downloadsProvider: CatalogDownloadsProvider?\n\n    public init(hostVersion: SoftwareVersion, guestVersion: SoftwareVersion, guestPlatform: CatalogGuestPlatform, appVersion: SoftwareVersion, mobileDeviceVersion: SoftwareVersion, cpuCoreCount: Int? = nil, memorySizeMB: Int? = nil, downloadsProvider: CatalogDownloadsProvider? = nil) {\n        self.hostVersion = hostVersion\n        self.guestVersion = guestVersion\n        self.guestPlatform = guestPlatform\n        self.appVersion = appVersion\n        self.mobileDeviceVersion = mobileDeviceVersion\n        self.cpuCoreCount = cpuCoreCount\n        self.memorySizeMB = memorySizeMB\n        self.downloadsProvider = downloadsProvider\n    }\n}\n\npublic extension ResolvedCatalog {\n    init(environment: CatalogResolutionEnvironment, catalog: SoftwareCatalog) {\n        self.groups = catalog.groups.map { group in\n            let images = catalog.restoreImages.filter({ $0.group == group.id })\n\n            /// Resolve images one by one to prevent error on single image from taking down entire catalog.\n            var resolvedImages = [ResolvedRestoreImage]()\n            for image in images {\n                do {\n                    let resolvedImage = try ResolvedRestoreImage(environment: environment, catalog: catalog, image: image)\n                    resolvedImages.append(resolvedImage)\n                } catch {\n                    logger.error(\"Error resolving image \\(image.id, privacy: .public) - \\(error, privacy: .public)\")\n                }\n            }\n\n            return ResolvedCatalogGroup(group: group, restoreImages: resolvedImages)\n        }\n    }\n}\n\npublic extension ResolvedRestoreImage {\n    init(environment: CatalogResolutionEnvironment, catalog: SoftwareCatalog, image: RestoreImage) throws {\n        try self.init(\n            image: image,\n            channel: catalog.channel(with: image.channel),\n            features: catalog.features.map { ResolvedVirtualizationFeature(feature: $0, status: .supported, platform: environment.guestPlatform) },\n            requirements: ResolvedRequirementSet(requirements: catalog.requirementSet(with: image.requirements), status: .supported),\n            status: .supported,\n            localFileURL: environment.downloadsProvider?.localFileURL(for: image),\n            deviceSupportVersion: catalog.deviceSupportVersion(for: image)\n        )\n\n        update(with: environment)\n    }\n\n    mutating func update(with environment: CatalogResolutionEnvironment) {\n        /// Adds the guest OS version to the environment so that features/requirements that depend on it get the correct status.\n        let versionedEnvironment = environment.guest(version: image.version)\n\n        /// Mobile device requirement is isolated from min host/app requirements.\n        if versionedEnvironment.mobileDeviceVersion < image.mobileDeviceMinVersion {\n            if let deviceSupportVersion {\n                self.status = .unsupported(title: deviceSupportVersion.title, message: deviceSupportVersion.instructions)\n            } else {\n                self.status = .mobileDeviceOutdated\n            }\n        }\n\n        features = features.map { $0.updated(with: versionedEnvironment) }\n\n        requirements.update(with: versionedEnvironment)\n\n        /// Only override status if it's \"more important\" than the current status.\n        if requirements.status.isMoreImportant(than: status) {\n            status = requirements.status\n        }\n    }\n}\n\nextension SoftwareCatalog {\n    func deviceSupportVersion(for image: RestoreImage) -> CatalogDeviceSupportVersion? {\n        deviceSupportVersions.first(where: {\n            $0.mobileDeviceMinVersion == image.mobileDeviceMinVersion\n            || ($0.osVersion.major == image.version.major && $0.osVersion.minor == image.version.minor)\n            || $0.osVersion.major == image.version.major\n        })\n    }\n}\n\npublic extension ResolvedVirtualizationFeature {\n    mutating func update(with environment: CatalogResolutionEnvironment) {\n        guard !feature.unsupportedPlatform else {\n            self.status = .unsupportedGuestPlatform(feature, platform: environment.guestPlatform)\n            return\n        }\n\n        guard environment.hostVersion >= self.feature.minVersionHost else {\n            if self.feature.minVersionGuest == self.feature.minVersionHost {\n                self.status = .unsupportedHostAndGuestAligned(feature)\n            } else {\n                self.status = .unsupportedHost(feature)\n            }\n            return\n        }\n\n        guard environment.guestVersion >= self.feature.minVersionGuest else {\n            if self.feature.minVersionGuest == self.feature.minVersionHost {\n                self.status = .unsupportedHostAndGuestAligned(feature)\n            } else {\n                self.status = .unsupportedGuest(feature)\n            }\n            return\n        }\n\n        self.status = .supported\n    }\n\n    func updated(with environment: CatalogResolutionEnvironment) -> Self {\n        var mSelf = self\n        mSelf.update(with: environment)\n        return mSelf\n    }\n}\n\npublic extension ResolvedRequirementSet {\n    mutating func update(with environment: CatalogResolutionEnvironment) {\n        guard environment.hostVersion >= self.requirements.minVersionHost else {\n            self.status = .unsupportedHost(requirements)\n            return\n        }\n        self.status = .supported\n    }\n\n    func updated(with environment: CatalogResolutionEnvironment) -> Self {\n        var mSelf = self\n        mSelf.update(with: environment)\n        return mSelf\n    }\n}\n\nextension ResolvedFeatureStatus {\n    static func unsupported(_ message: String?...) -> Self {\n        .unsupported(title: nil, message: message.compactMap({ $0 }).joined(separator: \"\\n\"))\n    }\n\n    static func unsupportedHostAndGuestAligned(_ feature: VirtualizationFeature) -> Self {\n        .unsupported(\"Requires macOS \\(feature.minVersionHost.shortDescription) or later.\", feature.detail)\n    }\n\n    static func unsupportedHost(_ feature: VirtualizationFeature) -> Self {\n        .unsupportedHost(feature.minVersionHost, detail: feature.detail)\n    }\n\n    static func unsupportedHost(_ requirements: RequirementSet) -> Self {\n        .unsupportedHost(requirements.minVersionHost)\n    }\n\n    static func unsupportedHost(_ minVersionHost: SoftwareVersion, detail: String? = nil) -> Self {\n        .unsupported(\"Requires a Mac on macOS \\(minVersionHost.shortDescription) or later.\", detail)\n    }\n\n    static func unsupportedGuest(_ feature: VirtualizationFeature) -> Self {\n        .unsupported(\"Requires virtual machine running macOS \\(feature.minVersionHost.shortDescription) or later.\", feature.detail)\n    }\n\n    static func unsupportedGuestPlatform(_ feature: VirtualizationFeature, platform: CatalogGuestPlatform) -> Self {\n        .unsupported(\"Not supported for \\(platform.name) guests.\", feature.detail)\n    }\n\n    static var mobileDeviceOutdated: Self {\n        .unsupported(title: Self.defaultDeviceSupportUpdateNeededTitle,  message: Self.defaultDeviceSupportUpdateNeededInstructions)\n    }\n\n    static let defaultDeviceSupportUpdateNeededTitle = \"Device Support Update Required\"\n\n    static let defaultDeviceSupportUpdateNeededInstructions = \"\"\"\n    This version of macOS requires device support files which are not currently installed on your system.\n    \n    It's likely that this is a beta for a major macOS version and your Mac is not running the corresponding macOS beta.\n    \n    Device support files can be obtained by installing the Xcode beta, they are sometimes made available separately in the Apple Developer portal.\n    \"\"\"\n}\n\nstruct CatalogError: LocalizedError, CustomStringConvertible {\n    var errorDescription: String?\n    init(_ errorDescription: String) {\n        self.errorDescription = errorDescription\n    }\n    var description: String { errorDescription ?? \"\" }\n}\n\npublic extension SoftwareCatalog {\n    func group(with id: CatalogGroup.ID) throws -> CatalogGroup {\n        guard let group = groups.first(where: { $0.id == id }) else {\n            throw CatalogError(\"Group not found with id \\\"\\(id)\\\"\")\n        }\n        return group\n    }\n    func channel(with id: CatalogGroup.ID) throws -> CatalogChannel {\n        guard let channel = channels.first(where: { $0.id == id }) else {\n            throw CatalogError(\"Channel not found with id \\\"\\(id)\\\"\")\n        }\n        return channel\n    }\n    func requirementSet(with id: RequirementSet.ID) throws -> RequirementSet {\n        guard let requirements = requirementSets.first(where: { $0.id == id }) else {\n            throw CatalogError(\"Requirement set not found with id \\\"\\(id)\\\"\")\n        }\n        return requirements\n    }\n}\n\npublic extension CatalogResolutionEnvironment {\n    /**\n     Catalog resolution environment for the current state of the host.\n\n     This property is computed because certain aspects can change during the app's lifecycle, such as MobileDevice version and downloads.\n     */\n    static var current: CatalogResolutionEnvironment {\n        CatalogResolutionEnvironment(\n            hostVersion: .currentHost,\n            guestVersion: .currentHost,\n            guestPlatform: .mac,\n            appVersion: Bundle.main.softwareVersion,\n            mobileDeviceVersion: MobileDeviceFramework.current?.version ?? .init(major: 0, minor: 0, patch: 0),\n            downloadsProvider: VBSettings.current\n        )\n    }\n\n    func guest(platform: CatalogGuestPlatform, version: SoftwareVersion) -> Self {\n        var mSelf = self\n        mSelf.guestPlatform = platform\n        mSelf.guestVersion = version\n        return mSelf\n    }\n\n    func guest(platform: CatalogGuestPlatform) -> Self {\n        var mSelf = self\n        mSelf.guestPlatform = platform\n        return mSelf\n    }\n\n    func guest(version: SoftwareVersion) -> Self {\n        var mSelf = self\n        mSelf.guestVersion = version\n        return mSelf\n    }\n}\n\nextension SoftwareVersion {\n    static let currentHost: SoftwareVersion = {\n        #if DEBUG\n        if let simulatedVersionString = UserDefaults.standard.string(forKey: \"VBSimulateHostVersion\") {\n            return SoftwareVersion(string: simulatedVersionString)!\n        }\n        #endif\n        \n        let v = ProcessInfo.processInfo.operatingSystemVersion\n        return SoftwareVersion(major: v.majorVersion, minor: v.minorVersion, patch: v.patchVersion)\n    }()\n}\n\n// MARK: - Resolved Feature Detail\n\npublic extension ResolvedVirtualizationFeature {\n    var detail: String {\n        switch status {\n        case .supported:\n            switch platform {\n            case .mac:\n                if minVersionHost == minVersionGuest {\n                    \"Supported. Requires macOS \\(minVersionHost.shortDescription) or later.\"\n                } else {\n                    \"Supported. Requires host on macOS \\(minVersionHost.shortDescription) or later and guest on macOS \\(minVersionGuest.shortDescription) or later.\"\n                }\n            case .linux: \"Supported. Requires host on macOS \\(minVersionHost.shortDescription) or later.\"\n            default: \"Supported.\"\n            }\n        case .warning(_, let message), .unsupported(_, let message):\n            message\n        }\n    }\n}\n\n// MARK: - Resolved Status Merging\n\npublic extension ResolvedFeatureStatus {\n    var isSupported: Bool {\n        if case .supported = self {\n            true\n        } else {\n            false\n        }\n    }\n\n    var isWarning: Bool {\n        if case .warning = self {\n            true\n        } else {\n            false\n        }\n    }\n\n    var isUnsupported: Bool {\n        if case .unsupported = self {\n            true\n        } else {\n            false\n        }\n    }\n\n    var level: Int {\n        switch self {\n        case .supported: 0\n        case .warning: 1\n        case .unsupported: 2\n        }\n    }\n\n    func isMoreImportant(than other: ResolvedFeatureStatus) -> Bool { level > other.level }\n}\n"
  },
  {
    "path": "VirtualCore/Source/VirtualCatalog/SoftwareCatalog+DownloadMatching.swift",
    "content": "import Foundation\nimport BuddyFoundation\nimport OSLog\n\nprivate let matchLogger = Logger(subsystem: VirtualCoreConstants.subsystemName, category: \"SoftwareCatalog+DownloadMatching\")\n\npublic extension URL {\n    private static let virtualBuddySoftwareCatalogDataKey = \"codes.rambo.VirtualBuddy.SoftwareCatalogData\"\n\n    /// Custom metadata stored by VirtualBuddy as an extended attribute.\n    /// This is used to match restore images with those in a software catalog even if they are\n    /// renamed by the user or moved within the same volume.\n    struct VirtualBuddyCatalogData: Codable, Hashable, Sendable {\n        /// The build number for the OS version represented by the restore image file.\n        public var build: String\n        /// The original name of the corresponding file in the software catalog.\n        public var filename: String\n\n        public init(build: String, filename: String) {\n            self.build = build\n            self.filename = filename\n        }\n\n        public init(_ image: RestoreImage) {\n            self.init(build: image.build, filename: image.url.lastPathComponent)\n        }\n    }\n\n    var vb_softwareCatalogData: VirtualBuddyCatalogData? {\n        get {\n            if let value: VirtualBuddyCatalogData = vb_decodeExtendedAttribute(forKey: Self.virtualBuddySoftwareCatalogDataKey) {\n                value\n            } else if let downloadedFromURL = vb_whereFromsSpotlightMetadata.first,\n                      let build = downloadedFromURL.lastPathComponent.matchAppleOSBuild()\n            {\n                /// The `com.apple.metadata:kMDItemWhereFroms` extended attribute can be used to determine where a file was originally downloaded from.\n                /// If the original download URL had a well-formed OS version build in it, then we can use that attribute even if the file doesn't have the custom VirtualBuddy attribute.\n                VirtualBuddyCatalogData(build: build, filename: downloadedFromURL.lastPathComponent)\n            } else {\n                nil\n            }\n        }\n        nonmutating set {\n            guard let newValue else {\n                try? vb_removeExtendedAttribute(forKey: Self.virtualBuddySoftwareCatalogDataKey)\n                return\n            }\n\n            try? vb_encodeExtendedAttribute(newValue, forKey: Self.virtualBuddySoftwareCatalogDataKey)\n        }\n    }\n}\n\nextension URL {\n    /// Parses a Spotlight attribute that includes the URL that was used to download the file.\n    /// This attribute is added automatically and can be used when matching local files with software catalog contents.\n    var vb_whereFromsSpotlightMetadata: [URL] {\n        guard let data = vb_extendedAttributeData(forKey: \"com.apple.metadata:kMDItemWhereFroms\", base64: false) else { return [] }\n        guard let plist = try? PropertyListSerialization.propertyList(from: data, format: nil) as? [Any] else { return [] }\n\n        return plist\n            .compactMap { $0 as? String }\n            .compactMap { URL(string: $0) }\n    }\n\n    /// Loads properties that can be used to match a local file URL with a restore image in the software catalog.\n    /// Used when matching user-provided restore image files or previously-downloaded restore images with catalog content.\n    struct RestoreImageStub: Hashable, Sendable, DownloadableCatalogContent, CustomStringConvertible {\n        var id: String { build }\n        var build: String\n        var url: URL\n\n        init(build: String, url: URL) {\n            self.build = build\n            self.url = url\n        }\n\n        init(url: URL) {\n            /// We need some way to determine the OS build corresponding to this file URL.\n            /// This will be first read from the extended attributes set by the app itself when it downloads a software image.\n            /// This metadata will survive file renames and files being moved within the same volume.\n            /// If no metadata can be found, attempt to parse an OS build string from the file name itself.\n            let build = url.vb_softwareCatalogData?.build ?? url.lastPathComponent.matchAppleOSBuild() ?? \"\"\n\n            self.init(build: build, url: url)\n        }\n\n        var description: String { \"\\(url.lastPathComponent) (build \\(build.isEmpty ? \"?\" : build))\" }\n    }\n\n    /// Container for properties of a restore image that can be inferred from a local file by reading from extended attributes or parsing from the file name.\n    var vb_restoreImageStub: RestoreImageStub { RestoreImageStub(url: self) }\n\n    /// Attempts to infer the OS version represented by a restore image file or URL.\n    var vb_restoreImageVersion: SoftwareVersion? {\n        let candidates = [\n            vb_softwareCatalogData?.filename,\n            lastPathComponent,\n            vb_whereFromsSpotlightMetadata.first?.lastPathComponent\n        ].compactMap { $0 }\n\n        for candidate in candidates {\n            if let version = candidate.matchAppleOSVersion() {\n                return version\n            }\n        }\n\n        return nil\n    }\n}\n\npublic extension SoftwareCatalog {\n    /// Returns the restore image in the catalog that corresponds to the restore image in the file URL.\n    ///\n    /// This matches local restore images with catalog images by file name, build number, or using extended attributes that\n    /// the app automatically sets on restore images downloaded through the app.\n    func restoreImageMatchingDownloadableCatalogContent(at fileURL: URL) -> RestoreImage? {\n        restoreImages.vb_elementMatchingDownloadableCatalogContent(at: fileURL)\n    }\n}\n\npublic extension ResolvedCatalog {\n    func restoreImageMatchingDownloadableCatalogContent(at fileURL: URL) -> ResolvedRestoreImage? {\n        let restoreImages: [ResolvedRestoreImage] = groups.flatMap(\\.restoreImages)\n        return restoreImages.vb_elementMatchingDownloadableCatalogContent(at: fileURL)\n    }\n}\n\nextension Array where Element: DownloadableCatalogContent {\n    func vb_elementMatchingDownloadableCatalogContent(at url: URL) -> Element? {\n        if let match = first(where: { $0.url.lastPathComponent.caseInsensitiveCompare(url.lastPathComponent) == .orderedSame }) {\n            matchLogger.debug(\"Matched by file name: \\(url.lastPathComponent.quoted) <> \\(match.url.lastPathComponent.quoted)\")\n            return match\n        } else if url.isFileURL,\n                  let catalogData = url.vb_softwareCatalogData,\n                  let match = first(where: { $0.build == catalogData.build || $0.url.lastPathComponent.caseInsensitiveCompare(catalogData.filename) == .orderedSame }) {\n            matchLogger.debug(\"Matched by metadata: \\(url.lastPathComponent.quoted) <> \\(match.url.lastPathComponent.quoted)\")\n            return match\n        } else if let build = url.lastPathComponent.matchAppleOSBuild(),\n                  let match = first(where: { $0.build.caseInsensitiveCompare(build) == .orderedSame })\n        {\n            matchLogger.debug(\"Matched by build: \\(url.lastPathComponent.quoted) <> \\(match.url.lastPathComponent.quoted)\")\n            return match\n        } else {\n            return nil\n        }\n    }\n}\n\n// MARK: - Best-effort Resolution\n\npublic extension SoftwareCatalog {\n    /// Attempts to resolve a restore image using catalog matching. If no catalog entry matches,\n    /// creates a best-effort inferred restore image using metadata from the URL.\n    func resolvedRestoreImage(matching url: URL, guestType: VBGuestType) -> ResolvedRestoreImage? {\n        if let match = restoreImageMatchingDownloadableCatalogContent(at: url) {\n            return try? ResolvedRestoreImage(\n                environment: .current.guestType(guestType),\n                catalog: self,\n                image: match\n            )\n        }\n\n        guard let inferredImage = inferredRestoreImage(for: url, guestType: guestType) else {\n            return nil\n        }\n\n        return try? ResolvedRestoreImage(\n            environment: .current.guestType(guestType),\n            catalog: self,\n            image: inferredImage\n        )\n    }\n}\n\nprivate extension SoftwareCatalog {\n    func inferredRestoreImage(for url: URL, guestType: VBGuestType) -> RestoreImage? {\n        guard let version = url.vb_restoreImageVersion else { return nil }\n\n        let build = url.vb_restoreImageStub.build\n        let resolvedBuild = build.isEmpty ? url.deletingPathExtension().lastPathComponent : build\n        let groupID = groupID(for: version) ?? \"custom\"\n        let channelID = channels.first?.id ?? \"custom\"\n        let requirementID = bestRequirementSetID(for: version) ?? \"custom\"\n        let name = inferredName(for: version, guestType: guestType)\n        let mobileDeviceMinVersion = inferredMobileDeviceMinVersion(for: version)\n\n        return RestoreImage(\n            id: resolvedBuild,\n            group: groupID,\n            channel: channelID,\n            requirements: requirementID,\n            name: name,\n            build: resolvedBuild,\n            version: version,\n            mobileDeviceMinVersion: mobileDeviceMinVersion,\n            url: url,\n            downloadSize: nil\n        )\n    }\n\n    func inferredName(for version: SoftwareVersion, guestType: VBGuestType) -> String {\n        switch guestType {\n        case .mac:\n            return \"macOS \\(version.shortDescription)\"\n        case .linux:\n            return \"Linux \\(version.shortDescription)\"\n        }\n    }\n\n    func groupID(for version: SoftwareVersion) -> CatalogGroup.ID? {\n        groups.first(where: { $0.majorVersion.major == version.major })?.id\n    }\n\n    func inferredMobileDeviceMinVersion(for version: SoftwareVersion) -> SoftwareVersion {\n        if let deviceSupportVersion = deviceSupportVersions.first(where: {\n            ($0.osVersion.major == version.major && $0.osVersion.minor == version.minor)\n            || $0.osVersion.major == version.major\n        }) {\n            return deviceSupportVersion.mobileDeviceMinVersion\n        }\n\n        return .empty\n    }\n\n    func bestRequirementSetID(for version: SoftwareVersion) -> RequirementSet.ID? {\n        guard !requirementSets.isEmpty else { return nil }\n\n        if let minHost13 = requirementSets.first(where: { $0.id == \"min_host_13\" }),\n           let minHost12 = requirementSets.first(where: { $0.id == \"min_host_12\" }),\n           let threshold = SoftwareVersion(string: \"13.3\")\n        {\n            return version >= threshold ? minHost13.id : minHost12.id\n        }\n\n        return requirementSets\n            .max(by: { $0.minVersionHost < $1.minVersionHost })?\n            .id\n    }\n}\n"
  },
  {
    "path": "VirtualCore/Source/VirtualCatalog/SoftwareCatalog.swift",
    "content": "import Foundation\nimport BuddyFoundation\n\n/// Adopted by all models present in a VirtualBuddy software catalog.\npublic protocol CatalogModel: Identifiable, Hashable, Codable, Sendable { }\n\n/// Defines a set of requirements a given software image needs from the virtual machine in order to work.\npublic struct RequirementSet: CatalogModel {\n    /// Identifies the requirement set, used to reference a requirement set from a software image definition.\n    public var id: String\n    /// The minimum number of CPU cores.\n    public var minCPUCount: Int\n    /// The minimum amount of RAM the VM needs.\n    public var minMemorySizeMB: Int\n    /// The minimum host operating system version required to run the system.\n    public var minVersionHost: SoftwareVersion\n\n    public init(id: String, minCPUCount: Int, minMemorySizeMB: Int, minVersionHost: SoftwareVersion) {\n        self.id = id\n        self.minCPUCount = minCPUCount\n        self.minMemorySizeMB = minMemorySizeMB\n        self.minVersionHost = minVersionHost\n    }\n}\n\n/// Defines a feature of Virtualization and its associated requirements.\npublic struct VirtualizationFeature: CatalogModel {\n    /// Identifies the feature, used to reference a feature from a software image definition.\n    public var id: String\n    /// The minimum guest OS version required to use the feature.\n    public var minVersionGuest: SoftwareVersion\n    /// The minimum host OS version required to use the feature.\n    public var minVersionHost: SoftwareVersion\n    /// A user-facing name for the feature, which is used in the beginning of a phrase like `<Trackpad> requires macOS xx.xx...`.\n    public var name: String\n    /// Additional information displayed when the feature is not supported by the current configuration.\n    public var detail: String?\n    /// `true` if this feature is not supported due to the guest's platform (ex:  a feature that only works on Mac and not Linux).\n    public var unsupportedPlatform: Bool\n\n    public init(id: String, minVersionGuest: SoftwareVersion, minVersionHost: SoftwareVersion, name: String, detail: String? = nil, unsupportedPlatform: Bool = false) {\n        self.id = id\n        self.minVersionGuest = minVersionGuest\n        self.minVersionHost = minVersionHost\n        self.name = name\n        self.detail = detail\n        self.unsupportedPlatform = unsupportedPlatform\n    }\n\n    public enum CodingKeys: String, CodingKey {\n        case id, minVersionGuest, minVersionHost, name, detail, unsupportedPlatform\n    }\n}\n\n/// Defines an image that can be referenced by other items in the catalog.\n/// Currently used to represent macOS release groups by the corresponding default wallpaper image.\npublic struct CatalogGraphic: CatalogModel {\n    public struct Thumbnail: Hashable, Codable, Sendable {\n        public var url: URL\n        public var width: Int\n        public var height: Int\n        public var blurHash: String\n\n        public init(url: URL, width: Int, height: Int, blurHash: String) {\n            self.url = url\n            self.width = width\n            self.height = height\n            self.blurHash = blurHash\n        }\n    }\n\n    /// Identifies the graphic, used to reference a graphic from another catalog model.\n    public var id: String\n    /// URL to the graphic image file, in its highest resolution.\n    public var url: URL\n    /// Thumbnail representation of the image, with metadata and blur hash.\n    public var thumbnail: Thumbnail\n\n    public init(id: String, url: URL, thumbnail: Thumbnail) {\n        self.id = id\n        self.url = url\n        self.thumbnail = thumbnail\n    }\n}\n\n/// Defines a grouping of software images by major OS version.\n/// This can be used to group releases like `macOS Sonoma`, `macOS Sequoia`, etc,\n/// making it easier for users to find the desired OS version.\npublic struct CatalogGroup: CatalogModel {\n    /// Identifies the group, used to reference a group from a software image definition.\n    public var id: String\n    /// A user-facing name for the release group.\n    public var name: String\n    /// The major OS version for releases in this group.\n    public var majorVersion: SoftwareVersion\n    /// The image that can be used to represent this group.\n    public var image: CatalogGraphic\n    /// The image that can be used to represent this group when dark mode is enabled.\n    public var darkImage: CatalogGraphic?\n\n    public init(id: String, name: String, majorVersion: SoftwareVersion, image: CatalogGraphic, darkImage: CatalogGraphic?) {\n        self.id = id\n        self.name = name\n        self.majorVersion = majorVersion\n        self.image = image\n        self.darkImage = darkImage\n    }\n}\n\n/// Defines a release channel such as `Beta` or `Release`.\n/// Can be used to allow filtering for specific release types.\npublic struct CatalogChannel: CatalogModel {\n    /// Identifies the channel, used to reference a channel from a software image definition.\n    public var id: String\n    /// User-facing name for the channel.\n    public var name: String\n    /// User-facing note describing the contents in this channel.\n    public var note: String\n    /// SF Symbol name for icon that can be used to represent this channel.\n    public var icon: String\n\n    public init(id: String, name: String, note: String, icon: String) {\n        self.id = id\n        self.name = name\n        self.note = note\n        self.icon = icon\n    }\n}\n\n/// Describes a \"device support files\" installation and the instructions that should be presented to the user when it's required.\npublic struct CatalogDeviceSupportVersion: CatalogModel {\n    public var id: String\n    /// Hint for which MobileDevice version this entry refers to.\n    public var mobileDeviceMinVersion: SoftwareVersion\n    /// OS version this entry refers to. Matching will be attempted by `major.minor` first, then `major` only.\n    public var osVersion: SoftwareVersion\n    /// User-facing title displayed on the list of software images.\n    public var title: String\n    /// User-facing instructions displayed in interstitial or when user clicks the warning. May contain markdown.\n    public var instructions: String\n}\n\n/// Adopted by both ``RestoreImage`` and ``ResolvedRestoreImage`` to make download lookup more convenient to implement.\npublic protocol DownloadableCatalogContent: Identifiable, Hashable, Sendable {\n    var build: String { get }\n    var url: URL { get }\n}\n\n/// Defines an individual macOS restore image in the catalog.\npublic struct RestoreImage: CatalogModel, DownloadableCatalogContent {\n    /// Unique identifier for this restore image.\n    public var id: String\n    /// Identifier of the ``CatalogGroup`` this restore image is a part of.\n    public var group: CatalogGroup.ID\n    /// Identifier of the ``CatalogChannel`` this restore image is a part of.\n    public var channel: CatalogChannel.ID\n    /// Identifier of the ``RequirementSet`` describing the requirements for this image to be installed/run.\n    public var requirements: RequirementSet.ID\n    /// User-facing name for this restore image, usually in a form like `macOS 15.0 Developer Beta 4`.\n    public var name: String\n    /// OS build this restore image provides.\n    public var build: String\n    /// OS version this restore image provides.\n    public var version: SoftwareVersion\n    /// The minimum version of the MobileDevice framework required to install this guest.\n    public var mobileDeviceMinVersion: SoftwareVersion\n    /// URL to the IPSW file for this restore image.\n    public var url: URL\n    /// The size of the download in bytes.\n    public var downloadSize: UInt64?\n\n    public init(id: String, group: CatalogGroup.ID, channel: CatalogChannel.ID, requirements: RequirementSet.ID, name: String, build: String, version: SoftwareVersion, mobileDeviceMinVersion: SoftwareVersion, url: URL, downloadSize: UInt64?) {\n        self.id = id\n        self.group = group\n        self.channel = channel\n        self.requirements = requirements\n        self.name = name\n        self.build = build\n        self.version = version\n        self.mobileDeviceMinVersion = mobileDeviceMinVersion\n        self.url = url\n        self.downloadSize = downloadSize\n    }\n}\n\n/// This is the root data structure for the VirtualBuddy restore image catalog.\npublic struct SoftwareCatalog: Codable, Sendable {\n    /// The API version implemented by this software catalog.\n    public var apiVersion: Int\n    /// The minimum version of the app that can read this catalog.\n    /// The app should reject catalogs with a higher `minAppVersion` and\n    /// direct users to update the app in order to use the catalog.\n    public var minAppVersion: SoftwareVersion\n    /// Channel definitions.\n    public var channels: [CatalogChannel]\n    /// Release group definitions.\n    public var groups: [CatalogGroup]\n    /// Restore image definitions.\n    public var restoreImages: [RestoreImage]\n    /// Feature definitions.\n    public var features: [VirtualizationFeature]\n    /// Requirement set definitions.\n    public var requirementSets: [RequirementSet]\n    /// Device support files definitions.\n    public var deviceSupportVersions: [CatalogDeviceSupportVersion]\n\n    public init(apiVersion: Int, minAppVersion: SoftwareVersion, channels: [CatalogChannel], groups: [CatalogGroup], restoreImages: [RestoreImage], features: [VirtualizationFeature], requirementSets: [RequirementSet], deviceSupportVersions: [CatalogDeviceSupportVersion]) {\n        self.apiVersion = apiVersion\n        self.minAppVersion = minAppVersion\n        self.channels = channels\n        self.groups = groups\n        self.restoreImages = restoreImages\n        self.features = features\n        self.requirementSets = requirementSets\n        self.deviceSupportVersions = deviceSupportVersions\n    }\n\n    public static let empty = SoftwareCatalog(apiVersion: 0, minAppVersion: .empty, channels: [], groups: [], restoreImages: [], features: [], requirementSets: [], deviceSupportVersions: [])\n}\n\npublic extension SoftwareCatalog {\n    private static let decoder = JSONDecoder()\n    private static let encoder: JSONEncoder = {\n        let e = JSONEncoder()\n        e.outputFormatting = [.prettyPrinted, .sortedKeys]\n        return e\n    }()\n\n    init(data: Data) throws {\n        self = try Self.decoder.decode(Self.self, from: data)\n    }\n\n    init(contentsOf url: URL) throws {\n        let data = try Data(contentsOf: url)\n        try self.init(data: data)\n    }\n\n    func write(to url: URL) throws {\n        try Self.encoder.encode(self).write(to: url)\n    }\n}\n\npublic extension CatalogGraphic {\n    static let placeholder = CatalogGraphic(\n        id: \"placeholder\",\n        url: URL(string: \"https://example.com\")!,\n        thumbnail: Thumbnail(url: URL(string: \"https://example.com\")!, width: 640, height: 360, blurHash: \"XXX\")\n    )\n}\n\n// MARK: - Custom Codable Conformances\n\npublic extension VirtualizationFeature {\n    init(from decoder: any Decoder) throws {\n        let container = try decoder.container(keyedBy: CodingKeys.self)\n        self.id = try container.decode(String.self, forKey: .id)\n        self.minVersionGuest = try container.decode(SoftwareVersion.self, forKey: .minVersionGuest)\n        self.minVersionHost = try container.decode(SoftwareVersion.self, forKey: .minVersionHost)\n        self.name = try container.decode(String.self, forKey: .name)\n        self.detail = try container.decodeIfPresent(String.self, forKey: .detail)\n        self.unsupportedPlatform = (try? container.decodeIfPresent(Bool.self, forKey: .unsupportedPlatform)) ?? false\n    }\n}\n"
  },
  {
    "path": "VirtualCore/Source/VirtualCatalog/Utilities/BlurHashEncode.swift",
    "content": "#if canImport(AppKit)\nimport AppKit\n\npublic extension NSImage {\n    func draw(at point: CGPoint) {\n        draw(in: CGRect(origin: point, size: size))\n    }\n\n    func blurHash(numberOfComponents components: (Int, Int)) -> String? {\n        let scale: CGFloat = 1.0\n\n\t\tlet pixelWidth = Int(round(size.width * scale))\n\t\tlet pixelHeight = Int(round(size.height * scale))\n\n\t\tlet context = CGContext(\n\t\t\tdata: nil,\n\t\t\twidth: pixelWidth,\n\t\t\theight: pixelHeight,\n\t\t\tbitsPerComponent: 8,\n\t\t\tbytesPerRow: pixelWidth * 4,\n\t\t\tspace: CGColorSpace(name: CGColorSpace.sRGB)!,\n\t\t\tbitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue\n\t\t)!\n\t\tcontext.scaleBy(x: scale, y: -scale)\n\t\tcontext.translateBy(x: 0, y: -size.height)\n\n        NSGraphicsContext.current = NSGraphicsContext(cgContext: context, flipped: true)\n        context.saveGState()\n\t\tdraw(at: .zero)\n        context.restoreGState()\n\n\t\tguard let cgImage = context.makeImage(),\n\t\tlet dataProvider = cgImage.dataProvider,\n\t\tlet data = dataProvider.data,\n\t\tlet pixels = CFDataGetBytePtr(data) else {\n\t\t\tassertionFailure(\"Unexpected error!\")\n\t\t\treturn nil\n\t\t}\n\n        let width = cgImage.width\n        let height = cgImage.height\n        let bytesPerRow = cgImage.bytesPerRow\n\n        var factors: [(Float, Float, Float)] = []\n        for y in 0 ..< components.1 {\n            for x in 0 ..< components.0 {\n                let normalisation: Float = (x == 0 && y == 0) ? 1 : 2\n                let factor = multiplyBasisFunction(pixels: pixels, width: width, height: height, bytesPerRow: bytesPerRow, bytesPerPixel: cgImage.bitsPerPixel / 8, pixelOffset: 0) {\n                    normalisation * cos(Float.pi * Float(x) * $0 / Float(width)) as Float * cos(Float.pi * Float(y) * $1 / Float(height)) as Float\n                }\n                factors.append(factor)\n            }\n        }\n\n        let dc = factors.first!\n        let ac = factors.dropFirst()\n\n        var hash = \"\"\n\n\t\tlet sizeFlag = (components.0 - 1) + (components.1 - 1) * 9\n\t\thash += sizeFlag.encode83(length: 1)\n\n\t\tlet maximumValue: Float\n\t\tif ac.count > 0 {\n\t\t\tlet actualMaximumValue = ac.map({ max(abs($0.0), abs($0.1), abs($0.2)) }).max()!\n\t\t\tlet quantisedMaximumValue = Int(max(0, min(82, floor(actualMaximumValue * 166 - 0.5))))\n\t\t\tmaximumValue = Float(quantisedMaximumValue + 1) / 166\n\t\t\thash += quantisedMaximumValue.encode83(length: 1)\n\t\t} else {\n\t\t\tmaximumValue = 1\n\t\t\thash += 0.encode83(length: 1)\n\t\t}\n\n        hash += encodeDC(dc).encode83(length: 4)\n\n        for factor in ac {\n            hash += encodeAC(factor, maximumValue: maximumValue).encode83(length: 2)\n        }\n\n        return hash\n    }\n\n    private func multiplyBasisFunction(pixels: UnsafePointer<UInt8>, width: Int, height: Int, bytesPerRow: Int, bytesPerPixel: Int, pixelOffset: Int, basisFunction: (Float, Float) -> Float) -> (Float, Float, Float) {\n        var r: Float = 0\n        var g: Float = 0\n        var b: Float = 0\n\n        let buffer = UnsafeBufferPointer(start: pixels, count: height * bytesPerRow)\n\n        for x in 0 ..< width {\n            for y in 0 ..< height {\n                let basis = basisFunction(Float(x), Float(y))\n                r += basis * sRGBToLinear(buffer[bytesPerPixel * x + pixelOffset + 0 + y * bytesPerRow])\n                g += basis * sRGBToLinear(buffer[bytesPerPixel * x + pixelOffset + 1 + y * bytesPerRow])\n                b += basis * sRGBToLinear(buffer[bytesPerPixel * x + pixelOffset + 2 + y * bytesPerRow])\n            }\n        }\n\n        let scale = 1 / Float(width * height)\n\n        return (r * scale, g * scale, b * scale)\n    }\n}\n\nprivate func encodeDC(_ value: (Float, Float, Float)) -> Int {\n    let roundedR = linearTosRGB(value.0)\n    let roundedG = linearTosRGB(value.1)\n    let roundedB = linearTosRGB(value.2)\n    return (roundedR << 16) + (roundedG << 8) + roundedB\n}\n\nprivate func encodeAC(_ value: (Float, Float, Float), maximumValue: Float) -> Int {\n\tlet quantR = Int(max(0, min(18, floor(signPow(value.0 / maximumValue, 0.5) * 9 + 9.5))))\n\tlet quantG = Int(max(0, min(18, floor(signPow(value.1 / maximumValue, 0.5) * 9 + 9.5))))\n\tlet quantB = Int(max(0, min(18, floor(signPow(value.2 / maximumValue, 0.5) * 9 + 9.5))))\n\n\treturn quantR * 19 * 19 + quantG * 19 + quantB\n}\n\nprivate func signPow(_ value: Float, _ exp: Float) -> Float {\n    return copysign(pow(abs(value), exp), value)\n}\n\nprivate func linearTosRGB(_ value: Float) -> Int {\n    let v = max(0, min(1, value))\n    if v <= 0.0031308 { return Int(v * 12.92 * 255 + 0.5) }\n    else { return Int((1.055 * pow(v, 1 / 2.4) - 0.055) * 255 + 0.5) }\n}\n\nprivate func sRGBToLinear<Type: BinaryInteger>(_ value: Type) -> Float {\n    let v = Float(Int64(value)) / 255\n    if v <= 0.04045 { return v / 12.92 }\n    else { return pow((v + 0.055) / 1.055, 2.4) }\n}\n\nprivate let encodeCharacters: [String] = {\n    return \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz#$%*+,-.:;=?@[]^_{|}~\".map { String($0) }\n}()\n\nextension BinaryInteger {\n\tfunc encode83(length: Int) -> String {\n\t\tvar result = \"\"\n\t\tfor i in 1 ... length {\n\t\t\tlet digit = (Int(self) / pow(83, length - i)) % 83\n\t\t\tresult += encodeCharacters[Int(digit)]\n\t\t}\n\t\treturn result\n\t}\n}\n\nprivate func pow(_ base: Int, _ exponent: Int) -> Int {\n    return (0 ..< exponent).reduce(1) { value, _ in value * base }\n}\n#endif\n"
  },
  {
    "path": "VirtualCore/Source/VirtualCatalog/Utilities/String+AppleOSBuild.swift",
    "content": "import Foundation\nimport BuddyFoundation\n\nextension String {\n    static let appleOSBuildRegex = /[0-9]{2}[A-Z][0-9]{2,}[a-z]?/\n    static let appleOSVersionRegex = /[0-9]+(?:\\.[0-9]+){1,2}/\n\n    /// Returns the first regex match for an Apple OS build number (ex: `23A5276f`).\n    func matchAppleOSBuild() -> String? {\n        (try? Self.appleOSBuildRegex.firstMatch(in: self)?.output).flatMap { String($0) }\n    }\n\n    /// Returns the first regex match for an Apple OS version (ex: `15.5` or `15.5.1`).\n    func matchAppleOSVersion() -> SoftwareVersion? {\n        (try? Self.appleOSVersionRegex.firstMatch(in: self)?.output)\n            .flatMap { SoftwareVersion(string: String($0)) }\n    }\n}\n"
  },
  {
    "path": "VirtualCore/Source/VirtualCatalog/Utilities/URL+ExtendedAttributes.swift",
    "content": "import Foundation\nimport BuddyFoundation\n\nextension URL {\n    func vb_encodeExtendedAttribute<T: Encodable>(_ value: T, forKey key: String) throws {\n        let data = try JSONEncoder.vb_extendedAttribute.encode(value)\n        try vb_setExtendedAttributeData(data, forKey: key)\n    }\n\n    func vb_decodeExtendedAttribute<T: Decodable>(forKey key: String) -> T? {\n        try? vb_extendedAttributeData(forKey: key).flatMap {\n            try JSONDecoder.vb_extendedAttribute.decode(T.self, from: $0)\n        }\n    }\n\n    func vb_setExtendedAttributeData(_ value: Data, forKey key: String, base64: Bool = true) throws {\n        let effectiveValue = base64 ? value.base64EncodedData() : value\n\n        let size = effectiveValue.count\n        let err = effectiveValue.withUnsafeBytes { ptr in\n            setxattr(path, key, ptr.baseAddress, size, 0, 0)\n        }\n\n        guard err == 0 else {\n            throw \"setxattr error code \\(err)\"\n        }\n    }\n\n    func vb_extendedAttributeData(forKey key: String, base64: Bool = true) -> Data? {\n        var size = getxattr(path, key, nil, .max, 0, 0)\n\n        guard size > 0 else {\n            return nil\n        }\n\n        let pointer = UnsafeMutableRawPointer.allocate(byteCount: size, alignment: 2)\n        size = getxattr(path, key, pointer, size, 0, 0)\n\n        guard size > 0 else {\n            pointer.deallocate()\n            return nil\n        }\n\n        let data = Data(bytesNoCopy: pointer, count: size, deallocator: .free)\n\n        guard base64 else { return data }\n\n        return Data(base64Encoded: data)\n    }\n\n    func vb_removeExtendedAttribute(forKey key: String) throws {\n        let err = removexattr(path, key, 0)\n        guard err == 0 else {\n            throw \"removexattr error code \\(err)\"\n        }\n    }\n}\n\nprivate extension JSONEncoder {\n    static let vb_extendedAttribute = JSONEncoder()\n}\nprivate extension JSONDecoder {\n    static let vb_extendedAttribute = JSONDecoder()\n}\n"
  },
  {
    "path": "VirtualCore/Source/Virtualization/Helpers/CatalogExtensions.swift",
    "content": "//\n//  CatalogExtensions.swift\n//  VirtualBuddy\n//\n//  Created by Guilherme Rambo on 02/08/24.\n//\n\nimport Foundation\n\n@MainActor\npublic extension SoftwareCatalog {\n    /// The most up-to-date software catalog available for Mac releases.\n    /// This is updated when the API client fetches a new catalog from the server.\n    private(set) static var currentMacCatalog: SoftwareCatalog = {\n        do {\n            return try VBAPIClient.fetchBuiltInCatalog(for: .mac)\n        } catch {\n            assertionFailure(\"Built-in catalog load failed: \\(error)\")\n            return SoftwareCatalog.empty\n        }\n    }()\n\n    /// The most up-to-date software catalog available for Linux releases.\n    /// This is updated when the API client fetches a new catalog from the server.\n    private(set) static var currentLinuxCatalog: SoftwareCatalog = {\n        do {\n            return try VBAPIClient.fetchBuiltInCatalog(for: .linux)\n        } catch {\n            assertionFailure(\"Built-in catalog load failed: \\(error)\")\n            return SoftwareCatalog.empty\n        }\n    }()\n\n    static func current(for guestType: VBGuestType) -> SoftwareCatalog {\n        switch guestType {\n        case .mac: return .currentMacCatalog\n        case .linux: return .currentLinuxCatalog\n        }\n    }\n\n    static func setCurrent(_ catalog: SoftwareCatalog, for guestType: VBGuestType) {\n        switch guestType {\n        case .mac:\n            self.currentMacCatalog = catalog\n        case .linux:\n            self.currentLinuxCatalog = catalog\n        }\n    }\n}\n\npublic extension CatalogGuestPlatform {\n    init(_ guestType: VBGuestType) {\n        switch guestType {\n        case .mac:\n            self = .mac\n        case .linux:\n            self = .linux\n        }\n    }\n}\n\npublic extension CatalogResolutionEnvironment {\n    func guestType(_ guestType: VBGuestType) -> Self {\n        guest(platform: CatalogGuestPlatform(guestType))\n    }\n}\n\npublic extension VBVirtualMachine {\n    @MainActor\n    func resolveCatalogImage(_ image: RestoreImage, catalog: SoftwareCatalog? = nil) throws -> ResolvedRestoreImage {\n        try configuration.resolveCatalogImage(image, catalog: catalog)\n    }\n}\n\npublic extension VBMacConfiguration {\n    @MainActor\n    func resolveCatalogImage(_ image: RestoreImage, catalog: SoftwareCatalog? = nil) throws -> ResolvedRestoreImage {\n        let effectiveCatalog = catalog ?? SoftwareCatalog.current(for: systemType)\n        return try ResolvedRestoreImage(\n            environment: .current.guestType(systemType),\n            catalog: effectiveCatalog,\n            image: image\n        )\n    }\n}\n"
  },
  {
    "path": "VirtualCore/Source/Virtualization/Helpers/DirectoryObserver.swift",
    "content": "import Foundation\nimport Combine\nimport OSLog\n\nfinal class DirectoryObserver: NSObject, NSFilePresenter {\n\n    private let logger: Logger\n\n    var presentedItemURL: URL?\n\n    var presentedItemOperationQueue: OperationQueue = .main\n\n    let signal: PassthroughSubject<URL, Never>\n    let fileExtensions: Set<String>\n\n    init(presentedItemURL: URL?, fileExtensions: Set<String>, label: String, signal: PassthroughSubject<URL, Never>) {\n        self.logger = Logger(for: DirectoryObserver.self, label: label)\n        self.presentedItemURL = presentedItemURL\n        self.fileExtensions = fileExtensions\n        self.signal = signal\n\n        super.init()\n\n        NSFileCoordinator.addFilePresenter(self)\n    }\n\n    private func sendSignalIfNeeded(for url: URL) {\n        guard fileExtensions.contains(url.pathExtension) else { return }\n\n        signal.send(url)\n    }\n\n    func presentedSubitemDidAppear(at url: URL) {\n        logger.debug(\"Added: \\(url.path)\")\n\n        sendSignalIfNeeded(for: url)\n    }\n\n    func presentedSubitemDidChange(at url: URL) {\n        sendSignalIfNeeded(for: url)\n    }\n\n    func presentedSubitem(at oldURL: URL, didMoveTo newURL: URL) {\n        logger.debug(\"Moved: \\(oldURL.path) -> \\(newURL.path)\")\n\n        sendSignalIfNeeded(for: newURL)\n    }\n\n    func accommodatePresentedSubitemDeletion(at url: URL) async throws {\n        logger.debug(\"Deleted: \\(url.path)\")\n\n        sendSignalIfNeeded(for: url)\n    }\n\n}\n"
  },
  {
    "path": "VirtualCore/Source/Virtualization/Helpers/DiskImageGenerator.swift",
    "content": "//\n//  DiskImageGenerator.swift\n//  VirtualCore\n//\n//  Created by Guilherme Rambo on 19/07/22.\n//\n\nimport Foundation\n\nfileprivate extension VBManagedDiskImage.Format {\n    var hdiutilType: String {\n        switch self {\n        case .raw:\n            assertionFailure(\".raw not supported with hdiutil\")\n            return \"UDIF\"\n        case .dmg:\n            return \"UDIF\"\n        case .sparse:\n            return \"SPARSE\"\n        case .asif:\n            return \"ASIF\"\n        }\n    }\n}\n\npublic final class DiskImageGenerator {\n    public struct ImageSettings {\n        public var url: URL\n        public var template: VBManagedDiskImage\n        \n        public init(for image: VBManagedDiskImage, in vm: VBVirtualMachine) {\n            self.url = vm.diskImageURL(for: image)\n            self.template = image\n        }\n    }\n\n    public static func generateImage(with settings: ImageSettings) async throws {\n        guard settings.template.format.isSupported else {\n            throw \"Unsupported disk image format \\(settings.template.format.hdiutilType.quoted).\"\n        }\n\n        switch settings.template.format {\n        case .raw:\n            try generateRaw(with: settings)\n        case .dmg, .sparse:\n            try await generateDMG(with: settings)\n        case .asif:\n            try await generateBlankASIF(with: settings)\n        }\n    }\n\n    private static func generateRaw(with settings: ImageSettings) throws {\n        let diskFd = open(settings.url.path, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR)\n        if diskFd == -1 {\n            throw Failure(\"Cannot create disk image.\")\n        }\n\n        var result = ftruncate(diskFd, off_t(settings.template.size))\n        if result != 0 {\n            throw Failure(\"ftruncate() failed.\")\n        }\n\n        result = close(diskFd)\n        if result != 0 {\n            throw Failure(\"Failed to close the disk image.\")\n        }\n    }\n\n    private static func generateDMG(with settings: ImageSettings) async throws {\n        try await hdiutil(arguments: [\n            \"create\",\n            \"-layout\",\n            \"GPTSPUD\",\n            \"-type\",\n            settings.template.format.hdiutilType,\n            \"-megabytes\",\n            \"\\(settings.template.size / .storageMegabyte)\",\n            \"-fs\",\n            \"APFS\",\n            \"-volname\",\n            settings.template.filename,\n            \"-nospotlight\",\n            settings.url.path\n        ])\n    }\n\n    private static func generateBlankASIF(with settings: ImageSettings) async throws {\n        try await diskutil(arguments: [\n            \"image\",\n            \"create\",\n            \"blank\",\n            \"--fs\",\n            \"none\",\n            \"--format\",\n            settings.template.format.hdiutilType,\n            \"--size\",\n            \"\\(settings.template.size / .storageGigabyte)G\",\n            settings.url.path\n        ])\n    }\n\n    private static func hdiutil(arguments: [String]) async throws {\n        try await runCommand(\"/usr/bin/hdiutil\", with: arguments)\n    }\n\n    private static func diskutil(arguments: [String]) async throws {\n        try await runCommand(\"/usr/sbin/diskutil\", with: arguments)\n    }\n\n    private static func runCommand(_ path: String, with arguments: [String]) async throws {\n        let process = Process()\n        process.executableURL = URL(fileURLWithPath: path)\n        process.arguments = arguments\n\n        #if DEBUG\n        print(\"💻 \\(path) arguments: \\(process.arguments!.joined(separator: \" \"))\")\n        #endif\n\n        let err = Pipe()\n        let out = Pipe()\n        process.standardError = err\n        process.standardOutput = out\n        try process.run()\n\n        var error = \"\"\n        for try await line in err.fileHandleForReading.bytes.lines {\n            error.append(\"\\(line)\\n\")\n        }\n\n        process.waitUntilExit()\n\n        guard process.terminationStatus != 0 else { return }\n\n        if error.trimmingCharacters(in: .newlines).count > 0 {\n            throw Failure(error)\n        } else {\n            throw Failure(\"Command \\(path) failed with exit code \\(process.terminationStatus)\")\n        }\n    }\n\n}\n"
  },
  {
    "path": "VirtualCore/Source/Virtualization/Helpers/LinuxVirtualMachineConfigurationHelper.swift",
    "content": "/*\nSee LICENSE folder for this sample’s licensing information.\n\nAbstract:\nHelper that creates various configuration objects exposed in the `VZVirtualMachineConfiguration`.\n*/\n\nimport Foundation\nimport Virtualization\n\n@available(macOS 13.0, *)\nstruct LinuxVirtualMachineConfigurationHelper: VirtualMachineConfigurationHelper {\n    let vm: VBVirtualMachine\n    let savedState: VBSavedStatePackage?\n\n    init(vm: VBVirtualMachine) {\n        self.vm = vm\n        self.savedState = nil\n    }\n\n    func createInstallDevice(installImageURL: URL) throws -> VZStorageDeviceConfiguration {\n        let attachment = try VZDiskImageStorageDeviceAttachment(url: installImageURL, readOnly: true, cachingMode: .cached, synchronizationMode: .fsync)\n        let usbDeviceConfiguration = VZUSBMassStorageDeviceConfiguration(attachment: attachment)\n        return usbDeviceConfiguration\n    }\n\n    func createBootLoader() throws -> VZBootLoader {\n        let efi = VZEFIBootLoader()\n        let storeURL = vm.metadataDirectoryURL.appendingPathComponent(\"nvram\")\n        if FileManager.default.fileExists(atPath: storeURL.path) {\n            efi.variableStore = VZEFIVariableStore(url: storeURL)\n        } else {\n            efi.variableStore = try VZEFIVariableStore(creatingVariableStoreAt: storeURL, options: [])\n        }\n        return efi\n    }\n\n    func createGraphicsDevices() -> [VZGraphicsDeviceConfiguration] {\n        let graphicsConfiguration = VZVirtioGraphicsDeviceConfiguration()\n\n        graphicsConfiguration.scanouts = vm.configuration.hardware.displayDevices.map(\\.vzScanout)\n\n        return [graphicsConfiguration]\n    }\n\n    @available(macOS 13.0, *)\n    func createSpiceAgentConsoleDeviceConfiguration() -> VZVirtioConsoleDeviceConfiguration? {\n        let consoleDevice = VZVirtioConsoleDeviceConfiguration()\n\n        let spiceAgentPort = VZVirtioConsolePortConfiguration()\n        spiceAgentPort.name = VZSpiceAgentPortAttachment.spiceAgentPortName\n        spiceAgentPort.attachment = VZSpiceAgentPortAttachment()\n        consoleDevice.ports[0] = spiceAgentPort\n\n        return consoleDevice\n    }\n}\n\n// MARK: - Configuration Models -> Virtualization\n\n@available(macOS 13.0, *)\nextension VBDisplayDevice {\n\n    var vzScanout: VZVirtioGraphicsScanoutConfiguration {\n        VZVirtioGraphicsScanoutConfiguration(widthInPixels: width, heightInPixels: height)\n    }\n\n}\n"
  },
  {
    "path": "VirtualCore/Source/Virtualization/Helpers/MacOSVirtualMachineConfigurationHelper.swift",
    "content": "/*\nSee LICENSE folder for this sample’s licensing information.\n\nAbstract:\nHelper that creates various configuration objects exposed in the `VZVirtualMachineConfiguration`.\n*/\n\nimport Foundation\nimport Virtualization\n\nstruct MacOSVirtualMachineConfigurationHelper: VirtualMachineConfigurationHelper {\n    let vm: VBVirtualMachine\n    let savedState: VBSavedStatePackage?\n\n    func createInstallDevice(installImageURL: URL) throws -> VZStorageDeviceConfiguration {\n        fatalError()\n    }\n\n    func createBootLoader() -> VZBootLoader {\n        return VZMacOSBootLoader()\n    }\n\n    func createGraphicsDevices() -> [VZGraphicsDeviceConfiguration] {\n        let graphicsConfiguration = VZMacGraphicsDeviceConfiguration()\n        \n        graphicsConfiguration.displays = vm.configuration.hardware.displayDevices.map(\\.vzDisplay)\n        \n        return [graphicsConfiguration]\n    }\n\n    func createAdditionalBlockDevices() async throws -> [VZVirtioBlockDeviceConfiguration] {\n        var devices = try storageDeviceContainer.additionalBlockDevices(guestType: vm.configuration.systemType)\n\n        if vm.configuration.guestAdditionsEnabled, let disk = try? VZVirtioBlockDeviceConfiguration.guestAdditionsDisk {\n            devices.append(disk)\n        }\n\n        return devices\n    }\n\n    func createKeyboardConfiguration() -> VZKeyboardConfiguration {\n        if #available(macOS 14.0, *) {\n            switch vm.configuration.hardware.keyboardDevice.kind {\n            case .generic:\n                return VZUSBKeyboardConfiguration()\n            case .mac:\n                return VZMacKeyboardConfiguration()\n            }\n        } else {\n            return VZUSBKeyboardConfiguration()\n        }\n    }\n\n    func createEntropyDevices() -> [VZEntropyDeviceConfiguration] {\n        [VZVirtioEntropyDeviceConfiguration()]\n    }\n\n    @available(macOS 15.0, *)\n    func createUSBControllers() -> [VZUSBControllerConfiguration] {\n        let xhci = VZXHCIControllerConfiguration()\n        return [xhci]\n    }\n}\n\n// MARK: - Configuration Models -> Virtualization\n\nextension VBDisplayDevice {\n\n    var vzDisplay: VZMacGraphicsDisplayConfiguration {\n        VZMacGraphicsDisplayConfiguration(widthInPixels: width, heightInPixels: height, pixelsPerInch: pixelsPerInch)\n    }\n\n}\n"
  },
  {
    "path": "VirtualCore/Source/Virtualization/Helpers/RandomNameGenerator.swift",
    "content": "//\n//  RandomNameGenerator.swift\n//  VirtualCore\n//\n//  Created by Guilherme Rambo on 19/07/22.\n//\n\nimport Foundation\n\npublic final class RandomNameGenerator {\n\n    public static let shared = RandomNameGenerator()\n\n    private var adjectives = [String]()\n    private var animals = [String]()\n\n    private init() {\n        guard let animalsData = NSDataAsset(name: \"Animals\", bundle: .virtualCore)?.data,\n              let adjectivesData = NSDataAsset(name: \"Adjectives\", bundle: .virtualCore)?.data\n        else {\n            assertionFailure(\"Couldn't load random name generator asssets\")\n            return\n        }\n\n        adjectives = String(decoding: adjectivesData, as: UTF8.self).components(separatedBy: .newlines)\n        animals = String(decoding: animalsData, as: UTF8.self).components(separatedBy: .newlines)\n    }\n\n    public func newName() -> String {\n        guard let adjective = adjectives.randomElement(),\n              let animal = animals.randomElement()\n        else {\n            return UUID().uuidString\n        }\n\n        return adjective + \" \" + animal\n    }\n\n}\n"
  },
  {
    "path": "VirtualCore/Source/Virtualization/Helpers/VBDebugUtil.h",
    "content": "#if DEBUG\n\n#import <Foundation/Foundation.h>\n\n@class VZVirtualMachine;\n\nNS_ASSUME_NONNULL_BEGIN\n\n/// This is used to stop VirtualBuddy in the debugger at specific points in a VM's lifecycle,\n/// so that exploring Virtualization internals can be done in Objective-C.\n@interface VBDebugUtil : NSObject\n\n+ (void)debugVirtualMachineBeforeStart:(VZVirtualMachine *_Nonnull)vm;\n+ (void)debugVirtualMachineAfterStart:(VZVirtualMachine *_Nonnull)vm;\n\n@end\n\nNS_ASSUME_NONNULL_END\n\n#endif\n"
  },
  {
    "path": "VirtualCore/Source/Virtualization/Helpers/VBDebugUtil.m",
    "content": "#if DEBUG\n\n#import \"VBDebugUtil.h\"\n\n@import Virtualization;\n\n#import <VirtualCore/VirtualizationPrivate.h>\n\n@implementation VBDebugUtil\n\n/// Runs in debug builds when VM is about to be started.\n+ (void)debugVirtualMachineBeforeStart:(VZVirtualMachine *_Nonnull)vm\n{\n    NSLog(@\"Debug virtual machine before start: %@\", vm);\n    return;\n}\n\n/// Runs in debug builds right after the VM has been started.\n+ (void)debugVirtualMachineAfterStart:(VZVirtualMachine *_Nonnull)vm\n{\n    NSLog(@\"Debug virtual machine after start: %@\", vm);\n    \n    __weak typeof(vm) weakVM = vm;\n    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(15 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{\n        if (!weakVM) return;\n\n        [self debugVirtualMachineAfterStartWithDelay:weakVM];\n    });\n}\n\n/// Runs in debug builds several seconds after the VM has been started (likely after it's finished booting up).\n+ (void)debugVirtualMachineAfterStartWithDelay:(VZVirtualMachine *_Nonnull)vm\n{\n    NSLog(@\"Debug virtual machine after start with delay: %@\", vm);\n    return;\n}\n\n@end\n\n#endif\n"
  },
  {
    "path": "VirtualCore/Source/Virtualization/Helpers/VZVirtualMachineConfiguration+NVRAM.swift",
    "content": "//\n//  VZVirtualMachineConfiguration+NVRAM.swift\n//  VirtualCore\n//\n//  Created by Guilherme Rambo on 11/04/22.\n//\n\nimport Foundation\nimport Virtualization\n\npublic struct Failure: LocalizedError {\n    public var errorDescription: String?\n    \n    public init(_ msg: String) { self.errorDescription = msg }\n}\n\npublic extension VZMacAuxiliaryStorage {\n\n    func fetchNVRAMVariables() throws -> [VBNVRAMVariable] {\n        var error: NSError?\n        let variables = _allNVRAMVariablesWithError(&error)\n        \n        if let error = error { throw error }\n        \n        let vars = variables.map { VBNVRAMVariable(name: $0.key, value: $0.value as? String) }\n        return vars\n    }\n    \n    func updateNVRAM(_ variable: VBNVRAMVariable) throws {\n        if let value = variable.value {\n            try _setValue(value, forNVRAMVariableNamed: variable.name)\n        } else {\n            try _removeNVRAMVariableNamed(variable.name)\n        }\n    }\n    \n}\n"
  },
  {
    "path": "VirtualCore/Source/Virtualization/Helpers/VirtualMachineConfigurationHelper.swift",
    "content": "/*\nSee LICENSE folder for this sample’s licensing information.\n\nAbstract:\nHelper that creates various configuration objects exposed in the `VZVirtualMachineConfiguration`.\n*/\n\nimport Foundation\nimport Virtualization\nimport BuddyFoundation\n\nprotocol VirtualMachineConfigurationHelper {\n    var vm: VBVirtualMachine { get }\n    var savedState: VBSavedStatePackage? { get }\n    func createInstallDevice(installImageURL: URL) throws -> VZStorageDeviceConfiguration\n    func createBootLoader() throws -> VZBootLoader\n    func createBootBlockDevice() async throws -> VZVirtioBlockDeviceConfiguration\n    func createAdditionalBlockDevices() async throws -> [VZVirtioBlockDeviceConfiguration]\n    func createKeyboardConfiguration() -> VZKeyboardConfiguration\n    func createGraphicsDevices() -> [VZGraphicsDeviceConfiguration]\n    func createEntropyDevices() -> [VZVirtioEntropyDeviceConfiguration]\n    @available(macOS 13.0, *)\n    func createSpiceAgentConsoleDeviceConfiguration() -> VZVirtioConsoleDeviceConfiguration?\n    @available(macOS 15.0, *)\n    func createUSBControllers() -> [VZUSBControllerConfiguration]\n}\n\nfunc createVZDiskImageStorageDeviceAttachment(url: URL, readOnly: Bool, guestType: VBGuestType) throws -> VZDiskImageStorageDeviceAttachment {\n    if guestType == .linux {\n        // Linux guest is bound to cause IO errors.\n        // Referring to https://github.com/utmapp/UTM/issues/4840, seems like setting the cachingMode to cached\n        // fixes this IO errors and disk corruption issues for Linux guest.\n        return try VZDiskImageStorageDeviceAttachment(url: url, readOnly: readOnly, cachingMode: .cached, synchronizationMode: .fsync)\n    } else {\n        return try VZDiskImageStorageDeviceAttachment(url: url, readOnly: readOnly)\n    }\n}\n\nextension VirtualMachineConfigurationHelper {\n\n    var storageDeviceContainer: VBStorageDeviceContainer { savedState ?? vm }\n\n    func createBootBlockDevice() async throws -> VZVirtioBlockDeviceConfiguration {\n        do {\n            let bootDevice = try storageDeviceContainer.bootDevice\n            let bootDiskImage = try storageDeviceContainer.bootDiskImage\n            \n            if !bootDevice.diskImageExists(for: storageDeviceContainer) {\n                guard storageDeviceContainer.allowDiskImageCreation else {\n                    throw Failure(\"Boot disk image does not exist.\")\n                }\n\n                let settings = DiskImageGenerator.ImageSettings(for: bootDiskImage, in: vm)\n                try await DiskImageGenerator.generateImage(with: settings)\n            }\n\n            let bootURL = storageDeviceContainer.diskImageURL(for: bootDiskImage)\n            let diskImageAttachment = try createVZDiskImageStorageDeviceAttachment(url: bootURL, readOnly: false, guestType: vm.configuration.systemType)\n\n            let disk = VZVirtioBlockDeviceConfiguration(attachment: diskImageAttachment)\n\n            return disk\n        } catch {\n            throw Failure(\"Failed to instantiate a disk image for the VM: \\(error.localizedDescription).\")\n        }\n    }\n    \n    func createAdditionalBlockDevices() async throws -> [VZVirtioBlockDeviceConfiguration] {\n        try storageDeviceContainer.additionalBlockDevices(guestType: vm.configuration.systemType)\n    }\n\n    func createKeyboardConfiguration() -> VZKeyboardConfiguration {\n        VZUSBKeyboardConfiguration()\n    }\n\n    func createEntropyDevices() -> [VZVirtioEntropyDeviceConfiguration] { [] }\n\n    @available(macOS 13.0, *)\n    func createSpiceAgentConsoleDeviceConfiguration() -> VZVirtioConsoleDeviceConfiguration? { nil }\n\n    @available(macOS 15.0, *)\n    func createUSBControllers() -> [VZUSBControllerConfiguration] { [] }\n\n}\n\nextension VBStorageDeviceContainer {\n    func additionalBlockDevices(guestType: VBGuestType) throws -> [VZVirtioBlockDeviceConfiguration] {\n        var output = [VZVirtioBlockDeviceConfiguration]()\n\n        for device in storageDevices {\n            guard device.isEnabled, !device.isBootVolume else { continue }\n\n            let url = diskImageURL(for: device)\n            let attachment = try createVZDiskImageStorageDeviceAttachment(url: url, readOnly: device.isReadOnly, guestType: guestType)\n\n            output.append(VZVirtioBlockDeviceConfiguration(attachment: attachment))\n        }\n\n        return output\n    }\n}\n\nextension VBMacConfiguration {\n\n    var vzNetworkDevices: [VZNetworkDeviceConfiguration] {\n        get throws {\n            try hardware.networkDevices.map { try $0.vzConfiguration }\n        }\n    }\n\n    var vzAudioDevices: [VZAudioDeviceConfiguration] {\n        hardware.soundDevices.map(\\.vzConfiguration)\n    }\n\n    var vzPointingDevices: [VZPointingDeviceConfiguration] {\n        get throws { try hardware.pointingDevice.vzConfigurations }\n    }\n\n}\n\nextension VBNetworkDevice {\n\n    var vzConfiguration: VZNetworkDeviceConfiguration {\n        get throws {\n            let config = VZVirtioNetworkDeviceConfiguration()\n\n            guard let addr = VZMACAddress(string: macAddress) else {\n                throw Failure(\"Invalid MAC address\")\n            }\n\n            config.macAddress = addr\n            config.attachment = try vzAttachment\n\n            return config\n        }\n    }\n\n    private var vzAttachment: VZNetworkDeviceAttachment {\n        get throws {\n            switch kind {\n            case .NAT:\n                return VZNATNetworkDeviceAttachment()\n            case .bridge:\n                let interface = try resolveBridge(with: id)\n                return VZBridgedNetworkDeviceAttachment(interface: interface)\n            }\n        }\n    }\n\n    private func resolveBridge(with identifier: String) throws -> VZBridgedNetworkInterface {\n        guard identifier != VBNetworkDeviceInterface.automatic.id else {\n            return try VZBridgedNetworkInterface.networkInterfaces.first.require(\"There are no network interfaces available on the host for bridging.\")\n        }\n\n        return try VZBridgedNetworkInterface.networkInterfaces.first(where: { $0.identifier == identifier })\n            .require(\"The bridged network interface \\(identifier.quoted) is not available.\")\n    }\n}\n\nextension VBPointingDevice {\n\n    var vzConfigurations: [VZPointingDeviceConfiguration] {\n        get throws {\n            switch kind {\n            case .mouse:\n                return [VZUSBScreenCoordinatePointingDeviceConfiguration()]\n            case .trackpad:\n                return [\n                    VZMacTrackpadConfiguration(),\n                    VZUSBScreenCoordinatePointingDeviceConfiguration()\n                ]\n            }\n        }\n    }\n\n}\n\nextension VBSoundDevice {\n\n    var vzConfiguration: VZAudioDeviceConfiguration {\n        let audioConfiguration = VZVirtioSoundDeviceConfiguration()\n\n        if enableInput {\n            let inputStream = VZVirtioSoundDeviceInputStreamConfiguration()\n            inputStream.source = VZHostAudioInputStreamSource()\n            audioConfiguration.streams.append(inputStream)\n        }\n\n        if enableOutput {\n            let outputStream = VZVirtioSoundDeviceOutputStreamConfiguration()\n            outputStream.sink = VZHostAudioOutputStreamSink()\n            audioConfiguration.streams.append(outputStream)\n        }\n\n        return audioConfiguration\n    }\n\n}\n\nextension VBMacConfiguration {\n    \n    var vzSharedFoldersFileSystemDevices: [VZDirectorySharingDeviceConfiguration] {\n        get throws {\n            var directories: [String: VZSharedDirectory] = [:]\n            \n            for folder in sharedFolders {\n                guard let dir = folder.vzSharedFolder else { continue }\n                \n                directories[folder.effectiveMountPointName] = dir\n            }\n\n            var devices: [VZDirectorySharingDeviceConfiguration] = []\n\n            // standard directory share\n            try VZVirtioFileSystemDeviceConfiguration.validateTag(VBSharedFolder.virtualBuddyShareName)\n            do {\n                let share = VZMultipleDirectoryShare(directories: directories)\n                let device = VZVirtioFileSystemDeviceConfiguration(tag: VBSharedFolder.virtualBuddyShareName)\n                device.share = share\n                devices.append(device)\n            }\n\n            if self.systemType == .linux && self.rosettaSharingEnabled {\n                // Rosetta directory share for Linux VMs\n                try VZVirtioFileSystemDeviceConfiguration.validateTag(VBSharedFolder.rosettaShareName)\n                let share = try VZLinuxRosettaDirectoryShare()\n                let device = VZVirtioFileSystemDeviceConfiguration(tag: VBSharedFolder.rosettaShareName)\n                device.share = share\n                devices.append(device)\n            }\n\n            return devices\n        }\n    }\n}\n\nextension VBSharedFolder {\n    \n    var vzSharedFolder: VZSharedDirectory? {\n        guard isAvailable, isEnabled else { return nil }\n        return VZSharedDirectory(url: url, readOnly: isReadOnly)\n    }\n    \n}\n"
  },
  {
    "path": "VirtualCore/Source/Virtualization/Screenshot/NSImage+DRMProtected.swift",
    "content": "import Cocoa\nimport BuddyKit\nimport Vision\n\n@available(macOS 15.0, *)\nextension NSImage {\n    /// This is kinda silly.\n    ///\n    /// All this does is it attempts to detect the string \"DRM Protected\" inside of a thumbnail image.\n    /// This is to work around a bug that happened in macOS 26 where screenshots used for thumbnails\n    /// would display a \"DRM Protected Video\" message with a black background.\n    ///\n    /// Some users ended up with these bad thumbnails in their library and with the new background hash\n    /// thumbnails I didn't want them to get a black background with a white blurry blob as their VM thumbnail.\n    ///\n    /// See: https://github.com/insidegui/VirtualBuddy/discussions/533\n    func detectDRMProtectedVideoBug() async -> Bool {\n        guard let cgImage else { return false }\n\n        var request = RecognizeTextRequest()\n        request.automaticallyDetectsLanguage = false\n        request.recognitionLanguages = [.init(components: .init(languageCode: .english, script: nil, region: nil))]\n        request.recognitionLevel = .fast\n        request.minimumTextHeightFraction = 0.05\n\n        guard let observations = try? await request.perform(on: cgImage) else { return false }\n\n        return observations.contains(where: { $0.text.localizedCaseInsensitiveContains(\"DRM Protected\") })\n    }\n}\n\n@available(macOS 15.0, *)\nprivate extension RecognizedTextObservation {\n    var text: String {\n        topCandidates(1).first?.string ?? \"\"\n    }\n}\n"
  },
  {
    "path": "VirtualCore/Source/Virtualization/Screenshot/NSImage+HEIC.swift",
    "content": "import Cocoa\nimport struct AVFoundation.AVFileType\n\npublic extension NSImage {\n    static let defaultThumbnailProperties = [\n        kCGImageDestinationLossyCompressionQuality: 0.9,\n        kCGImageDestinationImageMaxPixelSize: 1024\n    ] as CFDictionary\n\n    static let defaultHEICProperties = [\n        kCGImageDestinationLossyCompressionQuality: 1,\n        kCGImageDestinationImageMaxPixelSize: 4096\n    ] as CFDictionary\n\n    // TODO: Adopt BuddyImageKit\n    @discardableResult\n    func vb_createThumbnail(at url: URL, options: CFDictionary = NSImage.defaultThumbnailProperties) throws -> NSImage {\n        guard let cgImage = self.cgImage(forProposedRect: nil, context: nil, hints: nil) else {\n            throw Failure(\"Failed to create a CGImage\")\n        }\n\n        try cgImage.vb_encodeHEIC(to: url, options: options)\n\n        guard let thumbnailImage = NSImage(contentsOf: url) else {\n            throw Failure(\"Failed to load generated thumbnail\")\n        }\n\n        return thumbnailImage\n    }\n\n    // TODO: Adopt BuddyImageKit\n    func vb_heicEncodedData(options: CFDictionary = NSImage.defaultHEICProperties) throws -> Data {\n        guard let cgImage = self.cgImage(forProposedRect: nil, context: nil, hints: nil) else {\n            throw Failure(\"Failed to create a CGImage\")\n        }\n\n        return try cgImage.vb_heicEncodedData(options: options)\n    }\n\n    // TODO: Adopt BuddyImageKit\n    @discardableResult\n    func vb_encodeHEIC(to url: URL, options: CFDictionary = NSImage.defaultHEICProperties) throws -> URL {\n        guard let cgImage = self.cgImage(forProposedRect: nil, context: nil, hints: nil) else {\n            throw Failure(\"Failed to create a CGImage\")\n        }\n\n        return try cgImage.vb_encodeHEIC(to: url, options: options)\n    }\n}\n\npublic extension CGImage {\n    // TODO: Adopt BuddyImageKit\n    func vb_heicEncodedData(options: CFDictionary = NSImage.defaultHEICProperties) throws -> Data {\n        guard let cfData = CFDataCreateMutable(kCFAllocatorDefault, 0) else {\n            throw Failure(\"Failed to create CFMutableData\")\n        }\n        guard let destination = CGImageDestinationCreateWithData(cfData, AVFileType.heic.rawValue as CFString, 1, nil) else {\n            throw Failure(\"Failed to create image destination\")\n        }\n\n        CGImageDestinationAddImage(destination, self, options)\n        CGImageDestinationFinalize(destination)\n\n        return cfData as Data\n    }\n\n    // TODO: Adopt BuddyImageKit\n    @discardableResult\n    func vb_encodeHEIC(to url: URL, options: CFDictionary = NSImage.defaultHEICProperties) throws -> URL {\n        guard let destination = CGImageDestinationCreateWithURL(url as CFURL, AVFileType.heic.rawValue as CFString, 1, nil) else {\n            throw Failure(\"Failed to create image destination\")\n        }\n\n        CGImageDestinationAddImage(destination, self, options)\n        CGImageDestinationFinalize(destination)\n\n        return url\n    }\n}\n"
  },
  {
    "path": "VirtualCore/Source/Virtualization/VBVirtualMachine+Virtualization.swift",
    "content": "//\n//  VBVirtualMachine+Virtualization.swift\n//  VirtualCore\n//\n//  Created by Guilherme Rambo on 21/07/22.\n//\n\nimport Foundation\nimport Virtualization\n\nextension VBVirtualMachine {\n\n    func fetchOrGenerateAuxiliaryStorage(hardwareModel: VZMacHardwareModel? = nil) throws -> VZMacAuxiliaryStorage {\n        if FileManager.default.fileExists(atPath: auxiliaryStorageURL.path) {\n            return VZMacAuxiliaryStorage(contentsOf: auxiliaryStorageURL)\n        } else {\n            return try generateAuxiliaryStorage(hardwareModel: hardwareModel)\n        }\n    }\n\n    @discardableResult\n    func generateAuxiliaryStorage(hardwareModel: VZMacHardwareModel? = nil) throws -> VZMacAuxiliaryStorage {\n        if FileManager.default.fileExists(atPath: auxiliaryStorageURL.path) {\n            try FileManager.default.removeItem(at: auxiliaryStorageURL)\n        }\n\n        let hw: VZMacHardwareModel\n        if let hardwareModel {\n            hw = hardwareModel\n        } else {\n            hw = try fetchOrGenerateHardwareModel(with: nil)\n        }\n\n        return try VZMacAuxiliaryStorage(\n            creatingStorageAt: auxiliaryStorageURL,\n            hardwareModel: hw\n        )\n    }\n\n    func fetchOrGenerateHardwareModel(with restoreImage: VZMacOSRestoreImage?) throws -> VZMacHardwareModel {\n        let hardwareModel: VZMacHardwareModel\n\n        if FileManager.default.fileExists(atPath: hardwareModelURL.path) {\n            guard let hardwareModelData = try? Data(contentsOf: hardwareModelURL) else {\n                throw Failure(\"Failed to retrieve hardware model data.\")\n            }\n\n            guard let hw = VZMacHardwareModel(dataRepresentation: hardwareModelData) else {\n                throw Failure(\"Failed to create hardware model.\")\n            }\n\n            hardwareModel = hw\n        } else {\n            guard let image = restoreImage else {\n                throw Failure(\"Hardware model data doesn't exist, but a restore image was not provided to create the initial data.\")\n            }\n\n            guard let hw = image.mostFeaturefulSupportedConfiguration?.hardwareModel else {\n                throw Failure(\"Failed to obtain hardware model from restore image\")\n            }\n\n            hardwareModel = hw\n\n            try hw.dataRepresentation.write(to: hardwareModelURL)\n        }\n\n        guard hardwareModel.isSupported else {\n            throw Failure(\"The hardware model is not supported on the current host\")\n        }\n\n        return hardwareModel\n    }\n\n    func fetchExistingMachineIdentifier() throws -> VZMacMachineIdentifier {\n        guard let machineIdentifierData = try? Data(contentsOf: machineIdentifierURL) else {\n            throw Failure(\"Failed to retrieve machine identifier data.\")\n        }\n\n        guard let mid = VZMacMachineIdentifier(dataRepresentation: machineIdentifierData) else {\n            throw Failure(\"Failed to create machine identifier.\")\n        }\n\n        return mid\n    }\n\n    func fetchOrGenerateMachineIdentifier() throws -> VZMacMachineIdentifier {\n        let identifier: VZMacMachineIdentifier\n\n        if FileManager.default.fileExists(atPath: machineIdentifierURL.path) {\n            identifier = try fetchExistingMachineIdentifier()\n        } else {\n            identifier = try generateNewMachineIdentifier()\n        }\n\n        return identifier\n    }\n\n    @discardableResult\n    func generateNewMachineIdentifier() throws -> VZMacMachineIdentifier {\n        if FileManager.default.fileExists(atPath: machineIdentifierURL.path) {\n            try FileManager.default.removeItem(at: machineIdentifierURL)\n        }\n\n        let identifier = VZMacMachineIdentifier()\n\n        try identifier.dataRepresentation.write(to: machineIdentifierURL)\n\n        return identifier\n    }\n\n}\n\npublic extension VBVirtualMachine {\n    var ECID: UInt64? {\n        guard let machineIdentifier = try? self.fetchExistingMachineIdentifier() else { return nil }\n        let data = machineIdentifier.dataRepresentation\n        guard let dict = try? PropertyListSerialization.propertyList(from: data, format: nil) as? [String: Any] else { return nil }\n        return dict[\"ECID\"] as? UInt64\n    }\n}\n"
  },
  {
    "path": "VirtualCore/Source/Virtualization/VMController.swift",
    "content": "//\n//  VMController.swift\n//  VirtualCore\n//\n//  Created by Guilherme Rambo on 07/04/22.\n//\n\nimport Cocoa\nimport Foundation\nimport Virtualization\nimport Combine\nimport OSLog\n\npublic struct VMSessionOptions: Hashable, Codable {\n    @DecodableDefault.False\n    public var bootInRecoveryMode = false {\n        didSet {\n            guard bootInRecoveryMode != oldValue else { return }\n            resolveMutuallyExclusiveOptions()\n        }\n    }\n\n    @DecodableDefault.False\n    public var bootInDFUMode = false {\n        didSet {\n            guard bootInDFUMode != oldValue else { return }\n            resolveMutuallyExclusiveOptions()\n        }\n    }\n\n    @DecodableDefault.False\n    public var bootOnInstallDevice = false\n\n    @DecodableDefault.False\n    public var autoBoot = false\n\n    /// Used when restoring from a previously-saved state.\n    public var stateRestorationPackageURL: URL?\n\n    public static let `default` = VMSessionOptions()\n\n    public init(bootInRecoveryMode: Bool = false, bootInDFUMode: Bool = false, bootOnInstallDevice: Bool = false, autoBoot: Bool = false, stateRestorationPackageURL: URL? = nil) {\n        self.bootInRecoveryMode = bootInRecoveryMode\n        self.bootInDFUMode = bootInDFUMode\n        self.bootOnInstallDevice = bootOnInstallDevice\n        self.autoBoot = autoBoot\n        self.stateRestorationPackageURL = stateRestorationPackageURL\n\n        resolveMutuallyExclusiveOptions()\n    }\n\n    private mutating func resolveMutuallyExclusiveOptions() {\n        if bootInDFUMode {\n            bootInRecoveryMode = false\n        }\n        if bootInRecoveryMode {\n            bootInDFUMode = false\n        }\n    }\n}\n\npublic enum VMState: Equatable {\n    case idle\n    case starting(_ message: String?)\n    case running(VZVirtualMachine)\n    case paused(VZVirtualMachine)\n    case savingState(VZVirtualMachine)\n    case stateSaveCompleted(VZVirtualMachine, VBSavedStatePackage)\n    case restoringState(VZVirtualMachine, VBSavedStatePackage)\n    case stopped(Error?)\n}\n\n@MainActor\npublic final class VMController: ObservableObject {\n\n    public let id: VBVirtualMachine.ID\n    private let name: String\n\n    private let library: VMLibraryController\n\n    private lazy var logger = Logger(for: Self.self)\n    \n    @Published\n    public var options = VMSessionOptions.default {\n        didSet {\n            instance?.options = options\n        }\n    }\n    \n    public typealias State = VMState\n    \n    @Published\n    public private(set) var state = State.idle\n    \n    private(set) var virtualMachine: VZVirtualMachine?\n\n    @Published\n    public var virtualMachineModel: VBVirtualMachine\n\n    public private(set) var savedStatesController: VMSavedStatesController\n\n    private lazy var cancellables = Set<AnyCancellable>()\n    \n    public init(with vm: VBVirtualMachine, library: VMLibraryController, options: VMSessionOptions? = nil) {\n        self.id = vm.id\n        self.name = vm.name\n        self.virtualMachineModel = vm\n        self.library = library\n        self.savedStatesController = VMSavedStatesController(library: library, virtualMachine: vm)\n        \n        #if DEBUG\n        if ProcessInfo.isSwiftUIPreview { self.savedStatesController = .preview }\n        #endif\n\n        virtualMachineModel.reloadMetadata()\n        if virtualMachineModel.metadata.installImageURL != nil && !virtualMachineModel.metadata.installFinished {\n            self.options.bootOnInstallDevice = true\n        }\n\n        if let options {\n            self.options = options\n        }\n\n        /// Ensure configuration is persisted whenever it changes.\n        $virtualMachineModel\n            .dropFirst()\n            .throttle(for: 0.5, scheduler: DispatchQueue.main, latest: true)\n            .sink { updatedModel in\n                do {\n                    try updatedModel.saveMetadata()\n                } catch {\n                    assertionFailure(\"Failed to save configuration: \\(error)\")\n                }\n            }\n            .store(in: &cancellables)\n\n        library.addController(self)\n\n        /// Make sure DFU mode flag is turned off if the app build doesn't allow DFU boot.\n        if !VBMacConfiguration.appBuildAllowsDFUMode {\n            self.options.bootInDFUMode = false\n        }\n    }\n\n    private var instance: VMInstance?\n    \n    private func createInstance() throws -> VMInstance {\n        let newInstance = VMInstance(with: virtualMachineModel, library: library, onVMStop: { [weak self] error in\n            self?.state = .stopped(error)\n        })\n        \n        newInstance.options = options\n        \n        return newInstance\n    }\n\n    public func start() async throws {\n        state = .starting(nil)\n\n        await waitForGuestDiskImageReadyIfNeeded()\n\n        try await updatingState {\n            let newInstance = try createInstance()\n            self.instance = newInstance\n\n            if #available(macOS 14.0, *), let restorePackageURL = options.stateRestorationPackageURL {\n                do {\n                    let package = try VBSavedStatePackage(url: restorePackageURL)\n                    try await newInstance.restoreState(from: package) { vm, package in\n                        try? await updatingState {\n                            state = .restoringState(vm, package)\n                        }\n                    }\n                } catch {\n                    guard !(error is CancellationError) else {\n                        state = .idle\n                        return\n                    }\n                    throw error\n                }\n            } else {\n                try await newInstance.startVM()\n            }\n\n            let vm = try newInstance.virtualMachine\n\n            state = .running(vm)\n            virtualMachineModel.metadata.installFinished = true\n        }\n    }\n\n    /// If the virtual machine supports the guest app and has the toggle to auto-mount the guest image enabled,\n    /// this method waits until the guest disk image is ready before returning.\n    ///\n    /// This is used to wait for the guest disk image to be ready before starting a virtual machine, which may occur\n    /// if the user launches VirtualBuddy then quickly attempts to start up a machine right after installing an app update.\n    ///\n    /// It will also alert the user in case guest disk image generation has failed so that they know there's something wrong/\n    private func waitForGuestDiskImageReadyIfNeeded() async {\n        guard virtualMachineModel.configuration.guestAdditionsEnabled,\n           virtualMachineModel.configuration.systemType.supportsGuestApp\n        else { return }\n\n        let guestDiskState = GuestAdditionsDiskImage.current.state\n\n        logger.info(\"Guest disk image state is \\(guestDiskState, privacy: .public)\")\n\n        switch guestDiskState {\n        case .ready:\n            break\n        case .installing:\n            await waitForGuestDiskImageReady()\n        case .installFailed(let error):\n            runGuestDiskImageErrorAlert(error: error)\n        }\n    }\n\n    private func waitForGuestDiskImageReady() async {\n        state = .starting(\"Preparing guest app disk image\")\n\n        for await state in GuestAdditionsDiskImage.current.$state.values {\n            switch state {\n            case .ready:\n                logger.debug(\"Guest disk image is ready 🚀\")\n                return\n            case .installFailed(let error):\n                logger.error(\"Guest disk image install failed - \\(error, privacy: .public)\")\n                return runGuestDiskImageErrorAlert(error: error)\n            case .installing:\n                logger.debug(\"Guest disk image is installing...\")\n            }\n        }\n    }\n\n    private func runGuestDiskImageErrorAlert(error: Error) {\n        logger.debug(#function)\n\n        let alertSuppressionKey = \"VBGuestDiskImageAlertSuppressed\"\n\n        guard !UserDefaults.standard.bool(forKey: alertSuppressionKey) else {\n            logger.debug(\"Guest disk image error alert suppressed, ignoring error.\")\n            return\n        }\n\n        let alert = NSAlert()\n        alert.messageText = \"Guest App Image Error\"\n        alert.informativeText =\n        \"\"\"\n        An error occurred when VirtualBuddy attempted to generate the disk image for the guest app. Restarting the app might fix it.\n        \n        The virtual machine will boot normally, but the guest app disk image will not be mounted.\n        \n        If the virtual machine already has the guest app installed, it will not be updated to the latest version.\n        \n        \\(error)\n        \"\"\"\n\n        alert.addButton(withTitle: \"Continue\")\n        alert.showsSuppressionButton = true\n\n        alert.runModal()\n\n        if let suppressionButton = alert.suppressionButton,\n           suppressionButton.state == .on\n        {\n            logger.info(\"Guest disk image error alert will be suppressed in the future.\")\n\n            UserDefaults.standard.set(true, forKey: alertSuppressionKey)\n        }\n    }\n\n    public func pause() async throws {\n        try await updatingState {\n            let instance = try ensureInstance()\n\n            try await instance.pause()\n            let vm = try instance.virtualMachine\n\n            state = .paused(vm)\n        }\n\n        unhideCursor()\n    }\n    \n    public func resume() async throws {\n        try await updatingState {\n            let instance = try ensureInstance()\n\n            try await instance.resume()\n            let vm = try instance.virtualMachine\n\n            state = .running(vm)\n        }\n\n        unhideCursor()\n    }\n    \n    public func stop() async throws {\n        try await updatingState {\n            let instance = try ensureInstance()\n\n            try await instance.stop()\n        }\n\n        unhideCursor()\n    }\n    \n    public func forceStop() async throws {\n        try await updatingState {\n            let instance = try ensureInstance()\n\n            try await instance.forceStop()\n\n            state = .stopped(nil)\n        }\n\n        unhideCursor()\n    }\n\n    @available(macOS 14.0, *)\n    public func saveState(snapshotName name: String) async throws {\n        try await updatingState {\n            let instance = try ensureInstance()\n            let vm = try instance.virtualMachine\n\n            do {\n                let package = try await instance.saveState(snapshotName: name) {\n                    state = .savingState(vm)\n                }\n\n                state = .stateSaveCompleted(vm, package)\n            } catch is CancellationError {\n                /// User cancellation is not an error, it may just be ignored here.\n                /// As of the current implementation of `VMInstance.saveState`, the VM won't be paused\n                /// because the only cancellation point is before that happens, but check for pause in here just in\n                /// case that behavior changes in the future.\n                try await resumeIfNeeded()\n            } catch {\n                throw error\n            }\n\n            try await resumeIfNeeded()\n        }\n\n        unhideCursor()\n    }\n\n    private func resumeIfNeeded() async throws {\n        guard !state.isRunning else { return }\n\n        try await Task.sleep(for: .seconds(1.5))\n\n        try await resume()\n    }\n\n    private func updatingState(perform block: () async throws -> Void) async throws {\n        do {\n            try await block()\n        } catch {\n            state = .stopped(error)\n            throw error\n        }\n    }\n\n    private func ensureInstance() throws -> VMInstance {\n        guard let instance = instance else {\n            throw CocoaError(.validationMissingMandatoryProperty)\n        }\n        \n        instance.options = options\n        \n        return instance\n    }\n\n    public func storeScreenshot(with data: Data) {\n        do {\n            try virtualMachineModel.write(data, forMetadataFileNamed: VBVirtualMachine.screenshotFileName)\n            try virtualMachineModel.invalidateThumbnail()            \n        } catch {\n            logger.error(\"Error storing screenshot: \\(error)\")\n        }\n    }\n\n    public func invalidate() {\n        library.removeController(self)\n    }\n\n    deinit {\n        #if DEBUG\n        print(\"\\(name) Bye bye 👋\")\n        #endif\n        library.removeController(self)\n\n        VBMemoryLeakDebugAssertions.vb_objectIsBeingReleased(self)\n    }\n\n}\n\npublic extension VMState {\n\n    static func ==(lhs: VMState, rhs: VMState) -> Bool {\n        switch lhs {\n        case .idle: return rhs.isIdle\n        case .starting: return rhs.isStarting\n        case .running: return rhs.isRunning\n        case .paused: return rhs.isPaused\n        case .stopped: return rhs.isStopped\n        case .savingState: return rhs.isSavingState\n        case .restoringState: return rhs.isRestoringState\n        case .stateSaveCompleted: return rhs.isStateSaveCompleted\n        }\n    }\n\n    var isIdle: Bool {\n        guard case .idle = self else { return false }\n        return true\n    }\n\n    var isStarting: Bool {\n        guard case .starting = self else { return false }\n        return true\n    }\n\n    var isRunning: Bool {\n        guard case .running = self else { return false }\n        return true\n    }\n\n    var isPaused: Bool {\n        guard case .paused = self else { return false }\n        return true\n    }\n\n    var isStopped: Bool {\n        guard case .stopped = self else { return false }\n        return true\n    }\n\n    var isSavingState: Bool {\n        guard case .savingState = self else { return false }\n        return true\n    }\n\n    var isRestoringState: Bool {\n        guard case .restoringState = self else { return false }\n        return true\n    }\n\n    var isStateSaveCompleted: Bool {\n        guard case .stateSaveCompleted = self else { return false }\n        return true\n    }\n\n    var canStart: Bool {\n        switch self {\n        case .idle, .stopped:\n            return true\n        default:\n            return false\n        }\n    }\n\n    var canResume: Bool {\n        switch self {\n        case .paused:\n            return true\n        default:\n            return false\n        }\n    }\n\n    var canPause: Bool {\n        switch self {\n        case .running:\n            return true\n        default:\n            return false\n        }\n    }\n\n}\n\npublic extension VMController {\n    \n    var canStart: Bool { state.canStart }\n\n    var canResume: Bool { state.canResume }\n\n    var canPause: Bool { state.canPause }\n\n}\n\npublic extension VMController {\n    /// Workaround for cursor disappearing due to it being captured\n    /// by Virtualization during state transitions.\n    func unhideCursor() {\n        Task {\n            try? await Task.sleep(nanoseconds: 100_000_000)\n            NSCursor.unhide()\n        }\n    }\n}\n\npublic extension VBMacConfiguration {\n    /// DFU mode option is currently shown in debug builds or when `VBShowDFUModeBootOption` is set in user defaults.\n    /// To enable in release builds: `defaults write codes.rambo.VirtualBuddy VBShowDFUModeBootOption -bool YES`\n    static var appBuildAllowsDFUMode: Bool {\n        #if DEBUG\n        return true\n        #else\n        return UserDefaults.standard.bool(forKey: \"VBShowDFUModeBootOption\")\n        #endif\n    }\n}\n"
  },
  {
    "path": "VirtualCore/Source/Virtualization/VMInstance.swift",
    "content": "//\n//  VMInstance.swift\n//  VirtualCore\n//\n//  Created by Guilherme Rambo on 12/04/22.\n//\n\nimport Cocoa\nimport Foundation\nimport Virtualization\nimport Combine\nimport OSLog\nimport VirtualWormhole\n\n@MainActor\npublic final class VMInstance: NSObject, ObservableObject {\n\n    private let library: VMLibraryController\n\n    private let logger: Logger\n\n    var options = VMSessionOptions.default\n\n    private var _virtualMachine: VZVirtualMachine?\n    \n    var virtualMachine: VZVirtualMachine {\n        get throws {\n            guard let vm = _virtualMachine else {\n                throw CocoaError(.validationMissingMandatoryProperty)\n            }\n            \n            return vm\n        }\n    }\n    \n    let wormhole: WormholeManager = .sharedHost\n    \n    private var isLoadingNVRAM = false\n    \n    var virtualMachineModel: VBVirtualMachine {\n        didSet {\n            precondition(oldValue.id == virtualMachineModel.id, \"Can't change the virtual machine identity after initializing the controller\")\n        }\n    }\n    \n    var onVMStop: (Error?) -> Void = { _ in }\n    \n    init(with vm: VBVirtualMachine, library: VMLibraryController, onVMStop: @escaping (Error?) -> Void) {\n        self.virtualMachineModel = vm\n        self.library = library\n        self.onVMStop = onVMStop\n        self.logger = Logger(subsystem: VirtualCoreConstants.subsystemName, category: \"VMInstance(\\(vm.name))\")\n    }\n    \n    // MARK: Create the Mac Platform Configuration\n\n    private static func loadRestoreImage(from url: URL) async throws -> VZMacOSRestoreImage {\n        try await withCheckedThrowingContinuation { continuation in\n            VZMacOSRestoreImage.load(from: url) { result in\n                continuation.resume(with: result)\n            }\n        }\n    }\n\n    public static func createMacPlatform(for model: VBVirtualMachine, installImageURL: URL?) async throws -> VZMacPlatformConfiguration {\n        let image: VZMacOSRestoreImage?\n\n        if let installImageURL = installImageURL {\n            image = try await loadRestoreImage(from: installImageURL)\n        } else {\n            image = nil\n        }\n\n        let macPlatform = VZMacPlatformConfiguration()\n\n        let hardwareModel = try model.fetchOrGenerateHardwareModel(with: image)\n\n        macPlatform.hardwareModel = hardwareModel\n\n        macPlatform.auxiliaryStorage = try model.fetchOrGenerateAuxiliaryStorage(hardwareModel: hardwareModel)\n\n        macPlatform.machineIdentifier = try model.fetchOrGenerateMachineIdentifier()\n\n        return macPlatform\n    }\n\n    @available(macOS 13.0, *)\n    public static func createGenericPlatform(for model: VBVirtualMachine, installImageURL: URL?) async throws -> VZGenericPlatformConfiguration {\n        let genericPlatform = VZGenericPlatformConfiguration()\n        return genericPlatform\n    }\n\n    // MARK: Create the Virtual Machine Configuration and instantiate the Virtual Machine\n\n    public static func makeConfiguration(for model: VBVirtualMachine, installImageURL: URL? = nil, savedState: VBSavedStatePackage? = nil) async throws -> VZVirtualMachineConfiguration {\n        let helper: VirtualMachineConfigurationHelper\n        let platform: VZPlatformConfiguration\n        let installDevice: [VZStorageDeviceConfiguration]\n        switch model.configuration.systemType {\n        case .mac:\n            helper = MacOSVirtualMachineConfigurationHelper(vm: model, savedState: savedState)\n            platform = try await Self.createMacPlatform(for: model, installImageURL: installImageURL)\n            installDevice = []\n        case .linux:\n            helper = LinuxVirtualMachineConfigurationHelper(vm: model)\n            platform = try await Self.createGenericPlatform(for: model, installImageURL: nil)\n            if let installImageURL {\n                installDevice = [try helper.createInstallDevice(installImageURL: installImageURL)]\n            } else {\n                installDevice = []\n            }\n        }\n        let c = VZVirtualMachineConfiguration()\n\n        c.platform = platform\n        c.bootLoader = try helper.createBootLoader()\n        c.cpuCount = model.configuration.hardware.cpuCount\n        c.memorySize = model.configuration.hardware.memorySize\n        c.graphicsDevices = helper.createGraphicsDevices()\n        c.networkDevices = try model.configuration.vzNetworkDevices\n        c.pointingDevices = try model.configuration.vzPointingDevices\n        c.keyboards = [helper.createKeyboardConfiguration()]\n        c.entropyDevices = helper.createEntropyDevices()\n        c.audioDevices = model.configuration.vzAudioDevices\n        c.directorySharingDevices = try model.configuration.vzSharedFoldersFileSystemDevices\n        if let spiceAgent = helper.createSpiceAgentConsoleDeviceConfiguration() {\n            c.consoleDevices = [spiceAgent]\n        }\n        if #available(macOS 15.0, *) {\n            c.usbControllers = helper.createUSBControllers()\n        }\n\n        let bootDevice = try await helper.createBootBlockDevice()\n        let additionalBlockDevices = try await helper.createAdditionalBlockDevices()\n\n        c.storageDevices = installDevice + [bootDevice] + additionalBlockDevices\n        \n        return c\n    }\n    \n    private func createVirtualMachine(savedState: VBSavedStatePackage?) async throws {\n        logger.debug(#function)\n\n        let installImage: URL?\n        if options.bootOnInstallDevice {\n            installImage = virtualMachineModel.metadata.installImageURL\n        } else {\n            installImage = nil\n        }\n        let config = try await Self.makeConfiguration(for: virtualMachineModel, installImageURL: installImage, savedState: savedState) // add install iso here for linux (hack)\n\n        await setupWormhole(for: config)\n\n        do {\n            try config.validate()\n\n            logger.info(\"Configuration validated\")\n        } catch {\n            logger.fault(\"Invalid configuration: \\(String(describing: error))\")\n            \n            throw Failure(\"Failed to validate configuration: \\(String(describing: error))\")\n        }\n\n        _virtualMachine = VZVirtualMachine(configuration: config)\n    }\n\n    private func setupWormhole(for config: VZVirtualMachineConfiguration) async {\n        guard virtualMachineModel.configuration.systemType == .mac else { return }\n\n        wormhole.activate()\n\n        let guestPort = VZVirtioConsoleDeviceSerialPortConfiguration()\n\n        let inputPipe = Pipe()\n        let outputPipe = Pipe()\n\n        let inputHandle = inputPipe.fileHandleForWriting\n        let outputHandle = outputPipe.fileHandleForReading\n\n        guestPort.attachment = VZFileHandleSerialPortAttachment(\n            fileHandleForReading: outputHandle,\n            fileHandleForWriting: inputHandle\n        )\n\n        config.serialPorts = [guestPort]\n\n        await wormhole.register(\n            input: inputPipe.fileHandleForReading,\n            output: outputPipe.fileHandleForWriting,\n            for: virtualMachineModel.wormholeID\n        )\n\n        streamGuestNotifications()\n        streamGuestDesktopPictureMessages()\n    }\n\n    private lazy var guestIOTasks = [Task<Void, Never>]()\n\n    public func streamGuestNotifications() {\n        logger.debug(#function)\n        \n        let notificationNames: Set<String> = [\n            \"com.apple.shieldWindowRaised\",\n            \"com.apple.shieldWindowLowered\"\n        ]\n\n        let task = Task {\n            do {\n                for await notification in try await wormhole.darwinNotifications(matching: notificationNames, from: virtualMachineModel.wormholeID) {\n                    if notification == \"com.apple.shieldWindowRaised\" {\n                        logger.debug(\"🔒 Guest locked\")\n                    } else if notification == \"com.apple.shieldWindowLowered\" {\n                        logger.debug(\"🔓 Guest unlocked\")\n                    }\n                }\n            } catch {\n                logger.error(\"Error subscribing to Darwin notifications: \\(error, privacy: .public)\")\n            }\n        }\n        guestIOTasks.append(task)\n    }\n\n    public func streamGuestDesktopPictureMessages() {\n        logger.debug(#function)\n\n        let task = Task {\n            do {\n                for await message in try await wormhole.desktopPictureMessages(from: virtualMachineModel.wormholeID) {\n                    do {\n                        let fileURL = virtualMachineModel.metadataFileURL(VBVirtualMachine.thumbnailFileName)\n\n                        try message.content.write(to: fileURL, options: .atomic)\n\n                        if let image = NSImage(data: message.content),\n                           let blurHash = image.blurHash(numberOfComponents: (Int.vbBlurHashSize, Int.vbBlurHashSize))\n                        {\n                            virtualMachineModel.metadata.backgroundHash = BlurHashToken(value: blurHash, size: .vbBlurHashSize)\n                        }\n\n                        try virtualMachineModel.saveMetadata()\n                    } catch {\n                        logger.error(\"Error handling desktop picture message: \\(error, privacy: .public)\")\n                    }\n                }\n            } catch {\n                logger.error(\"Error subscribing to desktop picture messages: \\(error, privacy: .public)\")\n            }\n        }\n\n        guestIOTasks.append(task)\n    }\n\n    func startVM() async throws {\n        try await bootstrap()\n\n        let vm = try ensureVM()\n\n        try await vm.start(options: startOptions)\n\n        #if DEBUG\n        VBDebugUtil.debugVirtualMachine(afterStart: vm)\n        #endif\n    }\n\n    private func bootstrap(savedState: VBSavedStatePackage? = nil) async throws {\n        try await createVirtualMachine(savedState: savedState)\n\n        let vm = try ensureVM()\n\n        vm.delegate = self\n\n        library.registerBootedVM(self)\n\n        #if DEBUG\n        VBDebugUtil.debugVirtualMachine(beforeStart: vm)\n        #endif\n    }\n\n    @available(macOS 13, *)\n    private var startOptions: VZVirtualMachineStartOptions {\n        switch virtualMachineModel.configuration.systemType {\n        case .mac:\n            return VZMacOSVirtualMachineStartOptions(options: options)\n        case .linux:\n            return VZVirtualMachineStartOptions()\n        }\n    }\n    \n    func pause() async throws {\n        logger.debug(#function)\n\n        let vm = try ensureVM()\n        \n        try await vm.pause()\n    }\n    \n    func resume() async throws {\n        logger.debug(#function)\n\n        let vm = try ensureVM()\n        \n        try await vm.resume()\n    }\n    \n    func stop() async throws {\n        logger.debug(#function)\n\n        let vm = try ensureVM()\n        \n        try vm.requestStop()\n    }\n    \n    func forceStop() async throws {\n        logger.debug(#function)\n\n        let vm = try ensureVM()\n        \n        try await vm.stop()\n\n        library.unregisterBootedVM(self)\n    }\n\n    @available(macOS 14.0, *)\n    @discardableResult\n    func saveState(snapshotName name: String, onStart: () -> ()) async throws -> VBSavedStatePackage {\n        logger.debug(#function)\n\n        let vm = try ensureVM()\n\n        guard confirmSaveStateIfNotOnAPFSVolume() else {\n            logger.info(\"State save denied by user.\")\n            throw CancellationError()\n        }\n\n        /// Callback so that caller may update UI to indicate that saving has actually started,\n        /// but only after the user has performed pre-save confirmation steps.\n        onStart()\n\n        logger.debug(\"Pausing to save state\")\n\n        try await pause()\n\n        logger.debug(\"VM paused, requesting state save\")\n\n        let package = try virtualMachineModel.createSavedStatePackage(in: library, snapshotName: name)\n\n        logger.debug(\"VM state package will be written to \\(package.url.path)\")\n\n        do {\n            try await package.createStorageDeviceClones(model: virtualMachineModel)\n\n            try await vm.saveMachineStateTo(url: package.dataFileURL)\n\n            logger.log(\"VM state saved to \\(package.dataFileURL.path)\")\n\n            return package\n        } catch {\n            try? FileManager.default.removeItem(at: package.url)\n\n            logger.error(\"VM state save failed: \\(error, privacy: .public)\")\n\n            throw error\n        }\n    }\n\n    /// Asks user for confirmation before saving state if the volume where the VirtualBuddy library\n    /// resides is not an APFS volume, meaning that cloning is not available.\n    @available(macOS 14.0, *)\n    private func confirmSaveStateIfNotOnAPFSVolume() -> Bool {\n        guard !library.isInAPFSVolume else { return true }\n\n        let suppressionKey = \"SuppressConfirmSaveStateNonAPFSVolumeAlert\"\n        guard !UserDefaults.standard.bool(forKey: suppressionKey) else { return true }\n\n        let alert = NSAlert()\n        alert.messageText = \"Disk Space Warning\"\n        alert.informativeText = \"\"\"\n        It seems like your virtual machine data can’t be cloned because your library isn’t in an APFS volume.\n        \n        Creating this snapshot might take up several gigabytes of storage space.\n        \n        Would you like to continue?\n        \"\"\"\n        alert.addButton(withTitle: \"Create Snapshot\")\n        alert.addButton(withTitle: \"Cancel\")\n        alert.showsSuppressionButton = true\n\n        guard alert.runModal() == .alertFirstButtonReturn else { return false }\n\n        if alert.suppressionButton?.state == .on {\n            UserDefaults.standard.set(true, forKey: suppressionKey)\n        }\n\n        return true\n    }\n\n    @available(macOS 14.0, *)\n    func restoreState(from package: VBSavedStatePackage, updateHandler: (_ vm: VZVirtualMachine, _ package: VBSavedStatePackage) async -> Void) async throws {\n        logger.debug(\"Restore state requested with package \\(package.url.path)\")\n\n        try await runSavedStateMigrationIfNeeded(for: package)\n\n        try package.validate(for: virtualMachineModel)\n\n        if _virtualMachine == nil {\n            logger.debug(\"Bootstrapping VM for state restoration\")\n\n            try await bootstrap(savedState: package)\n        }\n\n        let vm = try ensureVM()\n\n        await updateHandler(vm, package)\n\n        logger.debug(\"Restoring state from \\(package.dataFileURL.path)\")\n\n        do {\n            try await vm.restoreMachineStateFrom(url: package.dataFileURL)\n\n            logger.log(\"Successfully restored state from \\(package.dataFileURL.path), resuming VM\")\n\n            try await resume()\n\n            #if DEBUG\n            VBDebugUtil.debugVirtualMachine(afterStart: vm)\n            #endif\n        } catch {\n            logger.error(\"VM state restoration failed: \\(error, privacy: .public). State file: \\(package.dataFileURL.path)\")\n\n            throw error\n        }\n    }\n\n    @available(macOS 14.0, *)\n    private func runSavedStateMigrationIfNeeded(for package: VBSavedStatePackage) async throws {\n        guard package.needsStorageCloneMigration else { return }\n\n        guard confirmSavedStateMigration() else {\n            throw CancellationError()\n        }\n\n        guard confirmSaveStateIfNotOnAPFSVolume() else {\n            throw CancellationError()\n        }\n\n        try await package.createStorageDeviceClones(model: virtualMachineModel)\n    }\n\n    @available(macOS 14.0, *)\n    private func confirmSavedStateMigration() -> Bool {\n        let suppressionKey = \"SuppressConfirmSavedStateMigrationAlert\"\n\n        guard !UserDefaults.standard.bool(forKey: suppressionKey) else { return true }\n\n        let alert = NSAlert()\n        alert.messageText = \"Migration Required\"\n        alert.informativeText = \"\"\"\n        The virtual machine’s state was saved in an older version of VirtualBuddy that didn’t create clones of the storage devices. \\\n        This could lead to data corruption over time.\n\n        To use this saved state, we need to migrate it to include storage device clones.\n        \"\"\"\n        alert.addButton(withTitle: \"Migrate and Restore\")\n        alert.addButton(withTitle: \"Cancel\")\n        alert.showsSuppressionButton = true\n\n        guard alert.runModal() == .alertFirstButtonReturn else { return false }\n\n        if alert.suppressionButton?.state == .on {\n            UserDefaults.standard.set(true, forKey: suppressionKey)\n        }\n\n        return true\n    }\n\n    private func ensureVM() throws -> VZVirtualMachine {\n        guard let vm = _virtualMachine else {\n            let e = Failure(\"The virtual machine instance is not available.\")\n\n            DispatchQueue.main.async {\n                self.onVMStop(e)\n            }\n            \n            throw e\n        }\n        \n        return vm\n    }\n    \n}\n\n// MARK: - VZVirtualMachineDelegate\n\nextension VMInstance: VZVirtualMachineDelegate {\n    \n    public nonisolated func virtualMachine(_ virtualMachine: VZVirtualMachine, didStopWithError error: Error) {\n        MainActor.assumeIsolated {\n            handleGuestStopped(with: error)\n        }\n    }\n\n    public nonisolated func guestDidStop(_ virtualMachine: VZVirtualMachine) {\n        MainActor.assumeIsolated {\n            handleGuestStopped(with: nil)\n        }\n    }\n    \n    public nonisolated func virtualMachine(_ virtualMachine: VZVirtualMachine, networkDevice: VZNetworkDevice, attachmentWasDisconnectedWithError error: Error) {\n\n    }\n\n    private func handleGuestStopped(with error: Error?) {\n        guestIOTasks.forEach { $0.cancel() }\n        guestIOTasks.removeAll()\n\n        if let error {\n            logger.error(\"Guest stopped with error: \\(String(describing: error), privacy: .public)\")\n        } else {\n            logger.debug(\"Guest stopped\")\n        }\n\n        DispatchQueue.main.async { [self] in\n            library.unregisterBootedVM(self)\n\n            Task {\n                await wormhole.unregister(virtualMachineModel.wormholeID)\n            }\n\n            onVMStop(error)\n        }\n    }\n    \n}\n\nextension NSApplication {\n    \n    func entitlementValue<V>(for entitlement: String) -> V? {\n        guard let task = SecTaskCreateFromSelf(nil) else {\n            assertionFailure(\"SecTaskCreateFromSelf returned nil\")\n            return nil\n        }\n        \n        return SecTaskCopyValueForEntitlement(task, entitlement as CFString, nil) as? V\n    }\n    \n    func hasEntitlement(_ entitlement: String) -> Bool {\n        entitlementValue(for: entitlement) == true\n    }\n    \n}\n\nprivate extension VBVirtualMachine {\n    /// ``VBVirtualMachine/id`` uses the VM's filesystem URL,\n    /// but that looks ugly in logs and whatnot, so this returns a cleaned up version.\n    var wormholeID: WHPeerID {\n        let cleanID = URL(fileURLWithPath: id)\n            .deletingPathExtension()\n            .lastPathComponent\n        return cleanID.removingPercentEncoding ?? cleanID\n    }\n}\n\nextension VZMacOSVirtualMachineStartOptions {\n    convenience init(options: VMSessionOptions) {\n        self.init()\n\n        startUpFromMacOSRecovery = options.bootInRecoveryMode\n\n        if options.bootInDFUMode,\n           VBMacConfiguration.appBuildAllowsDFUMode,\n           self.responds(to: NSSelectorFromString(\"_setForceDFU:\"))\n        {\n            _forceDFU = true\n            startUpFromMacOSRecovery = false\n        }\n    }\n}\n"
  },
  {
    "path": "VirtualCore/Source/Virtualization/VMLibraryController.swift",
    "content": "//\n//  VMLibraryController.swift\n//  VirtualCore\n//\n//  Created by Guilherme Rambo on 10/04/22.\n//\n\nimport SwiftUI\nimport Combine\nimport OSLog\nimport BuddyFoundation\n\n@MainActor\npublic final class VMLibraryController: ObservableObject {\n\n    private let logger = Logger(for: VMLibraryController.self)\n\n    public enum State: Identifiable {\n        public enum ID: Int {\n            case loading\n            case loaded\n            case empty\n            case volumeNotMounted\n            case directoryMissing\n        }\n\n        public var id: ID {\n            switch self {\n            case .loading: .loading\n            case .loaded: .loaded\n            case .empty: .empty\n            case .volumeNotMounted: .volumeNotMounted\n            case .directoryMissing: .directoryMissing\n            }\n        }\n\n        case loading\n        case loaded([VBVirtualMachine])\n        case empty\n        case volumeNotMounted\n        case directoryMissing\n\n        var isVolumeNotMounted: Bool { id == .volumeNotMounted }\n    }\n    \n    @Published public private(set) var state = State.loading {\n        didSet {\n            if case .loaded(let machines) = state {\n                self.virtualMachines = machines\n            } else {\n                self.virtualMachines = []\n            }\n        }\n    }\n    \n    @Published public private(set) var virtualMachines: [VBVirtualMachine] = []\n\n    /// Identifiers for all VMs that are currently in a \"booted\" state (starting, booted, or paused).\n    @Published public private(set) var bootedMachineIdentifiers = Set<VBVirtualMachine.ID>()\n    private let bootedInstances = NSMapTable<NSString, VMInstance>(keyOptions: [.objectPersonality, .strongMemory], valueOptions: [.objectPersonality, .weakMemory])\n\n    /// Populated when ``bootedMachineIdentifiers`` is not empty, invalidated when the last booted machine identifier is unregistered via ``unregisterBootedVM(identifier:)``.\n    private var preventTerminationAssertion: PreventTerminationAssertion?\n\n    /// Observes notifications about volume mount/unmount that are used when library resides in a removable volume.\n    private var volumeNotificationsTask: Task<Void, Never>?\n\n    /// Set when the library is loaded.\n    private var isLibraryInRemovableVolume = false\n\n    let settingsContainer: VBSettingsContainer\n\n    private let filePresenter: DirectoryObserver\n    private let updateSignal = PassthroughSubject<URL, Never>()\n\n    private static let observedFileExtensions: Set<String> = [\n        VBVirtualMachine.bundleExtension,\n        \"plist\",\n        \"heic\"\n    ]\n\n    public init(settingsContainer: VBSettingsContainer = .current) {\n        self.settingsContainer = settingsContainer\n        self.settings = settingsContainer.settings\n        self.libraryURL = settingsContainer.settings.libraryURL\n        self.filePresenter = DirectoryObserver(\n            presentedItemURL: settingsContainer.settings.libraryURL,\n            fileExtensions: Self.observedFileExtensions,\n            label: \"Library\",\n            signal: updateSignal\n        )\n\n        loadMachines()\n        bind()\n    }\n\n    private var settings: VBSettings {\n        didSet {\n            self.libraryURL = settings.libraryURL\n        }\n    }\n\n    @Published\n    public private(set) var libraryURL: URL {\n        didSet {\n            guard oldValue != libraryURL else { return }\n            stopObservingRemovableVolumeNotifications()\n            loadMachines()\n        }\n    }\n\n    private lazy var cancellables = Set<AnyCancellable>()\n    \n    private lazy var fileManager = FileManager()\n\n    private var hasLoadedMachinesOnce = false\n\n    private func bind() {\n        settingsContainer.$settings.sink { [weak self] newSettings in\n            self?.settings = newSettings\n        }\n        .store(in: &cancellables)\n\n        updateSignal\n            .throttle(for: 0.1, scheduler: DispatchQueue.main, latest: true)\n            .sink { [weak self] url in\n                guard let self else { return }\n\n                /// Ignore file change notifications in the guest additions or downloads directories.\n                guard !url.path.contains(GuestAdditionsDiskImage.imagesRootURL.path) else {\n                    return\n                }\n                guard !url.path.contains(VBSettings.current.downloadsDirectoryURL.path) else {\n                    return\n                }\n\n                guard let bundleURL = url.virtualMachineBundleParent else {\n                    self.logger.fault(\"Failed to determine VM bundle URL for changed file \\(url.lastPathComponent)\")\n                    return\n                }\n\n                self.handleBundleChanged(at: bundleURL)\n            }\n            .store(in: &cancellables)\n    }\n\n    private func handleBundleChanged(at bundleURL: URL) {\n        logger.debug(\"Bundle changed: \\(bundleURL.lastPathComponent)\")\n\n        loadMachines()\n    }\n\n    public func loadMachines(createLibrary: Bool = false) {\n        #if DEBUG\n        guard !simulateState() else { return }\n        #endif\n\n        let path = libraryURL.path\n\n        logger.debug(\"Loading machines from \\(path.quoted)\")\n\n        if createLibrary, !libraryURL.isReadableDirectory {\n            do {\n                try fileManager.createDirectory(at: libraryURL, withIntermediateDirectories: true)\n            } catch {\n                NSApp.presentError(error)\n                return\n            }\n        }\n\n        isLibraryInRemovableVolume = Self.isLibraryURLInRemovableVolume(libraryURL)\n\n        observeRemovableVolumeNotifications()\n\n        guard libraryURL.isReadableDirectory else {\n            if isLibraryInRemovableVolume {\n                logger.warning(\"External volume library directory not found at \\(path.quoted)\")\n\n                state = .volumeNotMounted\n            } else {\n                logger.warning(\"Library directory not found at \\(path.quoted)\")\n\n                state = .directoryMissing\n            }\n            return\n        }\n\n        filePresenter.presentedItemURL = libraryURL\n\n        guard let enumerator = fileManager.enumerator(at: libraryURL, includingPropertiesForKeys: [.creationDateKey], options: [.skipsHiddenFiles, .skipsPackageDescendants, .skipsSubdirectoryDescendants], errorHandler: nil) else {\n            logger.fault(\"Failed to create directory enumerator for library at \\(path.quoted)\")\n            state = .directoryMissing\n            return\n        }\n        \n        var machines = [VBVirtualMachine]()\n\n        while let url = enumerator.nextObject() as? URL {\n            guard url.pathExtension == VBVirtualMachine.bundleExtension else { continue }\n            \n            do {\n                let machine = try VBVirtualMachine(bundleURL: url, createIfNeeded: false)\n\n                if let index = machines.firstIndex(where: { $0.id == machine.id }) {\n                    machines[index] = machine\n                } else {\n                    machines.append(machine)\n                }\n            } catch is VBVirtualMachine.BundleDirectoryMissingError {\n                /// This error can occur when the bundle is deleted in Finder.\n                logger.debug(\"Ignoring BundleDirectoryMissingError for \\(url.lastPathComponent)\")\n            } catch {\n                assertionFailure(\"Failed to construct VM model: \\(error)\")\n            }\n        }\n\n        let sortedMachines = machines.sorted(by: { $0.bundleURL.creationDate > $1.bundleURL.creationDate })\n\n        self.state = sortedMachines.isEmpty ? .empty : .loaded(sortedMachines)\n\n        if !hasLoadedMachinesOnce {\n            hasLoadedMachinesOnce = true\n\n            migrateBackgroundHashesForLegacyThumbnails()\n        }\n    }\n\n    public func reload(animated: Bool = true) {\n        if animated {\n            withAnimation(.spring()) {\n                loadMachines()\n            }\n        } else {\n            loadMachines()\n        }\n    }\n\n    public func validateNewName(_ name: String, for vm: VBVirtualMachine) throws {\n        /// No need to validate if name is not changed.\n        guard name != vm.name else { return }\n\n        try urlForRenaming(vm, to: name)\n    }\n\n    // MARK: - VM Controller References\n\n    private final class Coordinator {\n        private let lock = NSRecursiveLock()\n\n        private var _activeVMControllers = [VBVirtualMachine.ID: WeakReference<VMController>]()\n\n        /// References to all active `VMController` instances by VM identifier.\n        /// May hold references to invalidated controllers because this does not hold a strong reference to them.\n        var activeVMControllers: [VBVirtualMachine.ID: WeakReference<VMController>] {\n            get { lock.withLock { _activeVMControllers } }\n            set { lock.withLock { _activeVMControllers = newValue } }\n        }\n\n        func activeController(for virtualMachineID: VBVirtualMachine.ID) -> VMController? {\n            activeVMControllers[virtualMachineID]?.object\n        }\n\n        /// Called when a new `VMController` is initialized so that we can reference it\n        /// outside the scope of the view hierarchy (for automation).\n        func addController(_ controller: VMController) {\n            activeVMControllers[controller.id] = WeakReference(controller)\n        }\n\n        /// Called when a `VMController` is dying so that we can cleanup our reference to it.\n        func removeController(_ controller: VMController) {\n            activeVMControllers[controller.id] = nil\n        }\n    }\n\n    nonisolated(unsafe) private let coordinator = Coordinator()\n\n    public nonisolated var activeVMControllers: [WeakReference<VMController>] { Array(coordinator.activeVMControllers.values) }\n\n    public nonisolated func activeController(for virtualMachineID: VBVirtualMachine.ID) -> VMController? {\n        coordinator.activeVMControllers[virtualMachineID]?.object\n    }\n\n    /// Called when a new `VMController` is initialized so that we can reference it\n    /// outside the scope of the view hierarchy (for automation).\n    nonisolated func addController(_ controller: VMController) {\n        coordinator.activeVMControllers[controller.id] = WeakReference(controller)\n    }\n\n    /// Called when a `VMController` is dying so that we can cleanup our reference to it.\n    nonisolated func removeController(_ controller: VMController) {\n        coordinator.activeVMControllers[controller.id] = nil\n    }\n\n    // MARK: - Migration\n\n    private var alwaysAttemptLegacyThumbnailMigration: Bool {\n        #if DEBUG\n        UserDefaults.standard.bool(forKey: \"VBAlwaysAttemptLegacyThumbnailMigration\")\n        #else\n        false\n        #endif\n    }\n\n    private static let migratedLegacyThumbnailBackgroundHashesDefaultsKey = \"migratedLegacyThumbnailBackgroundHashes_2\"\n    private var migratedLegacyThumbnailBackgroundHashes: Bool {\n        get {\n            guard !alwaysAttemptLegacyThumbnailMigration else { return false }\n\n            return UserDefaults.standard.bool(forKey: Self.migratedLegacyThumbnailBackgroundHashesDefaultsKey)\n        }\n        set {\n            guard !alwaysAttemptLegacyThumbnailMigration else { return }\n\n            UserDefaults.standard.set(newValue, forKey: Self.migratedLegacyThumbnailBackgroundHashesDefaultsKey)\n        }\n    }\n\n    private func migrateBackgroundHashesForLegacyThumbnails() {\n        guard !migratedLegacyThumbnailBackgroundHashes else { return }\n\n        let machines = self.virtualMachines\n\n        /// Skip setting migration flag for empty machines in case user library has not been mounted yet.\n        guard !machines.isEmpty else { return }\n\n        defer {\n            migratedLegacyThumbnailBackgroundHashes = true\n        }\n\n        logger.debug(\"Generating missing background hashes for legacy thumbnails...\")\n\n        Task {\n            let needingMigration = machines.filter { $0.configuration.systemType == .mac && $0.metadata.backgroundHash == .virtualBuddyBackground }\n            guard !needingMigration.isEmpty else {\n                logger.debug(\"Found no machines needing background hash migration.\")\n                return\n            }\n\n            logger.debug(\"Found machines needing background hash migration: \\(needingMigration.map(\\.name).formatted(.list(type: .and)))\")\n\n            for var machine in needingMigration {\n                do {\n                    guard let thumbnail = machine.thumbnailImage() else {\n                        logger.debug(\"Ignoring \\(machine.name) for background hash migration because it doesn't have a thumbnail.\")\n                        continue\n                    }\n\n                    if #available(macOS 15.0, *) {\n                        let isDRMProtectedBug = await thumbnail.detectDRMProtectedVideoBug()\n\n                        guard !isDRMProtectedBug else {\n                            logger.notice(\"Invalidating thumbnail for \\(machine.name): detected \\\"DRM Protected Video\\\" bug in its thumbnail.\")\n                            try machine.invalidateThumbnail()\n                            try machine.invalidateScreenshot()\n                            continue\n                        }\n                    }\n\n                    let hash = try thumbnail.blurHash(numberOfComponents: (.vbBlurHashSize, .vbBlurHashSize))\n                        .require(\"Background hash generation failed.\")\n\n                    machine.metadata.backgroundHash = BlurHashToken(value: hash)\n                    try await MainActor.run { try machine.saveMetadata() }\n\n                    logger.info(\"Migrated background hash for \\(machine.name)\")\n                } catch {\n                    logger.warning(\"Error migrating background hash for \\(machine.name) - \\(error, privacy: .public)\")\n                }\n            }\n        }\n    }\n\n}\n\n// MARK: - Queries\n\npublic extension VMLibraryController {\n    func virtualMachines(matching predicate: (VBVirtualMachine) -> Bool) -> [VBVirtualMachine] {\n        virtualMachines.filter(predicate)\n    }\n\n    func virtualMachine(named name: String) -> VBVirtualMachine? {\n        virtualMachines(matching: { $0.name.caseInsensitiveCompare(name) == .orderedSame }).first\n    }\n}\n\n// MARK: - Management Actions\n\npublic extension VMLibraryController {\n\n    @discardableResult\n    func duplicate(_ vm: VBVirtualMachine) throws -> VBVirtualMachine {\n        let newName = \"Copy of \" + vm.name\n\n        let copyURL = try urlForRenaming(vm, to: newName)\n\n        try fileManager.copyItem(at: vm.bundleURL, to: copyURL)\n\n        var newVM = try VBVirtualMachine(bundleURL: copyURL)\n\n        newVM.bundleURL.creationDate = .now\n        newVM.uuid = UUID()\n\n        try newVM.saveMetadata()\n\n        reload()\n\n        return newVM\n    }\n\n    func moveToTrash(_ vm: VBVirtualMachine) async throws {\n        try await NSWorkspace.shared.recycle([vm.bundleURL])\n\n        reload()\n    }\n\n    func rename(_ vm: VBVirtualMachine, to newName: String) throws {\n        let newURL = try urlForRenaming(vm, to: newName)\n\n        try fileManager.moveItem(at: vm.bundleURL, to: newURL)\n\n        reload(animated: false)\n    }\n\n    @discardableResult\n    func urlForRenaming(_ vm: VBVirtualMachine, to name: String) throws -> URL {\n        guard name.count >= 3 else {\n            throw Failure(\"Name must be at least 3 characters long.\")\n        }\n\n        let newURL = vm\n            .bundleURL\n            .deletingLastPathComponent()\n            .appendingPathComponent(name)\n            .appendingPathExtension(VBVirtualMachine.bundleExtension)\n\n        guard !fileManager.fileExists(atPath: newURL.path) else {\n            throw Failure(\"Another virtual machine is already using this name, please choose another one.\")\n        }\n\n        return newURL\n    }\n    \n}\n\nextension NSWorkspace: @retroactive @unchecked Sendable { }\n\n// MARK: - Booted VM Tracking\n\npublic extension VMLibraryController {\n    /// Adds virtual machine to the list of booted machines.\n    func registerBootedVM(_ instance: VMInstance) {\n        let id = instance.virtualMachineModel.id\n\n        logger.debug(\"Registering booted VM \\(id.shortID, privacy: .public)\")\n\n        bootedMachineIdentifiers.insert(id)\n        bootedInstances.setObject(instance, forKey: id as NSString)\n\n        startPreventingAppTerminationIfNeeded()\n    }\n\n    /// Removes virtual machine from the list of booted machines.\n    func unregisterBootedVM(_ instance: VMInstance) {\n        let id = instance.virtualMachineModel.id\n        logger.debug(\"Unregistering booted VM \\(id.shortID, privacy: .public)\")\n\n        bootedMachineIdentifiers.remove(id)\n        bootedInstances.removeObject(forKey: id as NSString)\n\n        stopPreventingAppTerminationIfNeeded()\n    }\n\n    func shutdownAll() {\n        logger.debug(#function)\n\n        for instance in bootedInstances.dictionaryRepresentation().values {\n            let id = instance.virtualMachineModel.id\n            Task {\n                do {\n                    logger.debug(\"Requesting stop for \\(id.shortID, privacy: .public)\")\n                    \n                    try await instance.stop()\n                } catch {\n                    logger.error(\"Error requesting stop for \\(id.shortID, privacy: .public) - \\(error, privacy: .public)\")\n                }\n            }\n        }\n    }\n}\n\n// MARK: - App Termination Assertion\n\nprivate extension VMLibraryController {\n    func startPreventingAppTerminationIfNeeded() {\n        guard !bootedMachineIdentifiers.isEmpty else { return }\n        guard preventTerminationAssertion == nil || preventTerminationAssertion?.isValid == false else { return }\n\n        logger.notice(\"Start preventing app termination\")\n\n        preventTerminationAssertion = NSApp.preventTermination(reason: \"virtual machines are currently running\", shouldTerminate: { [weak self] _ in\n            self?.handleAppTerminationAttempt() ?? .terminateNow\n        })\n    }\n\n    func stopPreventingAppTerminationIfNeeded() {\n        guard bootedMachineIdentifiers.isEmpty else { return }\n        guard preventTerminationAssertion != nil else { return }\n\n        logger.notice(\"Stop preventing app termination\")\n\n        preventTerminationAssertion?.invalidate()\n        preventTerminationAssertion = nil\n    }\n\n    func handleAppTerminationAttempt() -> NSApplication.TerminateReply {\n        let alert = NSAlert()\n        alert.messageText = \"Quit VirtualBuddy?\"\n        alert.informativeText = \"VirtualBuddy is currently running virtual machines. Quitting the app without shutting them down first can result in data loss.\"\n\n        let button = alert.addButton(withTitle: \"Quit Now\")\n        button.hasDestructiveAction = true\n\n        let button2 = alert.addButton(withTitle: \"Shutdown\")\n        button2.keyEquivalent = \"\\r\"\n\n        alert.addButton(withTitle: \"Cancel\")\n\n        let response = alert.runModal()\n\n        switch response {\n        case .alertFirstButtonReturn:\n            return .terminateNow\n        case .alertSecondButtonReturn:\n            defer { shutdownAll() }\n            \n            return .terminateLater\n        default:\n            return .terminateCancel\n        }\n    }\n}\n\n// MARK: - Removable Volume Detection\n\n/// External volume detection\nextension VMLibraryController {\n    /// `true` if the specified file URL lives in a removable volume that could be unmounted when the app attempts to access it.\n    static func isLibraryURLInRemovableVolume(_ url: URL) -> Bool {\n        if let isInRemovableVolume = url.isInRemovableVolume {\n            isInRemovableVolume\n        } else if url.path.hasPrefix(\"/Volumes\") {\n            /// Assume removable volume when path starts with `/Volumes`.\n            /// This is technically wrong as an external volume can be mounted anywhere and the built-in data\n            /// volume is also located in `/Volumes`, but that one will always be mounted when this is used.\n            /// It's fine to do here because this is only meant to be used for reporting things in the UI.\n            true\n        } else {\n            /// Settings stores the removable volume state of the library directory when the user customizes it,\n            /// use that fact as the final response if above checks fail.\n            /// This is needed because we could be checking a URL for a directory that doesn't currently exist,\n            /// and the mount could be somewhere other than `/Volumes`, so we have to go with what we knew before.\n            VBSettings.current.isLibraryInRemovableVolume\n        }\n    }\n}\n\nextension URL {\n    /// Whether the file resides in a removable volume, or `nil` if it can't be determined.\n    var isInRemovableVolume: Bool? {\n        guard let volumeURL = try? resourceValues(forKeys: [.volumeURLKey]).volume else { return nil }\n        return (try? volumeURL.resourceValues(forKeys: [.volumeIsRemovableKey]))?.volumeIsRemovable\n    }\n}\n\nprivate extension VMLibraryController {\n    func observeRemovableVolumeNotifications() {\n        /// Only observe when library lives in a removable volume.\n        guard isLibraryInRemovableVolume else { return }\n\n        guard volumeNotificationsTask == nil || volumeNotificationsTask?.isCancelled == true else { return }\n\n        logger.debug(#function)\n\n        volumeNotificationsTask = Task.detached(priority: .background) { [weak self] in\n            await withTaskGroup { group in\n                group.addTask {\n                    for await notification in NSWorkspace.shared.notificationCenter.notifications(named: NSWorkspace.didMountNotification) {\n                        guard !Task.isCancelled else { return }\n                        await self?.handleVolumeNotification(notification)\n                    }\n                }\n\n                group.addTask {\n                    for await notification in NSWorkspace.shared.notificationCenter.notifications(named: NSWorkspace.didUnmountNotification) {\n                        guard !Task.isCancelled else { return }\n                        await self?.handleVolumeNotification(notification)\n                    }\n                }\n            }\n        }\n    }\n\n    func stopObservingRemovableVolumeNotifications() {\n        logger.debug(#function)\n\n        volumeNotificationsTask?.cancel()\n        volumeNotificationsTask = nil\n    }\n\n    func handleVolumeNotification(_ notification: Notification) {\n        let isMount = notification.name == NSWorkspace.didMountNotification\n\n        if isMount {\n            /// Don't care about mount notifications unless we're currently waiting for the volume to mount.\n            guard state.isVolumeNotMounted else { return }\n        }\n\n        guard let volumeURL = notification.userInfo?[NSWorkspace.volumeURLUserInfoKey] as? URL else { return }\n\n        /// Cheap way to check if mounted volume is our library volume.\n        guard libraryURL.path.hasPrefix(volumeURL.path) else { return }\n\n        logger.notice(\"Library volume mounted: \\(volumeURL.path.quoted)\")\n\n        /// Try to load machines again now that library is mounted.\n        loadMachines()\n    }\n}\n\n#if DEBUG\n// MARK: - Debug State Simulation\n\nprivate extension VMLibraryController {\n    func simulateState() -> Bool {\n        if UserDefaults.standard.bool(forKey: \"VBSimulateLibraryVolumeNotMounted\") {\n            state = .volumeNotMounted\n            return true\n        } else if UserDefaults.standard.bool(forKey: \"VBSimulateLibraryDirectoryMissing\") {\n            state = .directoryMissing\n            return true\n        } else if UserDefaults.standard.bool(forKey: \"VBSimulateLibraryEmpty\") {\n            state = .empty\n            return true\n        } else {\n            return false\n        }\n    }\n}\n#endif\n"
  },
  {
    "path": "VirtualCore/Source/Virtualization/VMSavedStatesController.swift",
    "content": "import SwiftUI\nimport Combine\nimport OSLog\n\n@MainActor\npublic final class VMSavedStatesController: ObservableObject {\n\n    @Published\n    public private(set) var states = [VBSavedStatePackage]()\n\n    private let logger = Logger(for: VMSavedStatesController.self)\n    private let filePresenter: DirectoryObserver\n    private let updateSignal = PassthroughSubject<URL, Never>()\n    private let directoryURL: URL\n    private let virtualMachine: VBVirtualMachine\n\n    public init(library: VMLibraryController, virtualMachine: VBVirtualMachine) {\n        self.virtualMachine = virtualMachine\n        self.directoryURL = library.savedStateDirectoryURL(for: virtualMachine)\n        self.filePresenter = DirectoryObserver(\n            presentedItemURL: directoryURL,\n            fileExtensions: [VBSavedStatePackage.fileExtension],\n            label: \"SavedStates\",\n            signal: updateSignal\n        )\n\n        loadStates()\n        bind()\n    }\n\n    private lazy var cancellables = Set<AnyCancellable>()\n\n    private lazy var fileManager = FileManager()\n\n    private func bind() {\n        updateSignal\n            .throttle(for: 0.1, scheduler: DispatchQueue.main, latest: true)\n            .sink { [weak self] _ in\n                self?.loadStates()\n            }\n            .store(in: &cancellables)\n    }\n\n    public func loadStates() {\n        guard let enumerator = fileManager.enumerator(at: directoryURL, includingPropertiesForKeys: [.creationDateKey], options: [.skipsHiddenFiles, .skipsPackageDescendants, .skipsSubdirectoryDescendants], errorHandler: nil) else {\n            logger.error(\"Failed to open directory at \\(self.directoryURL.path, privacy: .public)\")\n            return\n        }\n\n        var loadedStates = [VBSavedStatePackage]()\n\n        while let url = enumerator.nextObject() as? URL {\n            guard url.pathExtension == VBSavedStatePackage.fileExtension else { continue }\n\n            do {\n                let package = try VBSavedStatePackage(url: url)\n\n                loadedStates.append(package)\n            } catch {\n                assertionFailure(\"Failed to construct saved state package: \\(error)\")\n            }\n        }\n\n        loadedStates.sort(by: { $0.metadata.date > $1.metadata.date })\n\n        self.states = loadedStates\n    }\n\n    public func reload(animated: Bool = true) {\n        if animated {\n            withAnimation(.spring()) {\n                loadStates()\n            }\n        } else {\n            loadStates()\n        }\n    }\n\n}\n"
  },
  {
    "path": "VirtualCore/VirtualCore.h",
    "content": "//\n//  VirtualCore.h\n//  VirtualCore\n//\n//  Created by Guilherme Rambo on 07/04/22.\n//\n\n#import <Foundation/Foundation.h>\n\n//! Project version number for VirtualCore.\nFOUNDATION_EXPORT double VirtualCoreVersionNumber;\n\n//! Project version string for VirtualCore.\nFOUNDATION_EXPORT const unsigned char VirtualCoreVersionString[];\n\n// In this header, you should import all the public headers of your framework using statements like #import <VirtualCore/PublicHeader.h>\n\n#import <VirtualCore/VirtualizationPrivate.h>\n#import <VirtualCore/VBDebugUtil.h>\n"
  },
  {
    "path": "VirtualUI/Resources/VirtualUI.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "VirtualUI/Resources/VirtualUI.xcassets/FirstLaunchExperience.dataset/Contents.json",
    "content": "{\n  \"data\" : [\n    {\n      \"filename\" : \"FirstLaunchExperience.caar\",\n      \"idiom\" : \"universal\",\n      \"universal-type-identifier\" : \"com.apple.coreanimation-archive\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "VirtualUI/Resources/VirtualUI.xcassets/FullBleedBlurHash.dataset/Contents.json",
    "content": "{\n  \"data\" : [\n    {\n      \"filename\" : \"FullBleedBlurHash.caar\",\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "VirtualUI/Resources/VirtualUI.xcassets/GuestSymbol.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"GuestSymbol.svg\",\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  },\n  \"properties\" : {\n    \"preserves-vector-representation\" : true,\n    \"template-rendering-intent\" : \"template\"\n  }\n}\n"
  },
  {
    "path": "VirtualUI/Resources/VirtualUI.xcassets/StatusItemPanelChromeBorder.colorset/Contents.json",
    "content": "{\n  \"colors\" : [\n    {\n      \"color\" : {\n        \"color-space\" : \"srgb\",\n        \"components\" : {\n          \"alpha\" : \"1.000\",\n          \"blue\" : \"0.686\",\n          \"green\" : \"0.686\",\n          \"red\" : \"0.686\"\n        }\n      },\n      \"idiom\" : \"universal\"\n    },\n    {\n      \"appearances\" : [\n        {\n          \"appearance\" : \"luminosity\",\n          \"value\" : \"dark\"\n        }\n      ],\n      \"color\" : {\n        \"color-space\" : \"srgb\",\n        \"components\" : {\n          \"alpha\" : \"1.000\",\n          \"blue\" : \"0.251\",\n          \"green\" : \"0.251\",\n          \"red\" : \"0.251\"\n        }\n      },\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "VirtualUI/Resources/VirtualUI.xcassets/ThumbnailPlaceholder.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"ThumbnailPlaceholder.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"filename\" : \"ThumbnailPlaceholder@2x.png\",\n      \"idiom\" : \"universal\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  },\n  \"properties\" : {\n    \"template-rendering-intent\" : \"original\"\n  }\n}\n"
  },
  {
    "path": "VirtualUI/Resources/VirtualUI.xcassets/VBGuestType/Contents.json",
    "content": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  },\n  \"properties\" : {\n    \"provides-namespace\" : true\n  }\n}\n"
  },
  {
    "path": "VirtualUI/Resources/VirtualUI.xcassets/VBGuestType/linux.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"icon-linux.pdf\",\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  },\n  \"properties\" : {\n    \"preserves-vector-representation\" : true,\n    \"template-rendering-intent\" : \"template\"\n  }\n}\n"
  },
  {
    "path": "VirtualUI/Resources/VirtualUI.xcassets/VBGuestType/mac.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"icon-mac.pdf\",\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  },\n  \"properties\" : {\n    \"preserves-vector-representation\" : true,\n    \"template-rendering-intent\" : \"template\"\n  }\n}\n"
  },
  {
    "path": "VirtualUI/Resources/VirtualUI.xcassets/VirtualBuddyMono.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"VirtualBuddyMono.pdf\",\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  },\n  \"properties\" : {\n    \"preserves-vector-representation\" : true,\n    \"template-rendering-intent\" : \"template\"\n  }\n}\n"
  },
  {
    "path": "VirtualUI/Resources/VirtualUI.xcassets/VirtualBuddyMonoHappy.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"VirtualBuddyMonoHappy.pdf\",\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  },\n  \"properties\" : {\n    \"preserves-vector-representation\" : true,\n    \"template-rendering-intent\" : \"template\"\n  }\n}\n"
  },
  {
    "path": "VirtualUI/Resources/VirtualUI.xcassets/VirtualBuddyMonoSad.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"VirtualBuddyMonoSad.pdf\",\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  },\n  \"properties\" : {\n    \"preserves-vector-representation\" : true,\n    \"template-rendering-intent\" : \"template\"\n  }\n}\n"
  },
  {
    "path": "VirtualUI/Source/Building Blocks/CGFloat+OnePixel.swift",
    "content": "import CoreGraphics\n\npublic extension CGFloat {\n    \n    static func onePixel(in view: NSView?) -> CGFloat {\n        guard let screen = view?.window?.screen ?? NSScreen.main else { return 1 }\n\n        return 1 / screen.backingScaleFactor\n    }\n\n    static var onePixel: CGFloat {\n        let scale = NSScreen.main?.backingScaleFactor ?? 2\n        return 1 / scale\n    }\n    \n}\n"
  },
  {
    "path": "VirtualUI/Source/Building Blocks/ChromeBorderModifier.swift",
    "content": "import SwiftUI\n\nextension View {\n    func chromeBorder(\n        radius: CGFloat,\n        highlightEnabled: Bool = true,\n        rimEnabled: Bool = true,\n        shadowEnabled: Bool = true,\n        highlightIntensity: Double = 0.5,\n        placeholderAnimationEnabled: Bool = true\n    ) -> some View {\n        chromeBorder(\n            shape: RoundedRectangle(cornerRadius: radius, style: .continuous),\n            highlightEnabled: highlightEnabled,\n            rimEnabled: rimEnabled,\n            shadowEnabled: shadowEnabled,\n            highlightIntensity: highlightIntensity,\n            placeholderAnimationEnabled: placeholderAnimationEnabled\n        )\n    }\n\n    func chromeBorder<BorderShape: InsettableShape>(\n        shape: BorderShape,\n        highlightEnabled: Bool = true,\n        rimEnabled: Bool = true,\n        shadowEnabled: Bool = true,\n        highlightIntensity: Double = 0.5,\n        placeholderAnimationEnabled: Bool = true\n    ) -> some View {\n        modifier(ChromeBorderModifier(\n            shape: shape,\n            highlightEnabled: highlightEnabled,\n            rimEnabled: rimEnabled,\n            shadowEnabled: shadowEnabled,\n            highlightIntensity: highlightIntensity,\n            placeholderAnimationEnabled: placeholderAnimationEnabled\n        ))\n    }\n}\n\nprivate struct ChromeBorderModifier<BorderShape: InsettableShape>: ViewModifier {\n    var shape: BorderShape\n    var highlightEnabled = true\n    var rimEnabled = true\n    var shadowEnabled = true\n    var highlightIntensity = 0.5\n    var placeholderAnimationEnabled = true\n\n    @State private var animate = false\n\n    @Environment(\\.redactionReasons)\n    private var redaction\n\n    func body(content: Content) -> some View {\n        content\n            .shadow(color: .black.opacity(shadowEnabled ? 0.2 : 0), radius: 6, x: 0, y: 0)\n            .shadow(color: .black.opacity(rimEnabled ? 0.5 : 0), radius: 1, x: 0, y: 0)\n            .overlay {\n                if highlightEnabled {\n                    ZStack {\n                        shape\n                            .strokeBorder(Color.white, lineWidth: 1)\n\n                        LinearGradient(colors: [.white.opacity(0.4), .white.opacity(0.7)], startPoint: .top, endPoint: .bottom)\n                            .blendMode(.destinationOut)\n                    }\n                    .compositingGroup()\n                    .blendMode(.plusLighter)\n                    .opacity(highlightIntensity)\n                }\n            }\n            .overlay {\n                if placeholderAnimationEnabled, !redaction.isEmpty {\n                    LinearGradient(colors: [.white.opacity(0), .white.opacity(0.5), .white.opacity(0.6), .white.opacity(0.5), .white.opacity(0)], startPoint: .leading, endPoint: .trailing)\n                        .scaleEffect(x: animate ? 1 : 2, anchor: .trailing)\n                        .scaleEffect(x: animate ? 2 : 1, anchor: .leading)\n                        .clipShape(shape)\n                        .blendMode(.plusLighter)\n                        .opacity(0.2)\n                }\n            }\n            .task(id: placeholderAnimationEnabled && !redaction.isEmpty) {\n                if placeholderAnimationEnabled, !redaction.isEmpty {\n                    withAnimation(.easeInOut(duration: 5).repeatForever()) {\n                        animate.toggle()\n                    }\n                }\n            }\n    }\n}\n\n#if DEBUG\n@available(macOS 14.0, *)\n#Preview {\n    @Previewable @State var isRedacted = true\n    \n    Button {\n\n    } label: {\n        CatalogGroupView(group: .placeholder)\n    }\n    .buttonStyle(CatalogGroupButtonStyle(isSelected: false))\n    .aspectRatio(CatalogGroupPicker.buttonAspectRatio, contentMode: .fit)\n    .frame(width: 220)\n    .padding(32)\n    .redacted(reason: isRedacted ? [.placeholder] : [])\n    .task {\n        try? await Task.sleep(for: .seconds(3))\n        isRedacted = false\n    }\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/Building Blocks/Configuration Controls/EphemeralTextField.swift",
    "content": "//\n//  EphemeralTextField.swift\n//  VirtualUI\n//\n//  Created by Guilherme Rambo on 18/07/22.\n//\n\nimport SwiftUI\nimport VirtualCore\n\nprivate struct EphemeralTextFieldContentWidthKey: PreferenceKey {\n    static var defaultValue: CGFloat = 0\n\n    static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {\n        value = nextValue()\n    }\n}\n\nstruct EphemeralTextField<Value, StaticContent, EditableContent>: View where StaticContent: View, EditableContent: View, Value: Equatable {\n    \n    @Binding var value: Value\n    var staticContent: (Value) -> StaticContent\n    var editableContent: (Binding<Value>) -> EditableContent\n    var clamp: (Value) -> Value\n    var validate: (Value) -> Bool\n    var alignment: Alignment\n    var setFocus: BoolSubject\n    \n    init(_ value: Binding<Value>,\n         alignment: Alignment = .trailing,\n         setFocus: BoolSubject = .init(),\n         @ViewBuilder staticContent: @escaping (Value) -> StaticContent,\n         @ViewBuilder editableContent: @escaping (Binding<Value>) -> EditableContent,\n         clamp: @escaping (Value) -> Value = { $0 },\n         validate: @escaping (Value) -> Bool = { _ in  true })\n    {\n        self._value = value\n        self._internalValue = .init(wrappedValue: value.wrappedValue)\n        self.alignment = alignment\n        self.setFocus = setFocus\n        self.staticContent = staticContent\n        self.editableContent = editableContent\n        self.clamp = clamp\n        self.validate = validate\n    }\n    \n    @State private var internalValue: Value\n    \n    @Environment(\\.isEnabled) private var isEnabled\n    \n    @Environment(\\.unfocusActiveField)\n    private var unfocus\n    \n    @FocusState\n    private var isFocused: Bool\n\n    @State private var isInEditMode = false\n    \n    @State private var contentWidth: CGFloat = 40\n    \n    @State private var shakeOffset: CGFloat = 0\n\n    var body: some View {\n        ZStack {\n            staticContent(internalValue)\n                .lineLimit(1)\n                .contentShape(Rectangle())\n                .onTapGesture {\n                    beginEditing()\n                }\n                .onHover { isHovered = $0 }\n                .opacity(isInEditMode ? 0 : 1)\n                .background {\n                    GeometryReader { proxy in\n                        Color.clear\n                            .preference(key: EphemeralTextFieldContentWidthKey.self, value: proxy.size.width)\n                    }\n                }\n                .onPreferenceChange(EphemeralTextFieldContentWidthKey.self) { newValue in\n                    contentWidth = newValue\n                }\n            \n            if isInEditMode {\n                editableContent($internalValue)\n                    .focused($isFocused)\n                    .textFieldStyle(.plain)\n                    .frame(width: contentWidth, alignment: alignment)\n                    .onChange(of: value) { newValue in\n                        // Unfocus when changing the value externally.\n                        isFocused = false\n                        internalValue = newValue\n                    }\n                    .onSubmit { commit() }\n                    .onExitCommand { cancel() }\n                    .onReceive(unfocus) { action in\n                        switch action {\n                        case .commit:\n                            commit()\n                        case .cancel:\n                            cancel()\n                        }\n                    }\n                    .onChange(of: isFocused) { newValue in\n                        if !newValue { isInEditMode = false }\n                    }\n            }\n        }\n        .monospacedDigit()\n        .multilineTextAlignment(TextAlignment(alignment))\n        .padding(.vertical, 4)\n        .padding(.horizontal, 8)\n        .background(hoverBackground)\n        .padding(.vertical, -4)\n        .padding(.horizontal, -8)\n        .offset(x: shakeOffset)\n        .onChange(of: isInEditMode) { newValue in\n            if newValue {\n                internalValue = value\n                isFocused = true\n            }\n        }\n        .onChange(of: value) { newValue in\n            internalValue = newValue\n        }\n        .onReceive(setFocus) { focus in\n            guard focus != isInEditMode else { return }\n            \n            if focus {\n                beginEditing()\n            } else {\n                cancel()\n            }\n        }\n    }\n\n    private func beginEditing() {\n        guard isEnabled else { return }\n\n        isInEditMode = true\n    }\n    \n    private func commit() {\n        guard validate(internalValue) else {\n            self.internalValue = value\n            shake()\n            return\n        }\n        \n        /// Update the external value with the edited value on submit,\n        /// limiting to the allowed range.\n        value = clamp(internalValue)\n        internalValue = clamp(internalValue)\n\n        isFocused = false\n    }\n    \n    private func cancel() {\n        /// Unfocus the field when pressing the escape key.\n        isFocused = false\n        \n        /// Ugly hack alert!\n        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {\n            /// Restore initial value when cancelling out of the field.\n            internalValue = value\n        }\n    }\n    \n    private func shake() {\n        withAnimation(.easeInOut(duration: 0.04).repeatCount(7, autoreverses: true)) {\n            shakeOffset = -7\n        }\n        /// Ugly hack alert (2)!\n        DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {\n            shakeOffset = 0\n        }\n    }\n    \n    @State private var isHovered = false\n    \n    private var drawHoverBackground: Bool {\n        isEnabled && (isHovered || isInEditMode)\n    }\n    \n    @ViewBuilder\n    private var hoverBackground: some View {\n        RoundedRectangle(cornerRadius: 6, style: .continuous)\n            .foregroundColor(.white.opacity(0.07))\n            .opacity(drawHoverBackground ? 1 : 0)\n            .animation(.easeOut(duration: 0.24), value: isHovered)\n    }\n}\n\nextension TextAlignment {\n    init(_ alignment: Alignment) {\n        switch alignment.horizontal {\n        case .leading:\n            self = .leading\n        case .trailing:\n            self = .trailing\n        default:\n            self = .center\n        }\n    }\n}\n\n#if DEBUG\n\nstruct EphemeralTextField_Previews: PreviewProvider {\n    static var previews: some View {\n        _Template()\n    }\n\n    struct _Template: View {\n        @State var text = \"Hello, World\"\n\n        var body: some View {\n            VStack {\n                EphemeralTextField($text) { value in\n                    Text(value)\n                } editableContent: { value in\n                    TextField(\"\", text: value)\n                }\n            }\n            .frame(maxWidth: .infinity, maxHeight: .infinity)\n            .padding()\n        }\n    }\n}\n\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/Building Blocks/Configuration Controls/NumericPropertyControl.swift",
    "content": "import SwiftUI\nimport Combine\nimport VirtualCore\n\nstruct NumericPropertyControl<Value: BinaryInteger, F: Formatter>: View {\n    @Binding var value: Value\n    var range: ClosedRange<Value>\n    var step: Value? = nil\n    var hideSlider = false\n    var label: String\n    var formatter: F\n    var unfocus = VoidSubject()\n    var spacing: CGFloat = 2\n\n    var body: some View {\n        VStack(alignment: .leading, spacing: spacing) {\n            HStack {\n                PropertyControlLabel(label)\n                \n                Spacer()\n\n                NumericValueField(\n                    label: label,\n                    value: $value,\n                    range: range,\n                    formatter: formatter\n                )\n                    .textFieldStyle(.plain)\n                    .multilineTextAlignment(.trailing)\n                    .monospacedDigit()\n            }\n\n            if !hideSlider {\n                Group {\n                    if let step {\n                        Slider(value: $value.sliderValue, in: range.sliderRange, step: Double(step), onEditingChanged: sliderEditingChanged)\n                    } else {\n                        Slider(value: $value.sliderValue, in: range.sliderRange, onEditingChanged: sliderEditingChanged)\n                    }\n                }\n                .controlSize(.mini)\n            }\n        }\n        .transition(.asymmetric(insertion: .offset(x: 0, y: -40), removal: .offset(x: 0, y: 40)).combined(with: .opacity))\n    }\n    \n    private func sliderEditingChanged(_ isEditing: Bool) {\n        guard isEditing else { return }\n        unfocus.send()\n    }\n}\n\nextension NumberFormatter {\n    static let numericPropertyControlDefault: NumberFormatter = {\n        let f = NumberFormatter()\n        f.numberStyle = .decimal\n        f.maximumFractionDigits = 0\n        f.minimumFractionDigits = 0\n        f.hasThousandSeparators = false\n        return f\n    }()\n}\n\n#if DEBUG\nstruct PropertySlider_Previews: PreviewProvider {\n    static var previews: some View {\n        _Template()\n    }\n\n    struct _Template: View {\n        @State private var value = 1\n\n        var body: some View {\n            NumericPropertyControl(value: $value, range: 0...10, step: 1, hideSlider: false, label: \"Preview\", formatter: NumberFormatter.numericPropertyControlDefault)\n                .padding()\n                .frame(maxWidth: 200)\n        }\n    }\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/Building Blocks/Configuration Controls/NumericValueField.swift",
    "content": "import SwiftUI\nimport Combine\nimport VirtualCore\n\nstruct NumericValueField<Value: BinaryInteger, F: Formatter>: View {\n\n    var label: String\n    @Binding var value: Value\n    var range: ClosedRange<Value>\n    var formatter: F\n\n    @Environment(\\.unfocusActiveField)\n    private var unfocus\n\n    init(label: String, value: Binding<Value>, range: ClosedRange<Value>, formatter: F) {\n        self.label = label\n        self._value = value\n        self.range = range\n        self.formatter = formatter\n    }\n\n    @FocusState\n    private var isFocused: Bool\n\n    @State private var isInEditMode = false\n\n    var body: some View {\n        EphemeralTextField($value) { currentValue in\n            Text(formatter.string(for: currentValue) ?? \"\")\n        } editableContent: { binding in\n            TextField(label, value: binding, formatter: formatter)\n        } clamp: { $0.limited(to: range) }\n    }\n}\n\nextension BinaryInteger {\n    func limited(to range: ClosedRange<Self>) -> Self {\n        Swift.min(range.upperBound, Swift.max(range.lowerBound, self))\n    }\n}\n\n#if DEBUG\n\nstruct NumericValueField_Previews: PreviewProvider {\n    static var previews: some View {\n        _Template()\n    }\n\n    struct _Template: View {\n        @State var value = 1\n        \n        @Environment(\\.unfocusActiveField)\n        private var unfocusActiveField\n\n        private let formatter: NumberFormatter = {\n            let f = NumberFormatter()\n            f.numberStyle = .decimal\n            f.maximumFractionDigits = 0\n            f.minimumFractionDigits = 0\n            f.hasThousandSeparators = false\n            return f\n        }()\n\n        var body: some View {\n            VStack {\n                NumericValueField(\n                    label: \"Test\",\n                    value: $value,\n                    range: 1...10,\n                    formatter: formatter\n                )\n\n                Button(\"Unfocus\") {\n                    unfocusActiveField.send(.cancel)\n                }\n                .controlSize(.small)\n            }\n            .padding()\n        }\n    }\n}\n\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/Building Blocks/Configuration Controls/PropertyControl.swift",
    "content": "//\n//  PropertyControl.swift\n//  VirtualUI\n//\n//  Created by Guilherme Rambo on 18/07/22.\n//\n\nimport SwiftUI\n\nstruct PropertyControl<Content: View>: View {\n    \n    var label: String\n    var spacing: CGFloat\n    var content: () -> Content\n    \n    init(_ label: String, spacing: CGFloat = 4, @ViewBuilder content: @escaping () -> Content) {\n        self.label = label\n        self.spacing = spacing\n        self.content = content\n    }\n    \n    var body: some View {\n        VStack(alignment: .leading, spacing: spacing) {\n            PropertyControlLabel(label)\n            content().labelsHidden()\n        }\n    }\n    \n}\n\nstruct PropertyControlLabel: View {\n    init(_ title: String) {\n        self.title = title\n    }\n    \n    var title: String\n    \n    var body: some View {\n        Text(title)\n            .foregroundColor(.white.opacity(0.7))\n            .blendMode(.plusLighter)\n    }\n}\n"
  },
  {
    "path": "VirtualUI/Source/Building Blocks/Configuration Controls/SharedFocusEnvironment.swift",
    "content": "import SwiftUI\nimport Combine\n\nenum UnfocusFieldAction {\n    case commit\n    case cancel\n}\n\ntypealias UnfocusFieldSubject = PassthroughSubject<UnfocusFieldAction, Never>\n\nextension EnvironmentValues {\n    var unfocusActiveField: UnfocusFieldSubject {\n        get { self[UnfocusActiveFieldEnvironmentKey.self] }\n        set { self[UnfocusActiveFieldEnvironmentKey.self] = newValue }\n    }\n}\n\nextension View {\n    func unfocusOnTap() -> some View {\n        modifier(UnfocusOnTap())\n    }\n}\n\nprivate struct UnfocusOnTap: ViewModifier {\n    \n    @Environment(\\.unfocusActiveField)\n    private var unfocus\n    \n    func body(content: Content) -> some View {\n        content\n            .onTapGesture { unfocus.send(.commit) }\n    }\n    \n}\n\nprivate struct UnfocusActiveFieldEnvironmentKey: EnvironmentKey {\n    static var defaultValue = UnfocusFieldSubject()\n}\n"
  },
  {
    "path": "VirtualUI/Source/Building Blocks/ControlGroupChrome.swift",
    "content": "import SwiftUI\n\npublic enum ControlGroupLevel: Int {\n    case primary\n    case secondary\n}\n\npublic struct ControlGroupMetrics {\n    static let primaryGroupRadius: Double = 14\n    static let secondaryGroupRadius: Double = 8\n}\n\npublic extension ControlGroupLevel {\n    var cornerRadius: Double {\n        switch self {\n        case .primary: ControlGroupMetrics.primaryGroupRadius\n        case .secondary: ControlGroupMetrics.secondaryGroupRadius\n        }\n    }\n}\n\npublic extension View {\n    func controlGroup(level: ControlGroupLevel = .primary) -> some View {\n        modifier(ControlGroupChrome(level: level, shapeBuilder: {\n            RoundedRectangle(cornerRadius: level.cornerRadius, style: .continuous)\n        }))\n    }\n\n    func controlGroup<S>(_ shape: S, level: ControlGroupLevel = .primary) -> some View where S: InsettableShape {\n        modifier(ControlGroupChrome<S>(level: level, shapeBuilder: { shape }))\n    }\n}\n\nprivate struct ControlGroupChrome<Shape: InsettableShape>: ViewModifier {\n    @Environment(\\.colorScheme)\n    private var colorScheme\n\n    var level: ControlGroupLevel\n    var shapeBuilder: () -> Shape\n\n    var dark: Bool { colorScheme == .dark }\n\n    private var innerRimOpacity: Double {\n        switch level {\n        case .primary:\n            return dark ? 0.15 : 0\n        case .secondary:\n            return dark ? 0.1 : 0\n        }\n    }\n\n    private var outerRimOpacity: Double {\n        switch level {\n        case .primary:\n            return dark ? 0.5 : 0.8\n        case .secondary:\n            return dark ? 0.4 : 0.7\n        }\n    }\n\n    private var shadowOpacity: Double {\n        switch level {\n        case .primary:\n            return dark ? 0.2 : 0.1\n        case .secondary:\n            return dark ? 0.1 : 0.05\n        }\n    }\n\n    private var material: Material {\n        switch level {\n        case .primary:\n            return .thin\n        case .secondary:\n            return .regular\n        }\n    }\n\n    func body(content: Content) -> some View {\n        content\n            .background(material, in: shape)\n            .clipShape(shape)\n            .shadow(color: Color.black.opacity(outerRimOpacity), radius: 1, x: 0, y: 0)\n            .shadow(color: Color.black.opacity(shadowOpacity), radius: dark ? 8 : 10, x: 0, y: 0)\n            .chromeBorder(shape: shape, highlightEnabled: true, rimEnabled: false, shadowEnabled: false, highlightIntensity: innerRimOpacity)\n            .unfocusOnTap()\n            .containerShape(shape)\n    }\n\n    private var shape: Shape {\n        shapeBuilder()\n    }\n}\n\n#if DEBUG\nstruct ControlGroupChrome_Previews: PreviewProvider {\n    static var previews: some View {\n        VStack {\n            Text(\"Primary Group\")\n\n            VStack {\n                Text(\"Secondary Group\")\n            }\n            .padding()\n            .controlGroup(level: .secondary)\n        }\n        .padding(30)\n        .controlGroup()\n        .padding(50)\n    }\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/Building Blocks/SliderConversion.swift",
    "content": "//\n//  SliderConversion.swift\n//  VirtualUI\n//\n//  Created by Guilherme Rambo on 17/07/22.\n//\n\nimport SwiftUI\n\nextension Binding where Value: BinaryInteger {\n    var sliderValue: Binding<Double> {\n        .init {\n            Double(wrappedValue)\n        } set: { wrappedValue = Value($0) }\n    }\n}\n\nextension ClosedRange where Bound: BinaryInteger {\n    var sliderRange: ClosedRange<Double> {\n        Double(lowerBound)...Double(upperBound)\n    }\n}\n\nextension Binding where Value == UInt64 {\n    var gbValue: Binding<Int> {\n        .init {\n            Int(wrappedValue / 1024 / 1024 / 1024)\n        } set: { wrappedValue = Value($0 * 1024 * 1024 * 1024) }\n    }\n\n    var gbStorageValue: Binding<Int> {\n        .init {\n            Int(wrappedValue / .storageGigabyte)\n        } set: { wrappedValue = Value(UInt64($0) * .storageGigabyte) }\n    }\n}\n\nextension UInt64 {\n    var gbStorageValue: Int {\n        Int(self / .storageGigabyte)\n    }\n}\n"
  },
  {
    "path": "VirtualUI/Source/Components/Array+Navigation.swift",
    "content": "import Foundation\n\nextension Array where Element: Identifiable {\n    func next(from selection: Element?) -> Element? {\n        guard let index = self.lastIndex(where: { $0.id == selection?.id }) else {\n            return nil\n        }\n        let nextIndex = index + 1\n        guard nextIndex < count else { return nil }\n        return self[nextIndex]\n    }\n\n    func previous(from selection: Element?) -> Element? {\n        guard let index = self.firstIndex(where: { $0.id == selection?.id }) else {\n            return nil\n        }\n        let previousIndex = index - 1\n        guard previousIndex >= 0 else { return nil }\n        return self[previousIndex]\n    }\n}\n"
  },
  {
    "path": "VirtualUI/Source/Components/BackportedContentUnavailableView.swift",
    "content": "import SwiftUI\n\nstruct BackportedContentUnavailableView<Actions: View>: View {\n    var title: LocalizedStringKey\n    var image: Image\n    var description: Text?\n    @ViewBuilder var actions: () -> Actions\n\n    init(_ title: LocalizedStringKey, image: Image, description: Text? = nil, @ViewBuilder actions: @escaping () -> Actions) {\n        self.title = title\n        self.image = image\n        self.description = description\n        self.actions = actions\n    }\n\n    init(_ title: LocalizedStringKey, systemImage: String, description: Text? = nil, @ViewBuilder actions: @escaping () -> Actions) {\n        self.init(title, image: Image(systemName: systemImage), description: description, actions: actions)\n    }\n\n    var body: some View {\n        VStack(spacing: 16) {\n            VStack(spacing: 6) {\n                image\n                    .imageScale(.large)\n                    .symbolVariant(.fill)\n\n                Text(title)\n            }\n            .font(.system(.title, design: .rounded, weight: .medium))\n            .foregroundStyle(.secondary)\n\n            description\n                .foregroundStyle(.secondary)\n                .multilineTextAlignment(.center)\n                .font(.system(.title3, weight: .regular))\n\n            if Actions.self != EmptyView.self {\n                EqualWidthHStack {\n                    actions()\n                }\n                .controlSize(.large)\n                .buttonStyle(.backportedContentUnavailableAction)\n            }\n        }\n        .textSelection(.enabled)\n        .frame(maxWidth: 520)\n    }\n}\n\nextension BackportedContentUnavailableView where Actions == EmptyView {\n    init(_ title: LocalizedStringKey, image: Image, description: Text? = nil) {\n        self.title = title\n        self.image = image\n        self.description = description\n        self.actions = { EmptyView() }\n    }\n\n    init(_ title: LocalizedStringKey, systemImage: String, description: Text? = nil) {\n        self.init(title, image: Image(systemName: systemImage), description: description)\n    }\n}\n\nprivate struct BackportedContentUnavailableActionButtonStyle: PrimitiveButtonStyle {\n    func makeBody(configuration: Configuration) -> some View {\n        Button(role: configuration.role) {\n            configuration.trigger()\n        } label: {\n            configuration.label\n                .frame(maxWidth: .infinity)\n        }\n    }\n}\n\nprivate extension PrimitiveButtonStyle where Self == BackportedContentUnavailableActionButtonStyle {\n    static var backportedContentUnavailableAction: BackportedContentUnavailableActionButtonStyle {\n        BackportedContentUnavailableActionButtonStyle()\n    }\n}\n\n#if DEBUG\n#Preview {\n    BackportedContentUnavailableView(\n        \"Hello World\",\n        systemImage: \"globe\",\n        description: Text(\"\"\"\n        Something that should be here is not here.\n        \n        Because that something that should be here is not here, you're seeing this message.\n        \"\"\")\n    ) {\n        Button(\"Primary Action\") {\n\n        }\n        .keyboardShortcut(.defaultAction)\n\n        Button(\"Other Action\") {\n\n        }\n    }\n    .frame(minWidth: 500, minHeight: 300)\n    .padding(32)\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/Components/BlurHash/BlurHashDecoding.swift",
    "content": "/**\n Copyright (c) 2018 Wolt Enterprises\n\n Permission is hereby granted, free of charge, to any person obtaining a copy\n of this software and associated documentation files (the \"Software\"), to deal\n in the Software without restriction, including without limitation the rights\n to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n copies of the Software, and to permit persons to whom the Software is\n furnished to do so, subject to the following conditions:\n\n The above copyright notice and this permission notice shall be included in all\n copies or substantial portions of the Software.\n\n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n SOFTWARE.\n */\n\nimport SwiftUI\n\npublic extension CGSize {\n    /// The size of blur hash used by VirtualBuddy.\n    ///\n    /// - warning: This can't be changed without updating the server-side catalog groups with updated blur hashes matching this size!\n    static let vbBlurHashSize = CGSize(width: Int.vbBlurHashSize, height: Int.vbBlurHashSize)\n}\n\npublic extension Float {\n    /// Default `punch` value used for blur hashes in VirtualBuddy.\n    static let vbBlurHashPunch: Float = 1\n}\n\npublic extension Image {\n    init(blurHash: String, size: CGSize = .vbBlurHashSize, punch: Float = .vbBlurHashPunch) {\n        if let decodedImage = NSImage(blurHash: blurHash, size: size, punch: punch) {\n            self.init(nsImage: decodedImage)\n        } else {\n            self.init(nsImage: .blurHashPlaceholder(size: size))\n        }\n    }\n\n    init(blurHash: BlurHashToken, punch: Float = .vbBlurHashPunch) {\n        self.init(\n            blurHash: blurHash.value,\n            size: CGSize(width: blurHash.size, height: blurHash.size),\n            punch: punch\n        )\n    }\n}\n\nprivate extension NSImage {\n    static func blurHashPlaceholder(size: CGSize) -> NSImage {\n        NSImage(size: size, flipped: true) { rect in\n            NSColor.quaternaryLabelColor.setFill()\n            rect.fill()\n            return true\n        }\n    }\n}\n\npublic extension NSImage {\n    static func blurHash(_ token: BlurHashToken) -> NSImage {\n        if let image = NSImage(blurHash: token.value, size: CGSize(width: token.size, height: token.size)) {\n            image\n        } else {\n            .blurHashPlaceholder(size: CGSize(width: token.size, height: token.size))\n        }\n    }\n\n    convenience init?(blurHash: String, size: CGSize, punch: Float = 1) {\n        guard blurHash.count >= 6 else { return nil }\n\n        let sizeFlag = String(blurHash[0]).decode83()\n        let numY = (sizeFlag / 9) + 1\n        let numX = (sizeFlag % 9) + 1\n\n        let quantisedMaximumValue = String(blurHash[1]).decode83()\n        let maximumValue = Float(quantisedMaximumValue + 1) / 166\n\n        guard blurHash.count == 4 + 2 * numX * numY else { return nil }\n\n        let colours: [(Float, Float, Float)] = (0 ..< numX * numY).map { i in\n            if i == 0 {\n                let value = String(blurHash[2 ..< 6]).decode83()\n                return decodeDC(value)\n            } else {\n                let value = String(blurHash[4 + i * 2 ..< 4 + i * 2 + 2]).decode83()\n                return decodeAC(value, maximumValue: maximumValue * punch)\n            }\n        }\n\n        let width = Int(size.width)\n        let height = Int(size.height)\n        let bytesPerRow = width * 3\n        guard let data = CFDataCreateMutable(kCFAllocatorDefault, bytesPerRow * height) else { return nil }\n        CFDataSetLength(data, bytesPerRow * height)\n        guard let pixels = CFDataGetMutableBytePtr(data) else { return nil }\n\n        for y in 0 ..< height {\n            for x in 0 ..< width {\n                var r: Float = 0\n                var g: Float = 0\n                var b: Float = 0\n\n                for j in 0 ..< numY {\n                    for i in 0 ..< numX {\n                        let basis = cos(Float.pi * Float(x) * Float(i) / Float(width)) * cos(Float.pi * Float(y) * Float(j) / Float(height))\n                        let colour = colours[i + j * numX]\n                        r += colour.0 * basis\n                        g += colour.1 * basis\n                        b += colour.2 * basis\n                    }\n                }\n\n                let intR = UInt8(linearTosRGB(r))\n                let intG = UInt8(linearTosRGB(g))\n                let intB = UInt8(linearTosRGB(b))\n\n                pixels[3 * x + 0 + y * bytesPerRow] = intR\n                pixels[3 * x + 1 + y * bytesPerRow] = intG\n                pixels[3 * x + 2 + y * bytesPerRow] = intB\n            }\n        }\n\n        let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue)\n\n        guard let provider = CGDataProvider(data: data) else { return nil }\n        guard let cgImage = CGImage(width: width, height: height, bitsPerComponent: 8, bitsPerPixel: 24, bytesPerRow: bytesPerRow,\n        space: CGColorSpaceCreateDeviceRGB(), bitmapInfo: bitmapInfo, provider: provider, decode: nil, shouldInterpolate: true, intent: .defaultIntent) else { return nil }\n\n        self.init(cgImage: cgImage, size: size)\n    }\n}\n\nprivate func decodeDC(_ value: Int) -> (Float, Float, Float) {\n    let intR = value >> 16\n    let intG = (value >> 8) & 255\n    let intB = value & 255\n    return (sRGBToLinear(intR), sRGBToLinear(intG), sRGBToLinear(intB))\n}\n\nprivate func decodeAC(_ value: Int, maximumValue: Float) -> (Float, Float, Float) {\n    let quantR = value / (19 * 19)\n    let quantG = (value / 19) % 19\n    let quantB = value % 19\n\n    let rgb = (\n        signPow((Float(quantR) - 9) / 9, 2) * maximumValue,\n        signPow((Float(quantG) - 9) / 9, 2) * maximumValue,\n        signPow((Float(quantB) - 9) / 9, 2) * maximumValue\n    )\n\n    return rgb\n}\n\nprivate func signPow(_ value: Float, _ exp: Float) -> Float {\n    return copysign(pow(abs(value), exp), value)\n}\n\nprivate func linearTosRGB(_ value: Float) -> Int {\n    let v = max(0, min(1, value))\n    if v <= 0.0031308 { return Int(v * 12.92 * 255 + 0.5) }\n    else { return Int((1.055 * pow(v, 1 / 2.4) - 0.055) * 255 + 0.5) }\n}\n\nprivate func sRGBToLinear<Type: BinaryInteger>(_ value: Type) -> Float {\n    let v = Float(Int64(value)) / 255\n    if v <= 0.04045 { return v / 12.92 }\n    else { return pow((v + 0.055) / 1.055, 2.4) }\n}\n\nprivate let encodeCharacters: [String] = {\n    return \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz#$%*+,-.:;=?@[]^_{|}~\".map { String($0) }\n}()\n\nprivate let decodeCharacters: [String: Int] = {\n    var dict: [String: Int] = [:]\n    for (index, character) in encodeCharacters.enumerated() {\n        dict[character] = index\n    }\n    return dict\n}()\n\nprivate extension String {\n    func decode83() -> Int {\n        var value: Int = 0\n        for character in self {\n            if let digit = decodeCharacters[String(character)] {\n                value = value * 83 + digit\n            }\n        }\n        return value\n    }\n}\n\nprivate extension String {\n    subscript (offset: Int) -> Character {\n        return self[index(startIndex, offsetBy: offset)]\n    }\n\n    subscript (bounds: CountableClosedRange<Int>) -> Substring {\n        let start = index(startIndex, offsetBy: bounds.lowerBound)\n        let end = index(startIndex, offsetBy: bounds.upperBound)\n        return self[start...end]\n    }\n\n    subscript (bounds: CountableRange<Int>) -> Substring {\n        let start = index(startIndex, offsetBy: bounds.lowerBound)\n        let end = index(startIndex, offsetBy: bounds.upperBound)\n        return self[start..<end]\n    }\n}\n"
  },
  {
    "path": "VirtualUI/Source/Components/CALayer+Asset.swift",
    "content": "import Cocoa\nimport OSLog\n\npublic extension CALayer {\n    static let log = Logger(subsystem: VirtualUIConstants.subsystemName, category: \"CALayer+Asset\")\n}\n\npublic extension CALayer {\n\n    /// Loads a `CALayer` from a Core Animation Archive asset.\n    ///\n    /// - Parameters:\n    ///   - assetName: The name of the asset in the asset catalog.\n    ///   - bundle: The bundle where the asset catalog is located.\n    /// - Returns: The `CALayer` loaded from the asset in the asset catalog, `nil` in case of failure.\n    static func load(assetNamed assetName: String, bundle: Bundle = .main) -> CALayer? {\n        guard let asset = NSDataAsset(name: assetName, bundle: bundle) else {\n            assertionFailure(\"Asset not found\")\n            log.fault(\"Missing asset \\(assetName, privacy: .public)\")\n            return nil\n        }\n\n        do {\n            let unarchiver = try NSKeyedUnarchiver(forReadingFrom: asset.data)\n            unarchiver.requiresSecureCoding = false\n\n            let rootObject = unarchiver.decodeObject(forKey: NSKeyedArchiveRootObjectKey)\n\n            guard let dictionary = rootObject as? NSDictionary else {\n                assertionFailure(\"Failed to load asset\")\n                log.fault(\"Failed to load asset \\(assetName, privacy: .public)\")\n                return nil\n            }\n\n            guard let layer = dictionary[\"rootLayer\"] as? CALayer else {\n                assertionFailure(\"Root layer not found\")\n                log.fault(\"Failed to load root layer from asset \\(assetName, privacy: .public)\")\n                return nil\n            }\n\n            return layer\n        } catch {\n            assertionFailure(String(describing: error))\n            log.fault(\"Unarchive failed: \\(String(describing: error), privacy: .public)\")\n            return nil\n        }\n    }\n\n    func sublayer<T: CALayer>(named name: String, of type: T.Type = CALayer.self) -> T? {\n        return sublayers?.first(where: { $0.name == name }) as? T\n    }\n\n    func sublayer<T: CALayer>(path: String, of type: T.Type = CALayer.self) -> T? {\n        let components = path.components(separatedBy: \".\")\n        var target: CALayer? = self\n        for component in components {\n            target = target?.sublayer(named: component, of: CALayer.self)\n        }\n        return target as? T\n    }\n}\n\nextension CALayer {\n    static func resize(_ targetLayer: CALayer?,\n                       within containerBounds: CGRect,\n                       multiplier: CGFloat = 1,\n                       offset: CGPoint = .zero,\n                       gravity: CALayerContentsGravity = .resizeAspect,\n                       resetPosition: Bool = false,\n                       disableAnimations: Bool = false)\n    {\n        assert([.resizeAspect, .resizeAspectFill, .resize, .center].contains(gravity), \"Unsupported layer gravity\")\n\n        if disableAnimations {\n            CATransaction.begin()\n            CATransaction.setDisableActions(true)\n            CATransaction.setAnimationDuration(0)\n        }\n        defer { if disableAnimations { CATransaction.commit() } }\n\n        let fn: (CGFloat, CGFloat) -> CGFloat = gravity == .resizeAspectFill ? max : min\n\n        guard let targetLayer = targetLayer else { return }\n\n        let layerWidth = targetLayer.bounds.width\n        let layerHeight = targetLayer.bounds.height\n\n        let aspectWidth  = containerBounds.width / layerWidth\n        let aspectHeight = containerBounds.height / layerHeight\n\n        let ratioWidth: CGFloat\n        let ratioHeight: CGFloat\n\n        switch gravity {\n        case .resize:\n            ratioWidth = aspectWidth * multiplier\n            ratioHeight = aspectHeight * multiplier\n        case .center:\n            ratioWidth = multiplier\n            ratioHeight = multiplier\n        default:\n            ratioWidth = fn(aspectWidth, aspectHeight) * multiplier\n            ratioHeight = fn(aspectWidth, aspectHeight) * multiplier\n        }\n\n        let scale = CATransform3DMakeScale(ratioWidth,\n                                           ratioHeight,\n                                           1)\n        let translation = CATransform3DMakeTranslation((containerBounds.width - (layerWidth * ratioWidth)) / 2.0 - offset.x,\n                                                       (containerBounds.height - (layerHeight * ratioHeight)) / 2.0 - offset.y,\n                                                       0)\n\n        if resetPosition {\n            targetLayer.anchorPoint = .zero\n            targetLayer.position = .zero\n        }\n\n        targetLayer.transform = CATransform3DConcat(scale, translation)\n    }\n}\n"
  },
  {
    "path": "VirtualUI/Source/Components/DecentFormView.swift",
    "content": "//\n//  AirFormView.swift\n//  AirUI\n//\n//  Created by Guilherme Rambo on 30/05/22.\n//  Copyright © 2022 Guilherme Rambo. All rights reserved.\n//\n\nimport SwiftUI\n\n/// A form that aligns labels and controls in a way consistent with AirBuddy's preferences UI.\n/// Contents should be instances of `AirFormControl`.\npublic struct DecentFormView<Content>: View where Content: View {\n    \n    var content: () -> Content\n    var spacing: CGFloat?\n    \n    public init(spacing: CGFloat? = nil, @ViewBuilder content: @escaping () -> Content) {\n        self.content = content\n        self.spacing = spacing\n    }\n    \n    public var body: some View {\n        VStack(alignment: .customLabelAlignmentGuideHorizontal, spacing: spacing) {\n            content()\n        }\n        .labelsHidden()\n    }\n    \n}\n\n/// Encapsulates a control and a label to be displayed within an `AirFormView`.\n/// The view can be flipped so that the label is shown to the right of the control, such as AirBuddy does with big switches in its settings UI.\npublic struct DecentFormControl<ContentA, ContentB>: View where ContentA: View, ContentB: View {\n    \n    var alignment: VerticalAlignment\n    var flipped: Bool\n    var leading: () -> ContentA\n    var trailing: () -> ContentB\n    \n    public init(alignment: VerticalAlignment = .firstTextBaseline,\n                flipped: Bool = false,\n                @ViewBuilder control: @escaping () -> ContentB,\n                @ViewBuilder label: @escaping () -> ContentA)\n    {\n        self.alignment = alignment\n        self.flipped = flipped\n        self.leading = label\n        self.trailing = control\n    }\n    \n    private var leadingContent: some View {\n        leading()\n            .alignedLabel(alignment)\n    }\n    \n    private var trailingContent: some View {\n        trailing()\n            .alignedLabel(alignment)\n    }\n    \n    public var body: some View {\n        HStack(alignment: .customLabelAlignmentGuide) {\n            Group {\n                if flipped {\n                    trailingContent\n                } else {\n                    leadingContent\n                }\n            }\n            .trailingAlignedLabel()\n\n            if flipped {\n                leadingContent\n            } else {\n                trailingContent\n            }\n        }\n    }\n    \n}\n\n/// A placeholder view that does not show any content on screen and can be used\n/// as the label in an `AirFormControl` where alignment of the control is desired,\n/// but no label should be shown.\npublic struct AirFormHiddenLabel: View {\n    public init() { }\n    \n    public var body: some View {\n        Text(String(\"_\"))\n            .opacity(0)\n            .accessibilityHidden(true)\n    }\n}\n\npublic extension DecentFormControl where ContentA == AirFormHiddenLabel {\n    \n    init(alignment: VerticalAlignment = .firstTextBaseline,\n                @ViewBuilder control: @escaping () -> ContentB)\n    {\n        self.init(alignment: alignment, flipped: false, control: control, label: { AirFormHiddenLabel() })\n    }\n    \n}\n\npublic extension View {\n    \n    func alignedLabel(_ alignment: VerticalAlignment = .firstTextBaseline) -> some View {\n        alignmentGuide(.customLabelAlignmentGuide, computeValue: { $0[alignment] })\n    }\n    \n    func trailingAlignedLabel() -> some View {\n        alignmentGuide(.customLabelAlignmentGuideHorizontal, computeValue: { $0[.trailing] })\n    }\n    \n}\n\npublic extension VerticalAlignment {\n    private struct CustomLabelAlignment: AlignmentID {\n        static func defaultValue(in context: ViewDimensions) -> CGFloat {\n            context[VerticalAlignment.bottom]\n        }\n    }\n\n    static let customLabelAlignmentGuide = VerticalAlignment(\n        CustomLabelAlignment.self\n    )\n}\n\npublic extension HorizontalAlignment {\n    private struct CustomLabelAlignmentHorizontal: AlignmentID {\n        static func defaultValue(in context: ViewDimensions) -> CGFloat {\n            context[HorizontalAlignment.trailing]\n        }\n    }\n\n    static let customLabelAlignmentGuideHorizontal = HorizontalAlignment(\n        CustomLabelAlignmentHorizontal.self\n    )\n}\n"
  },
  {
    "path": "VirtualUI/Source/Components/EqualWidthHStack.swift",
    "content": "/*\nCopyright © 2022 Apple Inc.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n*/\n\nimport SwiftUI\n\n/// A custom horizontal stack that offers all its subviews the width of its\n/// widest subview.\n///\n/// This custom layout arranges views horizontally, giving each the width needed\n/// by the widest subview.\n///\n/// ![Three rectangles arranged in a horizontal line. Each rectangle contains\n/// one smaller rectangle. The smaller rectangles have varying widths. Dashed\n/// lines above each of the container rectangles show that the larger rectangles\n/// all have the same width as each other.](voting-buttons)\n///\n/// The custom stack implements the protocol's two required methods. First,\n/// ``sizeThatFits(proposal:subviews:cache:)`` reports the container's size,\n/// given a set of subviews.\n///\n/// ```swift\n/// let maxSize = maxSize(subviews: subviews)\n/// let spacing = spacing(subviews: subviews)\n/// let totalSpacing = spacing.reduce(0) { $0 + $1 }\n///\n/// return CGSize(\n///     width: maxSize.width * CGFloat(subviews.count) + totalSpacing,\n///     height: maxSize.height)\n/// ```\n///\n/// This method combines the largest size in each dimension with the horizontal\n/// spacing between subviews to find the container's total size. Then,\n/// ``placeSubviews(in:proposal:subviews:cache:)`` tells each of the subviews\n/// where to appear within the layout's bounds.\n///\n/// ```swift\n/// let maxSize = maxSize(subviews: subviews)\n/// let spacing = spacing(subviews: subviews)\n///\n/// let placementProposal = ProposedViewSize(width: maxSize.width, height: maxSize.height)\n/// var nextX = bounds.minX + maxSize.width / 2\n///\n/// for index in subviews.indices {\n///     subviews[index].place(\n///         at: CGPoint(x: nextX, y: bounds.midY),\n///         anchor: .center,\n///         proposal: placementProposal)\n///     nextX += maxSize.width + spacing[index]\n/// }\n/// ```\n///\n/// The method creates a single size proposal for the subviews, and then uses\n/// that, along with a point that changes for each subview, to arrange the\n/// subviews in a horizontal line with default spacing.\nstruct EqualWidthHStack: Layout {\n    /// Returns a size that the layout container needs to arrange its subviews\n    /// horizontally.\n    /// - Tag: sizeThatFitsHorizontal\n    func sizeThatFits(\n        proposal: ProposedViewSize,\n        subviews: Subviews,\n        cache: inout Void\n    ) -> CGSize {\n        guard !subviews.isEmpty else { return .zero }\n\n        let maxSize = maxSize(subviews: subviews)\n        let spacing = spacing(subviews: subviews)\n        let totalSpacing = spacing.reduce(0) { $0 + $1 }\n\n        return CGSize(\n            width: maxSize.width * CGFloat(subviews.count) + totalSpacing,\n            height: maxSize.height)\n    }\n\n    /// Places the subviews in a horizontal stack.\n    /// - Tag: placeSubviewsHorizontal\n    func placeSubviews(\n        in bounds: CGRect,\n        proposal: ProposedViewSize,\n        subviews: Subviews,\n        cache: inout Void\n    ) {\n        guard !subviews.isEmpty else { return }\n\n        let maxSize = maxSize(subviews: subviews)\n        let spacing = spacing(subviews: subviews)\n\n        let placementProposal = ProposedViewSize(width: maxSize.width, height: maxSize.height)\n        var nextX = bounds.minX + maxSize.width / 2\n\n        for index in subviews.indices {\n            subviews[index].place(\n                at: CGPoint(x: nextX, y: bounds.midY),\n                anchor: .center,\n                proposal: placementProposal)\n            nextX += maxSize.width + spacing[index]\n        }\n    }\n\n    /// Finds the largest ideal size of the subviews.\n    private func maxSize(subviews: Subviews) -> CGSize {\n        let subviewSizes = subviews.map { $0.sizeThatFits(.unspecified) }\n        let maxSize: CGSize = subviewSizes.reduce(.zero) { currentMax, subviewSize in\n            CGSize(\n                width: max(currentMax.width, subviewSize.width),\n                height: max(currentMax.height, subviewSize.height))\n        }\n\n        return maxSize\n    }\n\n    /// Gets an array of preferred spacing sizes between subviews in the\n    /// horizontal dimension.\n    private func spacing(subviews: Subviews) -> [CGFloat] {\n        subviews.indices.map { index in\n            guard index < subviews.count - 1 else { return 0 }\n            return subviews[index].spacing.distance(\n                to: subviews[index + 1].spacing,\n                along: .horizontal)\n        }\n    }\n}\n"
  },
  {
    "path": "VirtualUI/Source/Components/HostingWindowController/FB18383725Window.swift",
    "content": "//\n//  FB18383725Window.swift\n//  VirtualBuddy\n//\n//  Created by Guilherme Rambo on 25/6/25.\n//\n\nimport SwiftUI\nimport Combine\nimport BuddyKit\n\n/// Implements a workaround for a crash that occurs in macOS 26 when built with the macOS 26 SDK.\nclass FB18383725Window: NSWindow {\n    private var toolbarToRestoreAfterFullScreenTransition: NSToolbar?\n\n    private var cancellables = Set<AnyCancellable>()\n\n    override func toggleFullScreen(_ sender: Any?) {\n        guard #available(macOS 26, *) else {\n            return super.toggleFullScreen(sender)\n        }\n\n        /**\n         Filed as `FB18383725`.\n\n         Really ugly hack to work around a crash in macOS 26 when built with the macOS 26 SDK.\n         The crash occurs when the window content is an `NSHostingController` and the root view has a `toolbar` modifier:\n\n         ```\n         *** Terminating app due to uncaught exception 'NSGenericException', reason: 'The window has been marked as needing another Layout Window pass, but it has already had more Layout Window passes than there are views in the window.\n         <NSWindow: 0xb21e4d000> 0x20d8 (8408) {{0, -180}, {1512, 1077}} en'\n         terminating due to uncaught exception of type NSException\n         */\n\n        /// Grab the current toolbar, then remove it from the window.\n        toolbarToRestoreAfterFullScreenTransition = toolbar\n        toolbar = nil\n\n        /// Trigger full screen toggle.\n        super.toggleFullScreen(sender)\n\n        /// When window finishes entering or exiting full screen, set its toolbar back to the previous value.\n        guard cancellables.isEmpty else { return }\n\n        NotificationCenter.default\n            .publisher(for: NSWindow.didEnterFullScreenNotification, object: self)\n            .sink { [weak self] _ in\n                self?.restoreToolbarAfterFullScreenTransitionIfNeeded()\n            }\n            .store(in: &cancellables)\n\n        NotificationCenter.default\n            .publisher(for: NSWindow.didExitFullScreenNotification, object: self)\n            .sink { [weak self] _ in\n                self?.restoreToolbarAfterFullScreenTransitionIfNeeded()\n            }\n            .store(in: &cancellables)\n    }\n\n    private func restoreToolbarAfterFullScreenTransitionIfNeeded() {\n        guard #available(macOS 26, *) else { return }\n\n        UILog(#function)\n\n        toolbar = toolbarToRestoreAfterFullScreenTransition\n    }\n}\n"
  },
  {
    "path": "VirtualUI/Source/Components/HostingWindowController/HostingWindowController.swift",
    "content": "import Cocoa\nimport SwiftUI\nimport VirtualCore\n\nlet defaultHostingWindowStyleMask: NSWindow.StyleMask = [.titled, .miniaturizable, .resizable, .closable, .fullSizeContentView]\n\npublic final class HostingWindowController<Content>: NSWindowController, NSWindowDelegate where Content: View {\n\n    public typealias WindowCloseBlock = ((HostingWindowController<Content>) -> Void)\n\n    /// Invoked shortly before the hosting window controller's window is closed.\n    private var onWindowClose: WindowCloseBlock?\n\n    private static func makeDefaultWindow() -> NSWindow {\n        HostingWindow(\n            contentRect: NSRect(x: 0, y: 0, width: NSView.noIntrinsicMetric, height: NSView.noIntrinsicMetric),\n            styleMask: defaultHostingWindowStyleMask,\n            backing: .buffered,\n            defer: false,\n            screen: nil\n        )\n    }\n\n    public init(id: String? = nil, rootView: Content, windowFactory: (() -> NSWindow)? = nil, onWindowClose: WindowCloseBlock? = nil) {\n        let window = windowFactory?() ?? Self.makeDefaultWindow()\n\n        if let id {\n            window.identifier = .init(id)\n        }\n        \n        super.init(window: window)\n\n        let controller = NSHostingController(\n            rootView: rootView\n                .environment(\\.closeWindow, { [weak self] in self?.close() })\n                .environment(\\.cocoaWindow, window)\n        )\n        \n        contentViewController = controller\n        window.setContentSize(controller.view.fittingSize)\n        \n        window.isReleasedWhenClosed = false\n        window.delegate = self\n\n        self.onWindowClose = onWindowClose\n    }\n    \n    public required init?(coder: NSCoder) {\n        fatalError()\n    }\n    \n    public override func showWindow(_ sender: Any?) {\n        super.showWindow(sender)\n        \n        window?.center()\n    }\n\n    public func windowWillClose(_ notification: Notification) {\n        contentViewController = nil\n        \n        onWindowClose?(self)\n        \n        /// Ensures references in the closure are not retained past the lifetime of the window.\n        /// Opening a new hosting window controller requires going through ``OpenCocoaWindowAction``,\n        /// which sets up the callback again if needed.\n        onWindowClose = nil\n    }\n    \n    var viewRendersWindowChrome: Bool = false {\n        didSet {\n            guard viewRendersWindowChrome != oldValue else { return }\n            DispatchQueue.main.async { self.configureChrome() }\n        }\n    }\n    \n    private func configureChrome() {\n        if viewRendersWindowChrome {\n            window?.styleMask.remove(.titled)\n            window?.styleMask.insert(.borderless)\n            window?.backgroundColor = .clear\n            window?.isOpaque = false\n        } else {\n            window?.styleMask.remove(.borderless)\n            window?.styleMask.insert(.titled)\n            window?.backgroundColor = .windowBackgroundColor\n            window?.isOpaque = true\n        }\n    }\n    \n    var onWindowOcclusionStateChanged: ((NSWindow.OcclusionState) -> Void)? = nil\n    \n    public func windowDidChangeOcclusionState(_ notification: Notification) {\n        guard let state = window?.occlusionState else { return }\n        onWindowOcclusionStateChanged?(state)\n    }\n    \n    public func window(_ window: NSWindow, willUseFullScreenPresentationOptions proposedOptions: NSApplication.PresentationOptions = []) -> NSApplication.PresentationOptions {\n        [\n            .autoHideDock,\n            .autoHideMenuBar,\n            .autoHideToolbar,\n            .fullScreen\n        ]\n    }\n\n    var confirmBeforeClosingCallback: () async -> Bool {\n        get {\n            guard let hostingWindow = window as? HostingWindow else {\n                preconditionFailure(\"confirmBeforeClosing can't be used with custom window types in HostingWindowController\")\n            }\n            return hostingWindow.confirmBeforeClosingCallback\n        }\n        set {\n            precondition(window is HostingWindow, \"confirmBeforeClosing can't be used with custom window types in HostingWindowController\")\n            (window as? HostingWindow)?.confirmBeforeClosingCallback = newValue\n        }\n    }\n    \n}\n\nextension HostingWindowController: WindowChromeConsumer { }\n\nprotocol WindowChromeConsumer: AnyObject {\n    var viewRendersWindowChrome: Bool { get set }\n    var onWindowOcclusionStateChanged: ((NSWindow.OcclusionState) -> Void)? { get set }\n    var confirmBeforeClosingCallback: () async -> Bool { get set }\n}\n\nfileprivate final class HostingWindow: VBRestorableWindow {\n    \n    override var canBecomeKey: Bool { true }\n    override var canBecomeMain: Bool { true }\n    override var acceptsFirstResponder: Bool { true }\n\n    var confirmBeforeClosingCallback: () async -> Bool = { true }\n\n    var enableMemoryLeakAssertion = true\n\n    override func performClose(_ sender: Any?) {\n        /// This addresses a weird issue introduced after #257 where for some reason `VMController`\n        /// was being retained by a SwiftUI button when closing a VM window using Command+W\n        /// after opening multiple VM windows.\n        /// The button that was retaining the controller though a closure context was one of\n        /// the toolbar buttons, and for some reason not going directly through our `close()`\n        /// implementation here was triggering the leak :(\n        close()\n    }\n\n    override func close() {\n        Task { @MainActor in\n            guard await confirmBeforeClosingCallback() else { return }\n            await MainActor.run { closeWithoutConfirmation() }\n        }\n    }\n\n    private func closeWithoutConfirmation() {\n        super.close()\n\n        VBMemoryLeakDebugAssertions.vb_objectShouldBeReleasedSoon(self)\n    }\n\n    deinit {\n        VBMemoryLeakDebugAssertions.vb_objectIsBeingReleased(self)\n    }\n\n}\n"
  },
  {
    "path": "VirtualUI/Source/Components/HostingWindowController/OpenCocoaWindowAction.swift",
    "content": "import SwiftUI\nimport Combine\n\npublic extension EnvironmentValues {\n    \n    /// Call to open a SwiftUI view hierarchy in a new macOS window.\n    /// The SwiftUI view defines the size and resizability of the window, use SwiftUI's `frame`\n    /// modifier to customize the behavior.\n    ///\n    /// See ``OpenCocoaWindowAction`` for more information.\n    fileprivate(set) var openCocoaWindow: OpenCocoaWindowAction {\n        get { self[OpenCocoaWindowActionKey.self] }\n        set { self[OpenCocoaWindowActionKey.self] = newValue }\n    }\n    \n}\n\nfileprivate struct OpenCocoaWindowActionKey: EnvironmentKey {\n    static let defaultValue = OpenCocoaWindowAction.default\n}\n\n/// An action that opens a new Mac window with SwiftUI content.\n///\n/// Read the `openCocoaWindow` environment value to get an instance of this structure for a given Environment.\n/// Call the instance to open a new window with the SwiftUI content provided. You call the instance directly because it defines a ``callAsFunction(_:)``\n/// method that Swift calls when you call the instance.\n///\n/// ## Example:\n///\n/// ```swift\n/// struct TestNewWindowView: View {\n///\n///     @Environment(\\.openCocoaWindow) private var openWindow\n///\n///     var body: some View {\n///         Button {\n///             openWindow {\n///                 Text(\"I'm in a new window\")\n///                     .frame(minWidth: 200, maxWidth: .infinity, minHeight: 200, maxHeight: .infinity)\n///             }\n///         } label: {\n///             Text(\"Open New Window\")\n///         }\n///     }\n///\n/// }\n/// ```\npublic struct OpenCocoaWindowAction {\n\n    public static let `default` = OpenCocoaWindowAction()\n\n    /// A token represents a window that's been opened by ``OpenCocoaWindowAction``.\n    ///\n    /// You may use the token to close the window from outside the view hierarchy hosted within it, such\n    /// as from the SwiftUI view hierarchy that created the window in the first place.\n    ///\n    /// ``Token`` is an `ObservableObject`, so your view may observe it in order to be notified of\n    /// when the window has been closed via the ``isOpen`` published property.\n    public final class Token: Hashable, ObservableObject {\n        \n        private var id: String\n\n        init(id: String) {\n            self.id = id\n        }\n        \n        fileprivate lazy var cancellables = Set<AnyCancellable>()\n        \n        fileprivate func invalidate() {\n            cancellables.removeAll()\n        }\n        \n        /// `true` when the window represented by this token has been opened and hasn't yet been closed\n        /// by either the user or your code.\n        @Published public internal(set) var isOpen = false\n        \n        /// Closes the window that was opened using ``OpenCocoaWindowAction``.\n        @MainActor\n        public func close() {\n            OpenCocoaWindowManager.shared[self]?.close()\n        }\n    }\n    \n    @MainActor\n    private var manager: OpenCocoaWindowManager { .shared }\n    \n    /// Opens a new Mac window with the SwiftUI content provided.\n    /// - Parameter content: A view builder that provides the contents of the window.\n    /// - Returns: A ``Token`` that can be used to monitor or modify the state of the new window.\n    ///\n    /// Don’t call this method directly. Swift calls it when you call the ``OpenCocoaWindowAction``\n    /// structure that you get from the Environment.\n    @MainActor\n    @discardableResult\n    public func callAsFunction<Content>(id: String? = nil, animationBehavior: NSWindow.AnimationBehavior? = nil, @ViewBuilder _ content: @escaping () -> Content, onClose: (() -> Void)? = nil) -> Token where Content: View {\n        let token = Token(id: id ?? UUID().uuidString)\n\n        if let existingController = manager[token] {\n            existingController.showWindow(nil)\n        } else {\n            let controller = HostingWindowController(id: id, rootView: content(), onWindowClose: { _ in onClose?() })\n\n            if let animationBehavior {\n                controller.window?.animationBehavior = animationBehavior\n            }\n\n            manager[token] = controller\n        }\n        \n        return token\n    }\n    \n    /// Closes a window previously opened through ``OpenCocoaWindowAction``.\n    /// - Parameter token: The token returned from calling ``OpenCocoaWindowAction``  as a function.\n    ///\n    /// You may also just call ``Token/close()`` instead on your token, which has the same effect.\n    @MainActor\n    public func close(_ token: Token) {\n        OpenCocoaWindowManager.shared[token]?.close()\n    }\n\n    @MainActor\n    public func close(id: String) {\n        close(Token(id: id))\n    }\n\n    @MainActor\n    public func hasWindow(for id: String) -> Bool {\n        OpenCocoaWindowManager.shared[Token(id: id)] != nil\n    }\n\n}\n\n// MARK: - Window Manager\n\n@MainActor\nprivate final class OpenCocoaWindowManager {\n    \n    static let shared = OpenCocoaWindowManager()\n    \n    private lazy var windowControllers = [OpenCocoaWindowAction.Token: NSWindowController]()\n    \n    subscript(_ token: OpenCocoaWindowAction.Token) -> NSWindowController? {\n        get { windowControllers[token] }\n        set {\n            windowControllers[token] = newValue\n            \n            if let newValue = newValue {\n                openWindow(for: newValue, token: token)\n            }\n        }\n    }\n    \n    private func openWindow(for controller: NSWindowController, token: OpenCocoaWindowAction.Token) {\n        controller.window?.isReleasedWhenClosed = true\n        controller.showWindow(nil)\n        \n        setupObservations(for: controller, token: token)\n    }\n    \n    private func setupObservations(for controller: NSWindowController, token: OpenCocoaWindowAction.Token) {\n        guard let window = controller.window else { return }\n        \n        NotificationCenter.default\n            .publisher(for: NSWindow.willCloseNotification, object: window)\n            .sink { [weak self] note in\n                guard let self = self else { return }\n                guard let window = note.object as? NSWindow else { return }\n                \n                self.handleWindowWillClose(window, token: token)\n            }\n            .store(in: &token.cancellables)\n    }\n    \n    private func handleWindowWillClose(_ window: NSWindow, token: OpenCocoaWindowAction.Token) {\n        token.isOpen = false\n        \n        windowControllers[token] = nil\n    }\n    \n}\n\n// MARK: - Token conformances\n\npublic extension OpenCocoaWindowAction.Token {\n    func hash(into hasher: inout Hasher) {\n        hasher.combine(id)\n    }\n    \n    static func == (lhs: OpenCocoaWindowAction.Token, rhs: OpenCocoaWindowAction.Token) -> Bool {\n        lhs.id == rhs.id\n    }\n}\n"
  },
  {
    "path": "VirtualUI/Source/Components/HostingWindowController/VBRestorableWindow+Resizing.swift",
    "content": "//\n//  VBRestorableWindow+Resizing.swift\n//  VirtualUI\n//\n//  Created by Guilherme Rambo on 09/03/23.\n//\n\nimport Cocoa\nimport AVFoundation\nimport VirtualCore\n\nextension VBRestorableWindow {\n\n    func resize(to size: VirtualMachineSessionUI.WindowSize, for display: VBDisplayDevice) {\n        guard let screen else { return }\n\n        let targetWidth: CGFloat\n        let targetHeight: CGFloat\n        let displaySize = CGSize(width: display.width, height: display.height)\n        let toolbarHeight: CGFloat = frameRect(forContentRect: NSRect()).height\n        // screen.visibleFrame.height is a \"net\" value after taking into account menu bar, dock, etc.\n        let availableHeight: CGFloat = screen.visibleFrame.height - toolbarHeight\n\n        switch size {\n        case .pointAccurate:\n            targetWidth = CGFloat(display.width)\n            targetHeight = CGFloat(display.height)\n        case .pixelAccurate:\n            targetWidth = CGFloat(display.width) / screen.backingScaleFactor\n            targetHeight = CGFloat(display.height) / screen.backingScaleFactor\n        case .fitScreen:\n            let windowAspectRatio = displaySize.width / displaySize.height\n            let containerAspectRatio = screen.visibleFrame.width / availableHeight\n            let widthFirst = windowAspectRatio > containerAspectRatio\n\n            if widthFirst {\n                targetWidth = screen.visibleFrame.width\n                targetHeight = targetWidth / windowAspectRatio\n            } else {\n                targetHeight = availableHeight\n                targetWidth = availableHeight * windowAspectRatio\n            }\n        }\n\n        // clamped targetSize to the available screen's real estate\n        let targetSize = CGSize(\n            width: min(targetWidth, screen.visibleFrame.width),\n            height: min(targetHeight, availableHeight)\n        )\n\n        let targetRect = NSRect(\n            x: screen.visibleFrame.origin.x + screen.visibleFrame.width / 2 - targetSize.width / 2,\n            y: screen.visibleFrame.origin.y + availableHeight / 2 - targetSize.height / 2,\n            width: targetSize.width,\n            height: targetSize.height\n        )\n\n        let frameRect = frameRect(forContentRect: targetRect)\n\n        withFrameConstraintsDisabled(size != .fitScreen) {\n            setFrame(frameRect, display: true)\n        }\n    }\n\n    func applyAspectRatio(_ ratio: CGSize?) {\n        guard let ratio else {\n            resizeIncrements = CGSize(width: 1, height: 1)\n            return\n        }\n\n        contentAspectRatio = ratio\n\n        let newFrame = frameRect(forContentRect: AVMakeRect(aspectRatio: ratio, insideRect: contentRect(forFrameRect: frame)))\n\n        setFrame(newFrame, display: true)\n    }\n\n}\n"
  },
  {
    "path": "VirtualUI/Source/Components/HostingWindowController/VBRestorableWindow.swift",
    "content": "//\n//  VBRestorableWindow.swift\n//  VirtualUI\n//\n//  Created by Guilherme Rambo on 09/03/23.\n//\n\nimport Cocoa\n\nclass VBRestorableWindow: FB18383725Window {\n\n    override func close() {\n        vbSaveFrame()\n\n        super.close()\n    }\n\n    private var savedFrameKey: String? {\n        guard let identifier, let screen else { return nil }\n        guard let screenNumber = screen.deviceDescription[.init(\"NSScreenNumber\")] as? Int else { return nil }\n\n        return \"window-\\(identifier.rawValue)-\\(screenNumber)\"\n    }\n\n    var hasSavedFrame: Bool { savedFrame != nil }\n\n    private var savedFrame: NSRect? {\n        guard let savedFrameKey else { return nil }\n        guard let dict = UserDefaults.standard.dictionary(forKey: savedFrameKey) else { return nil }\n        return NSRect(dictionaryRepresentation: dict as CFDictionary)\n    }\n\n    private var applicationWillTerminateObserver: Any?\n\n    private var keyRequested = false\n\n    override func makeKey() {\n        super.makeKey()\n\n        defer { keyRequested = true }\n\n        if applicationWillTerminateObserver == nil {\n            applicationWillTerminateObserver = NotificationCenter.default.addObserver(forName: NSApplication.willTerminateNotification, object: nil, queue: nil, using: { [weak self] _ in\n                self?.vbSaveFrame()\n            })\n        }\n\n        if !keyRequested, let savedFrame {\n            setFrame(savedFrame, display: true)\n        }\n    }\n\n    override func center() {\n        guard savedFrame == nil else {\n            makeKey()\n            return\n        }\n\n        super.center()\n    }\n\n    private func vbSaveFrame() {\n        guard let savedFrameKey else { return }\n        UserDefaults.standard.set(frame.dictionaryRepresentation, forKey: savedFrameKey)\n        UserDefaults.standard.synchronize()\n    }\n\n    private var frameConstraintsDisabled = false\n\n    func withFrameConstraintsDisabled(_ disabled: Bool = true, perform block: () -> Void) {\n        NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(resetConstraintsDisabledFlag), object: nil)\n\n        frameConstraintsDisabled = disabled\n\n        block()\n\n        perform(#selector(resetConstraintsDisabledFlag), with: nil, afterDelay: 0.2)\n    }\n\n    override func constrainFrameRect(_ frameRect: NSRect, to screen: NSScreen?) -> NSRect {\n        guard frameConstraintsDisabled else {\n            return super.constrainFrameRect(frameRect, to: screen)\n        }\n\n        return frameRect\n    }\n\n    @objc private func resetConstraintsDisabledFlag() {\n        frameConstraintsDisabled = false\n    }\n\n}\n"
  },
  {
    "path": "VirtualUI/Source/Components/HostingWindowController/WindowEnvironment.swift",
    "content": "//\n//  WindowEnvironment.swift\n//  StatusBuddy\n//\n//  Created by Guilherme Rambo on 21/12/21.\n//  Copyright © 2021 Guilherme Rambo. All rights reserved.\n//\n\nimport SwiftUI\nimport Combine\n\n// MARK: - Public API\n\npublic extension EnvironmentValues {\n    \n    /// Closes the window that's hosting this view.\n    /// Only available when the view hierarchy is being presented with `HostingWindowController`.\n    var closeWindow: () -> Void {\n        get { self[CloseWindowEnvironmentKey.self] }\n        set { self[CloseWindowEnvironmentKey.self] = newValue }\n    }\n    \n}\n\npublic extension View {\n    \n    /// Sets the title for the window that contains this SwiftUI view.\n    /// Only available when the view hierarchy is being presented with `HostingWindowController`.\n    func windowTitle(_ title: String) -> some View {\n        environment(\\.windowTitle, title)\n    }\n\n    /// Sets the title visibility for the window that contains this SwiftUI view.\n    /// Only available when the view hierarchy is being presented with `HostingWindowController`.\n    func windowTitleHidden(_ hidden: Bool) -> some View {\n        environment(\\.windowTitleVisibility, hidden ? .hidden : .visible)\n    }\n\n    /// Sets whether the title bar background is hidden for the window that contains this SwiftUI view.\n    /// Only available when the view hierarchy is being presented with `HostingWindowController`.\n    func windowTitleBarTransparent(_ transparent: Bool) -> some View {\n        environment(\\.windowTitleBarTransparent, transparent)\n    }\n    \n    /// When `true`, indicates that the view hierarchy is responsible for render its own chrome for\n    /// the Cocoa window that's hosting it. Causes `HostingWindowController` to configure\n    /// its window to provide no chrome.\n    func rendersWindowChrome(_ flag: Bool = true) -> some View {\n        environment(\\.rendersWindowChrome, flag)\n    }\n    \n    /// When `true`, the hosting window can be moved by either clicking and dragging in the title bar,\n    /// or by clicking and dragging the window's background when there's no title bar.\n    /// Only available when the view hierarchy is being presented with `HostingWindowController`.\n    func windowMovable(_ flag: Bool = true) -> some View {\n        environment(\\.windowMovable, flag)\n    }\n    \n    /// Sets the level of the Cocoa window that's hosting this view hierarchy.\n    /// Only available when the view hierarchy is being presented with `HostingWindowController`.\n    func windowLevel(_ level: NSWindow.Level) -> some View {\n        environment(\\.windowLevel, level)\n    }\n    \n    /// Sets the style mask of the Cocoa window that's hosting this view hierarchy.\n    /// Note that usage of `rendersWindowChrome` may override the style mask set using this modifier and vice-versa.\n    /// Only available when the view hierarchy is being presented with `HostingWindowController`.\n    func windowStyleMask(_ mask: NSWindow.StyleMask) -> some View {\n        environment(\\.windowStyleMask, mask)\n    }\n    \n    /// Performs the specific block when the window hosting the view hierarchy changes its occlusion state.\n    func onWindowOcclusionStateChanged(perform block: @escaping (NSWindow.OcclusionState) -> Void) -> some View {\n        environment(\\.onWindowOcclusionStateChanged, block)\n    }\n\n    func confirmBeforeClosingWindow(callback: @escaping () async -> Bool) -> some View {\n        environment(\\.confirmBeforeClosingWindow, callback)\n    }\n\n    func onWindowKeyChange(perform callback: @escaping (Bool) -> Void) -> some View {\n        modifier(OnWindowKeyChangedModifier(callback: callback))\n    }\n    \n}\n\n// MARK: - Hosting Window Environment Keys\n\nprivate struct CloseWindowEnvironmentKey: EnvironmentKey {\n    static let defaultValue: () -> Void = { }\n}\n\nprivate struct ConfirmBeforeClosingWindowEnvironmentKey: EnvironmentKey {\n    static let defaultValue: () async -> Bool = { false }\n}\n\nprivate struct WindowTitleEnvironmentKey: EnvironmentKey {\n    static let defaultValue: String = \"\"\n}\n\nprivate struct WindowTitleVisibilityEnvironmentKey: EnvironmentKey {\n    static let defaultValue: NSWindow.TitleVisibility = .visible\n}\n\nprivate struct WindowTitleBarTransparentEnvironmentKey: EnvironmentKey {\n    static let defaultValue: Bool = false\n}\n\nprivate struct HostingWindowKey: EnvironmentKey {\n    static let defaultValue: () -> NSWindow? = { nil }\n}\n\nprivate struct RendersWindowChromeEnvironmentKey: EnvironmentKey {\n    static let defaultValue: Bool = true\n}\n\nprivate struct WindowMovableEnvironmentKey: EnvironmentKey {\n    static let defaultValue: Bool = true\n}\n\nprivate struct WindowLevelEnvironmentKey: EnvironmentKey {\n    static let defaultValue: NSWindow.Level = .normal\n}\n\nprivate struct WindowStyleMaskEnvironmentKey: EnvironmentKey {\n    static let defaultValue: NSWindow.StyleMask = defaultHostingWindowStyleMask\n}\n\nprivate struct WindowOnOcclusionStateChangedEnvironmentKey: EnvironmentKey {\n    static let defaultValue: ((NSWindow.OcclusionState) -> Void)? = nil\n}\n\nextension EnvironmentValues {\n    \n    private var windowChromeConsumer: WindowChromeConsumer? {\n        cocoaWindow?.windowController as? WindowChromeConsumer\n    }\n    \n    /// Set and used internally by `HostingWindowController`.\n    var cocoaWindow: NSWindow? {\n        get { self[HostingWindowKey.self]() }\n        set { self[HostingWindowKey.self] = { [weak newValue] in newValue } }\n    }\n    \n    var windowTitle: String {\n        get { self[WindowTitleEnvironmentKey.self] }\n        set {\n            self[WindowTitleEnvironmentKey.self] = newValue\n            cocoaWindow?.title = newValue\n        }\n    }\n\n    var windowTitleVisibility: NSWindow.TitleVisibility {\n        get { self[WindowTitleVisibilityEnvironmentKey.self] }\n        set {\n            self[WindowTitleVisibilityEnvironmentKey.self] = newValue\n            cocoaWindow?.titleVisibility = newValue\n        }\n    }\n\n    var windowTitleBarTransparent: Bool {\n        get { self[WindowTitleBarTransparentEnvironmentKey.self] }\n        set {\n            self[WindowTitleBarTransparentEnvironmentKey.self] = newValue\n            cocoaWindow?.titlebarAppearsTransparent = newValue\n        }\n    }\n    \n    var rendersWindowChrome: Bool {\n        get { self[RendersWindowChromeEnvironmentKey.self] }\n        set {\n            self[RendersWindowChromeEnvironmentKey.self] = newValue\n            windowChromeConsumer?.viewRendersWindowChrome = newValue\n        }\n    }\n    \n    var windowMovable: Bool {\n        get { self[WindowMovableEnvironmentKey.self] }\n        set {\n            self[WindowMovableEnvironmentKey.self] = newValue\n            cocoaWindow?.isMovable = newValue\n            cocoaWindow?.isMovableByWindowBackground = newValue\n        }\n    }\n    \n    var windowLevel: NSWindow.Level {\n        get { self[WindowLevelEnvironmentKey.self] }\n        set {\n            self[WindowLevelEnvironmentKey.self] = newValue\n            cocoaWindow?.level = newValue\n        }\n    }\n    \n    var windowStyleMask: NSWindow.StyleMask {\n        get { self[WindowStyleMaskEnvironmentKey.self] }\n        set {\n            self[WindowStyleMaskEnvironmentKey.self] = newValue\n            guard let cocoaWindow = cocoaWindow else {\n                return\n            }\n\n            var effectiveNewValue = newValue\n            \n            // Can't remove .fullScreen when the window is in full screen (causes crash).\n            if cocoaWindow.styleMask.contains(.fullScreen) {\n                effectiveNewValue.insert(.fullScreen)\n            }\n            \n            // TODO: HACK!\n            /// Setting styleMask without this dispatch results in a crash when\n            /// built with the macOS 15 SDK and running under macOS 15.\n            /// The crash is a precondition failure: `setting value during update: 360192`.\n            DispatchQueue.main.async {\n                cocoaWindow.styleMask = effectiveNewValue\n            }\n        }\n    }\n    \n    var onWindowOcclusionStateChanged: ((NSWindow.OcclusionState) -> Void)? {\n        get { self[WindowOnOcclusionStateChangedEnvironmentKey.self] }\n        set {\n            self[WindowOnOcclusionStateChangedEnvironmentKey.self] = newValue\n            windowChromeConsumer?.onWindowOcclusionStateChanged = newValue\n        }\n    }\n\n    var confirmBeforeClosingWindow: () async -> Bool {\n        get { self[ConfirmBeforeClosingWindowEnvironmentKey.self] }\n        set {\n            self[ConfirmBeforeClosingWindowEnvironmentKey.self] = newValue\n            windowChromeConsumer?.confirmBeforeClosingCallback = newValue\n        }\n    }\n    \n}\n\nprivate struct OnWindowKeyChangedModifier: ViewModifier {\n\n    var callback: (Bool) -> Void\n\n    @Environment(\\.cocoaWindow)\n    private var window\n\n    func body(content: Content) -> some View {\n        content\n            .onReceive(NotificationCenter.default.publisher(for: NSWindow.didBecomeKeyNotification, object: window)) { notification in\n                guard notification.object as? NSWindow === window else { return }\n                callback(true)\n            }\n            .onReceive(NotificationCenter.default.publisher(for: NSWindow.didResignKeyNotification, object: window)) { notification in\n                guard notification.object as? NSWindow === window else { return }\n                callback(false)\n            }\n    }\n\n}\n"
  },
  {
    "path": "VirtualUI/Source/Components/KeyboardNavigationModifier.swift",
    "content": "import SwiftUI\n\nextension View {\n    @ViewBuilder\n    func keyboardNavigation(autofocus: Bool = true, onMove: @escaping (_ direction: MoveCommandDirection) -> Void) -> some View {\n        if #available(macOS 14.0, *) {\n            modifier(KeyboardNavigationModifier_Modern(autofocus: autofocus, onMove: onMove))\n        } else {\n            modifier(KeyboardNavigationModifier_Legacy(autofocus: autofocus, onMove: onMove))\n        }\n    }\n}\n\n@available(macOS 14.0, *)\nprivate struct KeyboardNavigationModifier_Modern: ViewModifier {\n    var autofocus: Bool\n    var onMove: (MoveCommandDirection) -> Void\n\n    @FocusState private var isFocused\n\n    func body(content: Content) -> some View {\n        content\n            .focusable(true)\n            .focused($isFocused)\n            .onMoveCommand { direction in\n                onMove(direction)\n            }\n            .focusEffectDisabled()\n            .task {\n                guard autofocus else { return }\n                isFocused = true\n            }\n    }\n\n}\n\nprivate struct KeyboardNavigationModifier_Legacy: ViewModifier {\n    var autofocus: Bool\n    var onMove: (MoveCommandDirection) -> Void\n\n    @FocusState private var isFocused\n\n    func body(content: Content) -> some View {\n        content\n            .overlay {\n                /// Horrible hack to hide the focus ring while still allowing for keyboard navigation.\n                Rectangle()\n                    .frame(width: 0, height: 0)\n                    .opacity(0)\n                    .focusable(true)\n                    .focused($isFocused)\n                    .onMoveCommand { direction in\n                        onMove(direction)\n                    }\n            }\n            .task {\n                guard autofocus else { return }\n                isFocused = true\n            }\n    }\n}\n\nextension View {\n    @ViewBuilder\n    func backported_focusEffectDisabled(_ disabled: Bool = true) -> some View {\n        if #available(macOS 14.0, *) {\n            focusEffectDisabled(disabled)\n        } else {\n            self\n        }\n    }\n}\n"
  },
  {
    "path": "VirtualUI/Source/Components/LogConsole.swift",
    "content": "import SwiftUI\nimport VirtualCore\nimport UniformTypeIdentifiers\n\nstruct LogConsole: View {\n    \n    @StateObject private var streamer: LogStreamer\n\n    init(predicate: LogStreamer.Predicate) {\n        self._streamer = .init(wrappedValue: LogStreamer(predicate: predicate))\n    }\n\n    init(streamer: LogStreamer) {\n        self._streamer = .init(wrappedValue: streamer)\n    }\n\n    @State private var searchTerm = \"\"\n\n    private var filteredEvents: [LogEntry] {\n        guard searchTerm.count >= 3 else { return streamer.events }\n        return streamer.events.filter {\n            $0.message.localizedCaseInsensitiveContains(searchTerm)\n        }\n    }\n\n    var body: some View {\n        ScrollView(.vertical) {\n            LazyVStack(alignment: .leading, spacing: 8) {\n                ForEach(filteredEvents) { entry in\n                    Text(entry.formattedTime + \" \")\n                        .foregroundColor(.secondary)\n                    + Text(entry.message)\n                        .foregroundColor(entry.level.color)\n                }\n            }\n            .font(.system(.body).monospaced())\n            .padding(.horizontal)\n            .padding(.top, 6)\n            .textSelection(.enabled)\n        }\n        .safeAreaInset(edge: .top, content: { searchBar })\n        .safeAreaInset(edge: .bottom, content: { bottomBar })\n        .onAppear(perform: streamer.activate)\n    }\n\n    @ViewBuilder\n    private var searchBar: some View {\n        ZStack {\n            searchField\n        }\n        .frame(maxWidth: .infinity)\n        .padding()\n        .background(Material.thick, in: Rectangle())\n        .overlay(alignment: .bottom) {\n            Divider()\n        }\n    }\n\n    @FocusState private var searchFieldFocused: Bool\n\n    @ViewBuilder\n    private var searchField: some View {\n        TextField(\"Search Logs\", text: $searchTerm)\n            .focused($searchFieldFocused)\n            .onExitCommand {\n                if searchTerm == \"\" { searchFieldFocused = false }\n                searchTerm = \"\"\n            }\n            .textFieldStyle(.roundedBorder)\n    }\n\n    private var fullLogText: String {\n        filteredEvents\n            .map(\\.description)\n            .joined(separator: \"\\n\")\n    }\n\n    @ViewBuilder\n    private var bottomBar: some View {\n        ZStack {\n            HStack(spacing: 16) {\n                Button {\n                    NSPasteboard.general.clearContents()\n                    NSPasteboard.general.setString(fullLogText, forType: .string)\n                } label: {\n                    Text(\"Copy Text\")\n                }\n\n                Button {\n                    NSSavePanel.run(saving: Data(fullLogText.utf8), as: .logFile)\n                } label: {\n                    Text(\"Save to File…\")\n                }\n            }\n            .padding(.vertical, 8)\n            .padding(.horizontal, 14)\n            .controlGroup(Capsule(style: .continuous), level: .secondary)\n        }\n        .padding()\n        .frame(maxWidth: .infinity, alignment: .bottomTrailing)\n        .controlSize(.small)\n        .buttonStyle(.link)\n    }\n}\n\nextension LogEntry.Level {\n    var color: Color {\n        switch self {\n        case .debug:\n            return .gray.opacity(0.6)\n        case .trace:\n            return .gray.opacity(0.8)\n        case .notice:\n            return .gray.opacity(0.9)\n        case .info:\n            return .gray\n        case .default:\n            return .primary\n        case .warning:\n            return .yellow\n        case .error:\n            return .orange\n        case .fault:\n            return .red\n        case .critical:\n            return Color(nsColor: .magenta)\n        }\n    }\n}\n\n#if DEBUG\nstruct LogConsole_Previews: PreviewProvider {\n    static var previews: some View {\n        LogConsole(streamer: .preview)\n    }\n}\n#endif\n\nextension UTType {\n    static let logFile: UTType = {\n        UTType(filenameExtension: \"log\", conformingTo: .log) ?? .plainText\n    }()\n}\n"
  },
  {
    "path": "VirtualUI/Source/Components/MaterialView.swift",
    "content": "//\n//  MaterialView.swift\n//  AirUI\n//\n//  Created by Guilherme Rambo on 16/03/22.\n//\n\nimport SwiftUI\n\npublic typealias MaterialType = NSVisualEffectView.Material\npublic typealias MaterialBlendingMode = NSVisualEffectView.BlendingMode\npublic typealias MaterialState = NSVisualEffectView.State\n\npublic extension MaterialBlendingMode {\n    \n    /// Uses within window blending mode when running in SwiftUI previews,\n    /// but the behind window blending mode when running normally.\n    /// Does not affect release builds, which always return `.behindWindow`.\n    static var behindWindowForPreviews: Self {\n        #if DEBUG\n        if ProcessInfo.isSwiftUIPreview {\n            return .withinWindow\n        } else {\n            return .behindWindow\n        }\n        #else\n        return .behindWindow\n        #endif\n    }\n    \n}\n\npublic struct MaterialView: NSViewRepresentable {\n    \n    public typealias NSViewType = NSVisualEffectView\n    \n    public init() { }\n\n    public func makeNSView(context: Context) -> NSVisualEffectView {\n        let v = NSVisualEffectView(frame: .zero)\n        \n        v.material = context.environment.materialType\n        v.blendingMode = context.environment.materialBlendingMode\n        v.state = context.environment.materialState\n        \n        return v\n    }\n    \n    public func updateNSView(_ nsView: NSVisualEffectView, context: Context) {\n        if (nsView.material != context.environment.materialType) {\n            nsView.material = context.environment.materialType\n        }\n        if (nsView.blendingMode != context.environment.materialBlendingMode) {\n            nsView.blendingMode = context.environment.materialBlendingMode\n        }\n        if context.environment.accessibilityReduceTransparency {\n            nsView.state = .inactive\n        } else {\n            if (nsView.state != context.environment.materialState) {\n                nsView.state = context.environment.materialState\n            }\n        }\n    }\n    \n}\n\npublic extension View {\n    \n    /// Applies a background material to the view, clipped to the specified shape.\n    /// - Parameters:\n    ///   - type: The type of material to be used. Will use the material from the current environment if `nil`.\n    ///   - blendMode: The material blending mode. Will use the blending mode from the current environment if `nil`.\n    ///   - state: The material state. Will use the state from the current environment if `nil`.\n    ///   - shape: The shape clipping the material.\n    /// - Returns: The modified view.\n    func materialBackground<S>(_ type: MaterialType? = nil,\n                               blendMode: MaterialBlendingMode? = nil,\n                               state: MaterialState? = nil,\n                               in shape: S) -> some View where S: Shape\n    {\n        background(\n            MaterialView()\n                .clipShape(shape)\n                .materialType(type)\n                .materialBlendingMode(blendMode)\n                .materialState(state)\n        )\n    }\n    \n    @ViewBuilder\n    func materialType(_ material: MaterialType?) -> some View {\n        if let material = material {\n            environment(\\.materialType, material)\n        } else {\n            self\n        }\n    }\n    \n    @ViewBuilder\n    func materialBlendingMode(_ mode: MaterialBlendingMode?) -> some View {\n        if let mode = mode {\n            environment(\\.materialBlendingMode, mode)\n        } else {\n            self\n        }\n    }\n    \n    @ViewBuilder\n    func materialState(_ state: MaterialState?) -> some View {\n        if let state = state {\n            environment(\\.materialState, state)\n        } else {\n            self\n        }\n    }\n    \n}\n\nfileprivate struct MaterialViewStateKey: EnvironmentKey {\n    static var defaultValue: MaterialState = .active\n}\n\nfileprivate struct MaterialViewBlendingModeKey: EnvironmentKey {\n    static var defaultValue: MaterialBlendingMode = .withinWindow\n}\n\nfileprivate struct MaterialViewMaterialKey: EnvironmentKey {\n    static var defaultValue: MaterialType = .popover\n}\n\nfileprivate extension EnvironmentValues {\n    \n    var materialState: MaterialState {\n        get { self[MaterialViewStateKey.self] }\n        set { self[MaterialViewStateKey.self] = newValue }\n    }\n    \n    var materialType: MaterialType {\n        get { self[MaterialViewMaterialKey.self] }\n        set { self[MaterialViewMaterialKey.self] = newValue }\n    }\n    \n    var materialBlendingMode: MaterialBlendingMode {\n        get { self[MaterialViewBlendingModeKey.self] }\n        set { self[MaterialViewBlendingModeKey.self] = newValue }\n    }\n    \n}\n"
  },
  {
    "path": "VirtualUI/Source/Components/NSAlert+Confirmation.swift",
    "content": "//\n//  NSAlert+Confirmation.swift\n//  VirtualBuddy\n//\n//  Created by Guilherme Rambo on 29/06/22.\n//\n\nimport Cocoa\n\npublic extension NSAlert {\n\n    static func runConfirmationAlert(title: String,\n                                     message: String,\n                                     continueButtonTitle: String = \"Continue\",\n                                     cancelButtonTitle: String = \"Cancel\",\n                                     continueButtonIsDefault: Bool = false) async -> Bool\n    {\n        let alert = NSAlert()\n\n        alert.messageText = title\n        alert.informativeText = message\n\n        if continueButtonIsDefault {\n            alert.addButton(withTitle: continueButtonTitle)\n            alert.addButton(withTitle: cancelButtonTitle)\n        } else {\n            alert.addButton(withTitle: cancelButtonTitle)\n            alert.addButton(withTitle: continueButtonTitle)\n        }\n\n        let response: NSApplication.ModalResponse\n\n        if let window = NSApp?.keyWindow {\n            response = await alert.beginSheetModal(for: window)\n        } else {\n            response = alert.runModal()\n        }\n\n        return continueButtonIsDefault ? response == .alertFirstButtonReturn : response == .alertSecondButtonReturn\n    }\n\n}\n"
  },
  {
    "path": "VirtualUI/Source/Components/OnAppearOnce.swift",
    "content": "//\n//  OnAppearOnce.swift\n//  Uploader\n//\n//  Created by Guilherme Rambo on 22/04/22.\n//\n\nimport SwiftUI\n\npublic extension View {\n    \n    /// Performs the specified code block the first time the view this modifier is attached to appears.\n    /// - Parameter block: The callback to be performed only the first time the view appears.\n    func onAppearOnce(perform block: @escaping () -> Void) -> some View {\n        modifier(OnAppearOnce(callback: block))\n    }\n}\n\nprivate struct OnAppearOnce: ViewModifier {\n    \n    @State private var performed = false\n    let callback: () -> Void\n    \n    func body(content: Content) -> some View {\n        content\n            .onAppear {\n                guard !performed else { return }\n                performed = true\n                callback()\n            }\n    }\n    \n}\n"
  },
  {
    "path": "VirtualUI/Source/Components/OpenSavePanelUtils.swift",
    "content": "import SwiftUI\nimport UniformTypeIdentifiers\nimport OSLog\n\nprivate let logger = Logger(subsystem: VirtualUIConstants.subsystemName, category: \"OpenSavePanelUtils\")\n\npublic extension NSOpenPanel {\n\n    static func run(accepting contentTypes: Set<UTType>, directoryURL: URL? = nil, defaultDirectoryKey: String? = nil, prompt: String? = nil) -> URL? {\n        let panel = NSOpenPanel()\n\n        if contentTypes == [.folder] || contentTypes == [.directory] {\n            panel.canChooseFiles = false\n            panel.canChooseDirectories = true\n        } else {\n            panel.allowedContentTypes = Array(contentTypes)\n        }\n\n        if let prompt {\n            panel.prompt = prompt\n        }\n\n        panel.treatsFilePackagesAsDirectories = true\n\n        let defaultsKey = defaultDirectoryKey.flatMap { \"defaultDirectory-\\($0)\" }\n\n        if let defaultsKey, let defaultDirectoryPath = UserDefaults.standard.string(forKey: defaultsKey) {\n            panel.directoryURL = URL(fileURLWithPath: defaultDirectoryPath)\n        } else if let directoryURL {\n            panel.directoryURL = directoryURL\n        }\n\n        guard panel.runModal() == .OK, let url = panel.url else { return nil }\n\n        if let defaultsKey {\n            /// If user is choosing a folder, then just store the path to the folder itself.\n            /// If user is choosing files, then remove the last path component to save the path to the file's directory instead.\n            let effectiveURL = contentTypes.contains(.folder) ? url : url.deletingLastPathComponent()\n            UserDefaults.standard.set(effectiveURL.path, forKey: defaultsKey)\n        }\n\n        return url\n    }\n\n}\n\npublic extension NSSavePanel {\n\n    static func run(for contentTypes: Set<UTType>, directoryURL: URL? = nil) -> URL? {\n        let panel = NSSavePanel()\n\n        panel.allowedContentTypes = Array(contentTypes)\n\n        panel.treatsFilePackagesAsDirectories = true\n        panel.directoryURL = directoryURL\n\n        guard panel.runModal() == .OK, let url = panel.url else { return nil }\n\n        return url\n    }\n\n    static func run(saving data: Data, as contentType: UTType, directoryURL: URL? = nil) {\n        guard let url = run(for: [contentType], directoryURL: directoryURL) else { return }\n\n        do {\n            try data.write(to: url, options: .atomic)\n        } catch {\n            logger.error(\"Save failed: \\(error.localizedDescription, privacy: .public)\")\n        }\n    }\n\n}\n"
  },
  {
    "path": "VirtualUI/Source/Components/RemoteImage/RemoteImage.swift",
    "content": "import SwiftUI\nimport CryptoKit\nimport VirtualCore\nimport OSLog\n\nstruct RemoteImage: View {\n    var url: URL\n    var blurHash: String?\n    var blurHashSize: CGSize\n    var loader: RemoteImageLoader\n\n    init(url: URL, blurHash: String? = nil, blurHashSize: CGSize = .vbBlurHashSize, loader: RemoteImageLoader = .default) {\n        self.url = url\n        self.blurHash = blurHash\n        self.blurHashSize = blurHashSize\n        self.loader = loader\n        self._nsImage = .init(initialValue: loader.cachedImage(for: url))\n    }\n\n    @State private var nsImage: NSImage?\n\n    var body: some View {\n        ZStack {\n            if let nsImage {\n                Image(nsImage: nsImage)\n                    .resizable()\n            } else {\n                if let blurHash {\n                    Image(blurHash: blurHash, size: blurHashSize)\n                        .resizable()\n                } else {\n                    Rectangle()\n                        .fill(.quaternary)\n                }\n            }\n        }\n        .task(id: url) {\n            nsImage = await loader.load(from: url)\n        }\n    }\n}\n\nfinal class RemoteImageLoader {\n    private let logger = Logger(subsystem: \"codes.rambo.RemoteImageLoader\", category: \"RemoteImageLoader\")\n\n    private let memoryCache = NSCache<NSString, NSImage>()\n\n    static let `default` = RemoteImageLoader()\n\n    private lazy var session: URLSession = {\n        let config = URLSessionConfiguration.default\n        return URLSession(configuration: config)\n    }()\n\n    func load(from remoteURL: URL) async -> NSImage? {\n        guard !remoteURL.isFileURL else {\n            return NSImage(contentsOf: remoteURL)\n        }\n        \n        if let cached = cachedImage(for: remoteURL) {\n            return cached\n        }\n\n        do {\n            let (fileURL, response) = try await session.download(from: remoteURL)\n\n            let stagedFileURL = try fileURL.temporaryCopy(usingPathExtensionFrom: remoteURL)\n\n            let code = (response as? HTTPURLResponse)?.statusCode ?? -1\n\n            guard code == 200 else {\n                throw Failure(\"HTTP \\(code)\")\n            }\n\n            guard let image = NSImage(contentsOf: stagedFileURL) else {\n                throw Failure(\"Image initialization failed\")\n            }\n\n            store(image: image, localFileURL: stagedFileURL, for: remoteURL)\n\n            return image\n        } catch {\n            logger.warning(\"Image download failed: \\(error, privacy: .public). Image URL: \\(remoteURL.absoluteString)\")\n\n            return nil\n        }\n    }\n\n    func cachedImage(for url: URL) -> NSImage? {\n        let key = cacheKey(for: url)\n\n        if let memImage = memoryCache.object(forKey: key as NSString) {\n            return memImage\n        } else {\n            let storageURL = diskURL(for: key)\n\n            guard FileManager.default.fileExists(atPath: storageURL.path) else {\n                return nil\n            }\n\n            return NSImage(contentsOf: storageURL)\n        }\n    }\n\n    private func cacheKey(for url: URL) -> String {\n        SHA256.hash(data: Data(url.absoluteString.utf8))\n            .map { String(format: \"%02x\", $0) }\n            .joined() + \".\\(url.pathExtension)\"\n    }\n\n    private func diskURL(for key: String) -> URL {\n        do {\n            let baseURL = try FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true)\n                .appendingPathComponent(Bundle.main.bundleURL.deletingPathExtension().lastPathComponent)\n                .appendingPathComponent(\"ImageCache\")\n\n            if !FileManager.default.fileExists(atPath: baseURL.path) {\n                try FileManager.default.createDirectory(at: baseURL, withIntermediateDirectories: true)\n            }\n\n            return baseURL.appendingPathComponent(key)\n        } catch {\n            assertionFailure(\"Failed to create cache directory: \\(error)\")\n            return URL(fileURLWithPath: NSTemporaryDirectory())\n        }\n    }\n\n    private func store(image: NSImage, localFileURL: URL, for url: URL) {\n        let key = cacheKey(for: url)\n\n        memoryCache.setObject(image, forKey: key as NSString)\n\n        let storageURL = diskURL(for: key)\n\n        do {\n            try FileManager.default.copyItem(at: localFileURL, to: storageURL)\n        } catch {\n            logger.error(\"Cache write failed: \\(error, privacy: .public)\")\n\n            assertionFailure(\"Cache write failed: \\(error)\")\n        }\n    }\n}\n\nprivate extension URL {\n    func temporaryCopy(usingPathExtensionFrom other: URL) throws -> URL {\n        let fileName = deletingPathExtension().lastPathComponent\n        let tempURL = URL(fileURLWithPath: NSTemporaryDirectory())\n            .appendingPathComponent(fileName)\n            .appendingPathExtension(other.pathExtension)\n        try FileManager.default.copyItem(at: self, to: tempURL)\n        return tempURL\n    }\n}\n"
  },
  {
    "path": "VirtualUI/Source/Components/SelfSizingGroupedForm.swift",
    "content": "import SwiftUI\nimport SwiftUIIntrospect\n\n/// A `Form` with the `.grouped` style that automatically resizes itself so that it\n/// perfectly fits its contents in the vertical axis.\nstruct SelfSizingGroupedForm<Content: View>: View {\n    var minHeight: CGFloat\n    @ViewBuilder var content: () -> Content\n\n    @State private var contentHeight: CGFloat = 0\n\n    private let disabled = UserDefaults.standard.bool(forKey: \"VBDisableSelfSizingGroupedForm\")\n\n    var body: some View {\n        ZStack {\n            Form {\n                content()\n            }\n            .formStyle(.grouped)\n            .introspect(.scrollView, on: .macOS(.v13, .v14, .v15, .v26)) { scrollView in\n                guard !disabled else { return }\n                guard let frame = scrollView.documentView?.frame else { return }\n                guard frame.height != contentHeight else { return }\n                guard frame.height > 0, frame.height.isFinite, !frame.height.isNaN else { return }\n                /// Ugly, I know, but I reaaaaally wanted the form to look a specific way 🥺\n                DispatchQueue.main.async {\n                    contentHeight = frame.height\n                }\n            }\n        }\n        .frame(height: max(minHeight, contentHeight))\n    }\n}\n\n#if DEBUG\n\nprivate struct _Preview: View {\n    @State var someText = \"Hello, World\"\n    @State var someBool = true\n\n    var body: some View {\n        SelfSizingGroupedForm(minHeight: 100) {\n            TextField(\"This is a text field\", text: $someText)\n            Toggle(\"This is a toggle\", isOn: $someBool)\n        }\n    }\n}\n\n#Preview(\"SelfSizingGroupedForm\") {\n    _Preview()\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/Components/SwiftUI Status Item/Components/Cocoa/StatusBarContentPanel.swift",
    "content": "import Cocoa\n\nfinal class StatusBarContentPanel: NSPanel {\n\n    override var canBecomeKey: Bool { true }\n\n    @objc func hasKeyAppearance() -> Bool {\n        return true\n    }\n\n    @objc func hasMainAppearance() -> Bool {\n        return true\n    }\n\n}\n"
  },
  {
    "path": "VirtualUI/Source/Components/SwiftUI Status Item/Components/Cocoa/StatusBarHighlightView.swift",
    "content": "import Cocoa\nimport SwiftUI\n\nstruct StatusBarHighlightView: NSViewRepresentable {\n\n    typealias NSViewType = NSView\n\n    var isHighlighted: Bool\n\n    func makeNSView(context: Context) -> NSViewType {\n        let v = NSView()\n\n        updateHighlight(in: v)\n\n        return v\n    }\n\n    func updateNSView(_ nsView: NSViewType, context: Context) {\n        updateHighlight(in: nsView)\n    }\n\n    private func updateHighlight(in view: NSViewType) {\n        NSStatusItem.vui_drawMenuBarHighlight(\n            in: view,\n            highlighted: isHighlighted,\n            inset: StatusItemButtonStyle.highlightCornerRadius * 0.5\n        )\n    }\n\n}\n\n#if DEBUG\n\nstruct StatusBarHighlightView_Previews: PreviewProvider {\n    static var previews: some View {\n        StatusBarHighlightView(isHighlighted: true)\n            .frame(width: 30, height: 30)\n            .previewDisplayName(\"Highlighted\")\n        StatusBarHighlightView(isHighlighted: false)\n            .frame(width: 30, height: 30)\n            .previewDisplayName(\"Normal\")\n    }\n}\n\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/Components/SwiftUI Status Item/Components/Cocoa/StatusItemMenuBarExtraView.swift",
    "content": "import Cocoa\n\nfinal class StatusItemMenuBarExtraView: NSView {\n\n    override var isOpaque: Bool { false }\n\n    override func viewDidMoveToSuperview() {\n        super.viewDidMoveToSuperview()\n\n        guard let superview else { return }\n\n        wantsLayer = true\n        layer?.masksToBounds = false\n\n        superview.wantsLayer = true\n        superview.layer?.masksToBounds = false\n\n        superview.superview?.layer?.masksToBounds = false\n    }\n\n}\n"
  },
  {
    "path": "VirtualUI/Source/Components/SwiftUI Status Item/Components/Cocoa/StatusItemPanelContentController.swift",
    "content": "import Cocoa\n\nfinal class StatusItemPanelContentController: NSViewController {\n\n    let child: NSViewController\n    var onContentSizeChange: ((CGSize) -> Void)?\n\n    init(child: NSViewController, onContentSizeChange: ((CGSize) -> Void)? = nil) {\n        self.child = child\n        self.onContentSizeChange = onContentSizeChange\n\n        super.init(nibName: nil, bundle: nil)\n    }\n\n    required init?(coder: NSCoder) {\n        fatalError()\n    }\n\n    override func loadView() {\n        view = NSView()\n        view.wantsLayer = true\n\n        addChild(child)\n        child.view.translatesAutoresizingMaskIntoConstraints = false\n        view.addSubview(child.view)\n\n        NSLayoutConstraint.activate([\n            child.view.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: StatusBarPanelChromeMetrics.shadowPadding),\n            child.view.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -StatusBarPanelChromeMetrics.shadowPadding),\n            child.view.topAnchor.constraint(equalTo: view.topAnchor, constant: StatusBarPanelChromeMetrics.shadowPadding),\n            child.view.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -StatusBarPanelChromeMetrics.shadowPadding),\n        ])\n    }\n\n    var contentSize: CGSize { view.bounds.size }\n\n    private var previouslyReportedSize: CGSize = .zero\n\n    override func viewDidLayout() {\n        super.viewDidLayout()\n\n        let newSize = view.bounds.size\n\n        guard newSize != previouslyReportedSize else { return }\n\n        previouslyReportedSize = newSize\n\n        self.onContentSizeChange?(newSize)\n    }\n\n}\n"
  },
  {
    "path": "VirtualUI/Source/Components/SwiftUI Status Item/Components/Cocoa/VUIAppKitViewControllerHost.swift",
    "content": "import Cocoa\nimport SwiftUI\n\n/// A SwiftUI view that hosts any AppKit view controller so that it can be presented in a\n/// context that requires the contents to be SwiftUI views, such as with ``StatusItemManager``.\nstruct VUIAppKitViewControllerHost<ChildController>: NSViewControllerRepresentable where ChildController: NSViewController {\n\n    typealias NSViewControllerType = _VUIViewControllerHostingController<ChildController>\n\n    private let contentControllerBuilder: () -> ChildController\n\n    init(with contentController: @escaping @autoclosure () -> ChildController) {\n        self.contentControllerBuilder = contentController\n    }\n\n    func makeNSViewController(context: Context) -> NSViewControllerType {\n        let host = _VUIViewControllerHostingController(with: contentControllerBuilder)\n        return host\n    }\n\n    func updateNSViewController(_ nsViewController: NSViewControllerType, context: Context) {\n\n    }\n\n}\n\nfinal class _VUIViewControllerHostingController<ChildController>: NSViewController where ChildController: NSViewController {\n\n    private let contentControllerBuilder: () -> ChildController\n\n    fileprivate init(with contentController: @escaping () -> ChildController) {\n        self.contentControllerBuilder = contentController\n\n        super.init(nibName: nil, bundle: nil)\n    }\n\n    required init?(coder: NSCoder) {\n        fatalError()\n    }\n\n    override func loadView() {\n        view = NSView()\n        view.wantsLayer = true\n\n        let contentController = contentControllerBuilder()\n\n        addChild(contentController)\n        contentController.view.translatesAutoresizingMaskIntoConstraints = false\n        view.addSubview(contentController.view)\n\n        NSLayoutConstraint.activate([\n            contentController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),\n            contentController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),\n            contentController.view.topAnchor.constraint(equalTo: view.topAnchor),\n            contentController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor)\n        ])\n    }\n\n}\n"
  },
  {
    "path": "VirtualUI/Source/Components/SwiftUI Status Item/Components/ObjC/NSApplication+MenuBar.h",
    "content": "#import <Cocoa/Cocoa.h>\n\nNS_ASSUME_NONNULL_BEGIN\n\n@interface NSApplication (MenuBar)\n\n- (void)__vui_setMenuBarVisible:(BOOL)visible;\n\n@end\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "VirtualUI/Source/Components/SwiftUI Status Item/Components/ObjC/NSApplication+MenuBar.m",
    "content": "#import \"NSApplication+MenuBar.h\"\n\n@import os.log;\n\n#import <dlfcn.h>\n\n#import <VirtualUI/VirtualUI-Swift.h>\n\nBoolean __vui_softLinkHIMenuBarRequestVisibility(Boolean visibility, Boolean *outAlreadyInState, void (^completion)(void));\n\n@implementation NSApplication (MenuBar)\n\n+ (os_log_t)__vui_menuBarLog\n{\n    static os_log_t _log;\n    static dispatch_once_t onceToken;\n    dispatch_once(&onceToken, ^{\n        _log = os_log_create([[_VirtualUIConstantsObjC subsystemName] UTF8String], \"NSApplication+VUIMenuBar\");\n    });\n    return _log;\n}\n\n- (void)__vui_setMenuBarVisible:(BOOL)visible\n{\n    os_log_t log = [NSApplication __vui_menuBarLog];\n\n    os_log_debug(log, \"Set menu bar visibility to %{public}d\", visible);\n\n    Boolean result = false;\n    Boolean alreadyInState = false;\n\n    result = __vui_softLinkHIMenuBarRequestVisibility(visible, &alreadyInState, ^{ });\n\n    os_log_debug(log, \"Set menu bar visibility result: %{public}d, already in state: %{public}d\", visible, alreadyInState);\n}\n\n@end\n\ntypedef Boolean (*_VUIHIMenuBarRequestVisibilityPtr)(Boolean visibility, Boolean *outAlreadyInState, void (^completion)(void));\n\nBoolean __vui_softLinkHIMenuBarRequestVisibility(Boolean visibility, Boolean *outAlreadyInState, void (^completion)(void)) {\n    static _VUIHIMenuBarRequestVisibilityPtr fnptr;\n\n    static dispatch_once_t onceToken;\n    dispatch_once(&onceToken, ^{\n        void *handle = dlopen(\"/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon\", RTLD_NOW);\n        if (!handle) {\n            __assert(\"Couldn't load Carbon dylib\", __FILE__, __LINE__);\n            return;\n        }\n\n        void *fn = dlsym(handle, \"_HIMenuBarRequestVisibility\");\n        if (!fn) {\n            __assert(\"Couldn't load _HIMenuBarRequestVisibility symbol\", __FILE__, __LINE__);\n            return;\n        }\n\n        fnptr = (_VUIHIMenuBarRequestVisibilityPtr)fn;\n    });\n\n    if (!fnptr) return false;\n\n    return fnptr(visibility, outAlreadyInState, completion);\n}\n"
  },
  {
    "path": "VirtualUI/Source/Components/SwiftUI Status Item/Components/ObjC/NSStatusBarPrivate.h",
    "content": "@import Cocoa;\n\nNS_ASSUME_NONNULL_BEGIN\n\n@interface NSStatusBar (Private)\n\n@property (nonatomic, readonly) CGFloat contentPadding;\n\n- (void)drawBackgroundInRect:(NSRect *)rect inView:(NSView *)view highlight:(BOOL)highlight;\n\n@end\n\n@interface NSStatusItem (Private)\n\n- (void)setAllowsVibrancy:(BOOL)flag;\n\n@end\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "VirtualUI/Source/Components/SwiftUI Status Item/Components/ObjC/NSStatusItem+.h",
    "content": "#import <Cocoa/Cocoa.h>\n\nNS_ASSUME_NONNULL_BEGIN\n\n@interface NSStatusItem (VUIAdditions)\n\n@property (nonatomic, strong) NSView *__nullable vui_contentView;\n\n- (void)vui_disableVibrancy;\n\n@property (class, nonatomic, readonly) CGFloat vui_idealPadding;\n\n+ (void)vui_drawMenuBarHighlightInView:(NSView *)view\n                           highlighted:(BOOL)isHighlighted\n                                 inset:(CGFloat)insetAmount;\n\n@end\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "VirtualUI/Source/Components/SwiftUI Status Item/Components/ObjC/NSStatusItem+.m",
    "content": "#import \"NSStatusItem+.h\"\n\n#import \"NSStatusBarPrivate.h\"\n\n#pragma clang diagnostic push\n#pragma clang diagnostic ignored \"-Wdeprecated-declarations\"\n\n@implementation NSStatusItem (VUIAdditions)\n\n- (NSView *)vui_contentView\n{\n    return self.view;\n}\n\n- (void)setVui_contentView:(NSView *)vui_contentView\n{\n    self.view = vui_contentView;\n}\n\n- (void)vui_disableVibrancy\n{\n    if ([self respondsToSelector:@selector(setAllowsVibrancy:)]) {\n        [self setAllowsVibrancy:NO];\n    }\n}\n\n+ (CGFloat)vui_idealPadding\n{\n    if ([[NSStatusBar systemStatusBar] respondsToSelector:@selector(contentPadding)]) {\n        return [[NSStatusBar systemStatusBar] contentPadding];\n    } else {\n        return 16.0;\n    }\n}\n\n+ (void)vui_drawMenuBarHighlightInView:(NSView *)view highlighted:(BOOL)isHighlighted inset:(CGFloat)insetAmount\n{\n    if (!view.window || !view.superview) return;\n    if (!view.window.isVisible) return;\n    if (view.bounds.size.width <= 0 || view.bounds.size.height <= 0) return;\n\n    if (![[NSStatusBar systemStatusBar] respondsToSelector:@selector(drawBackgroundInRect:inView:highlight:)]) {\n        return [self __vui_drawFallbackMenuBarHighlightInView:view];\n    }\n\n    NSRect rect = NSInsetRect(view.bounds, insetAmount, 0);\n\n    [[NSStatusBar systemStatusBar] drawBackgroundInRect:&rect inView:view highlight:isHighlighted];\n}\n\n+ (void)__vui_drawFallbackMenuBarHighlightInView:(NSView *)view\n{\n    [view lockFocus];\n    [[[NSColor blackColor] colorWithAlphaComponent:0.1] setFill];\n    NSRectFill(view.bounds);\n    [view unlockFocus];\n}\n\n@end\n\n#pragma clang diagnostic pop\n"
  },
  {
    "path": "VirtualUI/Source/Components/SwiftUI Status Item/Components/ScreenChangeModifier.swift",
    "content": "import SwiftUI\nimport Combine\n\ntypealias ViewScreenChangeBlock = (NSScreen?) -> Void\n\nextension View {\n    /// Calls the specified block whenever the window that's hosting the view moves between displays.\n    func onScreenChanged(perform block: @escaping ViewScreenChangeBlock) -> some View {\n        modifier(ScreenChangeModifier(onScreenChanged: block))\n    }\n\n    /// Injects the `screen` environment value, updating it whenever the window that's hosting\n    /// the view moves between displays.\n    func trackScreen() -> some View {\n        modifier(ScreenEnvironmentInjectionModifier())\n    }\n}\n\nprivate struct ScreenEnvironmentKey: EnvironmentKey {\n    static var defaultValue: NSScreen? = .main\n}\n\nextension EnvironmentValues {\n    fileprivate(set) var screen: NSScreen? {\n        get { self[ScreenEnvironmentKey.self] }\n        set { self[ScreenEnvironmentKey.self] = newValue }\n    }\n}\n\nprivate struct ScreenChangeModifier: ViewModifier {\n\n    var onScreenChanged: ViewScreenChangeBlock\n\n    func body(content: Content) -> some View {\n        content\n            .background(ScreenTrackingHostView(onScreenChanged: onScreenChanged))\n    }\n\n}\n\nprivate struct ScreenEnvironmentInjectionModifier: ViewModifier {\n\n    @State private var screen: NSScreen? = .main\n\n    func body(content: Content) -> some View {\n        content\n            .environment(\\.screen, screen)\n            .onScreenChanged { screen = $0 }\n    }\n\n}\n\nprivate struct ScreenTrackingHostView: NSViewRepresentable {\n\n    var onScreenChanged: ViewScreenChangeBlock\n\n    typealias NSViewType = _ScreenTrackingView\n\n    func makeNSView(context: Context) -> _ScreenTrackingView {\n        _ScreenTrackingView(onScreenChanged: onScreenChanged)\n    }\n\n    func updateNSView(_ nsView: _ScreenTrackingView, context: Context) {\n\n    }\n\n}\n\nprivate final class _ScreenTrackingView: NSView {\n\n    var onScreenChanged: ViewScreenChangeBlock\n    private var previousScreenDisplayID: Int?\n\n    init(onScreenChanged: @escaping ViewScreenChangeBlock) {\n        self.onScreenChanged = onScreenChanged\n\n        super.init(frame: .zero)\n    }\n\n    required init?(coder: NSCoder) {\n        fatalError()\n    }\n\n    override func hitTest(_ point: NSPoint) -> NSView? { nil }\n\n    override var isOpaque: Bool { false }\n\n    private var screenCancellable: AnyCancellable?\n\n    override func viewDidMoveToWindow() {\n        super.viewDidMoveToWindow()\n\n        screenCancellable?.cancel()\n\n        guard let window else { return }\n\n        screenCancellable = window\n            .publisher(for: \\.screen, options: [.initial, .new])\n            .sink { [weak self] screen in\n                guard let self = self else { return }\n\n                let currentScreenDisplayID = screen?.displayID?.intValue\n\n                guard currentScreenDisplayID != self.previousScreenDisplayID else { return }\n\n                self.onScreenChanged(screen)\n\n                self.previousScreenDisplayID = currentScreenDisplayID\n            }\n    }\n\n}\n\nextension NSScreen {\n    var displayID: NSNumber? { deviceDescription[NSDeviceDescriptionKey(\"NSScreenNumber\")] as? NSNumber }\n\n    var hasTallMenuBar: Bool { safeAreaInsets.top > 0 }\n}\n"
  },
  {
    "path": "VirtualUI/Source/Components/SwiftUI Status Item/Components/StatusBarPanelChrome.swift",
    "content": "import SwiftUI\n\nstruct StatusBarPanelChromeMetrics {\n    static var shadowPadding: CGFloat { 26 }\n    static var cornerRadius: CGFloat { 15 }\n    static var innerStrokeWidth: CGFloat { 1 }\n    static var innerStrokeInset: CGFloat { innerStrokeWidth * 0.5 }\n    static var outerStrokeWidth: CGFloat { .onePixel }\n}\n\nstruct StatusBarPanelChrome<Content: View, S: InsettableShape>: View {\n    var contentBuilder: () -> Content\n    var shape: S\n\n    var body: some View {\n        contentBuilder()\n            .background(chromeBackground)\n    }\n\n    @ViewBuilder\n    private var chromeBackground: some View {\n        MaterialView()\n            .materialType(.hudWindow)\n            .materialBlendingMode(.behindWindowForPreviews)\n            .clipShape(shape)\n            .shadow(color: Color.black.opacity(0.7), radius: 1, x: 0, y: 0)\n            .shadow(color: Color.black.opacity(0.28), radius: 13, x: 0, y: 0)\n            .compositingGroup()\n            .overlay(innerStroke)\n            .overlay(outerStroke)\n    }\n\n    @Environment(\\.colorScheme)\n    private var colorScheme\n\n    private var outerStrokeWidth: CGFloat { 0.5 }\n\n    private var innerStroke: some View {\n        shape\n            .inset(by: StatusBarPanelChromeMetrics.innerStrokeInset)\n            .stroke(Color.statusItemPanelChromeBorder.opacity(0.7), lineWidth: StatusBarPanelChromeMetrics.innerStrokeWidth)\n            .blendMode(.plusLighter)\n            .opacity(colorScheme == .dark ? 1 : 0)\n            .zIndex(9999)\n    }\n\n    private var outerStroke: some View {\n        shape\n            .inset(by: -outerStrokeWidth * 0.5)\n            .stroke(Color.black, lineWidth: outerStrokeWidth)\n            .opacity(colorScheme == .dark ? 1 : 0)\n    }\n\n}\n\nextension StatusBarPanelChrome where S == RoundedRectangle {\n\n    init(contentBuilder: @escaping () -> Content) {\n        self.init(contentBuilder: contentBuilder, shape: RoundedRectangle(cornerRadius: StatusBarPanelChromeMetrics.cornerRadius, style: .continuous))\n    }\n\n}\n\n#if DEBUG\nstruct StatusBarPanelChrome_Previews: PreviewProvider {\n    static var previews: some View {\n        StatusBarPanelChrome {\n            Text(\"Hello, World!\")\n                .frame(width: 300, height: 300)\n        }\n        .padding(100)\n        .previewDisplayName(\"Light\")\n\n        StatusBarPanelChrome {\n            Text(\"Hello, World!\")\n                .frame(width: 300, height: 300)\n        }\n        .padding(100)\n        .preferredColorScheme(.dark)\n        .previewDisplayName(\"Dark\")\n    }\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/Components/SwiftUI Status Item/Components/StatusItemButton.swift",
    "content": "import Cocoa\nimport SwiftUI\n\nprivate struct StatusItemHighlightedEnvironmentKey: EnvironmentKey {\n    static var defaultValue = false\n}\n\nextension EnvironmentValues {\n\n    /// Whether the status item should be drawn highlighted.\n    fileprivate(set) var isStatusItemHighlighted: Bool {\n        get { self[StatusItemHighlightedEnvironmentKey.self] }\n        set { self[StatusItemHighlightedEnvironmentKey.self] = newValue }\n    }\n\n}\n\n/// A button that's used in ``StatusItemManager`` to provide the contents for the status item\n/// when using the `.button` content type.\n/// Custom views can use ``StatusItemButtonLook`` to implement a view that's compatible\n/// with status items, but that aren't necessarily a button.\nstruct StatusItemButton<Label: View, Provider: StatusItemProvider>: View {\n\n    @EnvironmentObject private var provider: Provider\n\n    var label: () -> Label\n\n    init(@ViewBuilder label: @escaping () -> Label) {\n        self.label = label\n    }\n    \n    var body: some View {\n        Button {\n            provider.togglePanelVisible()\n        } label: {\n            label()\n        }\n        .buttonStyle(StatusItemButtonStyle())\n        .statusItemHighlightedEnvironment(from: provider)\n    }\n\n}\n\nextension View {\n    /// Reads properties from the specified provider and sets the `isStatusItemHighlighted` environment value accordingly.\n    /// Apply at the root of hierarchies that expect to be able to read the `isStatusItemHighlighted` value.\n    ///\n    /// This is handy because the `isStatusItemHighlighted` property from `StatusItemManager` is not always\n    /// enough to determine whether to draw the highlight, which also depends on status item occlusion state.\n    func statusItemHighlightedEnvironment<Provider>(from provider: Provider) -> some View where Provider: StatusItemProvider {\n        environment(\\.isStatusItemHighlighted,\n                     provider.isStatusItemHighlighted && !provider.isStatusItemOccluded)\n    }\n}\n\n/// A view that looks like ``StatusItemButton``, but can be used as a wrapper\n/// for completely custom status items that do not use the button view.\nstruct StatusItemButtonLook<Label: View>: View {\n\n    var label: () -> Label\n\n    init(@ViewBuilder label: @escaping () -> Label) {\n        self.label = label\n    }\n\n    @Environment(\\.isStatusItemHighlighted)\n    private var isHighlighted\n\n    @State private var screen: NSScreen?\n\n    private var height: CGFloat { StatusItemButtonStyle.effectiveHeight(for: screen) }\n\n    var body: some View {\n        label()\n            .font(.system(size: StatusItemButtonStyle.glyphFontSize))\n            .offset(y: StatusItemButtonStyle.glyphOffsetY)\n            .frame(minWidth: StatusItemButtonStyle.effectiveWidth, minHeight: height, maxHeight: height)\n            .background(background)\n            .contentShape(Rectangle())\n            .onScreenChanged { screen = $0 }\n    }\n\n    @ViewBuilder\n    private var background: some View {\n        StatusBarHighlightView(isHighlighted: isHighlighted)\n            .frame(width: nil)\n            .clipShape(RoundedRectangle(cornerRadius: StatusItemButtonStyle.highlightCornerRadius, style: .continuous))\n    }\n\n}\n\nstruct StatusItemButtonStyle: ButtonStyle {\n    static var heightRegular: CGFloat { 22 }\n    static var heightTall: CGFloat { 37 }\n\n    static var width: CGFloat { 36 }\n\n    static var verticalPadding: CGFloat { 6 }\n    static var horizontalPadding: CGFloat { NSStatusItem.vui_idealPadding }\n    static var glyphFontSize: CGFloat { 14 }\n    static var glyphOffsetY: CGFloat { 0.5 }\n\n    static var highlightCornerRadius: CGFloat { 4 }\n\n    static func effectiveHeight(for screen: NSScreen?) -> CGFloat {\n        guard let screen else { return Self.heightRegular }\n\n        if screen.hasTallMenuBar {\n            return Self.heightTall - Self.verticalPadding * 2\n        } else {\n            return Self.heightRegular\n        }\n    }\n\n    static var effectiveWidth: CGFloat {\n        Self.width - Self.horizontalPadding\n    }\n\n    func makeBody(configuration: Configuration) -> some View {\n        StatusItemButtonLook(label: { configuration.label })\n    }\n\n}\n"
  },
  {
    "path": "VirtualUI/Source/Components/SwiftUI Status Item/Components/StatusItemProviderProtocol.swift",
    "content": "import Cocoa\nimport SwiftUI\n\npublic protocol StatusItemProvider: ObservableObject {\n    /// `true` when the background of the status item's view should be highlighted.\n    var isStatusItemHighlighted: Bool { get }\n\n    var isStatusItemOccluded: Bool { get }\n\n    /// Show/hide the status item's content panel.\n    func togglePanelVisible()\n\n    /// Show a pop up menu produced by running the builder closure.\n    func showPopUpMenu(using builder: () -> NSMenu)\n}\n"
  },
  {
    "path": "VirtualUI/Source/Components/SwiftUI Status Item/StatusItemManager.swift",
    "content": "import Cocoa\nimport SwiftUI\nimport Combine\nimport OSLog\nimport notify\n\npublic final class StatusItemManager: NSObject, NSWindowDelegate, StatusItemProvider {\n\n    private lazy var logger: Logger = {\n        Logger(subsystem: VirtualUIConstants.subsystemName, category: \"StatusItemManager(\\(configuration.id))\")\n    }()\n\n    /// Configures the status item's identity and behavior.\n    public struct Configuration {\n        /// Unique identifier for this status item.\n        public internal(set) var id: String\n        /// The behavior for allowing removal of the status item by dragging out of the menu bar.\n        public var behavior: NSStatusItem.Behavior\n        /// Custom autosave name used by AppKit to store item's on/off and position preferences.\n        public var autosaveName: String?\n\n        public static var `default`: Configuration {\n            Configuration(id: UUID().uuidString, behavior: .removalAllowed, autosaveName: nil)\n        }\n\n        public func id(_ id: String) -> Self {\n            var mSelf = self\n            mSelf.id = id\n            return mSelf\n        }\n\n        public init(id: String, behavior: NSStatusItem.Behavior, autosaveName: String? = nil) {\n            self.id = id\n            self.behavior = behavior\n            self.autosaveName = autosaveName\n        }\n    }\n    \n    @Published public private(set) var isStatusItemHighlighted: Bool = false\n\n    @Published public private(set) var isPanelVisible = false\n\n    @Published public private(set) var isStatusItemVisible = true\n\n    /// `true` whenever the status item is not actually visible in the Menu Bar.\n    /// This differs from `isStatusItemVisible`, which reflects a user-defined setting.\n    /// It will be `false` if the status item is not visible in the Menu Bar because not enough space was available,\n    /// when using tools such as Bartender to hide status items, or if there's UI covering the status item.\n    @Published public private(set) var isStatusItemOccluded = false\n\n    public let willShowPanel = PassthroughSubject<Void, Never>()\n    public let willClosePanel = PassthroughSubject<Void, Never>()\n\n    private let configuration: Configuration\n\n    public enum StatusItemView<V: View> {\n        case button(label: () -> V)\n        case custom(body: () -> V)\n    }\n\n    public init<StatusItem: View, Content: View>(configuration: Configuration = .default,\n                                          statusItem: StatusItemView<StatusItem>,\n                                          content: @escaping @autoclosure () -> Content)\n    {\n        self.configuration = configuration\n\n        /// This is implemented this way due to a Swift compiler crash 🤦🏻‍♂️\n        let group: Group<_ConditionalContent<StatusItemButton<StatusItem, StatusItemManager>, StatusItem>> = Group {\n            switch statusItem {\n            case .button(let label):\n                StatusItemButton<StatusItem, StatusItemManager> {\n                    label()\n                }\n            case .custom(let customBody):\n                customBody()\n            }\n        }\n\n        self.statusItemViewBuilder = {\n            AnyView(erasing: group)\n        }\n        self.contentViewBuilder = {\n            AnyView(erasing: content())\n        }\n        \n        super.init()\n    }\n    \n    public convenience init<StatusItem: View, Content: NSViewController>(configuration: Configuration = .default,\n                                                                  statusItem: StatusItemView<StatusItem>,\n                                                                  content: @escaping @autoclosure () -> Content)\n    {\n        self.init(\n            configuration: configuration,\n            statusItem: statusItem,\n            content: VUIAppKitViewControllerHost(with: content())\n        )\n    }\n    \n    var statusItemToPanelPadding: CGFloat {\n        if screenTopInset > 0 { // Tall Menu Bar (i.e. device with notch)\n            return 12\n        } else { // Short Menu Bar (i.e. device without notch)\n            return 5\n        }\n    }\n\n    private var screenTopInset: CGFloat {\n        guard let screen = panel?.screen ?? NSScreen.main else { return 0 }\n        return screen.safeAreaInsets.top\n    }\n\n    private var statusItemViewBuilder: () -> AnyView\n    private var contentViewBuilder: () -> AnyView\n\n    private lazy var cancellables = Set<AnyCancellable>()\n\n    private lazy var item: NSStatusItem = {\n        let i = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)\n\n        i.behavior = configuration.behavior\n        i.autosaveName = configuration.autosaveName\n\n        return i\n    }()\n\n    private var installed = false\n\n    public func install() {\n        guard !installed else { return }\n        installed = true\n\n        setup()\n    }\n\n    private var eventObservers = [Any]()\n\n    private var statusItemWindowCancellable: AnyCancellable?\n\n    private func setup() {\n        registerEventObservers()\n\n        item.vui_disableVibrancy()\n\n        let contentView = StatusItemMenuBarExtraView()\n\n        item.vui_contentView = contentView\n\n        let hostingView = NSHostingView(rootView: statusItemViewBuilder().environmentObject(self))\n\n        observeStatusItemOcclusionState(with: hostingView)\n\n        hostingView.translatesAutoresizingMaskIntoConstraints = false\n        contentView.addSubview(hostingView)\n\n        NSLayoutConstraint.activate([\n            hostingView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),\n            hostingView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),\n            hostingView.topAnchor.constraint(equalTo: contentView.topAnchor),\n            hostingView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),\n        ])\n\n        $isPanelVisible.removeDuplicates().dropFirst(1).sink { [weak self] panelVisible in\n            guard let self = self else { return }\n\n            if panelVisible {\n                self.panelShown()\n            } else {\n                self.panelHidden()\n            }\n        }\n        .store(in: &cancellables)\n    }\n    \n    /// Call this method when a user preference has changed that affects the visibility of the managed item.\n    func visibilityPreferenceChanged(to newValue: Bool) {\n        logger.debug(\"Visibility preference changed to \\(newValue, privacy: .public)\")\n        \n        // Set the flag now to prevent delays for possible obsevers that could cause loops.\n        isStatusItemVisible = newValue\n        item.isVisible = newValue\n    }\n\n    private var panel: StatusBarContentPanel?\n\n    public func togglePanelVisible() {\n        if isPanelVisible {\n            hidePanel()\n            isStatusItemHighlighted = false\n        } else {\n            showPanel()\n            isStatusItemHighlighted = true\n        }\n    }\n    \n    private var primaryMenuObserver: Any?\n    \n    public func showPopUpMenu(using builder: () -> NSMenu) {\n        guard let view = item.vui_contentView else { return }\n\n        let origin = NSEvent.mouseLocation(in: view)\n        \n        let menu = builder()\n\n        if menu.responds(to: NSSelectorFromString(\"setAppearance:\")) {\n            menu.appearance = NSApp.effectiveAppearance\n        }\n        \n        isStatusItemHighlighted = true\n        \n        primaryMenuObserver = NotificationCenter.default.addObserver(forName: NSMenu.didEndTrackingNotification, object: menu, queue: .main, using: { [weak self] _ in\n            guard let self = self else { return }\n            self.primaryMenuObserver = nil\n            \n            self.isStatusItemHighlighted = false\n        })\n\n        menu.popUp(positioning: nil, at: origin, in: view)\n    }\n\n    public func showPanel() {\n        if let panel {\n            guard !panel.isVisible else { return }\n        }\n\n        defer { willShowPanel.send() }\n\n        let basePanelSize = NSSize(width: 300, height: 300)\n\n        // These were taken from ControlCenter on macOS 12.4\n        let style: NSWindow.StyleMask = [.fullSizeContentView, .nonactivatingPanel]\n        let level: NSWindow.Level = .popUpMenu\n\n        let newPanel = StatusBarContentPanel(contentRect: NSRect(origin: .zero, size: basePanelSize), styleMask: style, backing: .buffered, defer: false)\n        newPanel.backgroundColor = NSColor.clear\n        newPanel.isOpaque = false\n        newPanel.collectionBehavior = [.ignoresCycle, .fullScreenAuxiliary, .fullScreenDisallowsTiling]\n        newPanel.hidesOnDeactivate = false\n        newPanel.level = level\n        newPanel.hasShadow = false\n        newPanel.isMovable = false\n\n        let chrome = StatusBarPanelChrome {\n            self.contentViewBuilder()\n        }\n\n        let contentController = StatusItemPanelContentController(\n            child: NSHostingController(rootView: chrome)\n        )\n\n        newPanel.contentViewController = contentController\n\n        contentController.view.needsLayout = true\n\n        newPanel.delegate = self\n\n        self.panel = newPanel\n\n        /// Give the system time to perform a layout pass caused by the needsLayout call above,\n        /// so that by the time the panel is shown, we already know the size of the contents.\n        DispatchQueue.main.async { [self] in\n            finishShowingPanel()\n        }\n\n        isPanelVisible = true\n    }\n\n    private var contentController: StatusItemPanelContentController? {\n        panel?.contentViewController as? StatusItemPanelContentController\n    }\n\n    private func finishShowingPanel() {\n        guard let panel, let contentController else { return }\n\n        contentController.onContentSizeChange = { [weak self] newSize in\n            guard let self = self else { return }\n            guard let panel = self.panel else { return }\n            self.repositionContent(panel, contentSize: newSize, display: true, animate: false)\n        }\n\n        repositionContent(\n            panel,\n            contentSize: contentController.contentSize,\n            display: false,\n            animate: false\n        )\n\n        panel.makeKeyAndOrderFront(nil)\n    }\n\n    private func repositionContent(_ panel: NSWindow, contentSize: CGSize, display: Bool, animate: Bool) {\n        guard let refView = item.vui_contentView?.superview,\n              let refWindow = refView.window else\n        {\n            assertionFailure(\"Missing reference status item view or window\")\n            return\n        }\n\n        let reference = refWindow.convertToScreen(refView.frame)\n\n        let panelFrame = NSRect(\n            x: reference.midX - contentSize.width / 2,\n            y: reference.minY - contentSize.height - statusItemToPanelPadding + StatusBarPanelChromeMetrics.shadowPadding,\n            width: contentSize.width,\n            height: contentSize.height\n        )\n\n        #if DEBUG\n        logger.debug(\"⬛️ contentSize = \\(contentSize.width, privacy: .public)x\\(contentSize.height, privacy: .public)\")\n        #endif\n\n        panel.setFrame(panelFrame, display: display, animate: animate)\n    }\n\n    private var panelIsClosing = false\n\n    public func hidePanel(animated: Bool = true) {\n        guard isPanelVisible, !panelIsClosing else { return }\n\n        guard animated else {\n            panel?.close()\n            return\n        }\n\n        panelIsClosing = true\n\n        NSAnimationContext.runAnimationGroup { [weak self] ctx in\n            guard let panel = self?.panel else { return }\n            panel.animator().alphaValue = 0\n        } completionHandler: { [weak self] in\n            defer { self?.panelIsClosing = false}\n\n            guard let panel = self?.panel else { return }\n\n            panel.close()\n        }\n    }\n\n    public func windowWillClose(_ notification: Notification) {\n        willClosePanel.send()\n        \n        DispatchQueue.main.async {\n            self.panel = nil\n            self.isStatusItemHighlighted = false\n            self.isPanelVisible = false\n        }\n    }\n\n    public func windowDidResignKey(_ notification: Notification) {\n        logger.debug(\"🔑 RESIGNED KEY\")\n\n        delayedHidePanel()\n    }\n\n    public func windowDidBecomeKey(_ notification: Notification) {\n        logger.debug(\"🔑 BECAME KEY\")\n\n        panelHideDelayItem?.cancel()\n        panelHideDelayItem = nil\n    }\n\n    private var panelHideDelayItem: DispatchWorkItem?\n\n    private func delayedHidePanel() {\n        panelHideDelayItem?.cancel()\n        panelHideDelayItem = nil\n\n        let item = DispatchWorkItem { [weak self] in\n            guard let self = self else { return }\n\n            guard self.isPanelVisible, !self.panelIsClosing else { return }\n\n            /// Do not hide if we're showing a sheet in the panel, which can happen for alerts.\n            guard self.panel?.sheets.isEmpty == true else {\n                self.logger.debug(\"Panel hiding due to resigning key cancelled: showing a sheet\")\n                return\n            }\n\n            self.logger.debug(\"Hiding panel due to window resigning key\")\n\n            self.hidePanel()\n        }\n\n        panelHideDelayItem = item\n        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: item)\n    }\n    \n    private var highlightOffWorkItem: DispatchWorkItem?\n    \n    func flash() {\n        highlightOffWorkItem?.cancel()\n        highlightOffWorkItem = nil\n        \n        isStatusItemHighlighted = true\n        let item = DispatchWorkItem { [weak self] in\n            self?.isStatusItemHighlighted = false\n            self?.highlightOffWorkItem = nil\n        }\n        highlightOffWorkItem = item\n        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: item)\n    }\n\n    private func observeStatusItemOcclusionState(with hostingView: NSView) {\n        hostingView.publisher(for: \\.window, options: [.initial, .new]).sink { [weak self] window in\n            guard let self = self else { return }\n\n            self.statusItemWindowCancellable = nil\n\n            self.logger.debug(\"🤲🏻 Status item host window: \\(String(describing: window), privacy: .public)\")\n\n            guard let window else { return }\n\n            self.evaluateStatusItemOcclusion(in: window)\n\n            self.statusItemWindowCancellable = NotificationCenter.default.publisher(for: NSWindow.didChangeOcclusionStateNotification, object: window).sink(receiveValue: { note in\n                guard let w = note.object as? NSWindow else { return }\n\n                self.logger.debug(\"🤲🏻 Status item host window occlusion state: \\(w.occlusionState.description, privacy: .public)\")\n\n                self.evaluateStatusItemOcclusion(in: w)\n            })\n        }\n        .store(in: &cancellables)\n    }\n\n    private func evaluateStatusItemOcclusion(in window: NSWindow) {\n        let newState = !window.occlusionState.isVisible\n\n        guard newState != isStatusItemOccluded else { return }\n\n        isStatusItemOccluded = newState\n\n        logger.debug(\"🤲🏻 isStatusItemOccluded = \\(newState, privacy: .public)\")\n    }\n\n    deinit {\n        unregisterEventObservers()\n    }\n\n}\n\n// MARK: - Menu Bar Integration\n\nprivate extension StatusItemManager {\n\n    private func panelShown() {\n        logger.debug(\"🪟 \\(#function, privacy: .public)\")\n\n        postBeginMenuTrackingNotification()\n\n        NSApplication.shared.__vui_setMenuBarVisible(true)\n\n        notify_post(\"com.apple.hitoolbox.menubar.position.lock\")\n    }\n\n    private func panelHidden() {\n        logger.debug(\"🪟 \\(#function, privacy: .public)\")\n\n        postEndMenuTrackingNotification()\n\n        notify_post(\"com.apple.hitoolbox.menubar.position.unlock\")\n    }\n\n    func postBeginMenuTrackingNotification() {\n        let name = \"com.apple.HIToolbox.beginMenuTrackingNotification\"\n\n        let pidStr = \"\\(ProcessInfo.processInfo.processIdentifier)\"\n        logger.debug(\"🪟 \\(name, privacy: .public) \\(pidStr, privacy: .public)\")\n\n        DistributedNotificationCenter.default().post(name: .init(name), object: pidStr)\n    }\n\n    func postEndMenuTrackingNotification() {\n        let name = \"com.apple.HIToolbox.endMenuTrackingNotification\"\n\n        let pidStr = \"\\(ProcessInfo.processInfo.processIdentifier)\"\n        logger.debug(\"🪟 \\(name, privacy: .public) \\(pidStr, privacy: .public)\")\n\n        DistributedNotificationCenter.default().post(name: .init(name), object: pidStr)\n    }\n\n    func registerEventObservers() {\n        let clickOutsideObserver = NSEvent.addGlobalMonitorForEvents(matching: .leftMouseDown) { [weak self] _ in\n            guard let self = self else { return }\n\n            self.hidePanel()\n        }\n        if let clickOutsideObserver { eventObservers.append(clickOutsideObserver) }\n\n        let statusItemVisibilityObserver = item.observe(\\.isVisible, options: [.new, .old], changeHandler: { [weak self] _, change in\n            guard let self = self else { return }\n            guard let newValue = change.newValue else { return }\n            guard newValue != change.oldValue, newValue != self.isStatusItemVisible else { return }\n            \n            self.logger.debug(\"Status item visibility changed to \\(newValue, privacy: .public)\")\n\n            self.isStatusItemVisible = newValue\n        })\n        eventObservers.append(statusItemVisibilityObserver)\n    }\n\n    func unregisterEventObservers() {\n        eventObservers.removeAll()\n    }\n\n}\n\nextension NSEvent {\n    \n    /// Returns the current mouse cursor location relative to the view's coordinate space.\n    /// Handy for popping up contextual menus in response to clicking a view.\n    static func mouseLocation(in view: NSView) -> NSPoint {\n        let mp = NSEvent.mouseLocation\n\n        guard let window = view.window else { return mp }\n        \n        let p = window.convertFromScreen(NSRect(origin: mp, size: CGSize(width: 1, height: 1)))\n        \n        return view.convert(p, from: nil).origin\n    }\n    \n    /// Returns the current mouse cursor location relative to the view's coordinate space.\n    /// Handy for popping up contextual menus in response to clicking a view.\n    static func mouseLocation(in window: NSWindow) -> NSPoint {\n        let mp = NSEvent.mouseLocation\n\n        return window.convertFromScreen(NSRect(origin: mp, size: CGSize(width: 1, height: 1))).origin\n    }\n    \n}\n\nextension NSWindow.OcclusionState: @retroactive CustomStringConvertible {\n    public var description: String {\n        return isVisible ? \"\\(rawValue) (Visible)\" : \"\\(rawValue) (Hidden)\"\n    }\n\n    var isVisible: Bool { isStrictSuperset(of: .visible) }\n}\n"
  },
  {
    "path": "VirtualUI/Source/Components/VMArtworkView.swift",
    "content": "import SwiftUI\nimport VirtualCore\n\npublic struct VMArtworkView: View {\n    public var virtualMachine: VBVirtualMachine\n    public var showsIcon: Bool\n    public var iconSize: CGFloat\n    private let alwaysUseBlurHash: Bool\n\n    public init(virtualMachine: VBVirtualMachine, alwaysUseBlurHash: Bool = false, showsIcon: Bool = true, iconSize: CGFloat = 44) {\n        self.virtualMachine = virtualMachine\n        self.showsIcon = showsIcon\n        self.iconSize = iconSize\n        self.alwaysUseBlurHash = alwaysUseBlurHash\n        if alwaysUseBlurHash {\n            self._content = .init(initialValue: .blurHash(virtualMachine.metadata.backgroundHash))\n        } else {\n            self._content = .init(initialValue: virtualMachine.artworkContent)\n        }\n    }\n\n    enum Content {\n        case image(Image)\n        case blurHash(BlurHashToken)\n    }\n\n    @State private var content: Content\n\n    public var body: some View {\n        ZStack {\n            switch content {\n            case .image(let image): image.resizable()\n            case .blurHash(let token):\n                BlurHashFullBleedBackground(blurHash: token)\n                    .fullBleedBackgroundBrightness(-0.2)\n                    .fullBleedBackgroundSaturation(1.4)\n                    .fullBleedBackgroundIsThumbnail()\n            }\n\n            if showsIcon, case .blurHash = content {\n                virtualMachine.configuration.systemType.icon\n                    .resizable()\n                    .foregroundStyle(.white)\n                    .aspectRatio(contentMode: .fit)\n                    .frame(width: iconSize, height: iconSize)\n            }\n        }\n        .frame(maxWidth: .infinity, maxHeight: .infinity)\n        .task(id: virtualMachine.metadata) {\n            withTransaction(\\.disablesAnimations, true) {\n                if alwaysUseBlurHash {\n                    self.content = .blurHash(virtualMachine.metadata.backgroundHash)\n                } else {\n                    self.content = virtualMachine.artworkContent\n                }\n            }\n        }\n    }\n}\n\nprivate extension VBVirtualMachine {\n    var artworkContent: VMArtworkView.Content {\n        if let thumbnail {\n            .image(Image(nsImage: thumbnail))\n        } else {\n            .blurHash(metadata.backgroundHash)\n        }\n    }\n}\n\n#if DEBUG\nprivate struct PreviewWrapper: View {\n    var virtualMachine: VBVirtualMachine\n\n    var body: some View {\n        VMArtworkView(virtualMachine: virtualMachine)\n            .aspectRatio(contentMode: .fill)\n            .frame(width: 480, height: 270)\n            .clipShape(RoundedRectangle(cornerRadius: 8))\n            .padding(32)\n    }\n}\n\n\n#Preview(\"Mac - Thumbnail\") {\n    PreviewWrapper(virtualMachine: .preview)\n}\n\n#Preview(\"Mac - Blur Hash\") {\n    PreviewWrapper(virtualMachine: .previewBlurHash)\n}\n\n#Preview(\"Mac - None\") {\n    PreviewWrapper(virtualMachine: .previewNoArtwork)\n}\n\n#Preview(\"Linux - Thumbnail\") {\n    PreviewWrapper(virtualMachine: .previewLinux)\n}\n\n#Preview(\"Linux - Blur Hash\") {\n    PreviewWrapper(virtualMachine: .previewLinuxBlurHash)\n}\n\n#Preview(\"Linux - None\") {\n    PreviewWrapper(virtualMachine: .previewLinuxNoArtwork)\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/Definitions/PreviewSupport-VirtualUI.swift",
    "content": "#if DEBUG\nimport SwiftUI\nimport VirtualCore\n\n@MainActor\npublic extension VirtualMachineSessionUI {\n    static let preview = VirtualMachineSessionUI(controller: .preview)\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/Definitions/VirtualUIConstants.swift",
    "content": "//\n//  VirtualUIConstants.swift\n//  VirtualUI\n//\n//  Created by Guilherme Rambo on 20/07/22.\n//\n\nimport Foundation\nimport OSLog\n@_exported import VirtualCore\n\nstruct VirtualUIConstants {\n    static let subsystemName = \"codes.rambo.VirtualUI\"\n}\n\n@available(swift, obsoleted: 1.0, message: \"Provided for Objective-C compatibility, don't use in Swift code.\")\n@objcMembers\npublic final class _VirtualUIConstantsObjC: NSObject {\n    public class var subsystemName: String { VirtualUIConstants.subsystemName }\n}\n\nprivate final class _VirtualUIStub { }\n\npublic extension Bundle {\n    static let virtualUI = Bundle(for: _VirtualUIStub.self)\n}\n"
  },
  {
    "path": "VirtualUI/Source/Installer/Components/AuthenticatingWebView.swift",
    "content": "//\n//  AuthenticatingWebView.swift\n//  VirtualBuddy\n//\n//  Created by Guilherme Rambo on 07/06/22.\n//\n\nimport SwiftUI\nimport WebKit\n\nstruct AuthenticatingWebView: NSViewControllerRepresentable {\n\n    typealias NSViewControllerType = AuthenticatingWebViewController\n\n    let url: URL\n    let onCookiesChanged: ([HTTPCookie]) -> Void\n\n    init(url: URL, onCookiesChanged: @escaping ([HTTPCookie]) -> Void) {\n        self.url = url\n        self.onCookiesChanged = onCookiesChanged\n    }\n\n    func makeNSViewController(context: Context) -> AuthenticatingWebViewController {\n        AuthenticatingWebViewController(url: url) { 🍪 in\n            DispatchQueue.main.async {\n                self.onCookiesChanged(🍪)\n            }\n        }\n    }\n\n    func updateNSViewController(_ nsViewController: AuthenticatingWebViewController, context: Context) {\n\n    }\n\n}\n\nfinal class AuthenticatingWebViewController: NSViewController, WKUIDelegate, WKNavigationDelegate, WKHTTPCookieStoreObserver {\n\n    var url: URL\n    var cookiesChangedCallback: ([HTTPCookie]) -> Void\n\n    init(url: URL, cookiesChangedCallback: @escaping ([HTTPCookie]) -> Void) {\n        self.url = url\n        self.cookiesChangedCallback = cookiesChangedCallback\n\n        super.init(nibName: nil, bundle: nil)\n    }\n\n    required init?(coder: NSCoder) {\n        fatalError()\n    }\n\n    private var requestSent = false\n\n    private lazy var webView: WKWebView = {\n        let config = WKWebViewConfiguration()\n        let v = WKWebView(frame: .zero, configuration: config)\n\n        v.uiDelegate = self\n        v.navigationDelegate = self\n\n        config.websiteDataStore.httpCookieStore.add(self)\n\n        return v\n    }()\n\n    override func loadView() {\n        view = NSView()\n        view.wantsLayer = true\n\n        webView.translatesAutoresizingMaskIntoConstraints = false\n        view.addSubview(webView)\n\n        NSLayoutConstraint.activate([\n            webView.leadingAnchor.constraint(equalTo: view.leadingAnchor),\n            webView.trailingAnchor.constraint(equalTo: view.trailingAnchor),\n            webView.topAnchor.constraint(equalTo: view.topAnchor),\n            webView.bottomAnchor.constraint(equalTo: view.bottomAnchor)\n        ])\n    }\n\n    override func viewDidAppear() {\n        super.viewDidAppear()\n\n        guard !requestSent else { return }\n        requestSent = true\n\n        webView.load(URLRequest(url: url))\n    }\n\n    func cookiesDidChange(in cookieStore: WKHTTPCookieStore) {\n        print(\"Cookies changed\")\n\n        cookieStore.getAllCookies { [weak self] cookies in\n            self?.cookiesChangedCallback(cookies)\n        }\n    }\n}\n"
  },
  {
    "path": "VirtualUI/Source/Installer/Components/InstallationConsole.swift",
    "content": "//\n//  InstallationConsole.swift\n//  VirtualUI\n//\n//  Created by Guilherme Rambo on 20/07/22.\n//\n\nimport SwiftUI\nimport VirtualCore\n\nstruct InstallationConsole: View {\n\n    var overridePredicate: LogStreamer.Predicate? = nil\n\n    private var predicate: LogStreamer.Predicate {\n        overridePredicate ?? .process(\"com.apple.Virtualization.Installation\")\n    }\n\n    var body: some View {\n        LogConsole(predicate: predicate)\n            .frame(minWidth: 200, maxWidth: .infinity, minHeight: 100, maxHeight: 400)\n            .controlGroup(level: .secondary)\n    }\n\n}\n\n#if DEBUG\n#Preview {\n    InstallationConsole(overridePredicate: .process(\"Xcode\"))\n        .padding()\n        .frame(minWidth: 200, maxWidth: .infinity, minHeight: 200, maxHeight: .infinity, alignment: .bottom)\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/Installer/Components/InstallationWizardTitle.swift",
    "content": "//\n//  InstallationWizardTitle.swift\n//  VirtualBuddy\n//\n//  Created by Guilherme Rambo on 20/07/22.\n//\n\nimport SwiftUI\n\nstruct InstallationWizardTitle: View {\n    var text: String\n    init(_ text: String) { self.text = text }\n    \n    var body: some View {\n        Text(text)\n            .font(.system(.title, design: .rounded).weight(.medium))\n            .padding(.vertical, 22)\n            .multilineTextAlignment(.center)\n    }\n}\n\n"
  },
  {
    "path": "VirtualUI/Source/Installer/Components/RestoreImageURLInputView.swift",
    "content": "import SwiftUI\nimport VirtualCore\n\nstruct RestoreImageURLInputView: View {\n    @EnvironmentObject var viewModel: VMInstallationViewModel\n\n    var body: some View {\n        VirtualBuddyInstallerInputView {\n            TextField(\"Custom Download Link\", text: $viewModel.data.customInstallImageRemoteURL, onCommit: viewModel.next)\n        }\n        .onChange(of: viewModel.data.customInstallImageRemoteURL) { _ in\n            viewModel.validateCustomRemoteURL()\n        }\n    }\n}\n\n#if DEBUG\n#Preview {\n    VMInstallationWizard.preview(step: .restoreImageInput)\n}\n#endif // DEBUG\n"
  },
  {
    "path": "VirtualUI/Source/Installer/Components/VirtualBuddyInstallerInputView.swift",
    "content": "import SwiftUI\n\nstruct VirtualBuddyInstallerInputView<Content: View>: View {\n    @ViewBuilder var content: () -> Content\n\n    @FocusState private var isFocused: Bool\n\n    var body: some View {\n        content()\n            .focused($isFocused)\n            .task { isFocused = true }\n            .textFieldStyle(.roundedBorder)\n            .controlSize(.large)\n            .padding()\n            .controlGroup()\n            .frame(maxWidth: 500)\n    }\n}\n"
  },
  {
    "path": "VirtualUI/Source/Installer/Components/VirtualMachineNameInputView.swift",
    "content": "//\n//  VirtualMachineNameInputView.swift\n//  VirtualUI\n//\n//  Created by Guilherme Rambo on 01/08/22.\n//\n\nimport SwiftUI\nimport VirtualCore\n\nstruct VirtualMachineNameInputView: View {\n    @Binding var name: String\n\n    var body: some View {\n        VirtualBuddyInstallerInputView {\n            HStack {\n                TextField(\"Virtual Machine Name\", text: $name)\n\n                Spacer()\n\n                Button {\n                    name = RandomNameGenerator.shared.newName()\n                } label: {\n                    Image(systemName: \"arrow.clockwise\")\n                        .help(\"Generate new name\")\n                }\n                .buttonStyle(.borderless)\n                .font(.system(size: 15, weight: .medium, design: .rounded))\n                .keyboardShortcut(.init(\"r\", modifiers: .command))\n            }\n        }\n    }\n}\n\n#if DEBUG\n#Preview {\n    VMInstallationWizard.preview(step: .name)\n}\n#endif // DEBUG\n"
  },
  {
    "path": "VirtualUI/Source/Installer/Steps/GuestTypePicker.swift",
    "content": "//\n//  GuestTypePicker.swift\n//  VirtualUI\n//\n//  Created by Guilherme Rambo on 06/03/23.\n//\n\nimport SwiftUI\nimport VirtualCore\n\nextension VBGuestType {\n    var name: String {\n        switch self {\n        case .mac:\n            return \"macOS\"\n        case .linux:\n            return \"Linux\"\n        }\n    }\n\n    var icon: Image { Image(\"VBGuestType/\\(rawValue)\", bundle: .virtualUI) }\n}\n\nstruct GuestTypePicker: View {\n\n    static var buttonRadius: Double { 24 }\n    static var selectionIndicatorID = \"SelectionIndicator\"\n\n    @Binding var selection: VBGuestType\n\n    private var previousMethod: VBGuestType? { VBGuestType.allCases.previous(from: selection) }\n\n    private var nextMethod: VBGuestType? { VBGuestType.allCases.next(from: selection) }\n\n    @Namespace\n    private var selectionIndicator\n\n    var body: some View {\n        HStack(spacing: 16) {\n            ForEach(VBGuestType.supportedByHost) { type in\n                GuestTypeItemView(\n                    type: type,\n                    isSelected: selection == type,\n                    selectionIndicator: selectionIndicator\n                )\n                .onTapGesture {\n                    selection = type\n                }\n            }\n        }\n        .overlay {\n            RoundedRectangle(cornerRadius: Self.buttonRadius, style: .continuous)\n                .stroke(Color.accentColor, lineWidth: 2)\n                .blendMode(.plusLighter)\n                .opacity(0.6)\n                .matchedGeometryEffect(id: Self.selectionIndicatorID, in: selectionIndicator, isSource: false)\n                .animation(.snappy, value: selection)\n        }\n        .accessibilityRepresentation {\n            Picker(selection: $selection) {\n                ForEach(VBGuestType.allCases) { type in\n                    Text(type.name)\n                        .tag(type)\n                }\n            } label: { }\n        }\n        .keyboardNavigation { direction in\n            if direction == .right {\n                guard let nextMethod else { return }\n                selection = nextMethod\n            } else if direction == .left {\n                guard let previousMethod else { return }\n                selection = previousMethod\n            }\n        }\n        .frame(maxWidth: .infinity, maxHeight: .infinity)\n    }\n\n}\n\nstruct GuestTypeItemView: View {\n\n    let type: VBGuestType\n    let isSelected: Bool\n    let selectionIndicator: Namespace.ID\n\n    init(type: VBGuestType, isSelected: Bool, selectionIndicator: Namespace.ID) {\n        self.type = type\n        self.isSelected = isSelected\n        self.selectionIndicator = selectionIndicator\n    }\n\n    var lineWidth: CGFloat { isSelected ? 2 : 1 }\n\n    var body: some View {\n        VStack(spacing: 18) {\n            type.icon\n                .resizable()\n                .aspectRatio(contentMode: .fit)\n                .frame(maxHeight: 80)\n\n            Text(type.name)\n        }\n        .shadow(color: .black.opacity(0.5), radius: 1, x: 0.5, y: 0.5)\n        .padding()\n        .multilineTextAlignment(.center)\n        .font(.system(size: 24, weight: .medium, design: .rounded))\n        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)\n        .aspectRatio(1, contentMode: .fit)\n        .frame(maxWidth: 260)\n        .background {\n            ZStack {\n//                if isSelected {\n//                    Rectangle().foregroundStyle(Material.thick)\n//\n//                    Color.accentColor\n//                        .blendMode(.plusLighter)\n//                        .opacity(0.2)\n//                } else {\n                    Rectangle().foregroundStyle(Material.thin)\n//                }\n            }\n            .clipShape(shape)\n        }\n        .chromeBorder(shape: shape, shadowEnabled: false, highlightIntensity: 0.5)\n        .overlay {\n            if isSelected {\n                Rectangle()\n                    .opacity(0)\n                    .matchedGeometryEffect(id: GuestTypePicker.selectionIndicatorID, in: selectionIndicator, isSource: true)\n            }\n        }\n    }\n\n    private var borderColor: Color {\n        isSelected ? .accentColor : .primary.opacity(0.2)\n    }\n\n    private var shape: some InsettableShape {\n        RoundedRectangle(cornerRadius: GuestTypePicker.buttonRadius, style: .continuous)\n    }\n\n}\n\n#if DEBUG\n@available(macOS 14.0, *)\n#Preview {\n    @Previewable @State var selection: VBGuestType = .mac\n\n    GuestTypePicker(selection: $selection)\n        .padding(22)\n        .frame(width: VMInstallationWizard.maxContentWidth, height: 600)\n        .background(BlurHashFullBleedBackground(blurHash: .virtualBuddyBackground))\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/Installer/Steps/InstallConfigurationStepView.swift",
    "content": "//\n//  InstallConfigurationStepView.swift\n//  VirtualUI\n//\n//  Created by Guilherme Rambo on 20/07/22.\n//\n\nimport SwiftUI\nimport VirtualCore\n\nstruct InstallConfigurationStepView: View {\n    @StateObject private var viewModel: VMConfigurationViewModel\n    @State private var vm: VBVirtualMachine\n    var onSave: (VBVirtualMachine) -> Void\n    \n    init(vm: VBVirtualMachine, resolvedRestoreImage: ResolvedRestoreImage? = nil, onSave: @escaping (VBVirtualMachine) -> Void) {\n        self._vm = .init(wrappedValue: vm)\n        self._viewModel = .init(wrappedValue: VMConfigurationViewModel(vm, context: .preInstall, resolvedRestoreImage: resolvedRestoreImage))\n        self.onSave = onSave\n    }\n    \n    var body: some View {\n        VMConfigurationSheet(configuration: $vm.configuration, customConfirmationButtonAction: { configuration in\n            var updatedVM = vm\n            updatedVM.configuration = configuration\n            self.vm = updatedVM\n            onSave(updatedVM)\n        })\n            .environmentObject(viewModel)\n    }\n}\n\n#if DEBUG\nstruct VMInstallerConfigurationStepView_Previews: PreviewProvider {\n    static var previews: some View {\n        _Template()\n    }\n    \n    struct _Template: View {\n        @State private var vm = VBVirtualMachine.preview\n\n        var body: some View {\n            InstallConfigurationStepView(vm: vm, onSave: { _ in })\n        }\n    }\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/Installer/Steps/InstallMethod.swift",
    "content": "//\n//  InstallMethod.swift\n//  VirtualUI\n//\n//  Created by Guilherme Rambo on 06/03/23.\n//\n\nimport Foundation\nimport VirtualCore\n\nenum InstallMethod: String, Identifiable, CaseIterable, Codable, ProvidesEmptyPlaceholder {\n    var id: RawValue { rawValue }\n\n    case remoteOptions\n    case localFile\n    case remoteManual\n\n    static var empty: InstallMethod { .remoteOptions }\n}\n\nenum InstallMethodSelection: Identifiable, Hashable, Codable {\n    case remoteOptions(RestoreImage)\n    case localFile(URL)\n    case remoteManual(URL)\n\n    var id: InstallMethod {\n        switch self {\n        case .remoteOptions: .remoteOptions\n        case .localFile: .localFile\n        case .remoteManual: .remoteManual\n        }\n    }\n}\n\nextension InstallMethod {\n    func description(for type: VBGuestType) -> String {\n        switch self {\n            case .localFile:\n            switch type {\n            case .mac:\n                return \"Open custom IPSW file from local storage\"\n            case .linux:\n                return \"Open custom ISO file from local storage\"\n            }\n            case .remoteOptions:\n                return \"Download \\(type.name) installer from a list of options\"\n            case .remoteManual:\n                return \"Download \\(type.name) installer from a custom URL\"\n        }\n    }\n\n    var imageName: String {\n        switch self {\n            case .localFile:\n                return \"folder.fill\"\n            case .remoteOptions:\n                return \"square.and.arrow.down.fill\"\n            case .remoteManual:\n                return \"text.cursor\"\n        }\n    }\n}\n\nextension VBGuestType {\n    var customURLPrompt: String {\n        switch self {\n        case .mac:\n            return \"Enter the macOS IPSW URL\"\n        case .linux:\n            return \"Enter the Linux ISO URL\"\n        }\n    }\n\n    var restoreImagePickerPrompt: String {\n        \"Pick a \\(name) Version to Install\"\n    }\n\n    var installFinishedMessage: String {\n        \"Your \\(name) Virtual Machine is Ready!\"\n    }\n}\n"
  },
  {
    "path": "VirtualUI/Source/Installer/Steps/InstallMethodPicker.swift",
    "content": "//\n//  InstallMethodPicker.swift\n//  VirtualBuddy\n//\n//  Created by Guilherme Rambo on 07/06/22.\n//\n\nimport SwiftUI\n\nstruct InstallMethodPicker: View {\n\n    var guestType: VBGuestType\n    @Binding var selection: InstallMethod\n\n    @FocusState private var isFocused: Bool\n\n    private var selectionIndex: Int { InstallMethod.allCases.firstIndex(of: selection) ?? 0 }\n\n    private var previousMethod: InstallMethod? {\n        guard selectionIndex > 0 else { return nil }\n        return InstallMethod.allCases[selectionIndex - 1]\n    }\n\n    private var nextMethod: InstallMethod? {\n        guard selectionIndex < InstallMethod.allCases.count - 1 else { return nil }\n        return InstallMethod.allCases[selectionIndex + 1]\n    }\n\n    var body: some View {\n        VStack(spacing: 16) {\n            ForEach(InstallMethod.allCases) { method in\n                InstallMethodView(\n                    method: method,\n                    description: method.description(for: guestType),\n                    isSelected: selection == method\n                )\n                .onTapGesture {\n                    selection = method\n                }\n            }\n        }\n        .accessibilityRepresentation {\n            Picker(selection: $selection) {\n                ForEach(InstallMethod.allCases) { method in\n                    Text(method.description(for: guestType))\n                        .tag(method)\n                }\n            } label: { }\n        }\n        .overlay {\n            /// Horrible hack to hide the focus ring while still allowing for keyboard navigation.\n            Rectangle()\n                .frame(width: 0, height: 0)\n                .opacity(0)\n                .focusable(true)\n                .focused($isFocused)\n                .onMoveCommand { direction in\n                    if direction == .down {\n                        guard let nextMethod else { return }\n                        selection = nextMethod\n                    } else if direction == .up {\n                        guard let previousMethod else { return }\n                        selection = previousMethod\n                    }\n                }\n        }\n        .onAppearOnce {\n            isFocused = true\n        }\n    }\n\n}\n\nstruct InstallMethodView: View {\n\n    let method: InstallMethod\n    let description: String\n    let isSelected: Bool\n\n    var lineWidth: CGFloat { isSelected ? 2 : 1 }\n\n    var body: some View {\n        HStack {\n            Image(systemName: method.imageName)\n\n            Text(description)\n        }\n        .foregroundColor(isSelected ? .accentColor : .secondary)\n        .padding()\n        .multilineTextAlignment(.center)\n        .font(.system(size: 14))\n        .frame(maxWidth: .infinity, alignment: .leading)\n        .overlay(shape.stroke(borderColor, style: StrokeStyle(lineWidth: lineWidth)))\n        .materialBackground(.menu, blendMode: .withinWindow, state: isSelected ? .active : .inactive, in: shape)\n        .clipShape(shape)\n        .shadow(color: .black.opacity(0.24), radius: 8, x: 0, y: 0)\n    }\n\n    private var borderColor: Color {\n        isSelected ? .accentColor : .primary.opacity(0.2)\n    }\n\n    private var shape: some Shape {\n        RoundedRectangle(cornerRadius: 14, style: .continuous)\n    }\n\n}\n\n#if DEBUG\nstruct InstallMethodPicker_Previews: PreviewProvider {\n    static var previews: some View {\n        InstallMethodPicker(guestType: .mac, selection: .constant(.remoteOptions))\n    }\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/Installer/Steps/InstallProgressStepView.swift",
    "content": "//\n//  InstallProgressStepView.swift\n//  VirtualUI\n//\n//  Created by Guilherme Rambo on 20/07/22.\n//\n\nimport SwiftUI\nimport VirtualCore\nimport Virtualization\n\nstruct InstallProgressStepView: View {\n    @EnvironmentObject var viewModel: VMInstallationViewModel\n\n    private var progress: Double? {\n        switch viewModel.state {\n        case .loading(let progress, _): progress ?? 0\n        case .idle: 0\n        case .error: nil\n        }\n    }\n\n    private var status: Text? {\n        switch viewModel.state {\n            case .loading(_, let info): info.flatMap { Text($0) }\n            case .error(let message): Text(message)\n            case .idle: Text(\"Installing\")\n        }\n    }\n\n    private var style: VirtualBuddyMonoStyle {\n        switch viewModel.state {\n        case .idle, .loading: .default\n        case .error: .failure\n        }\n    }\n\n    var body: some View {\n        if let status {\n            VirtualBuddyMonoProgressView(progress: progress, status: status, style: style)\n                .textSelection(.enabled)\n        } else if let virtualMachine = viewModel.virtualMachine {\n            InstallerVirtualMachineView(virtualMachine: virtualMachine)\n                .frame(maxWidth: .infinity, maxHeight: .infinity)\n        } else {\n            VirtualBuddyMonoProgressView(progress: progress, status: Text(\"\"), style: style)\n        }\n    }\n}\n\nprivate struct InstallerVirtualMachineView: NSViewRepresentable {\n    typealias NSViewType = VZVirtualMachineView\n\n    let virtualMachine: VZVirtualMachine\n\n    func makeNSView(context: Context) -> VZVirtualMachineView {\n        VZVirtualMachineView(frame: .zero)\n    }\n\n    func updateNSView(_ nsView: VZVirtualMachineView, context: Context) {\n        nsView.virtualMachine = virtualMachine\n    }\n}\n\n#if DEBUG\n#Preview {\n    VMInstallationWizard.preview(step: .install)\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/Installer/Steps/Restore Image Selection/Components/BlurHashFullBleedBackground.swift",
    "content": "import SwiftUI\nimport VirtualCore\nimport BuddyKit\n\nprivate extension EnvironmentValues {\n    @Entry var fullBleedBackgroundTransitionDuration: TimeInterval = BlurHashFullBleedBackground.defaultTransitionDuration\n    @Entry var fullBleedBackgroundBrightness: Double = BlurHashFullBleedBackground.defaultBrightness\n    @Entry var fullBleedBackgroundSaturation: Double = BlurHashFullBleedBackground.defaultSaturation\n    @Entry var fullBleedBackgroundBlurRadius: Double = BlurHashFullBleedBackground.defaultBlurRadius\n    @Entry var fullBleedBackgroundIsThumbnail: Bool = false\n\n    var fullBleedBackgroundDimmed: Bool {\n        get {\n            fullBleedBackgroundBrightness < BlurHashFullBleedBackground.defaultBrightness\n        }\n        set {\n            fullBleedBackgroundBrightness = newValue ? BlurHashFullBleedBackground.defaultBrightnessDimmed : BlurHashFullBleedBackground.defaultBrightness\n            fullBleedBackgroundSaturation = newValue ? BlurHashFullBleedBackground.defaultSaturationDimmed : BlurHashFullBleedBackground.defaultSaturation\n        }\n    }\n}\n\npublic extension View {\n    func fullBleedBackgroundDimmed(_ dimmed: Bool = true) -> some View {\n        environment(\\.fullBleedBackgroundDimmed, dimmed)\n    }\n    func fullBleedBackgroundBrightness(_ brightness: Double?) -> some View {\n        environment(\\.fullBleedBackgroundBrightness, brightness ?? BlurHashFullBleedBackground.defaultBrightness)\n    }\n    func fullBleedBackgroundSaturation(_ saturation: Double?) -> some View {\n        environment(\\.fullBleedBackgroundSaturation, saturation ?? BlurHashFullBleedBackground.defaultSaturation)\n    }\n    func fullBleedBackgroundBlurRadius(_ radius: Double?) -> some View {\n        environment(\\.fullBleedBackgroundBlurRadius, radius ?? BlurHashFullBleedBackground.defaultBlurRadius)\n    }\n    func fullBleedBackgroundIsThumbnail(_ isThumbnail: Bool = true) -> some View {\n        environment(\\.fullBleedBackgroundIsThumbnail, isThumbnail)\n    }\n}\n\nstruct BlurHashFullBleedBackground: View {\n    static let defaultTransitionDuration: TimeInterval = 1.0\n\n    static let defaultBrightness: Double = -0.2\n    static let defaultBrightnessDimmed: Double = -0.3\n\n    static let defaultSaturation: Double = 1.3\n    static let defaultSaturationDimmed: Double = 0.8\n\n    static let defaultBlurRadius: Double = 22\n\n    static let reduceTransparencyMultiplierSaturation: Double = 0.4\n    static let reduceTransparencyOffsetBrightness: Double = -0.15\n    static let reduceTransparencyMultiplierBlurRadius: Double = 2.0\n\n    enum Content: Hashable {\n        case blurHash(BlurHashToken)\n        case customImage(NSImage)\n    }\n\n    var content: Content?\n\n    init(content: Content?) {\n        self.content = content\n    }\n\n    init(blurHash: BlurHashToken?) {\n        self.content = blurHash.flatMap { .blurHash($0) }\n    }\n\n    init(image: NSImage?) {\n        self.content = image.flatMap { .customImage($0) }\n    }\n\n    init(_ blurHashValue: String?) {\n        self.init(blurHash: blurHashValue.flatMap { BlurHashToken(value: $0) })\n    }\n\n    @Environment(\\.fullBleedBackgroundIsThumbnail)\n    private var isThumbnail\n\n    @Environment(\\.accessibilityReduceTransparency)\n    private var reduceTransparency\n\n    var body: some View {\n        ZStack {\n            _BlurHashRepresentable(content: content)\n\n            if reduceTransparency, !isThumbnail {\n                Color(nsColor: .windowBackgroundColor)\n                    .opacity(0.5)\n            }\n        }\n        .ignoresSafeArea()\n        .frame(maxWidth: .infinity, maxHeight: .infinity)\n    }\n}\n\nprivate struct _BlurHashRepresentable: NSViewRepresentable {\n    typealias Content = BlurHashFullBleedBackground.Content\n\n    var content: Content?\n\n    typealias NSViewType = _BlurHashNSView\n\n    func makeNSView(context: Context) -> _BlurHashNSView {\n        _BlurHashNSView(frame: .zero)\n    }\n\n    func updateNSView(_ nsView: _BlurHashNSView, context: Context) {\n        nsView.animationsDisabled = context.transaction.disablesAnimations\n        nsView.transitionDuration = context.environment.fullBleedBackgroundTransitionDuration\n\n        switch content {\n        case .blurHash(let token):\n            nsView.blurHash = token\n        case .customImage(let image):\n            nsView.customImage = image\n        case .none:\n            nsView.blurHash = nil\n            nsView.customImage = nil\n        }\n\n        /// Reduce transparency modifications only apply when the view is being used as a background, not when it's used as a standalone image thumbnail.\n        let reduceTransparency = context.environment.accessibilityReduceTransparency && !context.environment.fullBleedBackgroundIsThumbnail\n        let brightnessOffset = reduceTransparency ? BlurHashFullBleedBackground.reduceTransparencyOffsetBrightness : 0\n        let saturationMultiplier = reduceTransparency ? BlurHashFullBleedBackground.reduceTransparencyMultiplierSaturation : 1\n        let blurRadiusMultiplier = reduceTransparency ? BlurHashFullBleedBackground.reduceTransparencyMultiplierBlurRadius : 1\n\n        nsView.brightness = context.environment.fullBleedBackgroundBrightness + brightnessOffset\n        nsView.saturation = context.environment.fullBleedBackgroundSaturation * saturationMultiplier\n        nsView.blurRadius = context.environment.fullBleedBackgroundBlurRadius * blurRadiusMultiplier\n    }\n\n    final class _BlurHashNSView: NSView {\n        private lazy var assetLayer: CALayer = .load(assetNamed: \"FullBleedBlurHash\", bundle: .virtualUI) ?? CALayer()\n\n        var blurHash: BlurHashToken? {\n            didSet {\n                guard blurHash != oldValue else { return }\n                image = blurHash.flatMap { NSImage.blurHash($0) }\n            }\n        }\n\n        var customImage: NSImage? {\n            didSet {\n                guard customImage != oldValue else { return }\n                guard let customImage else {\n                    image = nil\n                    return\n                }\n\n                let scale = 0.01\n                let size = CGSize(width: customImage.size.width * scale, height: customImage.size.height * scale)\n                UILog(\"Custom image size: \\(size)\")\n\n                image = NSImage(size: size, flipped: true) { rect in\n                    customImage.draw(in: rect)\n                    return true\n                }\n            }\n        }\n\n        private var lastRenderedToken: BlurHashToken?\n        private var lastRenderedImage: NSImage?\n\n        @Invalidating(.layout)\n        private var image: NSImage? = nil\n\n        @Invalidating(.layout)\n        var transitionDuration: TimeInterval = BlurHashFullBleedBackground.defaultTransitionDuration\n\n        @Invalidating(.layout)\n        var animationsDisabled: Bool = false\n\n        var brightness: Double = BlurHashFullBleedBackground.defaultBrightness {\n            didSet {\n                withCurrentEnvironment {\n                    assetLayer.setValue(brightness, forKeyPath: \"filters.colorBrightness.inputAmount\")\n                }\n            }\n        }\n\n        var saturation: Double = BlurHashFullBleedBackground.defaultSaturation {\n            didSet {\n                withCurrentEnvironment {\n                    assetLayer.setValue(saturation, forKeyPath: \"filters.colorSaturate.inputAmount\")\n                }\n            }\n        }\n\n        var blurRadius: Double = BlurHashFullBleedBackground.defaultBlurRadius {\n            didSet {\n                withCurrentEnvironment {\n                    assetLayer.setValue(blurRadius, forKeyPath: \"filters.gaussianBlur.inputRadius\")\n                }\n            }\n        }\n\n        override func layout() {\n            CATransaction.begin()\n            CATransaction.setDisableActions(true)\n\n            if assetLayer.superlayer == nil {\n                platformLayer.addSublayer(assetLayer)\n            }\n\n            assetLayer.frame = bounds\n\n            CATransaction.commit()\n\n            guard blurHash != lastRenderedToken || image != lastRenderedImage else { return }\n            defer {\n                lastRenderedToken = blurHash\n                lastRenderedImage = image\n            }\n\n            withCurrentEnvironment {\n                assetLayer.contents = image\n            }\n        }\n\n        private func withCurrentEnvironment(perform block: () -> ()) {\n            CATransaction.begin()\n            CATransaction.setDisableActions(animationsDisabled)\n            CATransaction.setAnimationDuration(animationsDisabled ? 0 : transitionDuration)\n\n            block()\n\n            CATransaction.commit()\n        }\n    }\n}\n\n#if DEBUG\nextension BlurHashToken {\n    static let previewSequoia = BlurHashToken(value: \"eN86q8M1Hbyya7t-g$MpnTx,b;k9X8Vgr?osbHenWEeYoGj@aNaPah\")\n    static let previewSonoma = BlurHashToken(value: \"ec8rzYaIWBj?a}iqosaxj?fkRFa2axayj[t%ofV[ayf6V]pGkBf5fi\")\n    static let previewVentura = BlurHashToken(value: \"enHk%=ocoeW:Nb-8xEODaya#1fWWJAWExDEjR-jGazoJWCagw^s-Wp\")\n}\n\nprivate struct BlurHashFullBleedBackgroundPreview: View {\n    @State var token = BlurHashToken.virtualBuddyBackground\n\n    @State private var brightness: Double = BlurHashFullBleedBackground.defaultBrightness\n    @State private var effectiveBrightness: Double = BlurHashFullBleedBackground.defaultBrightness\n    @State private var dimmed = false\n\n    private let tokens: [BlurHashToken] = [\n        .virtualBuddyBackground,\n        .previewSequoia,\n        .previewSonoma,\n        .previewVentura\n    ]\n\n    var enableCycle = true\n\n    var body: some View {\n        BlurHashFullBleedBackground(blurHash: token)\n        /// Swap below environment modifiers to be able to test arbitrary brightness values in preview\n//            .environment(\\.fullBleedBackgroundBrightness, effectiveBrightness)\n            .environment(\\.fullBleedBackgroundDimmed, dimmed)\n            .task(id: enableCycle) {\n                guard enableCycle else { return }\n\n                let delay = 3\n\n                while true {\n                    do {\n                        try await Task.sleep(for: .seconds(delay))\n\n                        let index = tokens.firstIndex(of: token)!\n\n                        if index < tokens.count - 1 {\n                            token = tokens[index + 1]\n                        } else {\n                            token = tokens[0]\n                        }\n                    } catch {\n                        break\n                    }\n                }\n            }\n            .frame(width: 512, height: 512)\n            .overlay(alignment: .bottom) {\n                Form {\n                    Slider(value: $brightness, in: -1.0...1.0) {\n                        HStack {\n                            Text(\"Brightness\")\n                            Text(brightness, format: .percent.rounded(rule: .toNearestOrEven, increment: 1))\n                                .foregroundStyle(.secondary)\n                                .monospacedDigit()\n                                .frame(width: 40, alignment: .trailing)\n                        }\n                    }\n\n                    Toggle(\"Dimmed\", isOn: $dimmed)\n                }\n                .frame(width: 400)\n                .padding()\n                .background(Material.thin, in: RoundedRectangle(cornerRadius: 16))\n                .chromeBorder(radius: 16)\n                .padding()\n            }\n            .onChange(of: brightness) { newValue in\n                withTransaction(\\.disablesAnimations, true) {\n                    effectiveBrightness = newValue\n                }\n            }\n    }\n}\n\n#Preview(\"Blur Hash\") {\n    BlurHashFullBleedBackgroundPreview()\n}\n\n#Preview(\"Custom Image\") {\n    BlurHashFullBleedBackground(image: .blurHashPreview)\n}\n\nprivate extension NSImage {\n    static let blurHashPreview = NSImage(contentsOfFile: \"/System/Library/Desktop Pictures/Sonoma.heic\")!\n}\n#endif // DEBUG\n"
  },
  {
    "path": "VirtualUI/Source/Installer/Steps/Restore Image Selection/Components/CatalogGroupPicker.swift",
    "content": "import SwiftUI\nimport VirtualCore\n\nstruct CatalogGroupPicker: View {\n    static let buttonAspectRatio: Double = 320 / 180\n\n    @EnvironmentObject\n    private var controller: RestoreImageSelectionController\n\n    var groups: [ResolvedCatalogGroup]?\n    @Binding var selectedGroup: ResolvedCatalogGroup?\n\n    @State private var scrolledGroupID: ResolvedCatalogGroup.ID?\n\n    var width: CGFloat { 220 }\n    var spacing: CGFloat { containerPadding }\n\n    var body: some View {\n        if #available(macOS 14.0, *) {\n            container\n                .scrollPosition(id: $scrolledGroupID, anchor: .center)\n        } else {\n            container\n        }\n    }\n\n    @Environment(\\.containerPadding)\n    private var containerPadding\n\n    @FocusState\n    private var focus: RestoreImageSelectionFocus?\n\n    @ViewBuilder\n    private var container: some View {\n        ScrollView(.vertical, showsIndicators: false) {\n            LazyVStack(alignment: .center, spacing: spacing) {\n                if #available(macOS 14.0, *) {\n                    list.scrollTargetLayout()\n                } else {\n                    list\n                }\n            }\n            .frame(width: width)\n            .padding([.top, .leading, .bottom], containerPadding)\n            .padding(.trailing, containerPadding * 0.5)\n        }\n        .focusable()\n        .focused($focus, equals: RestoreImageSelectionFocus.groups)\n        .backported_focusEffectDisabled()\n        .onMoveCommand { direction in\n            switch direction {\n            case .up:\n                if let previous = groups?.previous(from: selectedGroup) {\n                    selectedGroup = previous\n                }\n            case .down:\n                if let next = groups?.next(from: selectedGroup) {\n                    selectedGroup = next\n                }\n            case .right:\n                controller.focusedElement = .images\n            default:\n                break\n            }\n        }\n        .accessibilityRepresentation {\n            if let groups {\n                Picker(\"Choose option\", selection: $selectedGroup) {\n                    ForEach(groups) { group in\n                        Text(group.name)\n                            .tag(group)\n                    }\n                }\n            }\n        }\n        .onChange(of: selectedGroup?.id) { groupID in\n            withAnimation(.snappy) {\n                scrolledGroupID = groupID\n            }\n        }\n        .onReceive(controller.$focusedElement) { focus = $0 }\n    }\n\n    @ViewBuilder\n    private var list: some View {\n        if let groups {\n            ForEach(groups) { group in\n                groupButton(for: group)\n            }\n        } else if controller.isLoading {\n            /// Placeholders are only displayed when controller is loading to avoid jumps when loading happens quickly (or not at all).\n            ForEach(0...5, id: \\.self) { _ in\n                groupButton(for: .placeholder)\n            }\n        }\n    }\n\n    @ViewBuilder\n    private func groupButton(for group: ResolvedCatalogGroup) -> some View {\n        Button {\n            selectedGroup = group\n        } label: {\n            CatalogGroupView(group: group)\n        }\n        .buttonStyle(CatalogGroupButtonStyle(isSelected: group.id == selectedGroup?.id))\n        .aspectRatio(Self.buttonAspectRatio, contentMode: .fit)\n    }\n}\n\nstruct CatalogGroupButtonStyle: ButtonStyle {\n    var isSelected: Bool\n\n    @Environment(\\.isFocused)\n    private var isFocused\n\n    func makeBody(configuration: Configuration) -> some View {\n        configuration.label\n            .chromeBorder(radius: CatalogGroupView.cornerRadius, highlightEnabled: !isSelected)\n            .overlay {\n                if isSelected {\n                    shape\n                        .strokeBorder(Color.white, lineWidth: 2)\n                        .blendMode(.plusLighter)\n                        .opacity(isFocused ? 0.8 : 0.4)\n                }\n            }\n            .scaleEffect(configuration.isPressed ? 0.98 : 1)\n    }\n\n    private var shape: some InsettableShape {\n        RoundedRectangle(cornerRadius: CatalogGroupView.cornerRadius, style: .continuous)\n    }\n}\n\n#if DEBUG\n#Preview {\n    VMInstallationWizard.preview\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/Installer/Steps/Restore Image Selection/Components/CatalogGroupView.swift",
    "content": "import SwiftUI\nimport VirtualCore\n\nstruct CatalogGroupView: View {\n    static var cornerRadius: CGFloat { 14 }\n\n    var group: ResolvedCatalogGroup\n\n    var thumbnail: CatalogGraphic.Thumbnail { group.darkImage.thumbnail }\n\n    @Environment(\\.redactionReasons)\n    private var redaction\n\n    var body: some View {\n        ZStack {\n            RemoteImage(\n                url: thumbnail.url,\n                blurHash: thumbnail.blurHash,\n                blurHashSize: .vbBlurHashSize\n            )\n\n            Text(group.name)\n                .font(.system(size: 32, weight: .semibold, design: .rounded))\n                .shadow(color: .black.opacity(0.2), radius: 3)\n                .lineLimit(1)\n                .minimumScaleFactor(0.3)\n                .padding(.horizontal, 22)\n                .opacity(redaction.isEmpty ? 1 : 0)\n        }\n        .clipShape(shape)\n        .contentShape(shape)\n    }\n\n    private var shape: some InsettableShape {\n        RoundedRectangle(cornerRadius: Self.cornerRadius, style: .continuous)\n    }\n}\n\nextension CatalogGroupView {\n    static let placeholder = CatalogGroupView(group: .placeholder)\n}\n\n#if DEBUG\n@available(macOS 14.0, *)\n#Preview {\n    @Previewable @State var isSelected = false\n    Button {\n        isSelected.toggle()\n    } label: {\n        CatalogGroupView(group: ResolvedCatalog.previewMac.groups[0])\n    }\n    .buttonStyle(CatalogGroupButtonStyle(isSelected: isSelected))\n    .aspectRatio(320/180, contentMode: .fit)\n    .frame(width: 320, height: 180)\n    .padding(64)\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/Installer/Steps/Restore Image Selection/Components/InstallProgressDisplayView.swift",
    "content": "import SwiftUI\nimport VirtualCore\nimport BuddyKit\n\nstruct InstallProgressDisplayView: View {\n    @EnvironmentObject private var viewModel: VMInstallationViewModel\n\n    var body: some View {\n        VirtualDisplayView {\n            VStack {\n                switch viewModel.step {\n                case .download:\n                    RestoreImageDownloadView()\n                case .install:\n                    InstallProgressStepView()\n                case .done:\n                    VirtualBuddyMonoProgressView(\n                        status: Text(viewModel.data.systemType.installFinishedMessage),\n                        style: .success\n                    )\n                default:\n                    EmptyView()\n                }\n            }\n        }\n    }\n}\n\n#if DEBUG\n#Preview {\n    VMInstallationWizard.preview(step: .done)\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/Installer/Steps/Restore Image Selection/Components/RestoreImageBrowser.swift",
    "content": "//\n//  RestoreImageBrowser.swift\n//  VirtualBuddy\n//\n//  Created by Guilherme Rambo on 02/08/24.\n//\n\nimport SwiftUI\nimport VirtualCore\nimport Combine\nimport BuddyKit\n\nstruct ChannelGroup: Identifiable, Hashable {\n    var id: CatalogChannel.ID { channel.id }\n    var order: Int\n    var channel: CatalogChannel\n    var images: [ResolvedRestoreImage]\n}\n\nstruct RestoreImageBrowser: View {\n    @EnvironmentObject\n    private var controller: RestoreImageSelectionController\n\n    @Binding var selection: ResolvedRestoreImage?\n\n    init(selection: Binding<ResolvedRestoreImage?>) {\n        self._selection = selection\n    }\n\n    @Environment(\\.containerPadding)\n    private var containerPadding\n\n    @Environment(\\.maxContentWidth)\n    private var maxContentWidth\n\n    @FocusState\n    private var focus: RestoreImageSelectionFocus?\n\n    @State private var scrolledImageID: ResolvedRestoreImage.ID?\n\n    var body: some View {\n        Group {\n            if #available(macOS 14.0, *) {\n                scrollView\n                    .scrollPosition(id: $scrolledImageID, anchor: .center)\n            } else {\n                scrollView\n            }\n        }\n        .safeAreaInset(edge: .top, spacing: 0) { Color.clear.frame(height: containerPadding) }\n        .safeAreaInset(edge: .bottom, spacing: 0) { Color.clear.frame(height: containerPadding) }\n        .focusable()\n        .focused($focus, equals: RestoreImageSelectionFocus.images)\n        .backported_focusEffectDisabled()\n        .onMoveCommand { direction in\n            switch direction {\n            case .down:\n                if let previous = controller.images.next(from: controller.selectedRestoreImage) {\n                    controller.selectedRestoreImage = previous\n                }\n            case .up:\n                if let next = controller.images.previous(from: controller.selectedRestoreImage) {\n                    controller.selectedRestoreImage = next\n                }\n            case .left:\n                controller.focusedElement = .groups\n            default:\n                break\n            }\n        }\n        .onChange(of: selection) { image in\n            guard let image else { return }\n\n            scrolledImageID = image.id\n\n            guard image.id != controller.selectedRestoreImage?.id else { return }\n\n            controller.selectedRestoreImage = image\n        }\n        .onReceive(controller.$focusedElement) { focus = $0 }\n        .onReceive(controller.$selectedRestoreImage.removeDuplicates()) {\n            guard let newSelection = $0 else { return }\n            guard newSelection.id != selection?.id else { return }\n            guard newSelection.image.group == controller.selectedGroup?.id else { return }\n\n            selection = $0\n        }\n    }\n\n    @Environment(\\.redactionReasons)\n    private var redaction\n\n    @ViewBuilder\n    private var scrollView: some View {\n        ScrollView(.vertical, showsIndicators: false) {\n            if #available(macOS 14.0, *) {\n                stack.scrollTargetLayout()\n            } else {\n                stack\n            }\n        }\n    }\n\n    @ViewBuilder\n    private var stack: some View {\n        LazyVStack(alignment: .leading, spacing: 8, pinnedViews: .sectionHeaders) {\n            if redaction.isEmpty {\n                ForEach(controller.channelGroups) { group in\n                    section(for: group)\n                }\n            } else if controller.isLoading {\n                /// Placeholders are only displayed when controller is loading to avoid jumps when loading happens quickly (or not at all).\n                ForEach(0...12, id: \\.self) { _ in\n                    RestoreImageButton(image: .placeholder, isSelected: false, action: { }, deleteDownload: { })\n                }\n            }\n        }\n        .padding(.trailing, containerPadding)\n        .padding(.leading, containerPadding * 0.5)\n    }\n\n    @ViewBuilder\n    private func section(for group: ChannelGroup) -> some View {\n        Section {\n            ForEach(group.images) { image in\n                RestoreImageButton(image: image, isSelected: image.id == selection?.id) {\n                    selection = image\n                } deleteDownload: {\n                    controller.deleteLocalDownload(for: image)\n                }\n                .tag(image)\n            }\n        }\n    }\n}\n\nprivate struct RestoreImageButton: View {\n    var image: ResolvedRestoreImage\n    var isSelected: Bool\n    var action: () -> ()\n    var deleteDownload: () -> ()\n\n    var body: some View {\n        Button {\n            action()\n        } label: {\n            label\n        }\n        .buttonStyle(RestoreImageButtonStyle(isSelected: isSelected))\n    }\n\n    @State private var showingSupportDetail = false\n\n    @ViewBuilder\n    var label: some View {\n        VStack(alignment: .leading, spacing: 6) {\n            HStack {\n                downloadState\n\n                Spacer()\n\n                details\n\n                supportState\n            }\n\n            if case .unsupported(let title, _) = image.status, let title {\n                Button {\n                    showingSupportDetail.toggle()\n                } label: {\n                    Text(title)\n                        .multilineTextAlignment(.trailing)\n                        .frame(maxWidth: .infinity, alignment: .trailing)\n                        .font(.subheadline)\n                }\n                .buttonStyle(.borderless)\n                .foregroundStyle(Color.red)\n                .blendMode(.plusLighter)\n                .popover(isPresented: $showingSupportDetail) {\n                    RestoreImageFeatureDetailView(image: image)\n                }\n            }\n        }\n        .monospacedDigit()\n        .contextMenu {\n            Button(\"Copy Download Link\") {\n                Pasteboard.general.string = image.url.absoluteString\n            }\n\n            Button(\"Copy Build Number\") {\n                Pasteboard.general.string = image.build\n            }\n\n            if image.isDownloaded {\n                Divider()\n\n                Button(role: .destructive) {\n                    deleteDownload()\n                } label: {\n                    Text(\"Delete Download…\")\n                }\n            }\n        }\n    }\n\n    @ViewBuilder\n    private var downloadState: some View {\n        HStack {\n            Image(systemName: image.isDownloaded ? \"internaldrive\" : \"arrow.down.circle\")\n                .frame(width: 16)\n                .foregroundStyle(.secondary)\n                .help(image.isDownloaded ? \"This version is available from your previous downloads.\" : \"This version needs to be downloaded.\")\n\n            Text(image.name)\n                .minimumScaleFactor(0.8)\n                .lineLimit(1)\n                .help(image.name)\n        }\n        .font(.headline)\n    }\n\n    @ViewBuilder\n    private var details: some View {\n        HStack(spacing: 4) {\n            Text(image.build)\n\n            Text(\"·\")\n\n            Text(image.formattedDownloadSize)\n        }\n        .font(.subheadline)\n        .foregroundStyle(.secondary)\n        .multilineTextAlignment(.trailing)\n    }\n\n    @ViewBuilder\n    private var supportState: some View {\n        RestoreImageFeatureStatusButton(image: image)\n    }\n}\n\nstruct RestoreImageFeatureStatusButton: View {\n    let image: ResolvedRestoreImage\n\n    var status: ResolvedFeatureStatus { image.status }\n\n    var helpText: String {\n        switch status {\n        case .supported: \"This version is supported on your Mac. Click for details about supported features.\"\n        case .warning(let title, let message): title ?? message\n        case .unsupported(let title, let message): title ?? message\n        }\n    }\n\n    @State private var showingDetail = false\n\n    var body: some View {\n        Button {\n            showingDetail.toggle()\n        } label: {\n            FeatureStatusLabel(status: status)\n        }\n        .buttonStyle(.borderless)\n        .help(helpText)\n        .popover(isPresented: $showingDetail) {\n            RestoreImageFeatureDetailView(image: image)\n        }\n    }\n}\n\nstruct FeatureStatusLabel: View {\n    var status: ResolvedFeatureStatus\n\n    var body: some View {\n        Image(systemName: status.systemImage)\n            .foregroundStyle(status.color)\n            .symbolVariant(.circle.fill)\n    }\n}\n\nstruct RestoreImageFeatureDetailView: View {\n    static var padding: Double { 12 }\n\n    let image: ResolvedRestoreImage\n\n    var body: some View {\n        VStack(spacing: 16) {\n            topLevelStatus\n\n            VStack(alignment: .leading, spacing: 8) {\n                ForEach(image.features) { feature in\n                    RestoreImageFeatureDetailItem(feature: feature)\n                }\n            }\n        }\n        .frame(width: 340, alignment: .leading)\n        .padding()\n    }\n\n    @ViewBuilder\n    private var topLevelStatus: some View {\n        switch image.status {\n        case .supported:\n            EmptyView()\n        case .warning(let title, let message), .unsupported(let title, let message):\n            VStack(alignment: .leading, spacing: 12) {\n                HStack {\n                    FeatureStatusLabel(status: image.status)\n\n                    if let title {\n                        Text(title)\n                    } else {\n                        Text(\"This Version May Not Work\")\n                    }\n                }\n                    .imageScale(.large)\n                    .font(.headline)\n\n                Group {\n                    if let attributedMessage = try? AttributedString(markdown: message, options: .init(allowsExtendedAttributes: true, interpretedSyntax: .inlineOnlyPreservingWhitespace, failurePolicy: .returnPartiallyParsedIfPossible, languageCode: nil)) {\n                        Text(attributedMessage)\n                    } else {\n                        Text(message)\n                    }\n                }\n                .fixedSize(horizontal: false, vertical: true)\n            }\n            .frame(maxWidth: .infinity, alignment: .leading)\n            .padding()\n            .background {\n                image.status.color\n                    .blendMode(.plusDarker)\n                    .opacity(0.2)\n            }\n            .controlGroup()\n            .textSelection(.enabled)\n        }\n    }\n}\n\nstruct RestoreImageFeatureDetailItem: View {\n    let feature: ResolvedVirtualizationFeature\n\n    var status: ResolvedFeatureStatus { feature.status }\n\n    var body: some View {\n        HStack(alignment: .center) {\n            VStack(alignment: .leading, spacing: 6) {\n                HStack(spacing: 4) {\n                    FeatureStatusLabel(status: status)\n\n                    Text(feature.name)\n\n                    Spacer()\n\n                }\n                .font(.headline)\n\n                Text(feature.detail)\n                    .foregroundStyle(.secondary)\n                    .fixedSize(horizontal: false, vertical: true)\n            }\n        }\n        .textSelection(.enabled)\n        .padding(RestoreImageFeatureDetailView.padding)\n        .frame(maxWidth: .infinity, alignment: .leading)\n        .controlGroup(level: .secondary)\n    }\n}\n\nextension ResolvedFeatureStatus {\n    var topLevelTitle: String {\n        switch self {\n        case .supported: \"This Version Should Work\"\n        case .warning: \"This Version May Not Work\"\n        case .unsupported: \"This Version Will Not Work\"\n        }\n    }\n\n    var systemImage: String {\n        switch self {\n        case .supported: \"checkmark\"\n        case .warning: \"exclamationmark.triangle\"\n        case .unsupported: \"xmark\"\n        }\n    }\n\n    var color: Color {\n        switch self {\n        case .supported: .green\n        case .warning: .yellow\n        case .unsupported: .red\n        }\n    }\n\n    var textColor: Color {\n        switch self {\n        case .supported: .green\n        case .warning, .unsupported: .yellow\n        }\n    }\n\n    var subtitle: String {\n        switch self {\n        case .supported: \"Supported\"\n        case .warning: \"Warning\"\n        case .unsupported: \"Not Supported\"\n        }\n    }\n}\n\nstruct RestoreImageButtonStyle: ButtonStyle {\n    var isSelected = false\n    var cornerRadius: CGFloat = 14\n\n    @Environment(\\.isFocused)\n    private var isFocused\n\n    func makeBody(configuration: Configuration) -> some View {\n        configuration.label\n            .padding(.vertical, 12)\n            .padding(.horizontal, 16)\n            .frame(maxWidth: .infinity, alignment: .leading)\n            .background(Material.thin, in: shape)\n            .background(Color.black.opacity(0.14).blendMode(.plusDarker), in: shape)\n            .chromeBorder(radius: cornerRadius, highlightEnabled: !isSelected, shadowEnabled: false, highlightIntensity: 0.4)\n            .overlay {\n                if isSelected {\n                    shape\n                        .strokeBorder(Color.white, lineWidth: 2)\n                        .blendMode(.plusLighter)\n                        .opacity(isFocused ? 0.8 : 0.4)\n                }\n            }\n    }\n\n    private var shape: some InsettableShape {\n        RoundedRectangle(cornerRadius: cornerRadius, style: .continuous)\n    }\n}\n\nextension ResolvedRestoreImage {\n    var formattedDownloadSize: String {\n        ByteCountFormatter.string(fromByteCount: downloadSize, countStyle: .file)\n    }\n}\n\n#if DEBUG\n@available(macOS 14.0, *)\n#Preview {\n    VMInstallationWizard.preview\n}\n\n#Preview(\"Feature Detail\") {\n    RestoreImageFeatureDetailView(image: .previewMac)\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/Installer/Steps/Restore Image Selection/Components/SoftwareCatalog+Placeholder.swift",
    "content": "import SwiftUI\nimport VirtualCore\n\nextension String {\n    static let placeholderID = \"__PLACEHOLDER__\"\n}\n\nextension URL {\n    static let catalogPlaceholder = URL(string: \"https://example.com\")!\n    static let catalogGroupPlaceholderImage = Bundle.virtualUI.url(forResource: \"CatalogGroupPlaceholder\", withExtension: \"heic\") ?? URL(filePath: \"/dev/null\")\n}\n\nextension CatalogGraphic {\n    static let placeholder = CatalogGraphic(\n        id: .placeholderID,\n        url: .catalogGroupPlaceholderImage,\n        thumbnail: CatalogGraphic.Thumbnail(\n            url: .catalogGroupPlaceholderImage,\n            width: 340,\n            height: 720,\n            blurHash: \"U0Eo[I?bfQ?b?bj[fQj[fQfQfQfQ?bj[fQj[\"\n        )\n    )\n}\n\nextension CatalogGroup {\n    static let placeholder = CatalogGroup(\n        id: .placeholderID,\n        name: \"macOS Placeholder\",\n        majorVersion: \"15.0\",\n        image: .placeholder,\n        darkImage: .placeholder\n    )\n}\n\nextension ResolvedCatalogGroup {\n    static let placeholder = ResolvedCatalogGroup(\n        group: .placeholder,\n        restoreImages: []\n    )\n}\n\nextension RestoreImage {\n    static let placeholder = RestoreImage(\n        id: .placeholderID,\n        group: .placeholderID,\n        channel: .placeholderID,\n        requirements: .placeholderID,\n        name: \"macOS 15.3 Developer Beta\",\n        build: \"ABC123F\",\n        version: \"15.3\",\n        mobileDeviceMinVersion: \"1.0\",\n        url: .catalogPlaceholder,\n        downloadSize: 1024 * 1024 * 1024 * 8\n    )\n}\n\nextension CatalogChannel {\n    static let placeholder = CatalogChannel(id: .placeholderID, name: \"Placeholder\", note: \"Placeholder\", icon: \"checkmark.seal\")\n}\n\nextension RequirementSet {\n    static let placeholder = RequirementSet(id: .placeholderID, minCPUCount: 0, minMemorySizeMB: 0, minVersionHost: \"1.0\")\n}\n\nextension ResolvedRequirementSet {\n    static let placeholder = ResolvedRequirementSet(requirements: .placeholder, status: .supported)\n}\n\nextension SoftwareCatalog {\n    static let placeholder = SoftwareCatalog(apiVersion: 1, minAppVersion: \"1.0\", channels: [.placeholder], groups: [.placeholder], restoreImages: [.placeholder], features: [], requirementSets: [.placeholder], deviceSupportVersions: [])\n}\n\nextension ResolvedRestoreImage {\n    static let placeholder = ResolvedRestoreImage(image: .placeholder, channel: .placeholder, features: [], requirements: .placeholder, status: .supported, localFileURL: nil, deviceSupportVersion: nil)\n}\n"
  },
  {
    "path": "VirtualUI/Source/Installer/Steps/Restore Image Selection/Components/VirtualBuddyMonoIcon.swift",
    "content": "import SwiftUI\nimport BuddyKit\n\nstruct VirtualBuddyMonoIcon: View {\n    var size: Double = 90\n    var style: VirtualBuddyMonoStyle = .default\n\n    var resource: ImageResource {\n        switch style {\n        case .default: .virtualBuddyMono\n        case .success: .virtualBuddyMonoHappy\n        case .failure: .virtualBuddyMonoSad\n        }\n    }\n\n    var body: some View {\n        Image(resource)\n            .resizable()\n            .aspectRatio(contentMode: .fit)\n            .frame(width: size, height: size)\n    }\n}\n"
  },
  {
    "path": "VirtualUI/Source/Installer/Steps/Restore Image Selection/Components/VirtualBuddyMonoProgressView.swift",
    "content": "import SwiftUI\nimport VirtualCore\nimport BuddyKit\n\nenum VirtualBuddyMonoStyle: Hashable {\n    case `default`\n    case success\n    case failure\n}\n\nstruct VirtualBuddyMonoProgressView: View {\n    var progress: Double?\n    var status: Text\n    var style: VirtualBuddyMonoStyle = .default\n\n    var spacing: Double { 16 }\n\n    private var foregroundColor: Color {\n        switch style {\n        case .default: .white\n        case .success: .green\n        case .failure: .red\n        }\n    }\n\n    var body: some View {\n        VStack {\n            Spacer()\n\n            VirtualBuddyMonoIcon(style: style)\n\n            Spacer()\n\n            VStack(spacing: spacing) {\n                RamRodProgressView(progress: progress ?? 0)\n                    .opacity(style == .default && progress != nil ? 1 : 0)\n\n                status\n                    .font(.subheadline)\n            }\n            .padding(.bottom, spacing)\n        }\n        .monospacedDigit()\n        .frame(width: 240)\n        .multilineTextAlignment(.center)\n        .foregroundStyle(foregroundColor)\n        .tint(foregroundColor)\n    }\n}\n\nprivate struct RamRodProgressView: View {\n    var progress: Double\n\n    var body: some View {\n        ZStack {\n            Rectangle().fill(Color(white: 0.16))\n\n            ProgressBarShapeView(progress: progress)\n        }\n            .clipShape(shape)\n            .overlay(shape.stroke(Color(white: 0.25), lineWidth: 1))\n            .frame(height: 6)\n    }\n\n    private var shape: some InsettableShape {\n        Capsule(style: .continuous)\n    }\n}\n\n/**\n You see, animating a white rectangle growing in width is a very expensive operation that SwiftUI\n is completely unable to do by itself without consuming an unhealthy amount of CPU,\n so this uses Core Animation instead to offload that expensive computation to the WindowServer/GPU.\n */\nprivate struct ProgressBarShapeView: NSViewRepresentable {\n    typealias NSViewType = _Representable\n\n    var progress: Double = 0\n\n    func makeNSView(context: Context) -> _Representable {\n        _Representable(frame: .zero)\n    }\n\n    func updateNSView(_ nsView: _Representable, context: Context) {\n        nsView.progress = progress\n    }\n\n    final class _Representable: NSView {\n        private lazy var bar = CALayer()\n\n        @Invalidating(.layout)\n        var progress: Double = 0\n\n        override init(frame frameRect: NSRect) {\n            super.init(frame: frameRect)\n\n            platformLayer.addSublayer(bar)\n            bar.backgroundColor = .white\n            bar.anchorPoint = CGPoint(x: 0, y: 0.5)\n        }\n\n        required init?(coder: NSCoder) {\n            fatalError()\n        }\n\n        override func layout() {\n            super.layout()\n\n            CATransaction.begin()\n            CATransaction.setDisableActions(true)\n\n            bar.position = CGPoint(x: bounds.minX, y: bounds.midY)\n            bar.frame.size.height = bounds.height\n\n            CATransaction.commit()\n\n            CATransaction.begin()\n            CATransaction.setAnimationDuration(0.2)\n            CATransaction.setAnimationTimingFunction(.init(name: .linear))\n            bar.frame.size.width = bounds.width * progress\n            CATransaction.commit()\n        }\n    }\n}\n\n#if DEBUG\n#Preview {\n    VMInstallationWizard.preview(step: .download)\n}\n#endif // DEBUG\n"
  },
  {
    "path": "VirtualUI/Source/Installer/Steps/Restore Image Selection/Components/VirtualDisplayView.swift",
    "content": "import SwiftUI\nimport BuddyKit\n\n/// A view that simulates a display chrome, currently used during installation.\nstruct VirtualDisplayView<Content: View>: View {\n    @ViewBuilder var content: () -> Content\n\n    @EnvironmentObject private var viewModel: VMInstallationViewModel\n\n    static var cornerRadius: CGFloat { 12 }\n\n    var body: some View {\n        ZStack {\n            content()\n                .frame(maxWidth: .infinity, maxHeight: .infinity)\n        }\n        .aspectRatio(16/9, contentMode: .fit)\n        .background(Color(white: 0.03))\n        .overlay {\n            LinearGradient(colors: [.white.opacity(0.7), .white.opacity(0.1)], startPoint: .init(x: 0.2, y: 0), endPoint: .init(x: 0.3, y: 1.2))\n                .blendMode(.plusLighter)\n                .opacity(0.1)\n        }\n        .clipShape(shape)\n        .chromeBorder(shape: shape, highlightEnabled: false)\n    }\n\n    private var shape: some InsettableShape {\n        RoundedRectangle(cornerRadius: Self.cornerRadius, style: .continuous)\n    }\n}\n\n#if DEBUG\n#Preview {\n    VMInstallationWizard.preview(step: .download)\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/Installer/Steps/Restore Image Selection/RestoreImageSelectionController.swift",
    "content": "import SwiftUI\nimport VirtualCore\nimport Combine\nimport OSLog\n\nenum RestoreImageSelectionFocus: Hashable {\n    case groups\n    case images\n}\n\nextension VBAPIClient {\n    static let shared = VBAPIClient()\n}\n\n@MainActor\nfinal class RestoreImageSelectionController: ObservableObject {\n\n    /// If loading takes less than this amount of time, then the controller will never even set the `isLoading` property.\n    private static let minLoadingTimeInMilliseconds = 100\n\n    private let logger = Logger(subsystem: VirtualUIConstants.subsystemName, category: String(describing: RestoreImageSelectionController.self))\n\n    init() {\n        $selectedGroup.removeDuplicates().sink { [weak self] group in\n            guard let self else { return }\n            guard let group else { return }\n\n            /// Selected group has changed, update available channel groups, images, and selected image.\n            let updatedChannelGroups = ChannelGroup.groups(with: group.restoreImages)\n            channelGroups = updatedChannelGroups\n            images = updatedChannelGroups.flatMap(\\.images)\n            selectedRestoreImage = updatedChannelGroups.first?.images.first\n        }\n        .store(in: &cancellables)\n    }\n\n    private lazy var api = VBAPIClient.shared\n\n    private var cancellables = Set<AnyCancellable>()\n\n    @Published private(set) var catalog: ResolvedCatalog?\n    @Published private(set) var channelGroups: [ChannelGroup] = []\n    @Published private(set) var images: [ResolvedRestoreImage] = []\n    @Published var selectedGroup: ResolvedCatalogGroup?\n    @Published var selectedRestoreImage: ResolvedRestoreImage?\n    @Published var errorMessage: String?\n    @Published var focusedElement = RestoreImageSelectionFocus.groups\n\n    @Published private(set) var isLoading = false {\n        didSet {\n            if !isLoading {\n                deferredLoadingTask?.cancel()\n                deferredLoadingTask = nil\n            }\n        }\n    }\n\n    /// The controller will only set the`isLoading` property if loading takes a while.\n    private var deferredLoadingTask: Task<Void, Never>?\n    private func deferredStartLoading() {\n        deferredLoadingTask?.cancel()\n        deferredLoadingTask = Task { [weak self] in\n            guard let self else { return }\n\n            defer { deferredLoadingTask = nil }\n\n            do {\n                try await Task.sleep(for: .milliseconds(Self.minLoadingTimeInMilliseconds))\n\n                logger.debug(\"Reached loading time delay, setting isLoading.\")\n\n                isLoading = true\n            } catch { }\n        }\n    }\n\n    private var inputCatalog: SoftwareCatalog?\n    private var guestType = VBGuestType.mac\n\n    func loadRestoreImageOptions(for guest: VBGuestType, skipCache: Bool = false) {\n        logger.debug(\"Loading restore image options.\")\n\n        guestType = guest\n\n        deferredStartLoading()\n\n        Task {\n            let start = ContinuousClock.now\n\n            defer {\n                logger.debug(\"Loading restore images took \\(start.duration(to: .now).formatted(.units(allowed: [.milliseconds])), privacy: .public)\")\n\n                isLoading = false\n            }\n\n            do {\n                #if DEBUG\n                if UserDefaults.standard.bool(forKey: \"VBSimulateSlowCatalogFetch\") {\n                    logger.notice(\"⚠️ Delaying restore image options load due to VBSimulateSlowCatalogFetch debug flag!\")\n                    try await Task.sleep(for: .seconds(2))\n                }\n                #endif\n\n                let catalog = try await api.fetchRestoreImages(for: guest, skipCache: skipCache)\n                inputCatalog = catalog\n\n                await refreshResolvedCatalog(with: catalog)\n            } catch {\n                logger.error(\"Loading restore images failed - \\(error, privacy: .public)\")\n                \n                await MainActor.run {\n                    self.catalog = nil\n                    self.errorMessage = error.localizedDescription\n                }\n            }\n        }\n    }\n\n    private func refreshResolvedCatalog(with catalog: SoftwareCatalog) async {\n        logger.debug(#function)\n\n        let platform: CatalogGuestPlatform = guestType == .linux ? .linux : .mac\n        let resolved = ResolvedCatalog(environment: .current.guest(platform: platform), catalog: catalog)\n\n        await MainActor.run {\n            self.selectedGroup = resolved.groups.first(where: { $0.id == selectedGroup?.id }) ?? resolved.groups.first\n            self.selectedRestoreImage = selectedGroup?.restoreImages.first(where: { $0.id == self.selectedRestoreImage?.id })\n            self.catalog = resolved\n        }\n    }\n\n    func deleteLocalDownload(for image: ResolvedRestoreImage) {\n        logger.debug(\"Delete download requested for \\(image.id)\")\n\n        /// Remove selection to force refresh of image browser.\n        selectedRestoreImage = nil\n\n        do {\n            let fileURL = try image.localFileURL.require(\"File not found.\")\n            Task {\n                do {\n                    try await NSWorkspace.shared.recycle([fileURL])\n\n                    if let inputCatalog {\n                        await refreshResolvedCatalog(with: inputCatalog)\n                    }\n                } catch {\n                    logger.error(\"Recycle failed for \\(fileURL.path) - \\(error, privacy: .public)\")\n\n                    NSApp.presentError(error)\n                }\n            }\n        } catch {\n            logger.error(\"Delete download failed for \\(image.id) - \\(error, privacy: .public)\")\n\n            NSApp.presentError(error)\n        }\n    }\n}\n\nextension ChannelGroup {\n    static func groups(with restoreImages: [ResolvedRestoreImage]) -> [ChannelGroup] {\n        var groupsByChannel = [CatalogChannel: ChannelGroup]()\n\n        for image in restoreImages {\n            /// Ensures images from each channel group are listed in the same order as the channels are ordered in the catalog.\n            let order = groupsByChannel.keys.count\n\n            groupsByChannel[image.channel, default: ChannelGroup(\n                order: order,\n                channel: image.channel,\n                images: []\n            )].images.append(image)\n        }\n\n        return groupsByChannel.values.sorted(by: { $0.order < $1.order })\n    }\n}\n"
  },
  {
    "path": "VirtualUI/Source/Installer/Steps/Restore Image Selection/RestoreImageSelectionStep.swift",
    "content": "//\n//  RestoreImageSelectionStep.swift\n//  VirtualBuddy\n//\n//  Created by Guilherme Rambo on 20/07/22.\n//\n\nimport SwiftUI\nimport VirtualCore\nimport Combine\n\nstruct RestoreImageSelectionStep: View {\n    @StateObject private var controller = RestoreImageSelectionController()\n    @EnvironmentObject private var viewModel: VMInstallationViewModel\n\n    @Environment(\\.containerPadding)\n    private var containerPadding\n\n    @Environment(\\.maxContentWidth)\n    private var maxContentWidth\n\n    private var browserInsetTop: CGFloat { 100 }\n\n    var body: some View {\n        HStack(spacing: 0) {\n            CatalogGroupPicker(groups: controller.catalog?.groups, selectedGroup: $controller.selectedGroup)\n\n            RestoreImageBrowser(selection: $viewModel.data.resolvedRestoreImage)\n        }\n        .redacted(reason: controller.isLoading ? .placeholder : [])\n        .frame(maxWidth: maxContentWidth)\n        .frame(maxWidth: .infinity)\n        .frame(maxWidth: .infinity, maxHeight: .infinity)\n        .environmentObject(controller)\n        .task(id: viewModel.data.systemType) { loadOptions() }\n        .task(id: controller.selectedGroup) {\n            if let group = controller.selectedGroup {\n                viewModel.data.backgroundHash = BlurHashToken(value: group.darkImage.thumbnail.blurHash)\n            } else {\n                viewModel.data.backgroundHash = .virtualBuddyBackground\n            }\n        }\n        .toolbar {\n            ToolbarItemGroup(placement: .primaryAction) {\n                Button {\n                    loadOptions(skipCache: true)\n                } label: {\n                    Image(systemName: \"arrow.clockwise\")\n                }\n                .help(Text(\"Reload\"))\n                .keyboardShortcut(\"r\", modifiers: .command)\n            }\n        }\n    }\n\n    private func loadOptions(skipCache: Bool = false) {\n        controller.loadRestoreImageOptions(for: viewModel.data.systemType, skipCache: skipCache)\n    }\n\n}\n\n#if DEBUG\n#Preview {\n    VMInstallationWizard.preview\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/Installer/Steps/RestoreImageDownloadView.swift",
    "content": "//\n//  RestoreImageDownloadView.swift\n//  VirtualUI\n//\n//  Created by Guilherme Rambo on 20/07/22.\n//\n\nimport SwiftUI\nimport VirtualCore\nimport Combine\n\nstruct RestoreImageDownloadView: View {\n    @EnvironmentObject var viewModel: VMInstallationViewModel\n    @State private var downloadState = DownloadState.idle\n\n    private var progress: Double? {\n        switch downloadState {\n        case .idle, .preCheck: 0\n        case .failed: nil\n        case .downloading(let progress, _): progress ?? 0\n        case .done: 1\n        }\n    }\n\n    private var status: Text {\n        switch downloadState {\n        case .idle: Text(\"Preparing Download\")\n        case .preCheck(let message): Text(message)\n        case .downloading(_, let eta): eta.flatMap { Text(formattedETA(from: $0)) } ?? Text(\"Downloading\")\n        case .done: Text(\"Done!\")\n        case .failed(let message): Text(message)\n        }\n    }\n\n    private var style: VirtualBuddyMonoStyle {\n        switch downloadState {\n        case .idle, .downloading, .preCheck: .default\n        case .failed: .failure\n        case .done: .success\n        }\n    }\n\n    var body: some View {\n        VirtualBuddyMonoProgressView(\n            progress: progress,\n            status: status,\n            style: style\n        )\n        .onReceive(viewModel.$downloadState) { downloadState = $0 }\n    }\n\n    private func formattedETA(from eta: Double) -> String {\n        let time = Int(eta)\n\n        let seconds = time % 60\n        let minutes = (time / 60) % 60\n        let hours = (time / 3600)\n\n        if hours >= 1 {\n            return String(format: \"%0.2d:%0.2d:%0.2d\",hours,minutes,seconds)\n        } else {\n            return String(format: \"%0.2d:%0.2d\",minutes,seconds)\n        }\n    }\n}\n\n#if DEBUG\n#Preview {\n    VMInstallationWizard.preview(step: .download)\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/Installer/VMInstallData.swift",
    "content": "import Foundation\nimport VirtualCore\nimport BuddyKit\n\nstruct VMInstallData: Hashable, Codable {\n    // MARK: Persisted State\n\n    @DecodableDefault.EmptyPlaceholder\n    var systemType: VBGuestType = .empty\n\n    var installMethod: InstallMethod { installMethodSelection?.id ?? .empty }\n\n    var installMethodSelection: InstallMethodSelection? = nil\n\n    var backgroundHash: BlurHashToken = .virtualBuddyBackground\n\n    var name = RandomNameGenerator.shared.newName()\n\n    /// URL to the local restore image that will be used to restore the VM.\n    /// This will be the custom local file selected by the user, or the URL to the local file\n    /// that's been downloaded from either a custom remote URL or a selected restore image option.\n    private(set) var localRestoreImageURL: URL? = nil\n\n    enum CodingKeys: String, CodingKey {\n        /// Cookie is not stored because it would end up in clear text on the file system when the struct is encoded...\n        case systemType, installMethodSelection, backgroundHash, name, localRestoreImageURL\n    }\n\n    // MARK: Temporary State\n\n    /// This can be the restore image selected by the user in the UI, or a matching restore image\n    /// from the software catalog inferred from a user-provided custom download link or existing local file.\n    @MainActor\n    private(set) var restoreImage: RestoreImage? = nil {\n        didSet {\n            /// Ensure background hash is set to whatever restore image group ends up being used,\n            /// even if it's matched from user-provided file/url.\n            if let group = catalog.groups.first(where: { $0.id == restoreImage?.group }),\n               let value = group.darkImage?.thumbnail.blurHash\n            {\n                backgroundHash = BlurHashToken(value: value)\n            } else {\n                backgroundHash = systemType == .mac ? .virtualBuddyBackground : .virtualBuddyBackgroundLinux\n            }\n        }\n    }\n    var resolvedRestoreImage: ResolvedRestoreImage? = nil {\n        didSet {\n            restoreImage = resolvedRestoreImage?.image\n            localRestoreImageURL = resolvedRestoreImage?.localFileURL\n        }\n    }\n\n    @DecodableDefault.EmptyString\n    var customInstallImageRemoteURL: String = \"\"\n\n    var cookie: String? = nil\n}\n\n// MARK: Convenience\n\nextension VMInstallData {\n    var downloadURL: URL? {\n        switch installMethodSelection {\n        case .remoteManual(let url): url\n        case .remoteOptions(let image): image.url\n        case .localFile: nil\n        case .none: nil\n        }\n    }\n\n    @MainActor\n    var catalog: SoftwareCatalog { SoftwareCatalog.current(for: systemType) }\n}\n\n// MARK: Updates / Validation\n\nextension VMInstallData {\n    func canContinue(from step: VMInstallationStep) -> Bool {\n        switch step {\n        case .systemType: true\n        case .restoreImageInput: installMethodSelection != nil\n        case .restoreImageSelection: restoreImage != nil\n        case .name: !name.isEmpty\n        case .configuration:\n            true // TODO: Implement\n        case .download:\n            true // TODO: Implement\n        case .install:\n            true // TODO: Implement\n        case .done:\n            true // TODO: Implement\n        }\n    }\n\n    private static let allowedCustomDownloadSchemes: Set<String> = [\n        \"http\",\n        \"https\",\n        \"ftp\"\n    ]\n\n    func validateCustomRestoreImageRemoteURL() -> Bool {\n        guard !customInstallImageRemoteURL.isEmpty else {\n            return false\n        }\n        guard let url = URL(string: customInstallImageRemoteURL) else {\n            return false\n        }\n\n        guard let scheme = url.scheme else {\n            return false\n        }\n\n        guard Self.allowedCustomDownloadSchemes.contains(scheme.lowercased()) else {\n            return false\n        }\n\n        return true\n    }\n\n    mutating func commitSelectedRestoreImage() throws {\n        UILog(\"\\(#function) \\(String(optional: restoreImage?.url.absoluteString.quoted))\")\n\n        installMethodSelection = try .remoteOptions(restoreImage.require(\"Please select one of the OS versions available.\"))\n    }\n\n    @MainActor\n    mutating func commitCustomRestoreImageURL() throws {\n        UILog(\"\\(#function) \\(customInstallImageRemoteURL.quoted)\")\n\n        let customURL = try URL(string: customInstallImageRemoteURL).require(\"Invalid URL: \\(customInstallImageRemoteURL.quoted).\")\n        installMethodSelection = .remoteManual(customURL)\n\n        /// Attempt to resolve a catalog image for custom URL.\n        resolveCatalogImage(for: customURL)\n    }\n\n    @MainActor\n    mutating func commitCustomRestoreImageLocalFile(path: String) {\n        UILog(\"\\(#function) \\(path.quoted)\")\n\n        let fileURL = URL(fileURLWithPath: path)\n        installMethodSelection = .localFile(fileURL)\n        commitLocalRestoreImageURL(fileURL)\n\n        /// Attempt to resolve a catalog image for custom local file.\n        resolveCatalogImage(for: fileURL, localFileURL: fileURL)\n    }\n\n    @MainActor\n    mutating func resolveCatalogImageIfNeeded(with model: VBVirtualMachine) throws {\n        guard resolvedRestoreImage == nil else { return }\n\n        switch installMethodSelection {\n        case .remoteOptions(let restoreImage):\n            resolvedRestoreImage = try model.resolveCatalogImage(restoreImage)\n        case .remoteManual(let url):\n            resolveCatalogImage(for: url)\n        case .localFile(let url):\n            resolveCatalogImage(for: url, localFileURL: url)\n        case .none:\n            break\n        }\n    }\n\n    mutating func commitLocalRestoreImageURL(_ url: URL) {\n        localRestoreImageURL = url\n    }\n\n    /// Removes any data associated with the current install method selection if the new selection is a different install method.\n    mutating func resetInstallMethodSelectionIfNeeded(selectedMethod: InstallMethod) {\n        guard let installMethodSelection else { return }\n        guard selectedMethod != installMethodSelection.id else { return }\n        self.installMethodSelection = nil\n        self.resolvedRestoreImage = nil\n    }\n\n    var needsDownload: Bool {\n        guard let downloadURL else { return false }\n\n        switch installMethodSelection {\n        case .none:\n            UILog(\"[\\(#function)] ⚠️ Method is nil!\")\n            return false\n        case .localFile:\n            UILog(\"[\\(#function)] Method is \\(installMethod), download never needed.\")\n            return false\n        case .remoteManual, .remoteOptions:\n            /// `localRestoreImageURL` is set when user selects a remote URL but that file has already been downloaded to the local library.\n            /// Check that the file name matches and skip download when that's the case.\n            /// Also ensure the local file actually exists, as it could have been deleted before the setup process read this property.\n            if let localRestoreImageURL, localRestoreImageURL.lastPathComponent == downloadURL.lastPathComponent, FileManager.default.fileExists(atPath: localRestoreImageURL.path) {\n                UILog(\"[\\(#function)] Method is \\(installMethod), remote URL is \\(downloadURL.absoluteString.quoted), found matching download at \\(localRestoreImageURL.path.quoted).\")\n\n                return false\n            } else {\n                UILog(\"[\\(#function)] Method is \\(installMethod), remote URL is \\(downloadURL.absoluteString.quoted), download is needed.\")\n\n                return true\n            }\n        }\n    }\n\n    @MainActor\n    mutating func resetRestoreImageSelection() {\n        installMethodSelection = nil\n        resolvedRestoreImage = nil\n        localRestoreImageURL = nil\n        restoreImage = nil\n    }\n}\n\n// MARK: - Catalog Resolution\n\nprivate extension VMInstallData {\n    @MainActor\n    mutating func resolveCatalogImage(for url: URL, localFileURL: URL? = nil) {\n        guard var resolved = catalog.resolvedRestoreImage(matching: url, guestType: systemType) else {\n            restoreImage = catalog.restoreImageMatchingDownloadableCatalogContent(at: url)\n            return\n        }\n\n        if let localFileURL {\n            resolved.localFileURL = localFileURL\n        }\n\n        resolvedRestoreImage = resolved\n    }\n}\n\nextension VBVirtualMachine.Metadata {\n    mutating func updateRestoreImageURLs(with data: VMInstallData) {\n        /// Always save whatever URL the restore image was downloaded from and the local file URL, regardless of the install method.\n        if let downloadURL = data.downloadURL {\n            updateInstallImageURL(downloadURL)\n        }\n        if let localRestoreImageURL = data.localRestoreImageURL {\n            updateInstallImageURL(localRestoreImageURL)\n        }\n    }\n}\n"
  },
  {
    "path": "VirtualUI/Source/Installer/VMInstallationViewModel.swift",
    "content": "//\n//  VMInstallationViewModel.swift\n//  VirtualBuddy\n//\n//  Created by Guilherme Rambo on 07/06/22.\n//\n\nimport Foundation\nimport UniformTypeIdentifiers\nimport Combine\nimport Virtualization\nimport VirtualCore\nimport BuddyKit\n\npublic enum VMInstallationStep: Int, Hashable, Codable {\n    case systemType\n    case restoreImageInput\n    case restoreImageSelection\n    case name\n    case configuration\n    case download\n    case install\n    case done\n}\n\n@MainActor\nfinal class VMInstallationViewModel: ObservableObject, @unchecked Sendable {\n\n    struct RestorableState: Codable {\n        var data: VMInstallData\n        var step: Step\n    }\n\n    typealias Step = VMInstallationStep\n\n    enum State: Hashable {\n        case idle\n        case loading(_ progress: Double?, _ info: String?)\n        case error(_ message: String)\n    }\n\n    private var restorableState: RestorableState {\n        RestorableState(\n            data: self.data,\n            step: self.step\n        )\n    }\n\n    @Published var machine: VBVirtualMachine?\n\n    @Published var data = VMInstallData() {\n        didSet {\n            guard data != oldValue else { return }\n            validate()\n        }\n    }\n\n    @Published private(set) var state = State.idle\n\n    @Published var step = Step.systemType {\n        didSet {\n            guard step != oldValue else { return }\n\n            performActions(for: step)\n\n            writeRestorationData()\n        }\n    }\n\n    var canGoBack: Bool {\n        switch step {\n        case .systemType:\n            false\n        case .restoreImageInput, .restoreImageSelection:\n            /// Only allow going back from restore image selection if a VM bundle has not been created yet.\n            /// It's possible for this condition to be reached when user goes back after the name step,\n            /// and we don't want the user to change the guest type after that.\n            machine == nil\n        case .name, .configuration, .done:\n            true\n        case .download:\n            if case .failed = downloadState {\n                true\n            } else {\n                false\n            }\n        case .install:\n            if case .error = state {\n                true\n            } else {\n                false\n            }\n        }\n    }\n\n    @Published private(set) var buttonTitle = \"Continue\"\n    @Published private(set) var showNextButton = true\n    @Published  var disableNextButton = false\n\n    private let library: VMLibraryController\n\n    init(library: VMLibraryController, restoring restoreVM: VBVirtualMachine?) {\n        self.library = library\n        /// Skip OS selection if there's only a single supported OS.\n        step = VBGuestType.supportedByHost.count > 1 ? .systemType : .restoreImageSelection\n\n        if let restoreVM {\n            restoreInstallation(with: restoreVM)\n        }\n    }\n\n    init(library: VMLibraryController, restoringAt restoreURL: URL?, initialStep: Step? = nil) {\n        self.library = library\n        /// Skip OS selection if there's only a single supported OS.\n        step = initialStep ?? (VBGuestType.supportedByHost.count > 1 ? .systemType : .restoreImageSelection)\n\n        if let restoreURL {\n            restoreInstallation(with: restoreURL)\n        }\n    }\n\n    private func restoreInstallation(with url: URL) {\n        do {\n            let vm = try VBVirtualMachine(bundleURL: url)\n\n            restoreInstallation(with: vm)\n        } catch {\n            assertionFailure(\"Couldn't restore install: \\(error)\")\n            NSAlert(error: error).runModal()\n        }\n    }\n\n    private func restoreInstallation(with model: VBVirtualMachine) {\n        do {\n            guard let restoreData = model.installRestoreData else {\n                throw CocoaError(.coderInvalidValue, userInfo: [NSLocalizedDescriptionKey: \"VM is missing install restore data\"])\n            }\n\n            var restoredState = try PropertyListDecoder.virtualBuddy.decode(RestorableState.self, from: restoreData)\n            try restoredState.data.resolveCatalogImageIfNeeded(with: model)\n\n            self.data = restoredState.data\n            self.machine = model\n            self.step = restoredState.step\n        } catch {\n            assertionFailure(\"Couldn't restore install: \\(error)\")\n            NSAlert(error: error).runModal()\n        }\n    }\n\n    private var preventTerminationAssertion: PreventTerminationAssertion?\n\n    private func startPreventingAppTermination(forReason reason: String) {\n        stopPreventingAppTermination()\n\n        preventTerminationAssertion = NSApp.preventTermination(reason: reason)\n    }\n\n    private func stopPreventingAppTermination() {\n        preventTerminationAssertion?.invalidate()\n        preventTerminationAssertion = nil\n    }\n\n    private func writeRestorationData() {\n        guard var machine else { return }\n\n        machine.metadata.updateRestoreImageURLs(with: data)\n\n        do {\n            let restoreData = try PropertyListEncoder.virtualBuddy.encode(restorableState)\n            machine.installRestoreData = restoreData\n            try machine.saveMetadata()\n            self.machine = machine\n        } catch {\n            assertionFailure(\"Failed to save install restore data: \\(error)\")\n        }\n    }\n\n    @Published private(set) var downloader: DownloadBackend?\n    @SubjectPublisher private(set) var downloadState: DownloadState = .idle\n\n    private func validate() {\n        disableNextButton = !data.canContinue(from: step)\n    }\n\n    func next() {\n        /// It's possible for the user to go back in case of a failed download/install,\n        /// in which case the naming step will be skipped because the VM has already been named.\n        func goNextAfterRestoreImageSelection() {\n            if machine == nil {\n                step = .name\n            } else {\n                step = .configuration\n            }\n        }\n\n        switch step {\n        case .systemType:\n            step = .restoreImageSelection\n        case .restoreImageInput:\n            commitCustomRestoreImageURL()\n\n            goNextAfterRestoreImageSelection()\n        case .restoreImageSelection:\n            commitSelectedRestoreImage()\n\n            goNextAfterRestoreImageSelection()\n        case .name:\n            step = .configuration\n        case .configuration:\n            step = data.needsDownload ? .download : .install\n        case .download:\n            step = .install\n        case .install:\n            step = .done\n        case .done:\n            break\n        }\n    }\n\n    func back() {\n        guard canGoBack else { return }\n\n        switch step {\n        case .systemType, .done:\n            break\n        case .restoreImageInput:\n            selectInstallMethod(.remoteOptions)\n        case .restoreImageSelection:\n            data.backgroundHash = .virtualBuddyBackground\n            step = .systemType\n        case .download, .install:\n            /// Always nuke restore image selection when backing out of download/install.\n            /// This ensures that the new selection will be respected when moving forward again.\n            data.resetRestoreImageSelection()\n            \n            state = .idle\n            step = .restoreImageSelection\n        case .name, .configuration:\n            /// **NOTE:** When going back from configuration step, all configuration is lost due to how configuration saving is currently tied\n            /// to the specific \"continue\" button in the configuration sheet view. This will be addressed alongside other configuration UI changes soon...\n\n            /// Re-trigger any UI prompts associated with the install method, such as entering remote URL or selecting local file.\n            selectInstallMethod(data.installMethod)\n        }\n    }\n\n    private func performActions(for step: Step) {\n        defer { validate() }\n\n        switch step {\n            case .systemType:\n                showNextButton = true\n            case .restoreImageInput:\n                showNextButton = true\n                validateCustomRemoteURL()\n            case .restoreImageSelection:\n                showNextButton = true\n            case .name:\n                showNextButton = true\n            case .configuration:\n                showNextButton = true\n\n                do {\n                    try prepareModel()\n                } catch {\n                    state = .error(\"Failed to prepare VM model: \\(error.localizedDescription)\")\n                }\n            case .download:\n                showNextButton = false\n                DispatchQueue.main.async { self.setupDownload() }\n            case .install:\n                Task { await startInstallation() }\n\n                showNextButton = false\n            case .done:\n                showNextButton = true\n                buttonTitle = \"Back to Library\"\n        }\n    }\n\n    func selectInstallMethod(_ method: InstallMethod) {\n        UILog(\"\\(#function) \\(method)\")\n\n        data.resetInstallMethodSelectionIfNeeded(selectedMethod: method)\n        \n        switch method {\n        case .localFile:\n            selectInstallFile()\n        case .remoteOptions:\n            step = .restoreImageSelection\n        case .remoteManual:\n            step = .restoreImageInput\n        }\n    }\n\n    private func commitCustomRestoreImageURL() {\n        do {\n            try data.commitCustomRestoreImageURL()\n        } catch {\n            state = .error(\"\\(error)\")\n        }\n    }\n\n    private func commitSelectedRestoreImage() {\n        guard data.installMethod != .localFile, data.installMethod != .remoteManual else { return }\n        \n        do {\n            try data.commitSelectedRestoreImage()\n        } catch {\n            state = .error(\"\\(error)\")\n        }\n    }\n\n    private func createDownloadBackend(cookie: String?) -> DownloadBackend {\n        let Backend: DownloadBackend.Type\n        #if DEBUG\n        if UserDefaults.standard.bool(forKey: \"VBSimulateDownload\") || ProcessInfo.isSwiftUIPreview {\n            Backend = SimulatedDownloadBackend.self\n        } else {\n            Backend = URLSessionDownloadBackend.self\n        }\n        #else\n        Backend = URLSessionDownloadBackend.self\n        #endif\n\n        return Backend.init(library: library, cookie: cookie)\n    }\n\n    private var downloadURL: URL? {\n        #if DEBUG\n        guard let url = data.downloadURL else {\n            if ProcessInfo.isSwiftUIPreview {\n                return .catalogPlaceholder\n            } else {\n                assertionFailure(\"Expected download URL to be available for download\")\n                return nil\n            }\n        }\n\n        return url\n        #else\n        return data.downloadURL\n        #endif\n    }\n\n    private func setupDownload() {\n        guard let url = downloadURL else {\n            return\n        }\n\n        /// Run TSS check before download to prevent user from spending the time/bandwidth to download a build that will not install successfully.\n        if data.systemType == .mac, VBSettings.current.enableTSSCheck {\n            UILog(\"Requesting TSS check before download.\")\n\n            Task {\n                downloadState = .preCheck(\"Checking Signing Status\")\n\n                let status = await VBAPIClient.shared.signingStatus(for: url)\n\n                switch status {\n                case .signed:\n                    UILog(\"TSS check signed, proceeding with download.\")\n\n                    startDownload(with: url)\n                case .unsigned(let message):\n                    UILog(\"TSS check UNSIGNED, failing now.\")\n\n                    downloadState = .failed(message)\n                    stopPreventingAppTermination()\n                case .checkFailed:\n                    /// Performing the check itself failed is ignored to avoid server-side issues preventing users from downloading/installing macOS.\n                    UILog(\"Ignoring check failed TSS status.\")\n\n                    startDownload(with: url)\n                }\n            }\n        } else {\n            startDownload(with: url)\n        }\n    }\n\n    private func startDownload(with url: URL) {\n        let backend = createDownloadBackend(cookie: data.cookie)\n\n        backend.statePublisher.sink { [weak self] state in\n            self?.downloadState = state\n        }\n        .store(in: &cancellables)\n\n        self.downloader = backend\n\n        backend.statePublisher\n            .receive(on: DispatchQueue.main)\n            .sink\n        { [weak self] state in\n            guard let self = self else { return }\n\n            switch state {\n            case .done(let localURL):\n                stopPreventingAppTermination()\n\n                self.handleDownloadCompleted(with: localURL)\n            case .failed:\n                downloader = nil\n                stopPreventingAppTermination()\n            case .idle, .preCheck, .downloading:\n                break\n            }\n        }.store(in: &cancellables)\n\n        startPreventingAppTermination(forReason: \"downloading operating system image\")\n\n        backend.startDownload(with: url)\n    }\n\n    func handleDownloadCompleted(with fileURL: URL) {\n        downloader = nil\n\n        do {\n            /// Attach metadata to the file so that VirtualBuddy can match it with the restore image in the software catalog\n            /// even if the user renames the file or moves it within the same volume.\n            if data.systemType == .mac, let restoreImage = data.restoreImage {\n                UILog(\"Attaching metadata for \\(restoreImage.name.quoted) to downloaded file \\(fileURL.lastPathComponent.quoted)\")\n                fileURL.vb_softwareCatalogData = .init(restoreImage)\n            }\n\n            try updateModelInstallerURL(with: fileURL)\n\n            next()\n        } catch {\n            state = .error(\"Failed to update the virtual machine settings after downloading the installer. \\(error)\")\n        }\n    }\n\n    private lazy var cancellables = Set<AnyCancellable>()\n\n    private var installer: RestoreBackend?\n    private var progressObservation: NSKeyValueObservation?\n\n    private func prepareModel() throws {\n        /// It's possible for the model to already be available in case installation failed and the user navigated back to restore image selection.\n        guard machine == nil else {\n            return\n        }\n        \n        let vmURL = library.libraryURL\n            .appendingPathComponent(data.name)\n            .appendingPathExtension(VBVirtualMachine.bundleExtension)\n\n        var model: VBVirtualMachine\n\n        switch data.systemType {\n        case .mac:\n            model = try VBVirtualMachine(bundleURL: vmURL, isNewInstall: true)\n        case .linux:\n            model = try VBVirtualMachine(creatingLinuxMachineAt: vmURL)\n        }\n        model.metadata.backgroundHash = data.backgroundHash\n\n        self.machine = model\n\n        writeRestorationData()\n    }\n\n    private func updateModelInstallerURL(with newURL: URL) throws {\n        assert(machine != nil || ProcessInfo.isSwiftUIPreview, \"This method requires the VM model to be available\")\n        assert(newURL.isFileURL || ProcessInfo.isSwiftUIPreview, \"This method should be updating the installer URL with a local file URL, not a remote one!\")\n        guard var machine else { return }\n\n        data.commitLocalRestoreImageURL(newURL)\n\n        machine.metadata.updateRestoreImageURLs(with: data)\n\n        try machine.saveMetadata()\n    }\n\n    private func startInstallation() async {\n        switch machine?.configuration.systemType {\n        case .mac:\n            startMacInstallation()\n        case .linux:\n            await startLinuxInstallation()\n        case .none:\n            state = .error(\"Missing VM model or system type\")\n        }\n    }\n\n    @available(macOS 13, *)\n    private func startLinuxInstallation() async {\n        guard let installURL = data.localRestoreImageURL else {\n            state = .error(\"Missing install image URL\")\n            return\n        }\n\n        guard let model = machine else {\n            state = .error(\"Missing VM model\")\n            return\n        }\n\n        do {\n            let config = try await VMInstance.makeConfiguration(for: model, installImageURL: installURL)\n\n            try config.validate()\n\n            step = .done\n        } catch let error as VZError {\n            handleVirtualMachineValidationError(error)\n        } catch {\n            state = .error(error.localizedDescription)\n        }\n    }\n\n    private func handleVirtualMachineValidationError(_ error: VZError) {\n        UILog(\"Validation failed with error \\(error)\")\n\n        let baseMessage = \"\"\"\n        Virtualization error \\(error.code.rawValue). \\(error.localizedDescription)\n        \"\"\"\n\n        switch error.code {\n        case .invalidDiskImage, .invalidRestoreImage, .restoreImageLoadFailed:\n            let imageType = switch data.systemType {\n            case .mac: \"restore image\"\n            case .linux: \"iso image\"\n            }\n            let detail = \"\"\"\n            The \\(imageType) on your computer may be corrupted from a faulty download.\n            Please delete the existing download and try again, or use a different \\(imageType).\n            \"\"\"\n\n            state = .error(baseMessage + \"\\n\" + detail)\n        default:\n            state = .error(baseMessage)\n        }\n    }\n\n    private func createRestoreBackend(for model: VBVirtualMachine, restoreURL: URL) -> RestoreBackend {\n        let Backend: RestoreBackend.Type\n        #if DEBUG\n        if UserDefaults.standard.bool(forKey: \"VBSimulateInstall\") || ProcessInfo.isSwiftUIPreview {\n            Backend = SimulatedRestoreBackend.self\n        } else if restoreURL == SimulatedDownloadBackend.localFileURL {\n            UILog(\"⚠️ Using simulated installer because the download was also simulated.\")\n            Backend = SimulatedRestoreBackend.self\n        } else {\n            Backend = VirtualizationRestoreBackend.self\n        }\n        #else\n        Backend = VirtualizationRestoreBackend.self\n        #endif\n\n        return Backend.init(model: model, restoringFromImageAt: restoreURL)\n    }\n\n    @Published private(set) var virtualMachine: VZVirtualMachine? = nil\n\n    private var installationTask: Task<Void, Never>?\n\n    private func startMacInstallation() {\n        installationTask = Task { await _runMacInstallation() }\n    }\n\n    private func _runMacInstallation() async {\n        guard let restoreURL = data.localRestoreImageURL else {\n            state = .error(\"Missing local restore image URL\")\n            return\n        }\n\n        guard let model = machine else {\n            state = .error(\"Missing VM model\")\n            return\n        }\n\n        state = .loading(nil, \"Preparing Installation\\nThis may take a moment\")\n\n        let backend = createRestoreBackend(for: model, restoreURL: restoreURL)\n        installer = backend\n\n        if let realBackend = backend as? VirtualizationRestoreBackend {\n            realBackend.virtualMachine.assign(to: &$virtualMachine)\n        }\n\n        progressObservation = backend.progress.observe(\\.completedUnitCount) { [weak self] progress, _ in\n            guard let self = self else { return }\n\n            DispatchQueue.main.async {\n                let percent = Double(progress.completedUnitCount) / Double(progress.totalUnitCount)\n                self.state = .loading(percent, nil)\n            }\n        }\n\n        @Sendable func cleanup() {\n            DispatchQueue.main.async { [weak self] in\n                guard let self else { return }\n                self.stopPreventingAppTermination()\n                self.library.loadMachines()\n                self.installationTask = nil\n                self.cleanupInstallerArtifacts()\n            }\n        }\n\n        defer { cleanup() }\n\n        await withTaskCancellationHandler {\n            do {\n                startPreventingAppTermination(forReason: \"restoring virtual machine\")\n\n                try await backend.install()\n\n                try Task.checkCancellation()\n\n                self.machine?.metadata.backgroundHash = self.data.backgroundHash\n                self.machine?.metadata.installFinished = true\n\n                self.step = .done\n\n                UILog(\"Installation task finished successfully\")\n            } catch is CancellationError {\n            } catch let error as VZError {\n                handleVirtualMachineValidationError(error)\n            } catch {\n                UILog(\"Installation task finished with error \\(error)\")\n\n                self.state = .error(error.localizedDescription)\n            }\n        } onCancel: {\n            UILog(\"Installation task cancelled\")\n            cleanup()\n        }\n    }\n\n    func validateCustomRemoteURL() {\n        let isValid = data.validateCustomRestoreImageRemoteURL()\n        disableNextButton = !isValid\n    }\n\n    func selectInstallFile() {\n        guard let url = NSOpenPanel.run(accepting: data.systemType.supportedRestoreImageTypes, defaultDirectoryKey: \"restoreImage\") else {\n            return\n        }\n\n        data.commitCustomRestoreImageLocalFile(path: url.path)\n\n        next()\n    }\n\n    private func cleanupInstallerArtifacts() {\n        UILog(#function)\n        \n        progressObservation?.invalidate()\n        progressObservation = nil\n\n        virtualMachine = nil\n        installer = nil\n    }\n\n    var confirmBeforeClosing: () async -> Bool {\n        { [weak self] in\n            guard let self else { return true }\n\n            guard self.needsConfirmationBeforeClosing else { return true }\n\n            let confirmed = await NSAlert.runConfirmationAlert(\n                title: \"Cancel Installation?\",\n                message: \"If you close the window now, the virtual machine will not be ready for use. You can continue the installation later.\",\n                continueButtonTitle: \"Cancel Installation\",\n                cancelButtonTitle: \"Continue\"\n            )\n\n            guard confirmed else { return false }\n\n            await cancelInstallation()\n\n            return true\n        }\n    }\n\n    func cancelInstallation() async {\n        UILog(#function)\n\n        downloader?.cancelDownload()\n        downloader = nil\n        await installer?.cancel()\n        installationTask?.cancel()\n    }\n\n    private var needsConfirmationBeforeClosing: Bool {\n        /// Require confirmation as soon as a VM bundle has been created for this install and it's not finished.\n        step != .done && machine != nil\n    }\n\n}\n"
  },
  {
    "path": "VirtualUI/Source/Installer/VMInstallationWizard.swift",
    "content": "//\n//  VMInstallationWizard.swift\n//  VirtualBuddy\n//\n//  Created by Guilherme Rambo on 07/06/22.\n//\n\nimport SwiftUI\nimport VirtualCore\nimport Combine\n\nextension EnvironmentValues {\n    /// Defines the padding for a container where the children must adopt the padding in their implementations.\n    /// Currently used for the `VMInstallationWizard` to allow children to apply padding in a custom way,\n    /// retaining the standard padding between all steps.\n    @Entry var containerPadding: CGFloat = 16\n\n    /// The maximum width for the content area in the current context.\n    @Entry var maxContentWidth: CGFloat? = nil\n}\n\npublic struct VMInstallationWizard: View {\n    static var padding: CGFloat { 22 }\n    static var maxContentWidth: CGFloat { 720 }\n\n    @ObservedObject var library: VMLibraryController\n    @StateObject var viewModel: VMInstallationViewModel\n\n    @Environment(\\.dismiss) var closeWindow\n\n    public init(library: VMLibraryController, restoringAt restoreURL: URL? = nil, initialStep: VMInstallationStep? = nil) {\n        self._library = .init(initialValue: library)\n        self._viewModel = .init(wrappedValue: VMInstallationViewModel(library: library, restoringAt: restoreURL, initialStep: initialStep))\n    }\n\n    private let stepValidationStateChanged = PassthroughSubject<Bool, Never>()\n\n    /// Some step views can't have the default padding applied because they need\n    /// to handle padding in a specific way. Those may read `containerPadding` from the environment.\n    private var effectivePadding: CGFloat {\n        switch viewModel.step {\n        case .restoreImageSelection, .configuration: 0\n        default: Self.padding\n        }\n    }\n\n    private var effectiveMaxContentWidth: CGFloat {\n        switch viewModel.step {\n        case .configuration: VMConfigurationSheet.minWidth\n        default: Self.maxContentWidth\n        }\n    }\n\n    private var hideBottomBar: Bool {\n        switch viewModel.step {\n        case .systemType, .restoreImageInput, .restoreImageSelection, .name:\n            false\n        case .configuration, .download, .install, .done:\n            true\n        }\n    }\n\n    @State private var showingConsole = false\n\n    public var body: some View {\n        NavigationStack {\n            VStack {\n                switch viewModel.step {\n                    case .systemType:\n                        guestSystemTypeSelection\n                    case .restoreImageInput:\n                        restoreImageURLInput\n                    case .restoreImageSelection:\n                        restoreImageSelection\n                    case .configuration:\n                        configureVM\n                    case .name:\n                        renameVM\n                    case .download, .install, .done:\n                        InstallProgressDisplayView().environmentObject(viewModel)\n                }\n            }\n            .onReceive(stepValidationStateChanged) { isValid in\n                viewModel.disableNextButton = !isValid\n            }\n            .navigationTitle(Text(\"Virtual Machine Setup\"))\n            .navigationSubtitle(Text(viewModel.step.subtitle))\n            .padding(effectivePadding)\n        }\n        .toolbar {\n            ToolbarItemGroup(placement: .navigation) {\n                Button {\n                    if viewModel.step == .done {\n                        closeWindow()\n                    } else {\n                        viewModel.back()\n                    }\n                } label: {\n                    Image(systemName: \"chevron.left\")\n                }\n                .disabled(!viewModel.canGoBack)\n            }\n\n            ToolbarItemGroup(placement: .confirmationAction) {\n                if viewModel.step == .done {\n                    Button(\"Done\") {\n                        closeWindow()\n                    }\n                    .keyboardShortcut(.defaultAction)\n                }\n            }\n\n            ToolbarItemGroup(placement: .primaryAction) {\n                if viewModel.step == .install {\n                    Toggle(isOn: $showingConsole) {\n                        Image(systemName: \"terminal\")\n                    }\n                    .help(\"Logs\")\n                }\n            }\n        }\n        .frame(minWidth: 800, maxWidth: .infinity, minHeight: 600, maxHeight: .infinity)\n        .overlay(alignment: .bottom) {\n            if showingConsole {\n                InstallationConsole()\n                    .padding(.horizontal, Self.padding * 2)\n                    .padding(.bottom, Self.padding)\n                    .transition(.move(edge: .bottom))\n            }\n        }\n        .animation(.snappy, value: showingConsole)\n        .safeAreaInset(edge: .bottom, spacing: 0) {\n            if !hideBottomBar {\n                bottomBar\n            }\n        }\n        .background {\n            BlurHashFullBleedBackground(blurHash: viewModel.data.backgroundHash)\n                .fullBleedBackgroundDimmed(dimBackground)\n        }\n        .environment(\\.containerPadding, Self.padding)\n        .environment(\\.maxContentWidth, effectiveMaxContentWidth)\n        .confirmBeforeClosingWindow { [weak viewModel] in\n            await viewModel?.confirmBeforeClosing() ?? true\n        }\n    }\n\n    private var dimBackground: Bool {\n        switch viewModel.step {\n        case .systemType: false\n        case .restoreImageInput: false\n        case .restoreImageSelection: false\n        case .name: false\n        case .configuration: false\n        case .download: true\n        case .install: true\n        case .done: false\n        }\n    }\n\n    @ViewBuilder\n    private var bottomBar: some View {\n        HStack {\n            Group {\n                switch viewModel.step {\n                case .restoreImageSelection:\n                    HStack(spacing: 12) {\n                        Button(\"Local File\") {\n                            viewModel.selectInstallMethod(.localFile)\n                        }\n\n                        Divider()\n                            .frame(height: 22)\n\n                        Button(\"Custom Link\") {\n                            viewModel.selectInstallMethod(.remoteManual)\n                        }\n                    }\n                default:\n                    EmptyView()\n                }\n            }\n            .buttonStyle(.link)\n\n            if case .error(let message) = viewModel.state {\n                Spacer()\n\n                errorView(message: message, multiline: false)\n            }\n\n            Spacer()\n\n            if viewModel.showNextButton {\n                nextButton\n            }\n        }\n        .virtualBuddyBottomBarStyle()\n    }\n\n    @ViewBuilder\n    private func errorView(message: String, multiline: Bool) -> some View {\n        Text(message)\n            .font(.subheadline)\n            .foregroundStyle(.red)\n            .textSelection(.enabled)\n            .multilineTextAlignment(.center)\n            .lineLimit(multiline ? nil : 1)\n            .minimumScaleFactor(0.8)\n            .help(message)\n    }\n\n    @ViewBuilder\n    private var nextButton: some View {\n        Button(viewModel.buttonTitle, action: {\n            if viewModel.step == .done {\n                library.loadMachines()\n                closeWindow()\n            } else {\n                viewModel.next()\n            }\n        })\n            .keyboardShortcut(.defaultAction)\n            .disabled(viewModel.disableNextButton)\n    }\n\n    @ViewBuilder\n    private var guestSystemTypeSelection: some View {\n        GuestTypePicker(selection: $viewModel.data.systemType)\n    }\n\n    @ViewBuilder\n    private var restoreImageURLInput: some View {\n        RestoreImageURLInputView().environmentObject(viewModel)\n    }\n\n    @ViewBuilder\n    private var restoreImageSelection: some View {\n        RestoreImageSelectionStep()\n            .environmentObject(viewModel)\n    }\n    \n    @ViewBuilder\n    private var configureVM: some View {\n        if let machine = viewModel.machine {\n            InstallConfigurationStepView(vm: machine, resolvedRestoreImage: viewModel.data.resolvedRestoreImage) { configuredModel in\n                viewModel.machine = configuredModel\n                try? viewModel.machine?.saveMetadata()\n\n                viewModel.next()\n            }\n        } else {\n            preparingStatus\n        }\n    }\n\n    @ViewBuilder\n    private var preparingStatus: some View {\n        Text(\"Preparing…\")\n            .foregroundStyle(.tertiary)\n            .frame(maxWidth: .infinity, maxHeight: .infinity)\n    }\n\n    @ViewBuilder\n    private var renameVM: some View {\n        VirtualMachineNameInputView(name: $viewModel.data.name)\n    }\n\n}\n\nextension VMInstallationStep {\n    var subtitle: String {\n        switch self {\n        case .systemType: \"Choose Operating System\"\n        case .restoreImageInput: \"Select Custom Restore Image\"\n        case .restoreImageSelection: \"Choose Version\"\n        case .name: \"Name Your Virtual Machine\"\n        case .configuration: \"Configure Your Virtual Machine\"\n        case .download: \"Downloading\"\n        case .install: \"Installing\"\n        case .done: \"Finished\"\n        }\n    }\n}\n\nextension View {\n    @ViewBuilder\n    func virtualBuddyBottomBarStyle() -> some View {\n        frame(maxWidth: .infinity)\n            .controlSize(.large)\n            .padding()\n            .background(Material.bar)\n            .overlay(alignment: .top) { Divider() }\n    }\n}\n\n#if DEBUG\nextension VMInstallationWizard {\n    @ViewBuilder\n    static func preview(step: VMInstallationStep) -> some View {\n        VMInstallationWizard(library: .preview, initialStep: step)\n            .frame(width: 900)\n    }\n\n    @ViewBuilder\n    static var preview: some View {\n        preview(step: .restoreImageSelection)\n    }\n}\n\n#Preview {\n    VMInstallationWizard.preview(step: .install)\n}\n#endif // DEBUG\n"
  },
  {
    "path": "VirtualUI/Source/Library/Components/FirstLaunchExperienceView.swift",
    "content": "import SwiftUI\nimport BuddyKit\n\nstruct FirstLaunchExperienceView: View {\n    var action: () -> ()\n\n    @State private var buttonRevealed = false\n\n    var body: some View {\n        ZStack {\n            AnimationView()\n                .ignoresSafeArea()\n                .frame(maxWidth: .infinity, maxHeight: .infinity)\n\n            VStack {\n                Spacer()\n\n                if buttonRevealed {\n                    Button {\n                        action()\n                    } label: {\n                        Text(\"Create Virtual Machine\")\n                            .font(.system(.title2, design: .rounded, weight: .medium))\n                    }\n                    .keyboardShortcut(.defaultAction)\n                    .buttonStyle(FirstLaunchButtonStyle())\n                    .controlSize(.large)\n                    .transition(.scale(scale: 1.5).combined(with: .opacity))\n                }\n\n                Spacer()\n            }\n        }\n        .task {\n            withAnimation(.snappy.delay(7.5)) {\n                buttonRevealed = true\n            }\n        }\n    }\n\n    private struct AnimationView: NSViewRepresentable {\n        typealias NSViewType = FirstLaunchExperienceNSView\n\n        func makeNSView(context: Context) -> FirstLaunchExperienceNSView {\n            FirstLaunchExperienceNSView(frame: .zero)\n        }\n\n        func updateNSView(_ nsView: FirstLaunchExperienceNSView, context: Context) {\n\n        }\n\n        final class FirstLaunchExperienceNSView: NSView {\n            override init(frame frameRect: NSRect) {\n                super.init(frame: frameRect)\n\n                setup()\n            }\n\n            required init?(coder: NSCoder) {\n                fatalError()\n            }\n\n            private lazy var assetLayer = CALayer.load(assetNamed: \"FirstLaunchExperience\", bundle: .virtualUI) ?? CALayer()\n\n            #if DEBUG\n            private var debugSkipAnimation: Bool { false }\n            #endif\n\n            private func setup() {\n                platformLayer.addSublayer(assetLayer)\n                assetLayer.isGeometryFlipped = false\n\n                assetLayer.beginTime = CACurrentMediaTime()\n                assetLayer.speed = 1\n\n                #if DEBUG\n                if debugSkipAnimation {\n                    assetLayer.timeOffset = 10\n                }\n                #endif\n            }\n\n            override var isFlipped: Bool { true }\n\n            override func layout() {\n                super.layout()\n\n                CATransaction.begin()\n                CATransaction.setDisableActions(true)\n                defer { CATransaction.commit() }\n\n                assetLayer.frame = bounds\n                assetLayer.sublayer(named: \"background\")?.frame = bounds\n                assetLayer.sublayer(path: \"background.fullBleed\")?.frame = bounds\n                assetLayer.sublayer(path: \"background.brighten\")?.frame = bounds\n                assetLayer.sublayer(named: \"backdrop\")?.frame = bounds\n                assetLayer.sublayer(named: \"iconTransform\")?.position = CGPoint(x: bounds.midX, y: bounds.midY)\n                assetLayer.sublayer(named: \"spotlightBleed\")?.frame = bounds\n                assetLayer.sublayer(named: \"scanlines\")?.frame.size.width = bounds.width\n                if let backdropMask = assetLayer.sublayer(named: \"backdrop\")?.mask {\n                    backdropMask.bounds.size = bounds.size\n                    backdropMask.position = CGPoint(x: bounds.midX, y: bounds.maxY)\n                }\n            }\n        }\n\n    }\n}\n\nprivate struct FirstLaunchButtonStyle: ButtonStyle {\n    func makeBody(configuration: Configuration) -> some View {\n        configuration.label\n            .foregroundStyle(.secondary)\n            .padding(.horizontal, 22)\n            .padding(.vertical, 14)\n            .background(Material.regular, in: shape)\n            .overlay {\n                ZStack {\n                    Color.accentColor\n                        .blendMode(.color)\n                        .opacity(configuration.isPressed ? 1 : 0.7)\n\n                    shape\n                        .inset(by: 10)\n                        .fill(Color.accentColor)\n                        .blur(radius: 10)\n                        .blendMode(.plusLighter)\n                        .opacity(configuration.isPressed ? 0.1 : 0)\n\n                    LinearGradient(colors: [.accentColor.opacity(0.9), .accentColor.opacity(0.4)], startPoint: .top, endPoint: .bottom)\n                        .mask {\n                            shape.strokeBorder(Color.white, lineWidth: 1)\n                        }\n                        .blendMode(.plusLighter)\n                        .opacity(0.3)\n                }\n            }\n            .clipShape(shape)\n            .shadow(color: .black.opacity(0.2), radius: 6, x: 0, y: 0)\n            .scaleEffect(configuration.isPressed ? 0.98 : 1)\n            .animation(configuration.isPressed ? .linear(duration: 0) : .snappy, value: configuration.isPressed)\n    }\n\n    var shape: some InsettableShape {\n        Capsule(style: .continuous)\n    }\n}\n\n#if DEBUG\n#Preview {\n    FirstLaunchExperienceView() { }\n        .frame(width: 900, height: 600)\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/Library/Components/LibraryItemView.swift",
    "content": "//\n//  LibraryItemView.swift\n//  VirtualUI\n//\n//  Created by Guilherme Rambo on 21/07/22.\n//\n\nimport SwiftUI\nimport VirtualCore\n\npublic extension EnvironmentValues {\n    /// This is injected by reading from ``VBSettings``.\n    /// When `true`, virtual machine thumbnails in the library show the actual desktop picture thumbnail instead of the blurred version.\n    @Entry var virtualBuddyShowDesktopPictureThumbnails = false\n}\n\n/// This button style achieves a couple of things:\n/// - Gives its label a `vbLibraryButtonPressed` environment value that can be used to react to button presses\n/// - Fixes an annoying behavior common to all standard SwiftUI button styles where pressing the space bar\n/// with one of its subviews in focus would trigger the button's action instead of entering a space in a text field, for example\nstruct VBLibraryItemButtonStyle: PrimitiveButtonStyle {\n\n    @State private var isPressed = false\n\n    /// The rectangle of the button's contents in the local coordinate space.\n    @State private var rect: CGRect = .zero\n\n    func makeBody(configuration: Configuration) -> some View {\n        configuration.label\n            .environment(\\.vbLibraryButtonPressed, isPressed)\n            .overlay {\n                GeometryReader { proxy in\n                    Color.clear\n                        .preference(key: VBLibraryButtonSizePreferenceKey.self, value: proxy.size)\n                }\n            }\n            .onPreferenceChange(VBLibraryButtonSizePreferenceKey.self) { rect = CGRect(origin: .zero, size: $0) }\n            .gesture(DragGesture(minimumDistance: 0, coordinateSpace: .local).onChanged { value in\n                /// Replicate standard button behavior where dragging outside the button cancels the click.\n                isPressed = rect.contains(value.location)\n            }.onEnded { _ in\n                /// If the button is not currently pressed, then don't perform the action.\n                guard isPressed else { return }\n\n                configuration.trigger()\n\n                isPressed = false\n            })\n    }\n\n}\n\nstruct VBLibraryButtonSizePreferenceKey: PreferenceKey {\n    static var defaultValue: CGSize = .zero\n\n    static func reduce(value: inout CGSize, nextValue: () -> CGSize) {\n        value = nextValue()\n    }\n}\n\nextension PrimitiveButtonStyle where Self == VBLibraryItemButtonStyle {\n    static var vbLibraryItem: VBLibraryItemButtonStyle { VBLibraryItemButtonStyle() }\n}\n\nprivate struct VBLibraryItemButtonPressedEnvironmentKey: EnvironmentKey {\n    static var defaultValue = false\n}\nprivate extension EnvironmentValues {\n    var vbLibraryButtonPressed: VBLibraryItemButtonPressedEnvironmentKey.Value {\n        get { self[VBLibraryItemButtonPressedEnvironmentKey.self] }\n        set { self[VBLibraryItemButtonPressedEnvironmentKey.self] = newValue }\n    }\n}\n\n@MainActor\nstruct LibraryItemView: View {\n\n    @EnvironmentObject var library: VMLibraryController\n\n    var vm: VBVirtualMachine\n    @State var name: String\n\n    @Environment(\\.vbLibraryButtonPressed)\n    private var isPressed\n\n    var nameFieldFocus = BoolSubject()\n\n    private var isVMBooted: Bool { library.bootedMachineIdentifiers.contains(vm.id) }\n\n    var body: some View {\n        VStack(spacing: 12) {\n            ArtworkView(virtualMachine: vm)\n\n            EphemeralTextField($name, alignment: .leading, setFocus: nameFieldFocus) { name in\n                Text(name)\n            } editableContent: { name in\n                TextField(\"VM Name\", text: name)\n                    .onSubmit { rename(name.wrappedValue) }\n            } validate: { name in\n                do {\n                    try library.validateNewName(name, for: vm)\n                    return true\n                } catch {\n                    return false\n                }\n            }\n            .font(.system(size: 16, weight: .medium, design: .rounded))\n            .disabled(isVMBooted)\n        }\n        .padding([.leading, .trailing, .top], 8)\n        .padding(.bottom, 12)\n        .background(Material.regular, in: backgroundShape)\n        .highlightBorder(backgroundShape, color: .accentColor, opacity: 0.2)\n        .clipShape(backgroundShape)\n        .shadow(color: Color.black.opacity(0.14), radius: 12)\n        .shadow(color: Color.black.opacity(0.56), radius: 1)\n        .scaleEffect(isPressed ? 0.98 : 1)\n        .contextMenu { contextMenuItems }\n        .task(id: vm.name) { self.name = vm.name }\n        .animation(isPressed ? .linear(duration: 0) : .snappy, value: isPressed)\n    }\n\n    private func rename(_ newName: String) {\n        guard newName != vm.name else { return }\n\n        do {\n            try library.rename(vm, to: name)\n        } catch {\n            NSAlert(error: error).runModal()\n        }\n    }\n\n    private var backgroundShape: some InsettableShape {\n        RoundedRectangle(cornerRadius: 12, style: .continuous)\n    }\n\n    @ViewBuilder\n    private var contextMenuItems: some View {\n        Button {\n            NSWorkspace.shared.selectFile(vm.bundleURL.path, inFileViewerRootedAtPath: vm.bundleURL.deletingLastPathComponent().path)\n        } label: {\n            Label(\"Show in Finder\", systemImage: \"folder\")\n        }\n\n        Divider()\n\n        Button {\n            duplicate()\n        } label: {\n            Text(\"Duplicate\")\n        }\n\n        Button {\n            nameFieldFocus.send(true)\n        } label: {\n            Text(\"Rename\")\n        }\n        .disabled(isVMBooted)\n\n        #if DEBUG\n        Button {\n            NSWorkspace.shared.open(vm.metadataDirectoryURL)\n        } label: {\n            Text(\"Open Data Folder…\")\n        }\n        #endif\n\n        Divider()\n\n        Button {\n            library.performMoveToTrash(for: vm)\n        } label: {\n            Text(\"Move to Trash\")\n        }\n        .disabled(isVMBooted)\n    }\n\n    private func duplicate() {\n        Task {\n            do {\n                try library.duplicate(vm)\n            } catch {\n                NSAlert(error: error).runModal()\n            }\n        }\n    }\n\n    private struct ArtworkView: View {\n        var virtualMachine: VBVirtualMachine\n\n        @Environment(\\.virtualBuddyShowDesktopPictureThumbnails)\n        private var showDesktopPicture\n\n        var body: some View {\n            VMArtworkView(virtualMachine: virtualMachine, alwaysUseBlurHash: !showDesktopPicture)\n                .id(virtualMachine.blurHashBackgroundContent)\n                .aspectRatio(contentMode: .fill)\n                .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)\n                .aspectRatio(16/9, contentMode: .fit)\n                .highlightBorder(shape)\n                .clipShape(shape)\n        }\n\n        private var shape: some InsettableShape {\n            RoundedRectangle(cornerRadius: 8, style: .continuous)\n        }\n    }\n\n}\n\nstruct HighlightBorderModifier<Shape: InsettableShape>: ViewModifier {\n    var shape: Shape\n    var color: Color\n    var opacity: Double\n\n    func body(content: Content) -> some View {\n        ZStack {\n            content\n\n            ZStack {\n                LinearGradient(colors: [color.opacity(1), color.opacity(0.4)], startPoint: .top, endPoint: .bottom)\n\n                shape\n                    .inset(by: 1)\n                    .blendMode(.destinationOut)\n            }\n            .compositingGroup()\n            .clipShape(shape)\n            .blendMode(.plusLighter)\n            .opacity(opacity)\n        }\n    }\n}\n\nextension View {\n    func highlightBorder<Shape: InsettableShape>(_ shape: Shape, color: Color = .white, opacity: Double = 0.14) -> some View {\n        modifier(HighlightBorderModifier(shape: shape, color: color, opacity: opacity))\n    }\n}\n\nextension VMLibraryController {\n    func performMoveToTrash(for vm: VBVirtualMachine) {\n        Task {\n            do {\n                try await moveToTrash(vm)\n            } catch {\n                NSAlert(error: error).runModal()\n            }\n        }\n    }\n}\n\n#if DEBUG\n#Preview {\n    LibraryView()\n        .environmentObject(VMLibraryController.preview)\n        .environmentObject(VirtualMachineSessionUIManager.shared)\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/Library/LibraryView.swift",
    "content": "//\n//  LibraryView.swift\n//  VirtualBuddy\n//\n//  Created by Guilherme Rambo on 10/04/22.\n//\n\nimport SwiftUI\nimport VirtualCore\n\npublic extension String {\n    static let vb_libraryWindowID = \"library\"\n}\n\nprivate extension UserDefaults {\n    /// Not using `AppStorage` because this can't update the view.\n    var hasSeenFirstLaunchExperience: Bool {\n        get { bool(forKey: #function) }\n        set {\n            set(newValue, forKey: #function)\n            synchronize()\n        }\n    }\n}\n\npublic struct LibraryView: View {\n    @ObservedObject private var settingsContainer = VBSettingsContainer.current\n\n    @EnvironmentObject private var library: VMLibraryController\n    @EnvironmentObject private var sessionManager: VirtualMachineSessionUIManager\n\n    @Environment(\\.openCocoaWindow)\n    private var openCocoaWindow\n\n    @Environment(\\.openVirtualBuddySettings)\n    private var openSettings\n\n    /// Whether the first launch experience has been presented in the current app session.\n    /// The defaults flag itself is not read again, but this is set whenever a non-empty library state is shown.\n    @State private var canShowFirstLaunchExperience = true\n\n    private var shouldShowFirstLaunchExperienceOnEmptyLibrary: Bool {\n        guard #available(macOS 15.0, *) else { return false }\n        return canShowFirstLaunchExperience && (!hasSeenFirstLaunchExperience || UserDefaults.standard.bool(forKey: \"VBForceFirstLaunchExperience\"))\n    }\n\n    /// Set on view initialization only so that when the defaults flag is updated, it doesn't cause the first launch experience to disappear.\n    private let hasSeenFirstLaunchExperience: Bool\n\n    public init() {\n        hasSeenFirstLaunchExperience = UserDefaults.standard.hasSeenFirstLaunchExperience\n    }\n\n    public var body: some View {\n        libraryContents\n            .frame(minWidth: 900, maxWidth: .infinity, minHeight: 600, maxHeight: .infinity)\n            .toolbar(content: { toolbarContents })\n            .task {\n                #if DEBUG\n                if UserDefaults.standard.bool(forKey: \"VBOpenSettings\") {\n                    openSettings()\n                }\n                #endif\n            }\n    }\n\n    @ToolbarContentBuilder\n    private var toolbarContents: some ToolbarContent {\n        ToolbarItemGroup(placement: .primaryAction) {\n            Button {\n                openCocoaWindow {\n                    VMInstallationWizard(library: library)\n                }\n            } label: {\n                Image(systemName: \"plus\")\n            }\n            .help(\"New virtual machine\")\n        }\n    }\n\n    private var gridSpacing: CGFloat { 16 }\n    private var gridItemMinSize: CGFloat { 240 }\n    private var gridColumns: [GridItem] {\n        [.init(.adaptive(minimum: gridItemMinSize), spacing: gridSpacing)]\n    }\n    \n    @ViewBuilder\n    private var libraryContents: some View {\n        ZStack {\n            BlurHashFullBleedBackground(content: .blurHash(.virtualBuddyBackground))\n                .ignoresSafeArea()\n                .frame(maxWidth: .infinity, maxHeight: .infinity)\n                .fullBleedBackgroundDimmed(![.loaded, .loading].contains(library.state.id))\n\n            Group {\n                switch library.state {\n                case .loaded(let machines):\n                    grid(machines)\n                        .task { canShowFirstLaunchExperience = false }\n                case .empty:\n                    if shouldShowFirstLaunchExperienceOnEmptyLibrary {\n                        FirstLaunchExperienceView {\n                            sessionManager.launchInstallWizard(library: library)\n                        }\n                        .task { UserDefaults.standard.hasSeenFirstLaunchExperience = true }\n                    } else {\n                        libraryEmptyMessage\n                    }\n                case .loading:\n                    EmptyView()\n                case .volumeNotMounted:\n                    libraryVolumeNotMountedMessage\n                case .directoryMissing:\n                    libraryDirectoryMissingMessage\n                }\n            }\n            .transition(.scale(scale: 1.5).combined(with: .opacity))\n        }\n        .animation(.snappy, value: library.state.id)\n    }\n\n    @ViewBuilder\n    private func grid(_ machines: [VBVirtualMachine]) -> some View {\n        ScrollView(.vertical) {\n            LazyVGrid(columns: gridColumns, spacing: gridSpacing) {\n                ForEach(machines) { vm in\n                    Button {\n                        sessionManager.launch(vm, library: library, options: nil)\n                    } label: {\n                        LibraryItemView(vm: vm, name: vm.name)\n                    }\n                    .buttonStyle(.vbLibraryItem)\n                    .environmentObject(library)\n                    .transition(.scale(scale: 0.3).combined(with: .opacity))\n                }\n            }\n            .padding()\n            .padding(.top)\n        }\n        .environment(\\.virtualBuddyShowDesktopPictureThumbnails, settingsContainer.settings.showDesktopPictureThumbnails)\n    }\n\n    @ViewBuilder\n    private var libraryVolumeNotMountedMessage: some View {\n        BackportedContentUnavailableView(\n            \"Library Not Mounted\",\n            systemImage: \"externaldrive.badge.questionmark\",\n            description: Text(\"\"\"\n            The volume containing your VirtualBuddy library is not currently mounted.\n            Once mounted, your virtual machines will appear here.\n            \"\"\")\n        ) {\n            Button(\"Try Again\") {\n                library.loadMachines()\n            }\n            .keyboardShortcut(.defaultAction)\n\n            Button(\"Open Settings\") {\n                openSettings()\n            }\n        }\n    }\n\n    @ViewBuilder\n    private var libraryDirectoryMissingMessage: some View {\n        BackportedContentUnavailableView(\n            \"Library Missing\",\n            systemImage: \"questionmark.folder\",\n            description: Text(\"\"\"\n            VirtualBuddy is unable to locate your library directory.\n\n            Review your settings to ensure the directory is set correctly. If it exists, there may be a permission problem.\n\n            If you’ve deleted the library directory, you can start a new empty library.\n            \"\"\")\n        ) {\n            Button(\"Open Settings\") {\n                openSettings()\n            }\n            .keyboardShortcut(.defaultAction)\n\n            Button(\"Create Empty Library\") {\n                library.loadMachines(createLibrary: true)\n            }\n        }\n    }\n\n    @ViewBuilder\n    private var libraryEmptyMessage: some View {\n        BackportedContentUnavailableView(\n            \"No Virtual Machines\",\n            systemImage: \"square.grid.2x2\",\n            description: Text(\"\"\"\n            You haven’t created any virtual machines yet. You can create a new one, or select a different library directory in settings.\n            \"\"\")\n        ) {\n            Button(\"Create Virtual Machine\") {\n                sessionManager.launchInstallWizard(library: library)\n            }\n            .keyboardShortcut(.defaultAction)\n\n            Button(\"Open Settings\") {\n                openSettings()\n            }\n        }\n    }\n}\n\nfileprivate extension URL {\n    var collapsedHomePath: String {\n        path.replacingOccurrences(of: NSHomeDirectory(), with: \"~\")\n    }\n}\n\n#if DEBUG\n#Preview {\n    LibraryView()\n        .environmentObject(VMLibraryController.preview)\n        .environmentObject(VirtualMachineSessionUIManager.shared)\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/Session/Components/ContinuousProgressIndicator.swift",
    "content": "import SwiftUI\n\nstruct ContinuousProgressIndicator<Content: View>: View {\n    var duration: TimeInterval\n    @ViewBuilder var content: (Double) -> Content\n\n    @State private var progress = Double(0)\n\n    var body: some View {\n        ZStack {\n            content(progress)\n        }\n        .task { @MainActor in\n            withAnimation(.easeIn(duration: duration)) {\n                progress = 1\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "VirtualUI/Source/Session/Components/MaskProgressView.swift",
    "content": "import SwiftUI\n\nstruct MaskProgressView<Mask: View, Foreground: ShapeStyle, Background: ShapeStyle>: View {\n    var progress: Double\n    var background: Background\n    var foreground: Foreground\n    @ViewBuilder var mask: (Double) -> Mask\n\n    var body: some View {\n        ZStack {\n            let content = mask(progress)\n\n            content\n                .foregroundStyle(background)\n\n            content\n                .foregroundStyle(foreground)\n                .mask {\n                    GeometryReader { proxy in\n                        Rectangle().fill(.white)\n                            .frame(width: proxy.size.width * progress, alignment: .leading)\n                    }\n                }\n        }\n    }\n}\n"
  },
  {
    "path": "VirtualUI/Source/Session/Components/NumberDisplayMode.swift",
    "content": "import SwiftUI\n\nenum NumberDisplayMode: Int, CaseIterable {\n    case hex\n    case decimal\n\n    var title: String {\n        switch self {\n        case .hex:\n            return \"Hex\"\n        case .decimal:\n            return \"Decimal\"\n        }\n    }\n}\n\nextension EnvironmentValues {\n    @Entry var numberDisplayMode: NumberDisplayMode = .decimal\n}\n\nprotocol FormattableNumber: CVarArg, FixedWidthInteger {\n    func formatted(mode: NumberDisplayMode) -> String\n}\n\nextension FormattableNumber where Self: SignedInteger {\n    func formatted(mode: NumberDisplayMode) -> String {\n        switch mode {\n        case .hex:\n            return String(format: \"0x%02llX\", Int64(self))\n        case .decimal:\n            return String(format: \"%lld\", Int64(self))\n        }\n    }\n}\n\nextension FormattableNumber where Self: UnsignedInteger {\n    func formatted(mode: NumberDisplayMode) -> String {\n        switch mode {\n        case .hex:\n            return String(format: \"0x%02llX\", UInt64(self))\n        case .decimal:\n            return String(format: \"%llu\", UInt64(self))\n        }\n    }\n}\n\nextension Int64: FormattableNumber { }\nextension UInt64: FormattableNumber { }\nextension Int32: FormattableNumber { }\nextension UInt32: FormattableNumber { }\nextension Int16: FormattableNumber { }\nextension UInt16: FormattableNumber { }\nextension Int8: FormattableNumber { }\nextension UInt8: FormattableNumber { }\n"
  },
  {
    "path": "VirtualUI/Source/Session/Components/SavedStatePicker.swift",
    "content": "import SwiftUI\nimport VirtualCore\n\nstruct SavedStatePicker: View {\n    @EnvironmentObject private var controller: VMSavedStatesController\n\n    @Binding var selectedStateURL: URL?\n\n    var body: some View {\n        Picker(\"State\", selection: $selectedStateURL) {\n            if controller.states.isEmpty {\n                Text(\"No Saved States\")\n                    .tag(Optional<URL>.none)\n            } else {\n                Text(\"Don’t Restore\")\n                    .tag(Optional<URL>.none)\n\n                Divider()\n            }\n\n            ForEach(controller.states) { state in\n                Text(state.url.deletingPathExtension().lastPathComponent)\n                    .tag(Optional<URL>.some(state.url))\n            }\n        }\n        .disabled(controller.states.isEmpty)\n    }\n}\n\n#if DEBUG\nprivate struct _Preview: View {\n    @StateObject private var controller = VMSavedStatesController.preview\n    @State private var selectedStateURL: URL?\n\n    var body: some View {\n        Form {\n            SavedStatePicker(selectedStateURL: $selectedStateURL)\n                .environmentObject(controller)\n        }\n        .formStyle(.grouped)\n    }\n}\n\n#Preview(\"SavedStatePicker\") {\n    _Preview()\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/Session/Components/SwiftUIVMView.swift",
    "content": "//\n//  SwiftUIVMView.swift\n//  VirtualBuddy\n//\n//  Created by Guilherme Rambo on 07/04/22.\n//\n\nimport SwiftUI\nimport Cocoa\nimport Virtualization\nimport VirtualCore\n\nstruct SwiftUIVMView: NSViewControllerRepresentable {\n    \n    typealias NSViewControllerType = VMViewController\n    \n    @Binding var controllerState: VMController.State\n    let captureSystemKeys: Bool\n    var isDFUModeVM: Bool\n    var vmECID: UInt64?\n    @Binding var automaticallyReconfiguresDisplay: Bool\n\n    func makeNSViewController(context: Context) -> VMViewController {\n        let controller = VMViewController()\n        controller.vmECID = vmECID\n        controller.isDFUModeVM = isDFUModeVM\n        controller.captureSystemKeys = captureSystemKeys\n        controller.automaticallyReconfiguresDisplay = automaticallyReconfiguresDisplay\n        return controller\n    }\n    \n    func updateNSViewController(_ nsViewController: VMViewController, context: Context) {\n        nsViewController.automaticallyReconfiguresDisplay = automaticallyReconfiguresDisplay\n\n        nsViewController.vmECID = vmECID\n        nsViewController.isDFUModeVM = isDFUModeVM\n\n        if case .running(let vm) = controllerState {\n            nsViewController.virtualMachine = vm\n        } else {\n            nsViewController.virtualMachine = nil\n        }\n    }\n    \n}\n\nfinal class VMViewController: NSViewController {\n\n    var isDFUModeVM: Bool = false {\n        didSet {\n            guard isDFUModeVM != oldValue, isViewLoaded else { return }\n\n            handleDFUTransition(.init(wasInDFU: oldValue, isInDFU: isDFUModeVM))\n        }\n    }\n\n    var vmECID: UInt64? {\n        didSet {\n            guard vmECID != nil, vmECID != oldValue, isDFUModeVM, isViewLoaded else { return }\n\n            /// Force update of DFU state to display the ECID.\n            handleDFUTransition(.enter)\n        }\n    }\n\n    var captureSystemKeys: Bool = false {\n        didSet {\n            guard captureSystemKeys != oldValue, isViewLoaded else { return }\n            vmView.capturesSystemKeys = captureSystemKeys\n        }\n    }\n\n    var automaticallyReconfiguresDisplay: Bool = true {\n        didSet {\n            guard #available(macOS 14.0, *) else { return }\n            vmView.automaticallyReconfiguresDisplay = automaticallyReconfiguresDisplay\n        }\n    }\n\n    var virtualMachine: VZVirtualMachine? {\n        didSet {\n            vmView.virtualMachine = virtualMachine\n        }\n    }\n\n    private var canShowDFUView: Bool {\n        #if DEBUG\n        return ProcessInfo.isSwiftUIPreview || virtualMachine != nil\n        #else\n        return virtualMachine != nil\n        #endif\n    }\n\n    private lazy var vmView: VZVirtualMachineView = {\n        VZVirtualMachineView(frame: .zero)\n    }()\n    \n    override func loadView() {\n        view = NSView()\n        view.wantsLayer = true\n        view.layer?.backgroundColor = NSColor.black.cgColor\n        \n        vmView.capturesSystemKeys = captureSystemKeys\n\n        if #available(macOS 14.0, *) {\n            vmView.automaticallyReconfiguresDisplay = automaticallyReconfiguresDisplay\n        }\n\n        vmView.translatesAutoresizingMaskIntoConstraints = false\n        view.addSubview(vmView)\n\n        NSLayoutConstraint.activate([\n            vmView.leadingAnchor.constraint(equalTo: view.leadingAnchor),\n            vmView.trailingAnchor.constraint(equalTo: view.trailingAnchor),\n            vmView.topAnchor.constraint(equalTo: view.topAnchor),\n            vmView.bottomAnchor.constraint(equalTo: view.bottomAnchor)\n        ])\n    }\n\n    override func viewDidAppear() {\n        super.viewDidAppear()\n        \n        guard let window = view.window else { return }\n        \n        window.makeFirstResponder(vmView)\n        \n        if isDFUModeVM { handleDFUTransition(.enter) }\n    }\n\n    enum DFUTransition: Hashable {\n        case enter\n        case exit\n        case invalid\n\n        init(wasInDFU: Bool, isInDFU: Bool) {\n            if wasInDFU, !isInDFU {\n                self = .exit\n            } else if isInDFU, !wasInDFU {\n                self = .enter\n            } else {\n                self = .invalid\n            }\n        }\n    }\n\n    private func handleDFUTransition(_ transition: DFUTransition) {\n        switch transition {\n        case .enter:\n            showDFUView()\n        case .exit:\n            hideDFUView()\n        case .invalid:\n            break\n        }\n    }\n\n    private var currentDFUView: NSView?\n\n    private func showDFUView() {\n        currentDFUView?.removeFromSuperview()\n\n        guard canShowDFUView else { return }\n\n        let dfuView = NSHostingView(rootView: DFUStatusView(ecid: vmECID))\n        dfuView.translatesAutoresizingMaskIntoConstraints = false\n        view.addSubview(dfuView)\n\n        NSLayoutConstraint.activate([\n            dfuView.leadingAnchor.constraint(greaterThanOrEqualTo: view.leadingAnchor, constant: 16),\n            dfuView.trailingAnchor.constraint(lessThanOrEqualTo: view.trailingAnchor, constant: -16),\n            dfuView.topAnchor.constraint(greaterThanOrEqualTo: view.topAnchor, constant: 16),\n            dfuView.bottomAnchor.constraint(lessThanOrEqualTo: view.bottomAnchor, constant: -16),\n            dfuView.centerXAnchor.constraint(equalTo: view.centerXAnchor),\n            dfuView.centerYAnchor.constraint(equalTo: view.centerYAnchor),\n        ])\n    }\n\n    private func hideDFUView() {\n        currentDFUView?.removeFromSuperview()\n    }\n}\n\nstruct DFUStatusView: View {\n    var ecid: UInt64?\n\n    @Environment(\\.numberDisplayMode)\n    private var numberDisplayMode\n\n    var body: some View {\n        VStack(spacing: 22) {\n            VStack {\n                Image(systemName: \"cpu\")\n                    .imageScale(.large)\n                Text(\"DFU Mode Active\")\n            }\n            .font(.system(.largeTitle, design: .rounded))\n\n            VStack(spacing: 8) {\n                Text(\"This virtual machine is running in DFU mode.\")\n                    .font(.system(.title2, design: .rounded, weight: .medium))\n\n                if let ecid {\n                    HStack(spacing: 0) {\n                        Text(\"ECID: \")\n                            .font(.headline)\n\n                        Text(\"\\(ecid.formatted(mode: numberDisplayMode))\")\n                            .textSelection(.enabled)\n                            .font(.headline.weight(.regular).monospaced())\n                    }\n                    .foregroundStyle(.secondary)\n                }\n            }\n        }\n    }\n}\n\n#if DEBUG\n#Preview(\"VM View - DFU\") {\n    SwiftUIVMView(\n        controllerState: .constant(.starting(nil)),\n        captureSystemKeys: false,\n        isDFUModeVM: true,\n        vmECID: 7788022887768653863,\n        automaticallyReconfiguresDisplay: .constant(false)\n    )\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/Session/Components/VMProgressOverlay.swift",
    "content": "import SwiftUI\n\nstruct VMProgressOverlay: View {\n    let message: String\n    let duration: TimeInterval\n    \n    var body: some View {\n        ContinuousProgressIndicator(duration: duration) { progress in\n            MaskProgressView(progress: progress * 1.2, background: .tertiary, foreground: .primary) { _ in\n                Text(message)\n                    .font(.system(.title, design: .rounded, weight: .semibold))\n            }\n            .scaleEffect(0.85 + 0.15 * progress)\n        }\n        .id(message)\n        .transition(.scale(scale: 0.2).combined(with: .opacity))\n    }\n}\n"
  },
  {
    "path": "VirtualUI/Source/Session/Components/VirtualMachineControls.swift",
    "content": "//\n//  VirtualMachineControls.swift\n//  VirtualUI\n//\n//  Created by Guilherme Rambo on 24/10/23.\n//\n\nimport SwiftUI\nimport VirtualCore\n\n@MainActor\nprotocol VirtualMachineStateController: ObservableObject {\n    var state: VMState { get }\n    \n    func start() async throws\n    func stop() async throws\n    func pause() async throws\n    func resume() async throws\n    \n    @available(macOS 14.0, *)\n    func saveState(snapshotName: String) async throws\n\n    var virtualMachineModel: VBVirtualMachine { get }\n}\n\nextension VMController: VirtualMachineStateController { }\n\n@available(macOS 14.0, *)\nstruct VirtualMachineControls<Controller: VirtualMachineStateController>: View {\n    @EnvironmentObject private var controller: Controller\n\n    @State private var actionTask: Task<Void, Never>?\n    @State private var isPopoverPresented = false\n    @State private var textFieldContent = \"\"\n    \n    var body: some View {\n        Group {\n            switch controller.state {\n            case .idle, .paused, .stopped, .savingState, .restoringState, .stateSaveCompleted:\n                Button {\n                    runToolbarAction {\n                        if controller.state.canResume {\n                            try await controller.resume()\n                        } else {\n                            try await controller.start()\n                        }\n                    }\n                } label: {\n                    Image(systemName: \"play\")\n                }\n                .disabled(controller.state.isSavingState || controller.state.isRestoringState)\n            case .starting, .running:\n                if #available(macOS 14.0, *), controller.virtualMachineModel.supportsStateRestoration {\n                    Button {\n                        /**\n                         Ability to save new states has been temporarily disabled in version 2 due to issues with its implementation.\n                         This prevents users from creating bad state saves before the correct implementation is shipped.\n                         */\n                        guard UserDefaults.standard.bool(forKey: \"VBForceEnableSaveStateFeature\") else {\n                            NSAlert(error: \"Sorry, this feature has been temporarily disabled. It will be back in a future update.\").runModal()\n                            return\n                        }\n                        \n                        runToolbarAction {\n                            textFieldContent = \"Save-\" + DateFormatter.savedStateFileName.string(from: .now)\n                            isPopoverPresented = true\n                        }\n                    } label: {\n                        Image(systemName: \"tray.and.arrow.down\")\n                    }\n                    .help(\"Save current state\")\n                    .popover(isPresented: $isPopoverPresented) {\n                        VStack {\n                            Text(\"Save current state\")\n                                .font(.headline)\n                            TextField(\"Name current state\", text: $textFieldContent)\n                                .textFieldStyle(RoundedBorderTextFieldStyle())\n                                .padding(.top, 15)\n                                .padding(.bottom, 15)\n\n                            HStack {\n                                Spacer()\n\n                                Button(\"Cancel\") {\n                                    isPopoverPresented = false\n                                }\n                                .padding(.trailing, 8)\n                                .keyboardShortcut(.cancelAction)\n\n                                Button(\"Save\") {\n                                    isPopoverPresented = false\n\n                                    runToolbarAction {\n                                        try await saveState()\n                                    }\n                                }\n                                .keyboardShortcut(.defaultAction)\n                            }\n                        }\n                        .frame(width: 300)\n                        .padding()\n                    }\n\n                    Button {\n                        runToolbarAction {\n                            try await controller.pause()\n                        }\n                    } label: {\n                        Image(systemName: \"pause\")\n                    }\n                    .help(\"Pause\")\n\n                    Button {\n                        runToolbarAction {\n                            try await controller.stop()\n                        }\n                    } label: {\n                        Image(systemName: \"power\")\n                    }\n                    .help(\"Shut down\")\n                }\n            }\n        }\n        .symbolVariant(.fill)\n        .disabled(actionTask != nil)\n    }\n\n    private func runToolbarAction(alertForErrors: Bool = false, action: @escaping () async throws -> Void) {\n        actionTask = Task {\n            defer { actionTask = nil }\n\n            do {\n                try await action()\n            } catch {\n                guard alertForErrors else { return }\n\n                NSAlert(error: error).runModal()\n            }\n        }\n    }\n\n    private func saveState() async throws {\n        do {\n            try await controller.saveState(snapshotName: textFieldContent)\n        } catch {\n            guard !(error is CancellationError) else { return }\n            throw error\n        }\n    }\n}\n\nprivate extension DateFormatter {\n    static let savedStateFileName: DateFormatter = {\n        let f = DateFormatter()\n        f.calendar = .init(identifier: .gregorian)\n        f.dateFormat = \"yyyy-MM-dd_HH;mm;ss\"\n        return f\n    }()\n}\n\n\n#if DEBUG\nprivate final class PreviewVirtualMachineStateController: VirtualMachineStateController {\n    @MainActor\n    @Published var state: VMState = .idle\n\n    @Published var virtualMachineModel = VBVirtualMachine.preview\n\n    @MainActor\n    func start() async throws {\n        state = .starting(nil)\n\n        try await Task.sleep(nanoseconds: 1 * NSEC_PER_SEC)\n\n        state = .running(.preview)\n    }\n\n    @MainActor\n    func stop() async throws {\n        try await Task.sleep(nanoseconds: 1 * NSEC_PER_SEC)\n\n        state = .stopped(nil)\n    }\n\n    @MainActor\n    func pause() async throws {\n        state = .paused(.preview)\n    }\n\n    @MainActor\n    func resume() async throws {\n        state = .running(.preview)\n    }\n\n    @available(macOS 14.0, *)\n    @MainActor\n    func saveState(snapshotName: String) async throws {\n        state = .paused(.preview)\n    }\n\n}\n\n@available(macOS 14.0, *)\nprivate struct _VirtualMachineControlsPreview: View {\n    var body: some View {\n        Text(\"Preview\")\n            .frame(minWidth: 200, maxWidth: .infinity, minHeight: 200, maxHeight: .infinity)\n            .toolbar {\n                ToolbarItemGroup(placement: .primaryAction) {\n                    VirtualMachineControls<PreviewVirtualMachineStateController>()\n                        .environmentObject(PreviewVirtualMachineStateController())\n                }\n            }\n    }\n}\n\n@available(macOS 14.0, *)\n#Preview {\n    _VirtualMachineControlsPreview()\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/Session/Configuration/VMSessionConfigurationView.swift",
    "content": "//\n//  VMSessionConfigurationView.swift\n//  VirtualBuddy\n//\n//  Created by Guilherme Rambo on 07/04/22.\n//\n\nimport SwiftUI\nimport VirtualCore\n\nstruct VMSessionConfigurationView: View {\n    @EnvironmentObject var controller: VMController\n\n    @State private var isShowingVMSettings = false\n\n    private var vm: VBVirtualMachine { controller.virtualMachineModel }\n\n    private var resolvedRestoreImage: ResolvedRestoreImage? {\n        let catalog = SoftwareCatalog.current(for: vm.configuration.systemType)\n\n        if let remoteURL = vm.metadata.remoteInstallImageURL,\n           let resolved = catalog.resolvedRestoreImage(matching: remoteURL, guestType: vm.configuration.systemType) {\n            return resolved\n        }\n\n        if let localURL = vm.metadata.installImageURL,\n           let resolved = catalog.resolvedRestoreImage(matching: localURL, guestType: vm.configuration.systemType) {\n            return resolved\n        }\n\n        return nil\n    }\n\n    var body: some View {\n        SelfSizingGroupedForm(minHeight: 100) {\n            if showSavedStatePicker {\n                SavedStatePicker(selectedStateURL: $controller.options.stateRestorationPackageURL)\n                    .environmentObject(controller.savedStatesController)\n            }\n            \n            if showInstallDeviceOption {\n                Toggle(\"Boot on install drive\", isOn: $controller.options.bootOnInstallDevice)\n            }\n            \n            if showRecoveryModeOption {\n                Toggle(\"Boot in recovery mode\", isOn: $controller.options.bootInRecoveryMode)\n                    .disabled(controller.options.bootInDFUMode)\n            }\n\n            if showDFUOption {\n                Toggle(\"Boot in DFU mode\", isOn: $controller.options.bootInDFUMode)\n                    .disabled(controller.options.bootInRecoveryMode)\n            }\n\n            Toggle(\"Capture system keyboard shortcuts\", isOn: $controller.virtualMachineModel.configuration.captureSystemKeys)\n\n            Button(\"Virtual Machine Settings…\") {\n                isShowingVMSettings.toggle()\n            }\n            .frame(maxWidth: .infinity, alignment: .trailing)\n        }\n        .sheet(isPresented: $isShowingVMSettings) {\n            VMConfigurationSheet(\n                configuration: $controller.virtualMachineModel.configuration\n            )\n            .environmentObject(VMConfigurationViewModel(vm, resolvedRestoreImage: resolvedRestoreImage))\n        }\n    }\n\n    private var showInstallDeviceOption: Bool { vm.configuration.systemType == .linux && vm.metadata.installImageURL != nil }\n\n    private var showRecoveryModeOption: Bool { vm.configuration.systemType == .mac }\n\n    private var showDFUOption: Bool { VBMacConfiguration.appBuildAllowsDFUMode && vm.configuration.systemType == .mac }\n\n    private var showSavedStatePicker: Bool { vm.configuration.systemType.supportsStateRestoration }\n}\n\n#if DEBUG\n#Preview {\n    VirtualMachineSessionViewPreview()\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/Session/VirtualMachineSessionUI.swift",
    "content": "import SwiftUI\nimport VirtualCore\nimport Combine\nimport AVFoundation\n\npublic final class VirtualMachineSessionUI: ObservableObject {\n\n    public enum WindowSize: Int {\n        case pointAccurate\n        case pixelAccurate\n        case fitScreen\n    }\n\n    @Published public var lockProportions = false\n\n    let setWindowAspectRatio = PassthroughSubject<CGSize?, Never>()\n    let resizeWindow = PassthroughSubject<WindowSize, Never>()\n    let makeWindowKey = PassthroughSubject<Void, Never>()\n\n    public let controller: VMController\n    public let virtualMachine: VBVirtualMachine\n\n    private lazy var cancellables = Set<AnyCancellable>()\n\n    @MainActor\n    public convenience init(with virtualMachine: VBVirtualMachine, library: VMLibraryController, options: VMSessionOptions?) {\n        self.init(controller: VMController(with: virtualMachine, library: library, options: options))\n    }\n\n    @MainActor\n    public init(controller: VMController) {\n        self.controller = controller\n        self.virtualMachine = controller.virtualMachineModel\n\n        $lockProportions.dropFirst().removeDuplicates().sink { [weak self] newValue in\n            guard let self = self else { return }\n\n            guard let display = self.virtualMachine.configuration.hardware.displayDevices.first else {\n                assertionFailure(\"VM doesn't have a display\")\n                return\n            }\n\n            let ratio = newValue ? CGSize(width: display.width, height: display.height) : nil\n\n            self.setWindowAspectRatio.send(ratio)\n        }\n        .store(in: &cancellables)\n    }\n\n    @MainActor\n    public func update(with newOptions: VMSessionOptions?) {\n        guard let newOptions else { return }\n\n        if newOptions != controller.options {\n            /// If we're trying to launch a virtual machine with custom options and those don't match the current options,\n            /// we must ensure that the VM is not currently running/paused, otherwise changing the options won't have any effect.\n            /// If the VM is currently not in a state where options can be changed, then the user will be prompted to shut it down before doing so.\n            guard controller.canStart else {\n                let alert = NSAlert()\n                alert.messageText = \"Virtual Machine Already in Use\"\n                alert.informativeText = \"\\\"\\(virtualMachine.name)\\\" is already in use. Please shut down the virtual machine before starting it with the new options.\"\n                alert.addButton(withTitle: \"OK\")\n                alert.runModal()\n                return\n            }\n        }\n\n        /// The VM is currently in a state where it's safe to change the options, just update the options for the controller.\n        controller.options = newOptions\n    }\n\n    @MainActor\n    public func bringToFront() {\n        makeWindowKey.send()\n    }\n\n    deinit {\n        VBMemoryLeakDebugAssertions.vb_objectIsBeingReleased(self)\n    }\n}\n\npublic struct VirtualMachineWindowCommands: View {\n    @EnvironmentObject private var manager: VirtualMachineSessionUIManager\n\n    @State private var focusedSessionReference: WeakReference<VirtualMachineSessionUI>?\n    private var focusedSession: VirtualMachineSessionUI? { focusedSessionReference?.object }\n\n    @AppStorage(\"vm.window.proportions.locked\")\n    private var lockProportions = false\n\n    public init() { }\n\n    public var body: some View {\n        Group {\n            Toggle(\"Lock Proportions\", isOn: $lockProportions)\n\n            Button(\"Point Accurate\") {\n                focusedSession?.resizeWindow.send(.pointAccurate)\n            }\n            .keyboardShortcut(\"1\", modifiers: .command)\n\n            Button(\"Pixel Accurate\") {\n                focusedSession?.resizeWindow.send(.pixelAccurate)\n            }\n            .keyboardShortcut(\"2\", modifiers: .command)\n\n            Button(\"Fit Screen\") {\n                focusedSession?.resizeWindow.send(.fitScreen)\n            }\n            .keyboardShortcut(\"3\", modifiers: .command)\n        }\n        .disabled(focusedSession == nil)\n        .onReceive(manager.focusedSessionChanged) { ref in\n            focusedSessionReference = ref\n            ref?.object?.lockProportions = lockProportions\n        }\n        .onChange(of: lockProportions) { [lockProportions] newValue in\n            guard newValue != lockProportions else { return }\n            focusedSession?.lockProportions = newValue\n        }\n\n        Divider()\n    }\n\n}\n"
  },
  {
    "path": "VirtualUI/Source/Session/VirtualMachineSessionUIManager.swift",
    "content": "import SwiftUI\nimport VirtualCore\nimport Combine\nimport OSLog\n\n/// Controls active virtual machine sessions, managing the lifecycle of session windows, controllers, and opening VMs from files or URLs.\n@MainActor\npublic final class VirtualMachineSessionUIManager: ObservableObject {\n    private let logger = Logger(for: VirtualMachineSessionUIManager.self)\n    \n    public let focusedSessionChanged = PassthroughSubject<WeakReference<VirtualMachineSessionUI>?, Never>()\n\n    public static let shared = VirtualMachineSessionUIManager()\n\n    private let openWindow = OpenCocoaWindowAction.default\n\n    private var sessions = [VBVirtualMachine.ID: VirtualMachineSessionUI]()\n\n    private init() { }\n\n    private func createSession(for vm: VBVirtualMachine, library: VMLibraryController, options: VMSessionOptions?) -> VirtualMachineSessionUI {\n        let ui = VirtualMachineSessionUI(with: vm, library: library, options: options)\n\n        sessions[vm.id] = ui\n\n        return ui\n    }\n\n    public func session(for vm: VBVirtualMachine) -> VirtualMachineSessionUI? { sessions[vm.id] }\n\n    public func launch(_ vm: VBVirtualMachine, library: VMLibraryController, options: VMSessionOptions?) {\n        guard !vm.needsInstall else {\n            recoverInstallation(for: vm, library: library)\n            return\n        }\n\n        if let existingSession = session(for: vm) {\n            existingSession.update(with: options)\n            existingSession.bringToFront()\n        } else {\n            launchNewSession(for: vm, library: library, options: options)\n        }\n    }\n\n    private func launchNewSession(for vm: VBVirtualMachine, library: VMLibraryController, options: VMSessionOptions?) {\n        let vmID = vm.id\n\n        let session = createSession(for: vm, library: library, options: options)\n\n        openWindow(id: vmID) {\n            VirtualMachineSessionView()\n                .environmentObject(session)\n                .environmentObject(session.controller)\n                .environmentObject(library)\n                .environmentObject(self)\n        } onClose: { [weak self] in\n            guard let self else { return }\n            guard let session = sessions[vmID] else { return }\n\n            VBMemoryLeakDebugAssertions.vb_objectShouldBeReleasedSoon(session)\n            VBMemoryLeakDebugAssertions.vb_objectShouldBeReleasedSoon(session.controller)\n\n            sessions[vmID] = nil\n        }\n    }\n\n    public func recoverInstallation(for vm: VBVirtualMachine, library: VMLibraryController) {\n        let alert = NSAlert()\n        alert.messageText = \"Finish Installation\"\n        alert.informativeText = \"In order to start this virtual machine, its operating system needs to be installed. Would you like to install it now?\"\n        alert.addButton(withTitle: \"Install\")\n        let deleteButton = alert.addButton(withTitle: \"Delete\")\n        deleteButton.hasDestructiveAction = true\n        alert.addButton(withTitle: \"Cancel\")\n\n        let choice = alert.runModal()\n\n        switch choice {\n        case .alertFirstButtonReturn:\n            launchInstallWizard(restoringAt: vm.bundleURL, library: library)\n        case .alertSecondButtonReturn:\n            library.performMoveToTrash(for: vm)\n        default:\n            break\n        }\n    }\n\n    public func launchInstallWizard(restoringAt restoreURL: URL? = nil, library: VMLibraryController) {\n        openWindow(animationBehavior: .documentWindow) {\n            VMInstallationWizard(library: library, restoringAt: restoreURL)\n                .environmentObject(library)\n        }\n    }\n\n    public func launchImportVirtualMachinePanel(library: VMLibraryController) {\n        guard let url = NSOpenPanel.run(accepting: VMImporterRegistry.default.supportedFileTypes, directoryURL: nil, defaultDirectoryKey: \"importVirtualMachine\", prompt: \"Import\") else {\n            return\n        }\n\n        open(fileURL: url, library: library)\n    }\n\n    private func importVirtualMachine(from path: FilePath, using importer: any VMImporter, library: VMLibraryController) async {\n        do {\n            guard await confirmImport(using: importer) else {\n                throw CancellationError()\n            }\n\n            logger.debug(\"Import authorized from \\(importer.appName) - \\(path)\")\n\n            var model = try await importer.importVirtualMachine(from: path, into: library)\n\n            model.metadata.importedFromAppName = importer.appName\n\n            try model.saveMetadata()\n\n            library.reload()\n\n            open(fileURL: model.bundleURL, library: library)\n        } catch is CancellationError {\n            logger.notice(\"Import cancelled\")\n        } catch {\n            NSApp.presentError(error)\n        }\n    }\n\n    private var importTask: Task<Void, Never>?\n\n    private func beginImportVirtualMachine(from path: FilePath, using importer: any VMImporter, library: VMLibraryController) {\n        importTask = Task {\n            await importVirtualMachine(from: path, using: importer, library: library)\n        }\n    }\n\n    func confirmImport(using importer: any VMImporter) async -> Bool {\n        #if DEBUG\n        guard !Self.testImportSkipConfirmation else { return true }\n        #endif\n        \n        return await NSAlert\n            .runConfirmationAlert(\n                title: \"Import From \\(importer.appName)?\",\n                message: \"VirtualBuddy will attempt to import your virtual machine from \\(importer.appName). All data from this virtual machine in \\(importer.appName) will be copied into your VirtualBuddy library. Would you like to proceed?\",\n                continueButtonTitle: \"Import\",\n                cancelButtonTitle: \"Cancel\",\n                continueButtonIsDefault: true\n            )\n    }\n}\n\n// MARK: - File Opening\n\n@MainActor\npublic extension VirtualMachineSessionUIManager {\n    func open(fileURL: URL, library: VMLibraryController) {\n        do {\n            let values = try fileURL.resourceValues(forKeys: [.contentTypeKey])\n\n            switch values.contentType {\n            case .none:\n                return\n            case .virtualBuddyVM:\n                handleOpenVirtualMachineFile(fileURL, library: library, options: nil)\n            case .virtualBuddySavedState:\n                handleOpenSavedStateFile(fileURL, library: library)\n            default:\n                let path = FilePath(fileURL)\n\n                if let importer = VMImporterRegistry.default.importer(for: path) {\n                    beginImportVirtualMachine(from: path, using: importer, library: library)\n                } else {\n                    throw Failure(\"Unsupported file type \\(values.contentType?.identifier ?? \"?\")\")\n                }\n            }\n        } catch {\n            logger.error(\"Error loading virtual machine from file at \\(fileURL.path, privacy: .public): \\(error, privacy: .public)\")\n        }\n    }\n}\n\n@MainActor\nprivate extension VirtualMachineSessionUIManager {\n    func handleOpenVirtualMachineFile(_ url: URL, library: VMLibraryController, options: VMSessionOptions?) {\n        if let loadedVM = library.virtualMachines.first(where: { $0.bundleURL.path == url.path }) {\n            launch(loadedVM, library: library, options: options)\n        } else {\n            do {\n                let vm = try VBVirtualMachine(bundleURL: url)\n\n                launch(vm, library: library, options: options)\n            } catch {\n                let alert = NSAlert(error: error)\n                alert.runModal()\n            }\n        }\n    }\n\n    func handleOpenSavedStateFile(_ url: URL, library: VMLibraryController) {\n        guard #available(macOS 14.0, *) else {\n            let alert = NSAlert()\n            alert.messageText = \"State Restoration Not Supported\"\n            alert.informativeText = \"Virtual machine state restoration requires macOS 14 or later.\"\n            alert.addButton(withTitle: \"OK\")\n            alert.runModal()\n            return\n        }\n\n        do {\n            let model = try library.virtualMachine(forSavedStatePackageURL: url)\n\n            let options = VMSessionOptions(stateRestorationPackageURL: url)\n\n            launch(model, library: library, options: options)\n        } catch {\n            NSAlert(error: error).runModal()\n        }\n    }\n}\n\n#if DEBUG\n// MARK: - Import Testing (Debug)\n@MainActor\nextension VirtualMachineSessionUIManager {\n    static let testImportSkipConfirmation = UserDefaults.standard.bool(forKey: \"VBTestImportSkipConfirmation\")\n    static let testImportVMPath: FilePath? = UserDefaults.standard.string(forKey: \"VBTestImport\").flatMap { FilePath($0) }\n\n    public func testImportVMIfEnabled(library: VMLibraryController) {\n        guard let path = Self.testImportVMPath else { return }\n\n        open(fileURL: path.url, library: library)\n    }\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/Session/VirtualMachineSessionView.swift",
    "content": "//\n//  VirtualMachineSessionView.swift\n//  VirtualBuddy\n//\n//  Created by Guilherme Rambo on 07/04/22.\n//\n\nimport SwiftUI\nimport VirtualCore\nimport Combine\n\npublic struct VirtualMachineSessionView: View {\n    @EnvironmentObject private var library: VMLibraryController\n    @EnvironmentObject private var sessionManager: VirtualMachineSessionUIManager\n    @EnvironmentObject private var controller: VMController\n    @EnvironmentObject private var ui: VirtualMachineSessionUI\n\n    @Environment(\\.cocoaWindow)\n    private var window\n\n    /// ``VirtualMachineSessionView`` should only be initialized by ``VirtualMachineSessionUIManager``.\n    internal init() { }\n\n    private var vbWindow: VBRestorableWindow? {\n        guard !ProcessInfo.isSwiftUIPreview else { return nil }\n        \n        guard let window = window as? VBRestorableWindow else {\n            assertionFailure(\"VM window must be a VBRestorableWindow\")\n            return nil\n        }\n        return window\n    }\n\n    public var body: some View {\n        ZStack {\n            controllerStateView\n        }\n            .edgesIgnoringSafeArea(.all)\n            .frame(minWidth: 400, maxWidth: .infinity, minHeight: 400, maxHeight: .infinity)\n            .background(backgroundView)\n            .environmentObject(controller)\n            .windowTitle(controller.virtualMachineModel.name)\n            .windowStyleMask([.titled, .miniaturizable, .closable, .resizable])\n            .confirmBeforeClosingWindow(callback: confirmBeforeClosing)\n            .onWindowKeyChange { [weak sessionManager, weak ui] isKey in\n                guard let sessionManager, let ui else { return }\n                sessionManager.focusedSessionChanged.send(isKey ? .init(ui) : nil)\n            }\n            .onAppearOnce {\n                guard vbWindow?.hasSavedFrame == false else { return }\n                guard let display = controller.virtualMachineModel.configuration.hardware.displayDevices.first else { return }\n                vbWindow?.resize(to: .fitScreen, for: display)\n            }\n            .onReceive(ui.resizeWindow) { size in\n                guard let display = controller.virtualMachineModel.configuration.hardware.displayDevices.first else {\n                    assertionFailure(\"VM doesn't have a display\")\n                    return\n                }\n\n                vbWindow?.resize(to: size, for: display)\n            }\n            .onReceive(ui.setWindowAspectRatio) { ratio in\n                vbWindow?.applyAspectRatio(ratio)\n            }\n            .onReceive(ui.makeWindowKey) {\n                window?.makeKeyAndOrderFront(nil)\n            }\n            .task {\n                if controller.options.autoBoot {\n                    Task { try? await controller.start() }\n                }\n            }\n            .toolbar {\n                if #available(macOS 14.0, *) {\n                    VirtualMachineControls<VMController>()\n                        .environmentObject(controller)\n                }\n            }\n    }\n    \n    @ViewBuilder\n    private var controllerStateView: some View {\n        switch controller.state {\n        case .idle:\n            startableStateView(with: nil)\n        case .stopped(let error):\n            startableStateView(with: error)\n        case .starting(let message):\n            VStack(spacing: 12) {\n                ProgressView()\n\n                if let message {\n                    Text(message)\n                        .foregroundStyle(.secondary)\n                        .font(.subheadline)\n                        .multilineTextAlignment(.center)\n                        .frame(maxWidth: 400)\n                }\n            }\n        case .running(let vm):\n            vmView(with: vm)\n        case .paused(let vm), .savingState(let vm), .restoringState(let vm, _), .stateSaveCompleted(let vm, _):\n            pausedView(with: vm)\n        }\n    }\n\n    @ViewBuilder\n    private func vmView(with vm: VZVirtualMachine) -> some View {\n        SwiftUIVMView(\n            controllerState: .constant(.running(vm)),\n            captureSystemKeys: controller.virtualMachineModel.configuration.captureSystemKeys,\n            isDFUModeVM: controller.options.bootInDFUMode,\n            vmECID: controller.virtualMachineModel.ECID,\n            automaticallyReconfiguresDisplay: .constant(controller.virtualMachineModel.configuration.hardware.displayDevices.count > 0 ? controller.virtualMachineModel.configuration.hardware.displayDevices[0].automaticallyReconfiguresDisplay : false)\n        )\n    }\n    \n    @ViewBuilder\n    private func pausedView(with vm: VZVirtualMachine) -> some View {\n        ZStack {\n            vmView(with: vm)\n\n            Rectangle()\n                .foregroundStyle(Material.regular)\n\n            ZStack {\n                switch controller.state {\n                case .paused:\n                    circularStartButton\n                case .savingState, .stateSaveCompleted:\n                    VMProgressOverlay(\n                        message: controller.state.isStateSaveCompleted ? \"State Saved!\" : \"Saving Virtual Machine State\",\n                        duration: controller.state.isStateSaveCompleted ? 0 : 14\n                    )\n                case .restoringState:\n                    VMProgressOverlay(message: \"Restoring Virtual Machine State\", duration: 14)\n                default:\n                    EmptyView()\n                }\n            }\n            .animation(.bouncy, value: controller.state)\n        }\n    }\n    \n    private func startableStateView(with error: Error?) -> some View {\n        VStack(spacing: 28) {\n            if let error = error {\n                Text(startupErrorMessage(for: error))\n                    .multilineTextAlignment(.center)\n                    .foregroundStyle(.secondary)\n                    .lineLimit(nil)\n                    .font(.caption)\n            }\n            \n            circularStartButton\n            \n            VMSessionConfigurationView()\n                .environment(\\.backgroundMaterial, Material.thin)\n                .environmentObject(controller)\n                .frame(maxWidth: 400)\n        }\n    }\n\n    private func startupErrorMessage(for error: Error) -> String {\n        if error.isMaximumActiveVirtualMachinesError {\n            return \"\"\"\n            VirtualBuddy can't start this virtual machine because macOS has reached the system limit for active virtual machines. \\\n            This is a system limitation. Shut down another virtual machine before starting this one.\n            \"\"\"\n        }\n\n        return \"The machine has stopped due to an error: \\(error.localizedDescription)\"\n    }\n    \n    @ViewBuilder\n    private var circularStartButton: some View {\n        Button {\n            if controller.canStart {\n                Task { try? await controller.start() }\n            } else if controller.canResume {\n                Task {\n                    try? await controller.resume()\n                }\n            }\n        } label: {\n            Image(systemName: \"play\")\n        }\n        .buttonStyle(VMCircularButtonStyle())\n    }\n\n    @ViewBuilder\n    private var backgroundView: some View {\n        VirtualMachineSessionBackgroundView(\n            content: controller.virtualMachineModel.blurHashBackgroundContent,\n            isRunning: controller.isRunning\n        )\n    }\n\n    private var confirmBeforeClosing: () async -> Bool {\n        { [weak controller] in\n            guard let controller else { return true }\n\n            if controller.isIdle || controller.isStopped { return true }\n\n            let confirmed = await NSAlert.runConfirmationAlert(\n                title: \"Stop Virtual Machine?\",\n                message: \"If you close the window now, the virtual machine will be stopped.\",\n                continueButtonTitle: \"Stop VM\",\n                cancelButtonTitle: \"Cancel\"\n            )\n\n            guard confirmed else { return false }\n\n            try? await controller.forceStop()\n\n            return true\n        }\n    }\n\n}\n\nstruct VirtualMachineSessionBackgroundView: View {\n    var content: BlurHashFullBleedBackground.Content\n    var isRunning: Bool\n\n    var body: some View {\n        ZStack {\n            Color.black\n\n            if !isRunning {\n                switch content {\n                case .blurHash(let token):\n                    BlurHashFullBleedBackground(blurHash: token)\n                        .fullBleedBackgroundBrightness(-0.2)\n                case .customImage(let image):\n                    BlurHashFullBleedBackground(image: image)\n                        .fullBleedBackgroundBrightness(-0.1)\n                        .fullBleedBackgroundSaturation(0.8)\n                }\n\n                Color.black.opacity(0.3)\n            }\n        }\n    }\n}\n\nstruct VMCircularButtonStyle: ButtonStyle {\n    \n    func makeBody(configuration: Configuration) -> some View {\n        mainContent(with: configuration)\n            .contentShape(Circle())\n    }\n    \n    private func mainContent(with configuration: Configuration) -> some View {\n        configuration.label\n            .font(.system(size: 50, weight: .bold, design: .rounded))\n            .foregroundStyle(.primary)\n            .padding(30)\n            .background(Circle().fill(Material.thin))\n            .brightness(configuration.isPressed ? 0.3 : 0)\n            .symbolVariant(.fill)\n    }\n    \n}\n\nextension VMController {\n    var isIdle: Bool {\n        return state == .idle\n    }\n    var isStarting: Bool {\n        guard case .starting = state else { return false }\n        return true\n    }\n    var isRunning: Bool {\n        guard case .running = state else { return false }\n        return true\n    }\n    var isStopped: Bool {\n        guard case .stopped = state else { return false }\n        return true\n    }\n}\n\nextension VBVirtualMachine {\n    var blurHashBackgroundContent: BlurHashFullBleedBackground.Content {\n        if let thumbnail {\n            .customImage(thumbnail)\n        } else {\n            .blurHash(metadata.backgroundHash)\n        }\n    }\n}\n\nprivate extension Error {\n    var isMaximumActiveVirtualMachinesError: Bool {\n        let nsError = self as NSError\n\n        guard nsError.domain == \"VZErrorDomain\" else { return false }\n\n        if nsError.code == 6 { return true }\n\n        if let reason = nsError.localizedFailureReason,\n           reason.localizedCaseInsensitiveContains(\"maximum supported number of active virtual machines\") {\n            return true\n        }\n\n        return nsError.localizedDescription.localizedCaseInsensitiveContains(\"maximum supported number of active virtual machines\")\n    }\n}\n\n#if DEBUG\nstruct VirtualMachineSessionViewPreview: View {\n    var body: some View {\n        VirtualMachineSessionView()\n            .frame(minWidth: 800, maxWidth: .infinity, minHeight: 500, maxHeight: .infinity)\n            .environmentObject(VMLibraryController.preview)\n            .environmentObject(VMController.preview)\n            .environmentObject(VirtualMachineSessionUI.preview)\n            .environmentObject(VirtualMachineSessionUIManager.shared)\n    }\n}\n\n#Preview {\n    VirtualMachineSessionViewPreview()\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/Settings/AutomationSettingsView.swift",
    "content": "//\n//  AutomationSettingsView.swift\n//  VirtualBuddy\n//\n//  Created by Guilherme Rambo on 20/06/25.\n//\n\nimport SwiftUI\nimport VirtualCore\nimport BuddyKit\nimport DeepLinkSecurity\n\nstruct AutomationSettingsView: View {\n    @EnvironmentObject private var sentinel: DeepLinkSentinel\n\n    private var store: DeepLinkManagementStore { sentinel.managementStore }\n\n    @State private var descriptors = [DeepLinkClientDescriptor]()\n\n    var body: some View {\n        Form {\n            Section {\n                if descriptors.isEmpty {\n                    Text(\"Apps that try to control VirtualBuddy will show up here.\")\n                        .foregroundStyle(.tertiary)\n                } else {\n                    ForEach(descriptors) { descriptor in\n                        Toggle(isOn: toggleBinding(for: descriptor)) {\n                            Label {\n                                Text(descriptor.displayName)\n                            } icon: {\n                                Image(nsImage: descriptor.icon.image)\n                                    .resizable()\n                                    .frame(width: 32, height: 32)\n                                    .aspectRatio(contentMode: .fit)\n                            }\n                        }\n                    }\n                }\n            } header: {\n                Text(\"Allow Apps to Control VirtualBuddy\")\n            } footer: {\n                if !descriptors.isEmpty {\n                    SettingsFooter {\n                        Text(\"\"\"\n                        These apps have previously tried to open a deep link in VirtualBuddy.\n                        \n                        When an app tries to open a deep link in VirtualBuddy for the first time, you'll be asked to grant permission. \\\n                        Once you've allowed it, the app can open deep links without asking again.\n                        \"\"\")\n                    }\n                }\n            }\n        }\n        .task {\n            for await descriptors in store.clientDescriptors() {\n                self.descriptors = descriptors\n            }\n        }\n        .navigationTitle(Text(\"Automation\"))\n    }\n\n    private func toggleBinding(for descriptor: DeepLinkClientDescriptor) -> Binding<Bool> {\n        .init {\n            descriptor.authorization == .authorized\n        } set: { granted in\n            Task { @MainActor in\n                do {\n                    try await sentinel.setAuthorization(granted ? .authorized : .denied, for: descriptor)\n                } catch {\n                    let alert = NSAlert(error: error)\n                    alert.runModal()\n                }\n            }\n        }\n    }\n}\n\n// MARK: - Preview\n\n#if DEBUG\nfinal class PreviewDeepLinkAuthUI: DeepLinkAuthUI {\n    func presentDeepLinkAuth(for request: OpenDeepLinkRequest) async throws -> DeepLinkClientAuthorization {\n        return .authorized\n    }\n}\n\nextension DeepLinkSentinel {\n    static let preview: DeepLinkSentinel = {\n        let s = DeepLinkSentinel(\n            authUI: PreviewDeepLinkAuthUI(),\n            authStore: MemoryDeepLinkAuthStore(),\n            managementStore: UserDefaultsDeepLinkManagementStore(namespace: \"preview\", inMemory: true)\n        )\n\n        Task {\n            do {\n                guard let enumerator = FileManager.default.enumerator(at: URL(fileURLWithPath: \"/System/Applications\"), includingPropertiesForKeys: [.contentTypeKey], options: [.skipsHiddenFiles, .skipsSubdirectoryDescendants, .skipsPackageDescendants]) else {\n                    throw Failure(\"Can't enumerate /System/Applications\")\n                }\n\n                while let url = enumerator.nextObject() as? URL {\n                    guard (try? url.resourceValues(forKeys: [.contentTypeKey]))?.contentType?.conforms(to: .application) == true else { continue }\n\n                    let auth: DeepLinkClientAuthorization = Int.random(in: 0...1024) % 2 == 0 ? .authorized : .denied\n\n                    guard let client = try? DeepLinkClient(url: url) else { continue }\n                    try? await s.authStore.setAuthorization(auth, for: client)\n                    let descriptor = DeepLinkClientDescriptor(client: client, authorization: auth)\n                    try? await s.managementStore.insert(descriptor)\n                }\n\n            } catch {\n                print(\"Error populating preview sentinel: \\(error)\")\n            }\n        }\n\n        return s\n    }()\n}\n\n#Preview(\"Automation Settings\") {\n    SettingsScreen.preview(.automation)\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/Settings/Components/BackwardsCompatibility.swift",
    "content": "import SwiftUI\n\nextension View {\n    @ViewBuilder\n    func toolbarRemovingSidebarToggle() -> some View {\n        if #available(macOS 14.0, *) {\n            toolbar(removing: .sidebarToggle)\n        } else {\n            self\n        }\n    }\n\n    @ViewBuilder\n    func sidebarAdaptableTabViewStyle() -> some View {\n        if #available(macOS 15.0, *) {\n            tabViewStyle(.sidebarAdaptable)\n        } else {\n            self\n        }\n    }\n}\n"
  },
  {
    "path": "VirtualUI/Source/Settings/Components/FileSystemPathFormControl.swift",
    "content": "import SwiftUI\nimport BuddyKit\nimport VirtualCore\nimport UniformTypeIdentifiers\n\nstruct FileSystemPathFormControl: View {\n    var url: URL\n    var contentTypes: Set<UTType>\n    var defaultDirectoryKey: String\n    var label: Text = Text(\"Location\")\n    var buttonLabel: Text = Text(\"Choose…\")\n    var setURL: (URL) -> ()\n\n    @State private var isDraggingURL = false\n\n    var body: some View {\n        LabeledContent {\n            HStack(alignment: .center, spacing: 4) {\n                Text(url.path)\n                    .lineLimit(1)\n                    .truncationMode(.middle)\n                    .help(url.path)\n\n                Button {\n                    url.revealInFinder()\n                } label: {\n                    Image(systemName: \"arrow.right\")\n                }\n                .buttonStyle(.link)\n                .foregroundStyle(Color.accentColor)\n                .font(.subheadline.weight(.medium))\n            }\n        } label: {\n            HStack {\n                label\n\n                Spacer()\n\n                Button {\n                    showOpenPanel()\n                } label: {\n                    buttonLabel\n                }\n                .controlSize(.small)\n            }\n        }\n        .labeledContentStyle(.vertical)\n        .dropDestination(for: URL.self) { items, _ in\n            guard items.count == 1 else { return false }\n            guard let type = FilePath(items[0]).contentType else { return false }\n            guard contentTypes.contains(where: { type.conforms(to: $0) }) else { return false }\n\n            setURL(items[0])\n\n            return true\n        } isTargeted: {\n            isDraggingURL = $0\n        }\n        .background {\n            if isDraggingURL {\n                Color.accentColor\n                    .clipShape(RoundedRectangle(cornerRadius: 4))\n                    .padding(-10)\n                    .opacity(0.4)\n            }\n        }\n    }\n\n    private func showOpenPanel() {\n        guard let newURL = NSOpenPanel.run(accepting: [.folder], directoryURL: url, defaultDirectoryKey: \"library\") else {\n            return\n        }\n\n        guard newURL != url else { return }\n\n        setURL(newURL)\n    }\n}\n"
  },
  {
    "path": "VirtualUI/Source/Settings/Components/OpenVirtualBuddySettingsAction.swift",
    "content": "//\n//  OpenVirtualBuddySettingsAction.swift\n//  VirtualBuddy\n//\n//  Created by Guilherme Rambo on 20/06/25.\n//\n\nimport SwiftUI\nimport VirtualCore\nimport DeepLinkSecurity\nimport BuddyKit\n\npublic struct OpenVirtualBuddySettingsAction {\n    public var run: @MainActor () -> ()\n\n    public init(run: @escaping @MainActor () -> () = { preconditionFailure(\"Missing openVirtualBuddySettings in environment.\") }) {\n        self.run = run\n    }\n\n    @MainActor\n    public func callAsFunction() {\n        run()\n    }\n}\n\npublic extension EnvironmentValues {\n    @Entry var openVirtualBuddySettings = OpenVirtualBuddySettingsAction()\n}\n"
  },
  {
    "path": "VirtualUI/Source/Settings/Components/SettingsFooter.swift",
    "content": "import SwiftUI\nimport BuddyKit\n\nstruct SettingsFooter: View {\n    var summaryText: () -> Text\n    var helpText: (() -> Text)? = nil\n\n    @State private var helpExpanded = false\n\n    var body: some View {\n        VStack(alignment: .listRowSeparatorLeading, spacing: 8) {\n            HStack(spacing: 0) {\n                summaryText()\n\n                Spacer()\n\n                if helpText != nil {\n                    Group {\n                        if #available(macOS 14.0, *) {\n                            HelpLink {\n                                helpExpanded.toggle()\n                            }\n                        } else {\n                            Button {\n                                helpExpanded.toggle()\n                            } label: {\n                                Image(systemName: \"questionmark.circle\")\n                            }\n                            .controlSize(.small)\n                            .buttonStyle(.borderless)\n                        }\n                    }\n                    .controlSize(.small)\n                }\n            }\n\n            if let helpText, helpExpanded {\n                helpText()\n            }\n        }\n        .settingsFooterStyle()\n    }\n}\n\nextension View {\n    @ViewBuilder\n    func settingsFooterStyle() -> some View {\n        let padding: Double = if #available(macOS 26, *) {\n            0\n        } else {\n            8\n        }\n        frame(maxWidth: .infinity, alignment: .leading)\n            .foregroundStyle(.secondary)\n            .font(.footnote)\n            .multilineTextAlignment(.leading)\n            .padding(.leading, padding)\n            .textSelection(.enabled)\n    }\n}\n"
  },
  {
    "path": "VirtualUI/Source/Settings/Components/VerticalLabeledContentStyle.swift",
    "content": "import SwiftUI\n\nstruct VerticalLabeledContentStyle: LabeledContentStyle {\n    static let defaultSpacing: Double = 6\n\n    var spacing: Double? = nil\n\n    func makeBody(configuration: Configuration) -> some View {\n        VStack(alignment: .listRowSeparatorLeading, spacing: spacing ?? Self.defaultSpacing) {\n            configuration.label\n\n            configuration.content\n                .foregroundStyle(.secondary)\n                .textSelection(.enabled)\n        }\n    }\n}\n\nextension LabeledContentStyle where Self == VerticalLabeledContentStyle {\n    static var vertical: VerticalLabeledContentStyle { VerticalLabeledContentStyle() }\n    static func vertical(spacing: Double) -> VerticalLabeledContentStyle { VerticalLabeledContentStyle(spacing: spacing) }\n}\n"
  },
  {
    "path": "VirtualUI/Source/Settings/GeneralSettingsView.swift",
    "content": "//\n//  GeneralSettingsView.swift\n//  VirtualBuddy\n//\n//  Created by Guilherme Rambo on 20/06/25.\n//\n\nimport SwiftUI\nimport VirtualCore\nimport BuddyKit\n\nstruct GeneralSettingsView: View {\n    @Binding var settings: VBSettings\n    @Binding var enableAutomaticUpdates: Bool\n    @Binding var alert: AlertContent\n\n    @State private var libraryPathText = \"\"\n\n    private var libraryPath: String { settings.libraryURL.absoluteURL.path(percentEncoded: false) }\n\n    var body: some View {\n        Form {\n            Section {\n                FileSystemPathFormControl(url: settings.libraryURL, contentTypes: [.folder], defaultDirectoryKey: \"library\") { newURL in\n                    setLibraryPath(to: newURL.path)\n                }\n            } header: {\n                Text(\"Storage\")\n            } footer: {\n                Text(\"VirtualBuddy saves your virtual machines and downloaded installer images here.\")\n                    .settingsFooterStyle()\n            }\n\n            Section {\n                Toggle(\"Automatically check for updates\", isOn: $enableAutomaticUpdates)\n\n                betaSection\n            } header: {\n                Text(\"App Updates\")\n            }\n        }\n        .navigationTitle(Text(\"General\"))\n        .task(id: settings.libraryURL.path) {\n            libraryPathText = settings.libraryURL.path\n        }\n    }\n\n    private func setLibraryPath(to newValue: String) {\n        let url = URL(fileURLWithPath: newValue)\n\n        if let errorMessage = url.performWriteTest() {\n            libraryPathText = settings.libraryURL.path\n\n            alert = AlertContent(errorMessage)\n        } else {\n            settings.libraryURL = url\n            libraryPathText = url.path\n\n            alert = .init()\n        }\n    }\n\n    @ViewBuilder\n    private var betaSection: some View {\n        LabeledContent(\"Beta Updates\") {\n            if settings.updateChannel == .beta {\n                Button(\"Disable\") {\n                    confirmDisableBeta()\n                }\n            } else {\n                Button(\"Join Beta\") {\n                    confirmJoinBeta()\n                }\n            }\n        }\n    }\n\n    private func confirmDisableBeta() {\n        if AppUpdateChannel.defaultChannel(for: .current) == .beta {\n            /// If beta is disabled while running a beta release, user needs to reinstall release build manually.\n            confirmDisableBetaNeedsReinstall()\n        } else {\n            /// If beta is disabled while running a non-beta release, no further action is needed.\n            settings.updateChannel = .release\n        }\n    }\n\n    /// Shown when user disables beta while running a beta release, which requires reinstalling a release version\n    /// in order to effectivelly get out of the beta train.\n    private func confirmDisableBetaNeedsReinstall() {\n        let alert = NSAlert()\n        alert.messageText = \"Disable VirtualBuddy Beta\"\n        alert.informativeText = \"In order to go back to the release version of VirtualBuddy, please download the latest release from GitHub and replace the current version you have installed.\"\n        alert.addButton(withTitle: \"Open Website\")\n        alert.addButton(withTitle: \"Cancel\")\n\n        guard alert.runModal() == .alertFirstButtonReturn else {\n            return\n        }\n\n        settings.updateChannel = .release\n\n        guard let url = URL(string: \"https://github.com/insidegui/VirtualBuddy/releases/latest\") else { return }\n\n        NSWorkspace.shared.open(url)\n    }\n\n    private func confirmJoinBeta() {\n        let alert = NSAlert()\n        alert.messageText = \"Join VirtualBuddy Beta\"\n        alert.informativeText = \"\"\"\n        Would like to join the beta and receive pre-release updates for testing?\n        \n        If you decide to stop receiving beta updates in the future, you will have to manually download and install the release version of VirtualBuddy.\n        \"\"\"\n        alert.addButton(withTitle: \"Join Beta\")\n        alert.addButton(withTitle: \"Cancel\")\n\n        guard alert.runModal() == .alertFirstButtonReturn else {\n            return\n        }\n\n        settings.updateChannel = .beta\n    }\n}\n\nextension URL {\n    func performWriteTest() -> String? {\n        if !FileManager.default.fileExists(atPath: path) {\n            return \"The directory \\(lastPathComponent) doesn't exist.\"\n        }\n\n        do {\n            let testFileURL = appendingPathComponent(\".vbtest-\\(UUID().uuidString)\")\n            guard FileManager.default.createFile(atPath: testFileURL.path, contents: nil) else {\n                throw CocoaError(.fileWriteNoPermission)\n            }\n            try FileManager.default.removeItem(at: testFileURL)\n            return nil\n        } catch {\n            return \"VirtualBuddy is unable to write files to the directory \\(lastPathComponent). Please check the permissions for that directory or choose a different one.\"\n        }\n    }\n}\n\nstruct LibraryPathError: LocalizedError {\n    var errorDescription: String?\n\n    init(_ msg: String) { self.errorDescription = msg }\n}\n\n#if DEBUG\n#Preview(\"Library Settings\") {\n    SettingsScreen.preview(.general)\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/Settings/SettingsScreen.swift",
    "content": "//\n//  PreferencesView.swift\n//  VirtualBuddy\n//\n//  Created by Guilherme Rambo on 05/06/22.\n//\n\nimport SwiftUI\nimport VirtualCore\nimport DeepLinkSecurity\nimport BuddyKit\n\npublic enum SettingsTab: Int, Identifiable, CaseIterable {\n    public var id: RawValue { rawValue }\n\n    case general\n    case virtualization\n    case automation\n}\n\nextension SettingsTab {\n    var label: Label<Text, Image> {\n        switch self {\n        case .general: Label(\"General\", systemImage: \"gear\")\n        case .virtualization: Label(\"Virtualization\", systemImage: \"cpu\")\n        case .automation: Label(\"Automation\", systemImage: \"rectangle.grid.1x2\")\n        }\n    }\n}\n\nprivate let kSelectedTabKey = \"SettingsScreen.selectedTab\"\n\npublic struct SettingsScreen: View {\n\n    #if DEBUG\n    private let previewTab: SettingsTab?\n    #endif\n\n    @EnvironmentObject private var container: VBSettingsContainer\n    @Binding private var enableAutomaticUpdates: Bool\n    private var deepLinkSentinel: () -> DeepLinkSentinel\n\n    public init(previewTab: SettingsTab? = nil, enableAutomaticUpdates: Binding<Bool>, deepLinkSentinel: @escaping @autoclosure () -> DeepLinkSentinel) {\n        self._enableAutomaticUpdates = enableAutomaticUpdates\n        self.deepLinkSentinel = deepLinkSentinel\n\n        #if DEBUG\n        self.previewTab = previewTab\n        #endif\n    }\n\n    @State private var alert = AlertContent()\n\n    @AppStorage(kSelectedTabKey)\n    private var selectedTab: SettingsTab = .general\n\n    public static var width: CGFloat { 640 }\n    public static var minHeight: CGFloat { 420 }\n\n    public var body: some View {\n        NavigationSplitView {\n            List(selection: $selectedTab) {\n                ForEach(SettingsTab.allCases) { tab in\n                    NavigationLink(value: tab) {\n                        tab.label\n                    }\n                }\n            }\n            .toolbarRemovingSidebarToggle()\n            .toolbar {\n                // HACK! Don't want sidebar toggle, but want the toolbar visible\n                Button(\"\") { }\n                    .opacity(0)\n                    .accessibilityHidden(true)\n            }\n            .navigationSplitViewColumnWidth(160)\n        } detail: {\n            switch selectedTab {\n            case .general:\n                GeneralSettingsView(\n                    settings: $container.settings,\n                    enableAutomaticUpdates: $enableAutomaticUpdates,\n                    alert: $alert\n                )\n            case .virtualization:\n                VirtualizationSettingsView(\n                    settings: $container.settings\n                )\n            case .automation:\n                AutomationSettingsView()\n                    .environmentObject(deepLinkSentinel())\n            }\n        }\n        .formStyle(.grouped)\n        .frame(minWidth: Self.width, maxWidth: Self.width, minHeight: Self.minHeight, maxHeight: .infinity)\n        .alert($alert)\n        .task {\n            #if DEBUG\n            guard let previewTab else { return }\n            self.selectedTab = previewTab\n            #endif\n        }\n    }\n}\n\n// MARK: - Previews\n\n#if DEBUG\nprivate extension VBSettingsContainer {\n    static let preview: VBSettingsContainer = {\n        VBSettingsContainer(with: UserDefaults())\n    }()\n}\n\nextension SettingsScreen {\n    @ViewBuilder\n    static func preview(_ tab: SettingsTab? = nil) -> some View {\n        SettingsScreen(previewTab: tab, enableAutomaticUpdates: .constant(true), deepLinkSentinel: .preview)\n            .environmentObject(VBSettingsContainer.preview)\n    }\n}\n\n#Preview(\"Settings\") {\n    SettingsScreen.preview()\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/Settings/VirtualizationSettingsView.swift",
    "content": "//\n//  VirtualizationSettingsView.swift\n//  VirtualBuddy\n//\n//  Created by Guilherme Rambo on 20/06/25.\n//\n\nimport SwiftUI\nimport VirtualCore\nimport BuddyKit\n\nstruct VirtualizationSettingsView: View {\n    @Binding var settings: VBSettings\n\n    #if DEBUG\n    private var _forceShowBootImageFormatSettings: Bool { false }\n    #endif\n\n    private var showBootImageFormatSection: Bool {\n        #if DEBUG\n        guard !_forceShowBootImageFormatSettings else { return true }\n        #endif\n\n        return VBManagedDiskImage.Format.asif.isSupported\n    }\n\n    var body: some View {\n        Form {\n            Section {\n                Toggle(\"Enable Apple Signing Status Check\", isOn: $settings.enableTSSCheck)\n            } header: {\n                Text(\"Signing Check\")\n            } footer: {\n                SettingsFooter {\n                    Text(\"Whether VirtualBuddy should verify macOS build signatures before downloading.\")\n                } helpText: {\n                    Text(\"\"\"\n                    With this enabled, VirtualBuddy will check if the selected macOS build is being signed by Apple \\\n                    before attempting to download it.\n                    \n                    Unsigned builds can not be installed in virtual machines, so this saves time and bandwidth, \\\n                    allowing you to choose another version before waiting for the entire download and install attempt.\n                    \"\"\")\n                }\n            }\n\n            if showBootImageFormatSection {\n                Section {\n                    Picker(\"Boot Image Format\", selection: $settings.bootDiskImagesUseASIF) {\n                        Text(\"Most Efficient\")\n                            .tag(true)\n                        Text(\"Most Compatible\")\n                            .tag(false)\n                    }\n                } header: {\n                    Text(\"Disk Images\")\n                } footer: {\n                    SettingsFooter {\n                        Text(\"Select the disk image format for new virtual machines\")\n                    } helpText: {\n                        Text(\"\"\"\n                        - **Most Efficient:** Uses the ASIF format. Requires macOS 26 or later on the host.\n                        \n                        - **Most Compatible:** Uses a raw image format, supported by all macOS versions.\n                        \n                        You should only change this setting if you plan on using the same virtual machines in hosts \\\n                        that are on macOS 15 or earlier.\n                        \"\"\")\n                    }\n                }\n            }\n        }\n        .navigationTitle(Text(\"Virtualization\"))\n    }\n}\n\n#if DEBUG\n#Preview(\"Virtualization Settings\") {\n    SettingsScreen.preview(.virtualization)\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/VM Configuration/Components/ConfigurationSection.swift",
    "content": "//\n//  ConfigurationSection.swift\n//  VirtualUI\n//\n//  Created by Guilherme Rambo on 18/07/22.\n//\n\nimport SwiftUI\nimport VirtualCore\n\nstruct ConfigurationSection<Header: View, Content: View>: View {\n\n    @Binding private var collapsedStateBinding: Bool\n    @State private var isCollapsed: Bool\n\n    var content: () -> Content\n    var header: () -> Header\n    var collapsingDisabled: Bool\n\n    init(_ collapsed: Binding<Bool>? = nil, collapsingDisabled: Bool = false, @ViewBuilder _ content: @escaping () -> Content, @ViewBuilder header: @escaping () -> Header) {\n        if collapsingDisabled {\n            self._collapsedStateBinding = .constant(false)\n            self._isCollapsed = .init(wrappedValue: false)\n        } else {\n            self._collapsedStateBinding = collapsed ?? .constant(true)\n            self._isCollapsed = .init(wrappedValue: collapsed?.wrappedValue ?? true)\n        }\n        self.collapsingDisabled = collapsingDisabled\n        self.header = header\n        self.content = content\n    }\n\n    var body: some View {\n        VStack(alignment: .leading, spacing: 0) {\n            styledHeader\n\n            if !isCollapsed {\n                VStack(alignment: .leading, spacing: 6) {\n                    content()\n                }\n                    .padding()\n                    .transition(.opacity)\n            }\n        }\n        .controlGroup()\n        .onChange(of: collapsedStateBinding) { newValue in\n            guard newValue != isCollapsed else { return }\n            isCollapsed = newValue\n        }\n        .onChange(of: isCollapsed) { newValue in\n            guard collapsedStateBinding != newValue else { return }\n            collapsedStateBinding = newValue\n        }\n    }\n\n    @ViewBuilder\n    private var styledHeader: some View {\n        HStack {\n            header()\n                .frame(maxWidth: .infinity, alignment: .leading)\n\n            Spacer()\n\n            if !collapsingDisabled {\n                Image(systemName: \"chevron.down\")\n                    .rotationEffect(.degrees(isCollapsed ? -90 : 0))\n            }\n        }\n            .font(.system(size: 14, weight: .medium, design: .rounded))\n            .padding(.horizontal)\n            .padding(.vertical, 12)\n            .frame(maxWidth: .infinity, alignment: .leading)\n            .background {\n                if isCollapsed {\n                    ContainerRelativeShape().inset(by: 2).foregroundStyle(Material.ultraThin)\n                }\n            }\n            .overlay(alignment: .bottom) {\n                Rectangle()\n                    .frame(maxWidth: .infinity, maxHeight: 0.5)\n                    .foregroundColor(.white.opacity(isCollapsed ? 0 : 0.1))\n                    .padding(.horizontal, 1)\n                    .blendMode(.plusLighter)\n            }\n            .contentShape(ContainerRelativeShape())\n            .onTapGesture {\n                guard !collapsingDisabled else { return }\n                \n                withAnimation(.default) {\n                    isCollapsed.toggle()\n                }\n            }\n    }\n\n}\n"
  },
  {
    "path": "VirtualUI/Source/VM Configuration/Components/GroupedList.swift",
    "content": "//\n//  GroupedList.swift\n//  VirtualUI\n//\n//  Created by Guilherme Rambo on 19/07/22.\n//\n\nimport SwiftUI\n\nstruct GroupedList<Content: View, HeaderAccessory: View, FooterAccessory: View, EmptyOverlay: View, AddButton: View, RemoveButton: View>: View {\n    \n    var content: () -> Content\n    var headerAccessory: () -> HeaderAccessory\n    var footerAccessory: () -> FooterAccessory\n    var emptyOverlay: () -> EmptyOverlay\n    var addButton: (Label<Text, Image>) -> AddButton?\n    var removeButton: (Label<Text, Image>) -> RemoveButton?\n    \n    init(@ViewBuilder _ content: @escaping () -> Content,\n         headerAccessory: @escaping () -> HeaderAccessory,\n         footerAccessory: @escaping () -> FooterAccessory,\n         emptyOverlay: @escaping () -> EmptyOverlay,\n         addButton: @escaping (Label<Text, Image>) -> AddButton? = { _ in nil },\n         removeButton: @escaping (Label<Text, Image>) -> RemoveButton? = { _ in nil })\n    {\n        self.content = content\n        self.headerAccessory = headerAccessory\n        self.footerAccessory = footerAccessory\n        self.emptyOverlay = emptyOverlay\n        self.addButton = addButton\n        self.removeButton = removeButton\n    }\n    \n    var body: some View {\n        VStack(alignment: .leading, spacing: 8) {\n            header\n\n            content()\n            .listStyle(.plain)\n            .frame(minHeight: 140)\n            .overlay { emptyOverlayContents }\n            .safeAreaInset(edge: .bottom, alignment: .leading, spacing: 0, content: {\n                listButtons\n            })\n            .materialBackground(.contentBackground, blendMode: .withinWindow, state: .active, in: listShape)\n            .controlGroup(level: .secondary)\n        }\n    }\n    \n    @ViewBuilder\n    private var emptyOverlayContents: some View {\n        VStack {\n            emptyOverlay()\n        }\n        .buttonStyle(.link)\n        .frame(maxWidth: .infinity)\n        .controlSize(.small)\n    }\n\n    private var listRadius: CGFloat { 8 }\n\n    private var listShape: some InsettableShape {\n        RoundedRectangle(cornerRadius: listRadius, style: .continuous)\n    }\n    \n    @State private var showTip = false\n    \n    @ViewBuilder\n    private var header: some View {\n        headerAccessory()\n            .padding(.horizontal, 2)\n    }\n    \n    private let addLabel = Label(\"Add\", systemImage: \"plus\")\n    private let removeLabel = Label(\"Remove\", systemImage: \"minus\")\n    \n    @ViewBuilder\n    private var listButtons: some View {\n        if addButton(addLabel) != nil || removeButton(removeLabel) != nil || FooterAccessory.self != EmptyView.self {\n            HStack {\n                Group {\n                    if let addButton = addButton(addLabel) {\n                        addButton\n                    }\n                    \n                    if let removeButton = removeButton(removeLabel) {\n                        removeButton\n                    }\n                }\n                .labelStyle(.iconOnly)\n                .applyGroupedListCommandButtonStyle()\n                \n                footerAccessory()\n            }\n            .padding(8)\n            .frame(maxWidth: .infinity, alignment: .leading)\n            .background(Material.thick, in: Rectangle())\n            .overlay(alignment: .top) {\n                Rectangle()\n                    .frame(height: 0.5)\n                    .foregroundColor(.black.opacity(0.5))\n            }\n        }\n    }\n    \n}\n\nextension GroupedList where HeaderAccessory == EmptyView, FooterAccessory == EmptyView, EmptyOverlay == EmptyView, AddButton == EmptyView, RemoveButton == EmptyView {\n    init(@ViewBuilder _ content: @escaping () -> Content)\n    {\n        self.content = content\n        self.headerAccessory = { EmptyView() }\n        self.footerAccessory = { EmptyView() }\n        self.emptyOverlay = { EmptyView() }\n        self.addButton = { _ in nil }\n        self.removeButton = { _ in nil }\n    }\n}\n\nextension GroupedList where FooterAccessory == EmptyView, EmptyOverlay == EmptyView, AddButton == EmptyView, RemoveButton == EmptyView {\n    init(@ViewBuilder _ content: @escaping () -> Content, headerAccessory: @escaping () -> HeaderAccessory)\n    {\n        self.content = content\n        self.headerAccessory = headerAccessory\n        self.footerAccessory = { EmptyView() }\n        self.emptyOverlay = { EmptyView() }\n        self.addButton = { _ in nil }\n        self.removeButton = { _ in nil }\n    }\n}\n\nextension GroupedList where HeaderAccessory == EmptyView, FooterAccessory == EmptyView {\n    init(@ViewBuilder _ content: @escaping () -> Content,\n         emptyOverlay: @escaping () -> EmptyOverlay,\n         addButton: @escaping (Label<Text, Image>) -> AddButton? = { _ in nil },\n         removeButton: @escaping (Label<Text, Image>) -> RemoveButton? = { _ in nil })\n    {\n        self.content = content\n        self.headerAccessory = { EmptyView() }\n        self.footerAccessory = { EmptyView() }\n        self.emptyOverlay = emptyOverlay\n        self.addButton = addButton\n        self.removeButton = removeButton\n    }\n}\n\nfileprivate extension View {\n    func applyGroupedListCommandButtonStyle() -> some View {\n        // the .accessoryBarAction looks nicer, but only available on macOS Sonoma\n        // on older version of macOS, use the default style (equivalent to .bordered)\n        if #available(macOS 14.0, *) {\n            return self.buttonStyle(.accessoryBarAction)\n        } else {\n            return self\n        }\n    }\n}\n"
  },
  {
    "path": "VirtualUI/Source/VM Configuration/Sections/DisplayConfigurationView.swift",
    "content": "//\n//  DisplayConfigurationView.swift\n//  VirtualUI\n//\n//  Created by Guilherme Rambo on 18/07/22.\n//\n\nimport SwiftUI\nimport VirtualCore\n\nstruct DisplayConfigurationView: View {\n    \n    @Binding var device: VBDisplayDevice\n    @Binding var selectedPreset: VBDisplayPreset?\n    var canChangePPI: Bool\n\n    @Environment(\\.resolvedRestoreImage)\n    private var resolvedRestoreImage\n\n    private var displayResizeStatus: ResolvedFeatureStatus? {\n        resolvedRestoreImage?.feature(id: CatalogFeatureID.displayResize)?.status\n    }\n\n    private var displayResizeUnsupported: Bool { displayResizeStatus?.isUnsupported == true }\n    \n    var body: some View {\n        if let warning = selectedPreset?.warning {\n            Text(warning)\n                .foregroundColor(.yellow)\n                .padding(.bottom, 8)\n        }\n        \n        NumericPropertyControl(\n            value: $device.width,\n            range: VBDisplayDevice.displayWidthRange,\n            label: \"Width (Pixels)\",\n            formatter: NumberFormatter.numericPropertyControlDefault,\n            spacing: VMConfigurationView.labelSpacing\n        )\n\n        NumericPropertyControl(\n            value: $device.height,\n            range: VBDisplayDevice.displayHeightRange,\n            label: \"Height (Pixels)\",\n            formatter: NumberFormatter.numericPropertyControlDefault,\n            spacing: VMConfigurationView.labelSpacing\n        )\n\n        if canChangePPI {\n            NumericPropertyControl(\n                value: $device.pixelsPerInch,\n                range: VBDisplayDevice.displayPPIRange,\n                label: \"Pixels Per Inch\",\n                formatter: NumberFormatter.numericPropertyControlDefault,\n                spacing: VMConfigurationView.labelSpacing\n            )\n        }\n        \n        if VBDisplayDevice.automaticallyReconfiguresDisplaySupportedByHost {\n            Group {\n                if displayResizeUnsupported {\n                    let helpMessage = displayResizeStatus?.supportMessage ?? \"Not supported.\"\n                    Toggle(\"Automatically Configure Display\", isOn: $device.automaticallyReconfiguresDisplay)\n                        .disabled(true)\n                        .help(helpMessage)\n                } else {\n                    Toggle(\"Automatically Configure Display\", isOn: $device.automaticallyReconfiguresDisplay)\n                }\n            }\n            .onChange(of: displayResizeUnsupported) { isUnsupported in\n                if isUnsupported {\n                    device.automaticallyReconfiguresDisplay = false\n                }\n            }\n            .onAppear {\n                if displayResizeUnsupported {\n                    device.automaticallyReconfiguresDisplay = false\n                }\n            }\n        }\n    }\n    \n    @ViewBuilder\n    var presetPicker: some View {\n        DisplayPresetPicker(display: $device, selection: $selectedPreset)\n    }\n    \n}\n\nprivate struct DisplayPresetPicker: View {\n\n    @Binding var display: VBDisplayDevice\n    @Binding var selection: VBDisplayPreset?\n    @State private var presets = [VBDisplayPreset]()\n\n    @Environment(\\.configurationGuestType)\n    private var guestType: VBGuestType\n\n    var body: some View {\n        Menu {\n            menuItems\n        } label: {\n            Image(systemName: \"wand.and.stars\")\n                .foregroundColor(.accentColor)\n        }\n        .menuStyle(.borderlessButton)\n        .help(\"Display Suggestions\")\n        .onAppear {\n            presets = VBDisplayPreset.availablePresets(for: guestType)\n        }\n    }\n    \n    @ViewBuilder\n    var menuItems: some View {\n        ForEach(presets) { preset in\n            Button(preset.name) {\n                selection = preset\n                display = preset.device\n            }\n        }\n    }\n    \n}\n\n#if DEBUG\nstruct DisplayConfigurationView_Previews: PreviewProvider {\n    static var previews: some View {\n        _ConfigurationSectionPreview {\n            DisplayConfigurationView(device: $0.hardware.displayDevices[0], selectedPreset: .constant(nil), canChangePPI: true)\n        }\n    }\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/VM Configuration/Sections/GuestAppConfigurationView.swift",
    "content": "//\n//  GuestAppConfigurationView.swift\n//  VirtualBuddy\n//\n//  Created by Guilherme Rambo on 19/06/25.\n//\n\nimport SwiftUI\nimport VirtualCore\n\nstruct GuestAppConfigurationView: View {\n    @Binding var configuration: VBMacConfiguration\n\n    @Environment(\\.resolvedRestoreImage)\n    private var resolvedRestoreImage\n\n    private var guestAppStatus: ResolvedFeatureStatus? {\n        resolvedRestoreImage?.feature(id: CatalogFeatureID.guestApp)?.status\n    }\n\n    private var guestAppUnsupported: Bool { guestAppStatus?.isUnsupported == true }\n    private var guestAppHelp: String? {\n        guestAppUnsupported ? (guestAppStatus?.supportMessage ?? \"Not supported.\") : nil\n    }\n\n    var body: some View {\n        VStack(alignment: .leading, spacing: 16) {\n            Group {\n                if let guestAppHelp {\n                    Toggle(\"Enable VirtualBuddy Guest App\", isOn: $configuration.guestAdditionsEnabled)\n                        .disabled(true)\n                        .help(guestAppHelp)\n                } else {\n                    Toggle(\"Enable VirtualBuddy Guest App\", isOn: $configuration.guestAdditionsEnabled)\n                }\n            }\n            .onChange(of: guestAppUnsupported) { isUnsupported in\n                if isUnsupported {\n                    configuration.guestAdditionsEnabled = false\n                }\n            }\n            .onAppear {\n                if guestAppUnsupported {\n                    configuration.guestAdditionsEnabled = false\n                }\n            }\n\n            Text(\"\"\"\n            The guest app mounts shared directories and shares the clipboard between your Mac and virtual machines.\n\n            To install the app in your virtual machine, look for a disk image named “Guest” in the Finder sidebar. \\\n            Double-click the VirtualBuddyGuest app icon to install the app. \n            \"\"\")\n                .font(.caption)\n                .foregroundStyle(.secondary)\n                .textSelection(.enabled)\n        }\n    }\n}\n\n#if DEBUG\n#Preview {\n    _ConfigurationSectionPreview { GuestAppConfigurationView(configuration: $0) }\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/VM Configuration/Sections/HardwareConfigurationView.swift",
    "content": "//\n//  HardwareConfigurationView.swift\n//  VirtualUI\n//\n//  Created by Guilherme Rambo on 18/07/22.\n//\n\nimport SwiftUI\nimport VirtualCore\n\nstruct HardwareConfigurationView: View {\n    \n    @Binding var device: VBMacDevice\n    \n    var body: some View {\n        NumericPropertyControl(\n            value: $device.cpuCount,\n            range: VBMacDevice.virtualCPUCountRange,\n            label: \"Virtual CPUs\",\n            formatter: NumberFormatter.numericPropertyControlDefault,\n            spacing: VMConfigurationView.labelSpacing\n        )\n\n        NumericPropertyControl(\n            value: $device.memorySize.gbValue,\n            range: VBMacDevice.memorySizeRangeInGigabytes,\n            label: \"Memory (GB)\",\n            formatter: NumberFormatter.numericPropertyControlDefault,\n            spacing: VMConfigurationView.labelSpacing\n        )\n    }\n    \n}\n\n#if DEBUG\nstruct HardwareConfigurationView_Previews: PreviewProvider {\n    static var previews: some View {\n        _ConfigurationSectionPreview { HardwareConfigurationView(device: $0.hardware) }\n    }\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/VM Configuration/Sections/KeyboardDeviceConfigurationView.swift",
    "content": "//\n//  KeyboardDeviceConfigurationView.swift\n//  VirtualUI\n//\n//  Created by Guilherme Rambo on 02/10/23.\n//\n\nimport SwiftUI\nimport VirtualCore\n\nstruct KeyboardDeviceConfigurationView: View {\n    @Binding var hardware: VBMacDevice\n\n    @Environment(\\.resolvedRestoreImage)\n    private var resolvedRestoreImage\n\n    private var macKeyboardFeature: ResolvedVirtualizationFeature? {\n        resolvedRestoreImage?.feature(id: CatalogFeatureID.macKeyboard)\n    }\n\n    private var macKeyboardStatus: ResolvedFeatureStatus? { macKeyboardFeature?.status }\n\n    private var macKeyboardUnsupported: Bool { macKeyboardStatus?.isUnsupported == true }\n\n    private var availableKinds: [VBKeyboardDevice.Kind] {\n        VBKeyboardDevice.Kind.allCases.filter { kind in\n            kind != .mac || !macKeyboardUnsupported\n        }\n    }\n\n    var body: some View {\n        PropertyControl(\"Device Type\", spacing: 8) {\n            VStack(alignment: .leading) {\n                Picker(\"Device Type\", selection: $hardware.keyboardDevice.kind) {\n                    ForEach(availableKinds) { kind in\n                        Text(kind.name)\n                            .tag(kind)\n                    }\n                }\n                .onChange(of: macKeyboardUnsupported) { isUnsupported in\n                    if isUnsupported, hardware.keyboardDevice.kind == .mac {\n                        hardware.keyboardDevice.kind = .generic\n                    }\n                }\n                .onAppear {\n                    if macKeyboardUnsupported, hardware.keyboardDevice.kind == .mac {\n                        hardware.keyboardDevice.kind = .generic\n                    }\n                }\n            }\n        }\n\n    }\n}\n\n#if DEBUG\nstruct KeyboardDeviceConfigurationView_Previews: PreviewProvider {\n    static var previews: some View {\n        _ConfigurationSectionPreview { KeyboardDeviceConfigurationView(hardware: $0.hardware) }\n    }\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/VM Configuration/Sections/NetworkConfigurationView.swift",
    "content": "//\n//  NetworkConfigurationView.swift\n//  VirtualUI\n//\n//  Created by Guilherme Rambo on 18/07/22.\n//\n\nimport SwiftUI\nimport VirtualCore\nimport BuddyFoundation\n\nenum NetworkDeviceSelection: Identifiable, Hashable {\n    var id: String {\n        switch self {\n        case .disabled: \"DISABLED\"\n        case .NAT: \"NAT\"\n        case .bridge(let interfaceID): \"BRIDGE_\\(interfaceID)\"\n        }\n    }\n\n    case disabled\n    case NAT\n    case bridge(_ interface: VBNetworkDeviceInterface.ID)\n}\n\nstruct NetworkConfigurationView: View {\n    \n    @Binding var hardware: VBMacDevice\n\n    var body: some View {\n        VStack(alignment: .leading, spacing: 16) {\n            NetworkDevicePicker(hardware: $hardware)\n\n            if hardware.networkDevices.isEmpty {\n                Text(\"This virtual machine will have no internet or local network access.\")\n                    .foregroundColor(.secondary)\n            } else {\n                macAddressField\n            }\n        }\n    }\n    \n    @ViewBuilder\n    private var macAddressField: some View {\n        PropertyControl(\"MAC Address\") {\n            EphemeralTextField($hardware.networkMACAddress, alignment: .leading) { addr in\n                Text(addr)\n                    .textCase(.uppercase)\n            } editableContent: { value in\n                TextField(\"\", text: .init(get: {\n                    value.wrappedValue.uppercased()\n                }, set: { value.wrappedValue = $0.uppercased() }))\n            } validate: { value in\n                return VBNetworkDevice.validateMAC(value)\n            }\n        }\n    }\n}\n\nextension VBMacDevice {\n    var networkDeviceSelection: NetworkDeviceSelection {\n        get {\n            if let device = networkDevices.first {\n                switch device.kind {\n                case .NAT: .NAT\n                case .bridge: .bridge(device.id)\n                }\n            } else {\n                .disabled\n            }\n        }\n        set {\n            let restoreMACAddress = networkMACAddress\n\n            switch newValue {\n            case .disabled:\n                networkDevices.removeAll()\n            case .NAT:\n                networkDevices = [.default.withMACAddress(restoreMACAddress)]\n            case .bridge(let id):\n                networkDevices = [.init(id: id, name: id, kind: .bridge).withMACAddress(restoreMACAddress)]\n            }\n        }\n    }\n\n    var networkMACAddress: String {\n        get {\n            switch networkDeviceSelection {\n            case .disabled: \"\"\n            case .NAT, .bridge: networkDevices.first?.macAddress ?? \"\"\n            }\n        }\n        set {\n            switch networkDeviceSelection {\n            case .disabled: break\n            case .NAT, .bridge:\n                guard !networkDevices.isEmpty else { return }\n                networkDevices[0].macAddress = newValue\n            }\n        }\n    }\n}\n\nextension VBNetworkDevice {\n    func withMACAddress(_ address: String) -> VBNetworkDevice {\n        guard !address.isEmpty else { return self }\n        var mself = self\n        mself.macAddress = address\n        return mself\n    }\n}\n\nstruct NetworkDevicePicker: View {\n    @Binding var hardware: VBMacDevice\n\n    @State private var selectedOption: VBNetworkDeviceInterface?\n\n    @State private var interfaces: [VBNetworkDeviceInterface] = [.automatic]\n\n    var body: some View {\n        PropertyControl(\"Interface\") {\n            HStack {\n                Picker(\"Interface\", selection: $hardware.networkDeviceSelection) {\n                    Text(\"Disabled\").tag(NetworkDeviceSelection.disabled)\n\n                    Text(\"NAT\").tag(NetworkDeviceSelection.NAT)\n\n                    Section(\"Bridge\") {\n                        ForEach(interfaces) { interface in\n                            Text(interface.name)\n                                .tag(NetworkDeviceSelection.bridge(interface.id))\n                        }\n                    }\n                }\n                .labelsHidden()\n\n                Spacer()\n\n                Button {\n                    refresh()\n                } label: {\n                    Image(systemName: \"arrow.clockwise\")\n                }\n                .buttonStyle(.plain)\n                .help(\"Reload interfaces\")\n            }\n            .task { refresh() }\n        }\n    }\n\n    private func refresh() {\n        interfaces = [.automatic] + VBNetworkDevice.bridgeInterfaces\n    }\n}\n\n#if DEBUG\n#Preview {\n    _ConfigurationSectionPreview(.networkPreviewNAT) {\n        NetworkConfigurationView(hardware: $0.hardware)\n    }\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/VM Configuration/Sections/PointingDeviceConfigurationView.swift",
    "content": "//\n//  PointingDeviceConfigurationView.swift\n//  VirtualUI\n//\n//  Created by Guilherme Rambo on 19/07/22.\n//\n\nimport SwiftUI\nimport VirtualCore\n\nstruct PointingDeviceConfigurationView: View {\n    @Binding var hardware: VBMacDevice\n\n    @Environment(\\.resolvedRestoreImage)\n    private var resolvedRestoreImage\n\n    private var trackpadFeature: ResolvedVirtualizationFeature? {\n        resolvedRestoreImage?.feature(id: CatalogFeatureID.trackpad)\n    }\n\n    private var trackpadStatus: ResolvedFeatureStatus? { trackpadFeature?.status }\n\n    private var trackpadUnsupported: Bool { trackpadStatus?.isUnsupported == true }\n\n    private var availableKinds: [VBPointingDevice.Kind] {\n        VBPointingDevice.Kind.allCases.filter { kind in\n            kind != .trackpad || !trackpadUnsupported\n        }\n    }\n    \n    var body: some View {\n        PropertyControl(\"Device Type\", spacing: 8) {\n            VStack(alignment: .leading) {\n                Picker(\"Device Type\", selection: $hardware.pointingDevice.kind) {\n                    ForEach(availableKinds) { kind in\n                        Text(kind.name)\n                            .tag(kind)\n                    }\n                }\n                .onChange(of: trackpadUnsupported) { isUnsupported in\n                    if isUnsupported, hardware.pointingDevice.kind == .trackpad {\n                        hardware.pointingDevice.kind = .mouse\n                    }\n                }\n                .onAppear {\n                    if trackpadUnsupported, hardware.pointingDevice.kind == .trackpad {\n                        hardware.pointingDevice.kind = .mouse\n                    }\n                }\n            }\n        }\n\n    }\n}\n\n#if DEBUG\nstruct PointingDeviceConfigurationView_Previews: PreviewProvider {\n    static var previews: some View {\n        _ConfigurationSectionPreview { PointingDeviceConfigurationView(hardware: $0.hardware) }\n    }\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/VM Configuration/Sections/Sharing/SharedFolderListItem.swift",
    "content": "//\n//  SharedFolderListItem.swift\n//  VirtualUI\n//\n//  Created by Guilherme Rambo on 19/07/22.\n//\n\nimport SwiftUI\nimport VirtualCore\n\nstruct SharedFolderListItem: View {\n    @Binding var folder: VBSharedFolder\n\n    var body: some View {\n        HStack(spacing: 2) {\n            Toggle(folder.shortName, isOn: $folder.isEnabled)\n            label\n        }\n            .lineLimit(1)\n            .truncationMode(.middle)\n            .controlSize(.mini)\n            .disabled(folder.errorMessage != nil)\n            .labelsHidden()\n            .padding(.vertical, 4)\n            /// Easier to hit trailing edge buttons without hovering floating scroll bar.\n            .padding(.trailing, 4)\n    }\n\n    @ViewBuilder\n    private var label: some View {\n        HStack(spacing: 4) {\n            folder.icon(maxHeight: 14)\n\n            Text(folder.shortName)\n                .help(folder.url.path)\n\n            Spacer()\n\n            if let errorMessage = folder.errorMessage {\n                Image(systemName: \"exclamationmark.triangle.fill\")\n                    .symbolRenderingMode(.multicolor)\n                    .help(errorMessage)\n            } else {\n                Button {\n                    folder.isReadOnly.toggle()\n                } label: {\n                    Image(systemName: folder.isReadOnly ? \"pencil.slash\" : \"pencil\")\n                }\n                .help(folder.isReadOnly ? \"Make writable\" : \"Make read only\")\n                .buttonStyle(.plain)\n                .disabled(!folder.isEnabled)\n            }\n        }\n        .padding(.leading, 6)\n        .opacity(folder.isEnabled ? 1 : 0.8)\n        .opacity(folder.errorMessage != nil ? 0.3 : 1)\n        .font(.system(size: 11))\n    }\n}\n\nextension VBSharedFolder {\n    func icon(maxHeight: CGFloat) -> some View {\n        let image: NSImage\n        if let externalVolumeURL {\n            image = NSWorkspace.shared.icon(forFile: externalVolumeURL.path)\n        } else {\n            image = NSWorkspace.shared.icon(forFile: url.path)\n        }\n        let dimension = min(image.size.width, image.size.height)\n        let scale = (maxHeight) / dimension\n        image.size = NSSize(width: image.size.width * scale, height: image.size.height * scale)\n        return Image(nsImage: image)\n            .resizable()\n            .aspectRatio(contentMode: .fit)\n            .frame(maxHeight: maxHeight)\n    }\n}\n"
  },
  {
    "path": "VirtualUI/Source/VM Configuration/Sections/Sharing/SharedFoldersManagementView.swift",
    "content": "//\n//  SharedFoldersManagementView.swift\n//  VirtualUI\n//\n//  Created by Guilherme Rambo on 19/07/22.\n//\n\nimport SwiftUI\nimport VirtualCore\n\nstruct SharedFoldersManagementView: View {\n    \n    @Binding var configuration: VBMacConfiguration\n\n    @Environment(\\.resolvedRestoreImage)\n    private var resolvedRestoreImage\n    \n    @StateObject private var availabilityProvider: SharedFoldersAvailabilityProvider\n    \n    init(configuration: Binding<VBMacConfiguration>) {\n        self._configuration = configuration\n        self._availabilityProvider = .init(wrappedValue: SharedFoldersAvailabilityProvider(configuration.wrappedValue))\n        self._showTip = .init(wrappedValue: !configuration.sharedFolders.isEmpty)\n    }\n    \n    @State private var isShowingError = false\n    @State private var errorMessage = \"Error\"\n    @State private var selection = Set<VBSharedFolder.ID>()\n    @State private var selectionBeingRemoved: Set<VBSharedFolder.ID>?\n    @State private var isShowingRemovalConfirmation = false\n    @State private var isShowingHelpPopover = false\n\n    private var fileSharingStatus: ResolvedFeatureStatus? {\n        guard configuration.systemType == .mac else { return nil }\n        return resolvedRestoreImage?.feature(id: CatalogFeatureID.fileSharing)?.status\n    }\n\n    private var fileSharingUnsupported: Bool { fileSharingStatus?.isUnsupported == true }\n    private var fileSharingHelp: String? {\n        fileSharingUnsupported ? (fileSharingStatus?.supportMessage ?? \"Not supported.\") : nil\n    }\n\n    private var rosettaStatus: ResolvedFeatureStatus? {\n        guard configuration.systemType == .linux else { return nil }\n        return resolvedRestoreImage?.feature(id: CatalogFeatureID.rosettaSharing)?.status\n    }\n\n    private var rosettaUnsupported: Bool { rosettaStatus?.isUnsupported == true }\n    private var rosettaHelp: String? {\n        rosettaUnsupported ? (rosettaStatus?.supportMessage ?? \"Not supported.\") : nil\n    }\n\n    @ViewBuilder\n    private var sharedFoldersList: some View {\n        GroupedList {\n            List(selection: $selection) {\n                ForEach($configuration.sharedFolders) { $folder in\n                    SharedFolderListItem(folder: $folder)\n                        .contextMenu { folderMenu(for: $folder) }\n                        .tag(folder.id)\n                }\n            }\n        } headerAccessory: {\n            headerAccessory\n        } footerAccessory: {\n            EmptyView()\n        } emptyOverlay: {\n            emptyOverlay\n        } addButton: { label in\n            Button {\n                addFolder()\n            } label: {\n                label\n            }\n            .help(\"Add shared folder\")\n        } removeButton: { label in\n            Button {\n                confirmRemoval()\n            } label: {\n                label\n            }\n            .help(\"Remove selection from shared folders\")\n            .disabled(selection.isEmpty)\n        }\n    }\n    \n    var body: some View {\n        VStack(alignment: .leading, spacing: 16) {\n            Group {\n                if let fileSharingHelp {\n                    sharedFoldersList\n                        .help(fileSharingHelp)\n                } else {\n                    sharedFoldersList\n                }\n            }\n            .disabled(fileSharingUnsupported)\n\n            if configuration.systemType == .mac, fileSharingUnsupported {\n                Text(VBMacConfiguration.fileSharingNotice)\n                    .font(.caption)\n                    .foregroundColor(.yellow)\n            }\n\n            if configuration.systemType == .linux {\n                Group {\n                    let rosettaToggleBind = if VBMacConfiguration.rosettaSupported && !rosettaUnsupported {\n                        $configuration.rosettaSharingEnabled\n                    } else {\n                        Binding.constant(false)\n                    }\n\n                    if let rosettaHelp {\n                        Toggle(\"Share Rosetta for Linux\", isOn: rosettaToggleBind)\n                            .disabled(true)\n                            .help(rosettaHelp)\n                    } else {\n                        Toggle(\"Share Rosetta for Linux\", isOn: rosettaToggleBind)\n                            .disabled(!VBMacConfiguration.rosettaSupported || rosettaUnsupported)\n                    }\n                }\n                .onChange(of: rosettaUnsupported) { isUnsupported in\n                    if isUnsupported {\n                        configuration.rosettaSharingEnabled = false\n                    }\n                }\n                .onAppear {\n                    if rosettaUnsupported {\n                        configuration.rosettaSharingEnabled = false\n                    }\n                }\n\n                if !rosettaUnsupported, let rosettaSharingNotice = VBMacConfiguration.rosettaSharingNotice() {\n                    Text(try! AttributedString(markdown: rosettaSharingNotice))\n                        .font(.caption)\n                        .foregroundColor(.yellow)\n                }\n            }\n        }\n        .onReceive(NSWorkspace.shared.notificationCenter.publisher(for: NSWorkspace.didMountNotification)) { note in\n            availabilityProvider.refreshAvailabilityIfNeeded(with: note)\n        }\n        .onReceive(NSWorkspace.shared.notificationCenter.publisher(for: NSWorkspace.didUnmountNotification)) { note in\n            availabilityProvider.refreshAvailabilityIfNeeded(with: note)\n        }\n        .onChange(of: configuration) { availabilityProvider.configuration = $0 }\n        .onChange(of: configuration.sharedFolders.count) { newValue in\n            if newValue > 0 {\n                withAnimation(.spring()) {\n                    showTip = true\n                }\n            }\n        }\n        .confirmationDialog(\"Remove Folders\", isPresented: $isShowingRemovalConfirmation, titleVisibility: .visible, presenting: selectionBeingRemoved) { folders in\n            Button(role: .cancel) {\n                isShowingRemovalConfirmation = false\n            } label: {\n                Text(\"Cancel\")\n            }\n\n            Button(role: .destructive) {\n                guard let selectionBeingRemoved else {\n                    assertionFailure(\"How did we get here without a selection?\")\n                    return\n                }\n\n                remove(selectionBeingRemoved)\n            } label: {\n                Text(removalConfirmationTitle(with: folders))\n            }\n        } message: { folders in\n            Text(removalConfirmationMessage(with: folders))\n        }\n    }\n    \n    @ViewBuilder\n    private var emptyOverlay: some View {\n        if configuration.sharedFolders.isEmpty {\n            Text(\"This VM has no shared folders.\")\n            Button(\"Add Shared Folder\") {\n                addFolder()\n            }\n            .buttonStyle(.link)\n        }\n    }\n\n    @State private var showTip = false\n    \n    @ViewBuilder\n    private var headerAccessory: some View {\n        HStack {\n            Text(\"Shared Folders\")\n                .frame(maxWidth: .infinity, alignment: .leading)\n\n            if showTip {\n                Button {\n                    isShowingHelpPopover.toggle()\n                } label: {\n                    Image(systemName: \"questionmark.circle.fill\")\n                }\n                .buttonStyle(.borderless)\n                .transition(.opacity)\n                .popover(isPresented: $isShowingHelpPopover) {\n                    mountTip\n                }\n                .help(\"Shared folders help\")\n            }\n        }\n    }\n    \n    @ViewBuilder\n    private var mountTip: some View {\n        let tooltipCommon = \"\"\"\n        To make your shared folders available in the virtual machine,\n        run the following command in Terminal (Applications > Utilities > Terminal):\n\n        ```\n        mkdir -p ~/Desktop/VirtualBuddyShared && mount -t virtiofs VirtualBuddyShared ~/Desktop/VirtualBuddyShared\n        ```\n\n        A folder named \"VirtualBuddyShared\" will show up on the Desktop.\n        \"\"\"\n\n        let rosettaVm = VBMacConfiguration.rosettaSupported && configuration.systemType == .linux\n\n        let tooltipRosetta = if rosettaVm {\n            \"\"\"\n\n\n            To make Rosetta binaries available in the Linux virtual machine,\n            run the following command in the Linux guest's Terminal:\n\n            ```\n            mount -t virtiofs Rosetta /mnt\n            ```\n\n            The Rosetta binaries will be ready in `/mnt`. Follow [this instruction](https://developer.apple.com/documentation/virtualization/running_intel_binaries_in_linux_vms_with_rosetta#3978489) to allow x86-64 binaries to be run on the Linux guest.\n            \"\"\"\n        } else {\n            \"\"\n        }\n        let tooltipRosettaInstall = if rosettaVm && !VBMacConfiguration.rosettaInstalled() {\n                \"\"\"\n\n\n                Rosetta cannot be used unless installed on the host.\n                To install Rosetta, run the following command in host's Terminal:\n\n                ```\n                softwareupdate --install-rosetta\n                ```\n                \"\"\"\n        } else {\n            \"\"\n        }\n\n        let tooltipTexts = [tooltipCommon, tooltipRosetta, tooltipRosettaInstall].joined()\n\n        let tooltipAttributedText = try! AttributedString(\n            markdown: tooltipTexts,\n            options: AttributedString.MarkdownParsingOptions(interpretedSyntax:.inlineOnlyPreservingWhitespace)\n        )\n        Text(tooltipAttributedText)\n        .textSelection(.enabled)\n        .foregroundColor(.white)\n        .padding()\n        .multilineTextAlignment(.leading)\n    }\n\n    @ViewBuilder\n    private func folderMenu(for folder: Binding<VBSharedFolder>) -> some View {\n        Group {\n            Toggle(\"Enabled\", isOn: folder.isEnabled)\n            \n            Toggle(\"Read Only\", isOn: folder.isReadOnly)\n\n            Divider()\n\n            Button(\"Reveal In Finder\") {\n                NSWorkspace.shared.selectFile(folder.wrappedValue.url.path, inFileViewerRootedAtPath: folder.wrappedValue.url.deletingLastPathComponent().path)\n            }\n        }\n        .disabled(!availabilityProvider.isFolderAvailable(folder.wrappedValue))\n\n        Button(\"Remove\") {\n            confirmRemoval(for: [folder.wrappedValue.id])\n        }\n    }\n\n    private func addFolder() {\n        guard let newFolderURL = NSOpenPanel.run(accepting: [.folder], defaultDirectoryKey: \"sharedFolders\") else {\n            return\n        }\n\n        do {\n            try configuration.addSharedFolder(with: newFolderURL)\n        } catch {\n            errorMessage = error.localizedDescription\n            isShowingError = true\n        }\n    }\n\n    private func confirmRemoval(for folders: Set<VBSharedFolder.ID>? = nil) {\n        let targetFolders = folders ?? selection\n\n        guard !targetFolders.isEmpty else { return }\n        selectionBeingRemoved = targetFolders\n        isShowingRemovalConfirmation = true\n    }\n\n    private func remove(_ identifiers: Set<VBSharedFolder.ID>) {\n        configuration.removeSharedFolders(with: identifiers)\n    }\n\n    private func removalConfirmationTitle(with selection: Set<VBSharedFolder.ID>) -> String {\n        guard selection.count == 1, let singleID = selection.first, let folder = configuration.sharedFolders.first(where: { $0.id == singleID }) else {\n            return \"Remove \\(selection.count) Folders\"\n        }\n\n        return \"Remove \\\"\\(folder.shortNameForDialogs)\\\"\"\n    }\n\n    private func removalConfirmationMessage(with selection: Set<VBSharedFolder.ID>) -> String {\n        guard selection.count == 1, let singleID = selection.first, let folder = configuration.sharedFolders.first(where: { $0.id == singleID }) else {\n            return \"Are you sure you'd like to remove \\(selection.count) shared folders?\"\n        }\n\n        return \"Are you sure you'd like to remove \\\"\\(folder.shortNameForDialogs)\\\" from the shared folders?\"\n    }\n}\n\nprivate final class SharedFoldersAvailabilityProvider: ObservableObject {\n    \n    var configuration: VBMacConfiguration\n    \n    init(_ configuration: VBMacConfiguration) {\n        self.configuration = configuration\n        refreshAvailability()\n    }\n    \n    @Published private(set) var folderAvailability: [VBSharedFolder.ID: Bool] = [:]\n    \n    func isFolderAvailable(_ folder: VBSharedFolder) -> Bool {\n        folderAvailability[folder.id] ?? false\n    }\n    \n    func refreshAvailability() {\n        for folder in configuration.sharedFolders {\n            folderAvailability[folder.id] = folder.isAvailable\n        }\n    }\n    \n    func refreshAvailabilityIfNeeded(with notification: Notification) {\n        guard let volumeURL = notification.userInfo?[\"NSWorkspaceVolumeURLKey\"] as? URL else { return }\n        guard configuration.hasSharedFolders(inVolume: volumeURL) else { return }\n        \n        refreshAvailability()\n    }\n    \n}\n\n#if DEBUG\nstruct SharedFoldersManagementView_Previews: PreviewProvider {\n    static var previews: some View {\n        SharingConfigurationView_Previews.previews\n    }\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/VM Configuration/Sections/Sharing/SharingConfigurationView.swift",
    "content": "//\n//  SharingConfigurationView.swift\n//  VirtualUI\n//\n//  Created by Guilherme Rambo on 18/07/22.\n//\n\nimport SwiftUI\nimport VirtualCore\n\nstruct SharingConfigurationView: View {\n    @Binding var configuration: VBMacConfiguration\n\n    var body: some View {\n        SharedFoldersManagementView(configuration: $configuration)\n    }\n}\n\n#if DEBUG\nstruct _ConfigurationSectionPreview<C: View>: View {\n\n    @State private var config: VBMacConfiguration\n    var content: (Binding<VBMacConfiguration>) -> C\n    var ungrouped: Bool\n\n    init(_ config: VBMacConfiguration = .preview, ungrouped: Bool = false, @ViewBuilder _ content: @escaping (Binding<VBMacConfiguration>) -> C) {\n        self._config = .init(wrappedValue: config)\n        self.ungrouped = ungrouped\n        self.content = content\n    }\n\n    var body: some View {\n        Group {\n            if ungrouped {\n                content($config)\n            } else {\n                ConfigurationSection(.constant(false), {\n                    content($config)\n                }, header: {\n                    Label(\"SwiftUI Preview\", systemImage: \"eye\")\n                })\n            }\n        }\n        .frame(maxWidth: 320, maxHeight: .infinity, alignment: .top)\n            .padding()\n            .controlGroup()\n            .padding(30)\n            .frame(maxWidth: .infinity, maxHeight: .infinity)\n    }\n\n}\n\nstruct SharingConfigurationView_Previews: PreviewProvider {\n    static var previews: some View {\n        _ConfigurationSectionPreview { SharingConfigurationView(configuration: $0) }\n\n        _ConfigurationSectionPreview(.preview.linuxVirtualMachine) {\n            SharingConfigurationView(configuration: $0) }\n            .previewDisplayName(\"Linux Sharing Configuration View\")\n\n        _ConfigurationSectionPreview(.preview.removingSharedFolders) { SharingConfigurationView(configuration: $0) }\n            .previewDisplayName(\"Empty\")\n    }\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/VM Configuration/Sections/SoundConfigurationView.swift",
    "content": "//\n//  SoundConfigurationView.swift\n//  VirtualUI\n//\n//  Created by Guilherme Rambo on 18/07/22.\n//\n\nimport SwiftUI\nimport VirtualCore\n\nstruct SoundConfigurationView: View {\n    @Binding var hardware: VBMacDevice\n\n    var body: some View {\n        VStack(alignment: .leading, spacing: 16) {\n            Toggle(\"Enable Sound\", isOn: soundEnabled)\n\n            if hardware.soundDevices.isEmpty {\n                Toggle(\"Enable Sound Input\", isOn: .constant(false))\n                    .disabled(true)\n            } else {\n                Toggle(\"Enable Sound Input\", isOn: $hardware.soundDevices[0].enableInput)\n            }\n        }\n    }\n\n    private var soundEnabled: Binding<Bool> {\n        .init(get: {\n            !hardware.soundDevices.isEmpty\n        }, set: { newValue in\n            if newValue, hardware.soundDevices.isEmpty {\n                hardware.soundDevices = [.default]\n            } else {\n                hardware.soundDevices.removeAll()\n            }\n        })\n    }\n}\n\n#if DEBUG\nstruct SoundConfigurationView_Previews: PreviewProvider {\n    static var previews: some View {\n        _ConfigurationSectionPreview { SoundConfigurationView(hardware: $0.hardware) }\n    }\n\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/VM Configuration/Sections/Storage/ManagedDiskImageEditor.swift",
    "content": "//\n//  ManagedDiskImageEditor.swift\n//  VirtualUI\n//\n//  Created by Guilherme Rambo on 20/07/22.\n//\n\nimport SwiftUI\nimport VirtualCore\n\nstruct ManagedDiskImageEditor: View {\n    @State private var image: VBManagedDiskImage\n    var minimumSize: UInt64\n    var isExistingDiskImage: Bool\n    var onSave: (VBManagedDiskImage) -> Void\n    var isBootVolume: Bool\n\n    init(image: VBManagedDiskImage, isExistingDiskImage: Bool, isForBootVolume: Bool, onSave: @escaping (VBManagedDiskImage) -> Void) {\n        self._image = .init(wrappedValue: image)\n        self.isExistingDiskImage = isExistingDiskImage\n        self.onSave = onSave\n        let fallbackMinimumSize = isForBootVolume ? VBManagedDiskImage.minimumBootDiskImageSize : VBManagedDiskImage.minimumExtraDiskImageSize\n        self.minimumSize = isExistingDiskImage ? image.size : fallbackMinimumSize\n        self.isBootVolume = isForBootVolume\n    }\n\n    private let formatter: ByteCountFormatter = {\n        let f = ByteCountFormatter()\n        f.allowedUnits = [.useGB, .useMB, .useTB]\n        f.formattingContext = .standalone\n        f.countStyle = .file\n        return f\n    }()\n\n    @State private var nameError: String?\n\n    @Environment(\\.dismiss)\n    private var dismiss\n\n    var body: some View {\n        VStack(alignment: .leading) {\n            VStack(alignment: .leading) {\n                if let nameError {\n                    Text(nameError)\n                        .font(.caption)\n                        .foregroundColor(.red)\n                        .fixedSize(horizontal: false, vertical: true)\n                        .lineLimit(nil)\n                        .padding(.bottom)\n                }\n            }\n\n            let maximumSize = isBootVolume ? VBManagedDiskImage.maximumBootDiskImageSize : VBManagedDiskImage.maximumExtraDiskImageSize\n            NumericPropertyControl(\n                value: $image.size.gbStorageValue,\n                range: minimumSize.gbStorageValue...maximumSize.gbStorageValue,\n                hideSlider: isExistingDiskImage,\n                label: isBootVolume ? \"Boot Disk Size (GB)\" : \"Disk Image Size (GB)\",\n                formatter: NumberFormatter.numericPropertyControlDefault\n            )\n            .disabled(isExistingDiskImage)\n            .foregroundColor(sizeWarning != nil ? .yellow : .primary)\n\n            VStack(alignment: .leading, spacing: 8) {\n                if !isExistingDiskImage, !isBootVolume {\n                    Text(\"You'll have to use Disk Utility in the guest operating system to initialize the disk image. If you see an error after it boots up, choose the \\\"Initialize\\\" option.\")\n                        .foregroundColor(.yellow)\n                }\n\n                if let sizeWarning {\n                    Text(sizeWarning)\n                        .foregroundColor(.yellow)\n                }\n\n                if isBootVolume {\n                    Text(sizeChangeInfo)\n                        .foregroundColor(.yellow)\n\n                    if let sizeMessagePrefix {\n                        Text(sizeMessagePrefix)\n                    }\n                } else {\n                    Text(sizeMessage)\n                }\n            }\n                .font(.callout)\n                .foregroundColor(.secondary)\n                .fixedSize(horizontal: false, vertical: true)\n                .lineLimit(nil)\n        }\n        .onChange(of: image) { newValue in\n            onSave(newValue)\n        }\n    }\n\n    private var sizeMessagePrefix: String? {\n        VBSettingsContainer.current.isLibraryInAPFSVolume ? \"The storage space you make available for the disk won't be used immediately, only the space that's used by the virtual machine will be consumed. \" : nil\n    }\n\n    private var sizeChangeInfo: String {\n        if isBootVolume {\n            return \"Be sure to reserve enough space, since it won't be possible to change the size of the disk later.\"\n        } else {\n            return \"It's not possible to change the size of an existing storage device.\"\n        }\n    }\n    \n    private var sizeMessage: String {\n        if isExistingDiskImage {\n            return sizeChangeInfo\n        } else {\n            return \"\\(sizeMessagePrefix ?? \"\")After adding the storage device, it won't be possible to change the size of its disk image with VirtualBuddy.\"\n        }\n    }\n\n    private var sizeWarning: String? {\n        guard !VBSettingsContainer.current.libraryVolumeCanFit(image.size) else { return nil }\n        let volumeDescription: String\n        if let volumeName = VBSettingsContainer.current.settings.libraryURL.containingVolumeName {\n            volumeDescription = \"\\\"\\(volumeName)\\\"\"\n        } else {\n            volumeDescription = \"where your library is stored\"\n        }\n\n        return \"The volume \\(volumeDescription) doesn't have enough free space to fit the full size of the disk image.\"\n    }\n}\n\n#if DEBUG\nstruct ManagedDiskImageEditor_Previews: PreviewProvider {\n    static var previews: some View {\n        StorageDeviceDetailView_Previews.previews\n    }\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/VM Configuration/Sections/Storage/StorageConfigurationView.swift",
    "content": "//\n//  StorageConfigurationView.swift\n//  VirtualUI\n//\n//  Created by Guilherme Rambo on 19/07/22.\n//\n\nimport SwiftUI\nimport VirtualCore\n\nstruct StorageConfigurationView: View {\n    @EnvironmentObject var viewModel: VMConfigurationViewModel\n\n    @Binding var hardware: VBMacDevice\n\n    @State private var selection = Set<VBStorageDevice.ID>()\n\n    @State private var isShowingDeviceConfigurationSheet = false\n    @State private var deviceBeingConfigured: VBStorageDevice?\n\n    private var hideBootDisk: Bool { viewModel.context == .preInstall }\n\n    private func shouldHide(_ device: VBStorageDevice) -> Bool {\n        if hideBootDisk {\n            return device.isBootVolume\n        } else {\n            return false\n        }\n    }\n\n    var body: some View {\n        GroupedList {\n            List(selection: $selection) {\n                ForEach($hardware.storageDevices.filter({ !shouldHide($0.wrappedValue) })) { $device in\n                    StorageDeviceListItem(device: $device) {\n                        configure(device)\n                    }\n                        .tag(device.id)\n                }\n            }\n        } emptyOverlay: {\n            EmptyView()\n        } addButton: { label in\n            Button {\n                create()\n            } label: {\n                label\n            }\n            .help(\"Add storage device\")\n        } removeButton: { label in\n            Button {\n                for deviceID in selection {\n                    guard let idx = hardware.storageDevices.firstIndex(where: { $0.id == deviceID }) else { continue }\n                    guard !hardware.storageDevices[idx].isBootVolume else { continue }\n                    hardware.storageDevices.remove(at: idx)\n                }\n            } label: {\n                label\n            }\n            .disabled(selection.isEmpty)\n            .help(\"Remove selected devices\")\n        }\n        .sheet(isPresented: $isShowingDeviceConfigurationSheet) {\n            let device = deviceBeingConfigured ?? .template\n            let isNewDevice = deviceBeingConfigured == nil\n\n            StorageDeviceDetailView(device: device, isNewDevice: isNewDevice, onSave: { updatedDevice in\n                if isNewDevice {\n                    try await createImageIfNeeded(for: updatedDevice)\n                }\n                \n                hardware.addOrUpdate(updatedDevice)\n            })\n            .environmentObject(viewModel)\n            .padding()\n            .frame(minWidth: 280, idealWidth: 340, maxWidth: .infinity)\n        }\n    }\n\n    private func configure(_ device: VBStorageDevice?) {\n        deviceBeingConfigured = device\n        isShowingDeviceConfigurationSheet = true\n    }\n    \n    private func create() {\n        configure(nil)\n    }\n    \n    private func createImageIfNeeded(for newDevice: VBStorageDevice) async throws {\n        guard newDevice.usesManagedDiskImage else { return }\n        \n        try await viewModel.createImage(for: newDevice)\n    }\n}\n\nstruct StorageDeviceListItem: View {\n    @Binding var device: VBStorageDevice\n    var configureDevice: () -> Void\n\n    var body: some View {\n        HStack(spacing: 4) {\n            Toggle(device.displayName, isOn: $device.isEnabled)\n                .disabled(device.isBootVolume)\n                .help(device.isBootVolume ? \"The boot storage device can't be disabled\" : \"Enable/disable this storage device\")\n\n            label\n        }\n            .lineLimit(1)\n            .truncationMode(.middle)\n            .labelsHidden()\n            .padding(6)\n    }\n\n    @ViewBuilder\n    private var label: some View {\n        HStack(spacing: 6) {\n            device.iconView\n\n            Text(device.displayName)\n\n            Spacer()\n\n            Button {\n                configureDevice()\n            } label: {\n                Image(systemName: \"ellipsis.circle\")\n            }\n            .help(\"Device settings\")\n            .buttonStyle(.plain)\n            .disabled(device.isBootVolume)\n        }\n        .padding(.leading, 6)\n        .opacity(device.isEnabled ? 1 : 0.8)\n\n    }\n}\n\nextension VBStorageDevice {\n    var icon: Image {\n        if isUSBMassStorageDevice {\n            return Image(systemName: \"externaldrive.fill\")\n        } else if isBootVolume {\n            return Image(systemName: \"wrench.and.screwdriver.fill\")\n        } else {\n            return Image(systemName: \"internaldrive.fill\")\n        }\n    }\n\n    @ViewBuilder\n    var iconView: some View {\n        icon\n        .resizable()\n        .aspectRatio(contentMode: .fit)\n        .frame(maxWidth: 16)\n        .symbolRenderingMode(.hierarchical)\n    }\n}\n\n#if DEBUG\nstruct StorageConfigurationView_Previews: PreviewProvider {\n    static var previews: some View {\n        _ConfigurationSectionPreview { StorageConfigurationView(hardware: $0.hardware) }\n            .environmentObject(VMConfigurationViewModel(.preview))\n    }\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/VM Configuration/Sections/Storage/StorageDeviceDetailView.swift",
    "content": "//\n//  StorageDeviceDetailView.swift\n//  VirtualUI\n//\n//  Created by Guilherme Rambo on 19/07/22.\n//\n\nimport SwiftUI\nimport VirtualCore\n\nstruct StorageDeviceDetailView: View {\n    @EnvironmentObject var viewModel: VMConfigurationViewModel\n    \n    @State private var device: VBStorageDevice\n    var onSave: (VBStorageDevice) async throws -> Void\n    \n    init(device: VBStorageDevice, isNewDevice: Bool, onSave: @escaping (VBStorageDevice) async throws -> Void) {\n        self._device = .init(wrappedValue: device)\n        self.isNewDevice = isNewDevice\n        self._imageType = .init(wrappedValue: isNewDevice ? nil : device.usesManagedDiskImage ? .managed : .custom)\n        self.onSave = onSave\n    }\n    \n    @State private var isLoading = false\n    \n    private var canSave: Bool {\n        guard !isLoading else { return false }\n        \n        if imageType == .managed {\n            return device.managedImage != nil\n        } else {\n            return device.customImageURL != nil\n        }\n    }\n    \n    @State private var managedImage: VBManagedDiskImage?\n    @State private var customImageURL: URL?\n\n    private var isNewDevice: Bool\n\n    @State private var nameError: String?\n\n    @Environment(\\.dismiss)\n    private var dismiss\n    \n    private var canEditName: Bool {\n        device.usesManagedDiskImage && !device.diskImageExists(for: viewModel.vm)\n    }\n\n    var body: some View {\n        VStack(alignment: .leading) {\n            if isNewDevice {\n                if imageType != nil {\n                    detail\n                } else {\n                    imageTypePicker\n                }\n            } else {\n                detail\n            }\n            \n            Spacer()\n\n            HStack {\n                Button(\"Cancel\") {\n                    dismiss()\n                }\n                .keyboardShortcut(.cancelAction)\n\n                Spacer()\n\n                Button(\"Done\") {\n                    save()\n                }\n                .keyboardShortcut(.defaultAction)\n                .disabled(!canSave)\n            }\n            .padding(.top)\n        }\n    }\n    \n    private func save() {\n        isLoading = true\n        \n        Task {\n            do {\n                try await onSave(device)\n                \n                dismiss()\n            } catch {\n                NSAlert(error: error).runModal()\n            }\n            \n            isLoading = false\n        }\n    }\n    \n    @ViewBuilder\n    private var detail: some View {\n        VStack(alignment: .leading, spacing: 2) {\n            HStack {\n                device.iconView\n\n                EphemeralTextField($device.nameBinding, alignment: .leading) { name in\n                    Text(name)\n                } editableContent: { binding in\n                    TextField(\"\", text: binding)\n                }\n                .disabled(!canEditName)\n            }\n            \n            diskImageDetail\n        }\n        .padding(.bottom)\n        \n        Spacer()\n\n        VStack(alignment: .leading, spacing: 16) {\n            VStack(alignment: .leading, spacing: 4) {\n                Toggle(\"External Device\", isOn: $device.isUSBMassStorageDevice)\n                    .disabled(!VBStorageDevice.hostSupportsUSBMassStorage)\n                \n                Text(VBStorageDevice.hostSupportsUSBMassStorage ? \"Exposes the disk image as an external USB mass storage device to the virtual machine.\" : \"This feature requires macOS 13 or later.\")\n                    .font(.callout)\n                    .foregroundColor(.secondary)\n                    .lineLimit(nil)\n                    .fixedSize(horizontal: false, vertical: true)\n\n            }\n            VStack(alignment: .leading, spacing: 4) {\n                Toggle(\"Read Only\", isOn: $device.isReadOnly)\n                \n                Text(\"Makes the storage device appear read-only to the virtual machine. This doesn't affect the disk image.\")\n                    .font(.callout)\n                    .foregroundColor(.secondary)\n                    .lineLimit(nil)\n                    .fixedSize(horizontal: false, vertical: true)\n            }\n        }\n        .disabled(device.isBootVolume)\n        .help(device.isBootVolume ? \"These options are not available for the boot device\" : \"\")\n    }\n    \n    @State private var imageType: ImageType?\n    \n    private enum ImageType: Int, Identifiable, CaseIterable {\n        var id: RawValue { rawValue }\n        \n        case managed\n        case custom\n        \n        var name: String {\n            switch self {\n            case .managed:\n                return \"VirtualBuddy Disk Image\"\n            case .custom:\n                return \"Custom Image File\"\n            }\n        }\n    }\n\n    @ViewBuilder\n    private var imageTypePicker: some View {\n        VStack(alignment: .leading) {\n            VStack(alignment: .leading, spacing: 32) {\n                HStack {\n                    device.iconView\n                    \n                    Text(\"New Storage Device\")\n                }\n                \n                VStack(alignment: .leading, spacing: 16) {\n                    Text(\"How would you like to create this storage device?\")\n                        .padding(.bottom, 6)\n                        .font(.headline)\n                    \n                    Button {\n                        imageType = .managed\n                        device.backing = .managedImage(.template)\n                    } label: {\n                        Label(\"Create a new disk image with VirtualBuddy\", systemImage: \"externaldrive.fill.badge.plus\")\n                    }\n\n                    Button {\n                        selectCustomImage()\n                    } label: {\n                        Label(\"Select an existing disk image file\", systemImage: \"folder.fill.badge.plus\")\n                    }\n                }\n                .buttonStyle(.link)\n                .symbolRenderingMode(.hierarchical)\n            }\n            .padding(.bottom)\n        }\n        .frame(maxWidth: .infinity, alignment: .leading)\n    }\n    \n    @ViewBuilder\n    private var diskImageDetail: some View {\n        VStack(alignment: .leading) {\n            switch device.backing {\n            case .managedImage(let image):\n                ManagedDiskImageEditor(\n                    image: image,\n                    isExistingDiskImage: device.diskImageExists(for: viewModel.vm),\n                    isForBootVolume: device.isBootVolume,\n                    onSave: { device.update(with: $0, type: .size) }\n                )\n            case .customImage(let url):\n                customDiskImageURLView(with: url)\n            }\n        }\n    }\n    \n    @ViewBuilder\n    private func customDiskImageURLView(with url: URL) -> some View {\n        PropertyControl(\"Custom Disk Image File:\", spacing: 8) {\n            HStack {\n                Text(url.path)\n                    .truncationMode(.middle)\n                    .lineLimit(1)\n                \n                Button(\"Change…\") {\n                    selectCustomImage()\n                }\n                .controlSize(.small)\n            }\n        }\n        .padding(.top)\n    }\n    \n    private func selectCustomImage() {\n        guard let url = NSOpenPanel.run(accepting: [.diskImage], defaultDirectoryKey: \"storageCustomImage\") else {\n            return\n        }\n        \n        device.backing = .customImage(url)\n        imageType = .custom\n    }\n\n    private func updateImage(with newImage: VBManagedDiskImage) {\n        device.backing = .managedImage(newImage)\n    }\n\n}\n\nextension VBStorageDevice {\n    var usesManagedDiskImage: Bool {\n        guard case .managedImage = backing else { return false }\n        return true\n    }\n    var usesCustomDiskImage: Bool {\n        guard case .customImage = backing else { return false }\n        return true\n    }\n    var managedImage: VBManagedDiskImage? {\n        guard case .managedImage(let image) = backing else { return nil }\n        return image\n    }\n    var customImageURL: URL? {\n        guard case .customImage(let url) = backing else { return nil }\n        return url\n    }\n}\n\nextension Binding where Value == VBStorageDevice {\n    var nameBinding: Binding<String> {\n        switch wrappedValue.backing {\n        case .customImage:\n            return .constant(wrappedValue.displayName)\n        case .managedImage:\n            return .init {\n                wrappedValue.managedImage?.filename ?? \"\"\n            } set: { newValue in\n                guard var image = wrappedValue.managedImage else { return }\n                image.filename = newValue\n                wrappedValue.backing = .managedImage(image)\n            }\n        }\n    }\n}\n\nenum VBStorageImageUpdate {\n    case name\n    case size\n}\n\nextension VBStorageDevice {\n    mutating func update(with image: VBManagedDiskImage, type: VBStorageImageUpdate) {\n        guard var managedImage else {\n            backing = .managedImage(image)\n            return\n        }\n\n        switch type {\n        case .name:\n            managedImage.filename = image.filename\n        case .size:\n            managedImage.size = image.size\n        }\n        backing = .managedImage(managedImage)\n    }\n}\n\n#if DEBUG\nstruct StorageDeviceDetailView_Previews: PreviewProvider {\n    static var previews: some View {\n        let config = VBMacConfiguration.preview\n        ForEach(config.hardware.storageDevices.indices, id: \\.self) {\n            preview(at: $0)\n                .previewDisplayName(config.hardware.storageDevices[$0].displayName)\n        }\n    }\n    \n    @ViewBuilder\n    static func preview(at index: Int) -> some View {\n        _ConfigurationSectionPreview(ungrouped: true) {\n            StorageDeviceDetailView(device: $0.wrappedValue.hardware.storageDevices[index],\n                                    isNewDevice: $0.wrappedValue.hardware.storageDevices[index].displayName == \"New Device\", onSave: { _ in })\n        }\n        .frame(maxHeight: 400)\n        .environmentObject(VMConfigurationViewModel(.preview))\n    }\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/VM Configuration/VMConfigurationSheet.swift",
    "content": "//\n//  VMConfigurationSheet.swift\n//  VirtualUI\n//\n//  Created by Guilherme Rambo on 18/07/22.\n//\n\nimport SwiftUI\nimport VirtualCore\n\npublic struct VMConfigurationSheet: View {\n\n    public static let minWidth: CGFloat = 520\n\n    @EnvironmentObject private var viewModel: VMConfigurationViewModel\n    \n    /// The VM configuration as it existed when the user opened the configuration UI.\n    /// Can be used to reset aspects of the configuration to their previous values.\n    private var initialConfiguration: VBMacConfiguration\n    \n    /// The configuration that gets saved with the VM.\n    /// Setting this saves the configuration.\n    @Binding private var savedConfiguration: VBMacConfiguration\n\n    @State private var showValidationErrors = false\n    \n    private var showsCancelButton: Bool { viewModel.context == .postInstall }\n    private var customConfirmationButtonAction: ((VBMacConfiguration) -> Void)? = nil\n    \n    /// Initializes the VM configuration sheet, bound to a VM configuration model.\n    /// - Parameter configuration: The binding that will be updated when the user saves the configuration by clicking the \"Done\" button.\n    public init(configuration: Binding<VBMacConfiguration>) {\n        self.init(configuration: configuration, showingValidationErrors: false)\n    }\n    \n    init(configuration: Binding<VBMacConfiguration>, showingValidationErrors: Bool = false, customConfirmationButtonAction: ((VBMacConfiguration) -> Void)? = nil) {\n        self.initialConfiguration = configuration.wrappedValue\n        self._savedConfiguration = configuration\n        self._showValidationErrors = .init(wrappedValue: showingValidationErrors)\n        self.customConfirmationButtonAction = customConfirmationButtonAction\n    }\n    \n    @Environment(\\.dismiss) private var dismiss\n\n    @Environment(\\.containerPadding) private var containerPadding\n    @Environment(\\.maxContentWidth) private var maxContentWidth\n\n    private var isInstall: Bool { viewModel.context == .preInstall }\n\n    public var body: some View {\n        ScrollView(.vertical) {\n            VMConfigurationView(initialConfiguration: initialConfiguration)\n                .environmentObject(viewModel)\n                .frame(maxWidth: isInstall ? maxContentWidth : nil)\n                .padding(containerPadding)\n                .frame(maxWidth: .infinity)\n        }\n        .frame(maxWidth: .infinity, maxHeight: .infinity)\n        .safeAreaInset(edge: .bottom, spacing: 0) {\n            buttons\n        }\n        .resizableSheet(minWidth: Self.minWidth, maxWidth: .infinity, minHeight: 500, maxHeight: .infinity)\n    }\n\n    @ViewBuilder\n    private var buttons: some View {\n        VStack(alignment: .leading, spacing: 16) {\n            if showValidationErrors {\n                validationErrors\n            }\n\n            HStack {\n                if showsCancelButton {\n                    Button(\"Cancel\") {\n                        dismiss()\n                    }\n                    .keyboardShortcut(.cancelAction)\n                }\n                \n                Spacer()\n                \n                Button(viewModel.context == .preInstall ? \"Continue\" : \"Done\") {\n                    validateAndSave()\n                }\n                .keyboardShortcut(.defaultAction)\n                .disabled(showValidationErrors)\n            }\n        }\n        .virtualBuddyBottomBarStyle()\n        .onChange(of: viewModel.config) { newValue in\n            guard showValidationErrors else { return }\n            \n            Task {\n                if await viewModel.validate() == .supported {\n                    showValidationErrors = false\n                }\n            }\n        }\n    }\n    \n    @ViewBuilder\n    private var validationErrors: some View {\n        if case .unsupported(let errors) = viewModel.supportState {\n            ForEach(errors, id: \\.self) { Text($0) }\n                .foregroundColor(.red)\n        }\n    }\n    \n    private func validateAndSave() {\n        showValidationErrors = true\n\n        Task {\n            let state = await viewModel.validate()\n            \n            guard state.allowsSaving else { return }\n            \n            savedConfiguration = viewModel.config\n            \n            if let customConfirmationButtonAction {\n                customConfirmationButtonAction(savedConfiguration)\n            } else {\n                dismiss()\n            }\n        }\n    }\n    \n}\n\n#if DEBUG\nstruct VMConfigurationSheet_Previews: PreviewProvider {\n    static var height: Double { 1200 }\n\n    static var previews: some View {\n        _Template(vm: .preview, context: .preInstall)\n            .previewDisplayName(\"Pre Install\")\n\n        _Template(vm: .preview, context: .postInstall)\n            .previewDisplayName(\"Post Install\")\n\n        _Template(vm: .previewLinux, context: .postInstall)\n            .previewDisplayName(\"Linux - Post\")\n\n        _Template(vm: .previewLinux, context: .preInstall)\n            .previewDisplayName(\"Linux - Pre\")\n    }\n\n    struct _Template: View {\n        @State var vm: VBVirtualMachine\n        var context: VMConfigurationContext\n\n        var body: some View {\n            if context == .postInstall {\n                PreviewSheet {\n                    VMConfigurationSheet(configuration: $vm.configuration)\n                        .environmentObject(VMConfigurationViewModel(vm, context: context))\n                        .frame(width: VMConfigurationSheet.minWidth, height: VMConfigurationSheet_Previews.height, alignment: .top)\n                }\n            } else {\n                VMConfigurationSheet(configuration: $vm.configuration)\n                    .environmentObject(VMConfigurationViewModel(vm, context: context))\n                    .frame(width: VMConfigurationSheet.minWidth, height: VMConfigurationSheet_Previews.height, alignment: .top)\n                    .background(BlurHashFullBleedBackground(blurHash: .virtualBuddyBackground))\n            }\n        }\n    }\n}\n\n/// Simulates a macOS sheet for SwiftUI previews.\nstruct PreviewSheet<Content: View>: View {\n    var content: () -> Content\n\n    init(@ViewBuilder _ content: @escaping () -> Content) {\n        self.content = content\n    }\n\n    var body: some View {\n        ZStack {}\n        .frame(width: VMConfigurationSheet.minWidth, height: VMConfigurationSheet_Previews.height)\n        .padding()\n        .background(Color.black.opacity(0.5))\n        .overlay {\n            content()\n                .controlGroup()\n        }\n    }\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/VM Configuration/VMConfigurationView.swift",
    "content": "//\n//  VMConfigurationView.swift\n//  VirtualBuddy\n//\n//  Created by Guilherme Rambo on 17/07/22.\n//\n\nimport SwiftUI\nimport VirtualCore\n\nextension EnvironmentValues {\n    /// Type of guest that's currently being configured in a `VMConfigurationView`.\n    @Entry fileprivate(set) var configurationGuestType: VBGuestType = .mac\n}\n\nprivate struct ResolvedRestoreImageKey: EnvironmentKey {\n    static let defaultValue: ResolvedRestoreImage? = nil\n}\n\nextension EnvironmentValues {\n    var resolvedRestoreImage: ResolvedRestoreImage? {\n        get { self[ResolvedRestoreImageKey.self] }\n        set { self[ResolvedRestoreImageKey.self] = newValue }\n    }\n}\n\nenum CatalogFeatureID {\n    static let fileSharing = \"file_sharing\"\n    static let guestApp = \"guest_app\"\n    static let trackpad = \"trackpad\"\n    static let macKeyboard = \"mac_keyboard\"\n    static let stateRestoration = \"state_restoration\"\n    static let displayResize = \"display_resize\"\n    static let rosettaSharing = \"rosetta_sharing\"\n}\n\nextension ResolvedRestoreImage {\n    func feature(id: String) -> ResolvedVirtualizationFeature? {\n        features.first { $0.id == id }\n    }\n}\n\nextension ResolvedFeatureStatus {\n    var supportMessage: String? {\n        switch self {\n        case .supported:\n            return nil\n        case .warning(let title, let message), .unsupported(let title, let message):\n            return title ?? message\n        }\n    }\n\n    var supportMessageColor: Color {\n        switch self {\n        case .supported:\n            return .secondary\n        case .warning:\n            return .yellow\n        case .unsupported:\n            return .red\n        }\n    }\n}\n\nstruct VMConfigurationView: View {\n    @EnvironmentObject private var viewModel: VMConfigurationViewModel\n    \n    var initialConfiguration: VBMacConfiguration\n\n    static var labelSpacing: CGFloat { 2 }\n\n    @AppStorage(\"config.general.collapsed\")\n    private var generalCollapsed = true\n\n    @AppStorage(\"config.storage.collapsed\")\n    private var storageCollapsed = true\n\n    @AppStorage(\"config.display.collapsed\")\n    private var displayCollapsed = true\n    \n    @AppStorage(\"config.pointing.collapsed\")\n    private var pointingCollapsed = true\n    \n    @AppStorage(\"config.keyboard.collapsed\")\n    private var keyboardCollapsed = true\n\n    @AppStorage(\"config.network.collapsed\")\n    private var networkCollapsed = true\n    \n    @AppStorage(\"config.sound.collapsed\")\n    private var soundCollapsed = true\n    \n    @AppStorage(\"config.sharing.collapsed\")\n    private var sharingCollapsed = true\n\n    @AppStorage(\"config.guestApp.collapsed\")\n    private var guestAppCollapsed = true\n\n    private var systemType: VBGuestType { viewModel.config.systemType }\n\n    private var showBootDiskSection: Bool { viewModel.context == .preInstall }\n\n    private var showPointingDeviceSection: Bool { systemType.supportsVirtualTrackpad }\n\n    private var showKeyboardDeviceSection: Bool { systemType.supportsKeyboardCustomization }\n\n    private var showDisplayPPISection: Bool { systemType.supportsDisplayPPI }\n\n    private var showGuestAppSection: Bool { systemType.supportsGuestApp }\n\n    var body: some View {\n        VStack(alignment: .leading, spacing: 16) {\n            if showBootDiskSection {\n                bootDisk\n            }\n\n            general\n\n            storage\n\n            display\n\n            if showPointingDeviceSection {\n                pointingDevice\n            }\n\n            if showKeyboardDeviceSection {\n                keyboardDevice\n            }\n\n            network\n\n            sound\n\n            if showGuestAppSection {\n                guestApp\n            }\n\n            sharing\n                .frame(minWidth: 0, idealWidth: VMConfigurationSheet.minWidth)\n        }\n        .font(.system(size: 12))\n        .environment(\\.configurationGuestType, viewModel.config.systemType)\n        .environment(\\.resolvedRestoreImage, viewModel.resolvedRestoreImage)\n    }\n\n    @ViewBuilder\n    private var general: some View {\n        ConfigurationSection($generalCollapsed) {\n            HardwareConfigurationView(device: $viewModel.config.hardware)\n        } header: {\n            SummaryHeader(\n                \"General\",\n                systemImage: \"memorychip\",\n                summary: viewModel.config.generalSummary\n            )\n        }\n    }\n\n    @ViewBuilder\n    private var bootDisk: some View {\n        ConfigurationSection(.constant(false), collapsingDisabled: true) {\n            if let image = (try? viewModel.vm.bootDevice)?.managedImage {\n                ManagedDiskImageEditor(image: image, isExistingDiskImage: false, isForBootVolume: true) { image in\n                    viewModel.updateBootStorageDevice(with: image)\n                }\n            } else {\n                Text(\"Something went terribly wrong: VM doesn't have a boot storage device with a managed disk image.\")\n                    .foregroundColor(.red)\n            }\n        } header: {\n            SummaryHeader(\n                \"Boot Disk\",\n                systemImage: \"wrench.and.screwdriver\"\n            )\n        }\n    }\n\n    private var storageSummary: String {\n        if showBootDiskSection {\n            return viewModel.config.hardware.storageDevices.count == 1 ? \"None\" : viewModel.config.storageSummary\n        } else {\n            return viewModel.config.storageSummary\n        }\n    }\n\n    @ViewBuilder\n    private var storage: some View {\n        ConfigurationSection($storageCollapsed) {\n            StorageConfigurationView(hardware: $viewModel.config.hardware)\n                .environmentObject(viewModel)\n        } header: {\n            SummaryHeader(\n                showBootDiskSection ? \"Additional Storage\" : \"Storage\",\n                systemImage: \"externaldrive\",\n                summary: storageSummary\n            )\n        }\n        .contextMenu {\n            Button(\"Reset General Settings\") {\n                viewModel.config.hardware.cpuCount = initialConfiguration.hardware.cpuCount\n                viewModel.config.hardware.memorySize = initialConfiguration.hardware.memorySize\n            }\n        }\n    }\n\n    @ViewBuilder\n    private var display: some View {\n        ConfigurationSection($displayCollapsed) {\n            DisplayConfigurationView(\n                device: $viewModel.config.hardware.displayDevices[0],\n                selectedPreset: $viewModel.selectedDisplayPreset,\n                canChangePPI: showDisplayPPISection\n            )\n        } header: {\n            SummaryHeader(\"Display\", systemImage: \"display\", summary: viewModel.config.displaySummary) {\n                DisplayConfigurationView(\n                    device: $viewModel.config.hardware.displayDevices[0],\n                    selectedPreset: $viewModel.selectedDisplayPreset,\n                    canChangePPI: showDisplayPPISection\n                )\n                .presetPicker\n                .frame(width: 24)\n            }\n        }\n    }\n    \n    @ViewBuilder\n    private var pointingDevice: some View {\n        ConfigurationSection($pointingCollapsed) {\n            PointingDeviceConfigurationView(hardware: $viewModel.config.hardware)\n        } header: {\n            SummaryHeader(\n                \"Pointing Device\",\n                systemImage: \"cursorarrow\",\n                summary: viewModel.config.pointingDeviceSummary\n            )\n        }\n    }\n\n    @ViewBuilder\n    private var keyboardDevice: some View {\n        ConfigurationSection($keyboardCollapsed) {\n            KeyboardDeviceConfigurationView(hardware: $viewModel.config.hardware)\n        } header: {\n            SummaryHeader(\n                \"Keyboard Device\",\n                systemImage: \"keyboard\",\n                summary: viewModel.config.keyboardDeviceSummary\n            )\n        }\n    }\n\n    @ViewBuilder\n    private var network: some View {\n        ConfigurationSection($networkCollapsed) {\n            NetworkConfigurationView(hardware: $viewModel.config.hardware)\n        } header: {\n            SummaryHeader(\n                \"Network\",\n                systemImage: \"network\",\n                summary: viewModel.config.networkSummary\n            )\n        }\n    }\n\n    @ViewBuilder\n    private var sound: some View {\n        ConfigurationSection($soundCollapsed) {\n            SoundConfigurationView(hardware: $viewModel.config.hardware)\n        } header: {\n            SummaryHeader(\n                \"Sound\",\n                systemImage: viewModel.config.hardware.soundDevices.isEmpty ? \"speaker.slash\" : \"speaker.3\",\n                summary: viewModel.config.soundSummary\n            )\n        }\n    }\n\n    @ViewBuilder\n    private var sharing: some View {\n        ConfigurationSection($sharingCollapsed) {\n            SharingConfigurationView(configuration: $viewModel.config)\n        } header: {\n            SummaryHeader(\n                \"Sharing\",\n                systemImage: \"folder\",\n                summary: viewModel.config.sharingSummary\n            )\n        }\n    }\n\n    @ViewBuilder\n    private var guestApp: some View {\n        ConfigurationSection($guestAppCollapsed) {\n            GuestAppConfigurationView(configuration: $viewModel.config)\n        } header: {\n            SummaryHeader(\n                \"Guest App\",\n                summary: viewModel.config.guestAppSummary\n            ) {\n                Image(.guestSymbol)\n                    .resizable()\n                    .aspectRatio(contentMode: .fit)\n                    .frame(width: 15)\n            }\n        }\n    }\n}\n\n// MARK: - Section Header\n\nprivate struct SummaryHeader<Icon: View, Accessory: View>: View {\n    var title: String\n    var summary: String?\n    @ViewBuilder var icon: () -> Icon\n    @ViewBuilder var accessory: () -> Accessory\n\n    init(_ title: String, summary: String? = nil, @ViewBuilder icon: @escaping () -> Icon, @ViewBuilder accessory: @escaping () -> Accessory) {\n        self.title = title\n        self.summary = summary\n        self.icon = icon\n        self.accessory = accessory\n    }\n\n    var body: some View {\n        HStack {\n            HStack {\n                icon().frame(width: 22)\n\n                Text(title)\n            }\n            accessory()\n\n            Spacer()\n\n            if let summary {\n                Text(summary)\n                    .font(.caption)\n                    .monospacedDigit()\n                    .foregroundColor(.secondary)\n            }\n        }\n    }\n}\n\nprivate extension SummaryHeader where Icon == Image {\n    init(_ title: String, image: Image, summary: String? = nil, @ViewBuilder accessory: @escaping () -> Accessory) {\n        self.title = title\n        self.summary = summary\n        self.icon = { image }\n        self.accessory = accessory\n    }\n\n    init(_ title: String, systemImage: String, summary: String? = nil, @ViewBuilder accessory: @escaping () -> Accessory) {\n        self.init(title, image: Image(systemName: systemImage), summary: summary, accessory: accessory)\n    }\n}\n\nprivate extension SummaryHeader where Icon == Image, Accessory == EmptyView {\n    init(_ title: String, image: Image, summary: String? = nil) {\n        self.init(title, image: image, summary: summary, accessory: { EmptyView() })\n    }\n\n    init(_ title: String, systemImage: String, summary: String? = nil) {\n        self.init(title, systemImage: systemImage, summary: summary, accessory: { EmptyView() })\n    }\n}\n\nprivate extension SummaryHeader where Accessory == EmptyView {\n    init(_ title: String, summary: String? = nil, @ViewBuilder icon: @escaping () -> Icon) {\n        self.init(title, summary: summary, icon: icon, accessory: { EmptyView() })\n    }\n}\n\n#if DEBUG\nstruct VMConfigurationView_Previews: PreviewProvider {\n    static var previews: some View {\n        VMConfigurationSheet_Previews.previews\n    }\n}\n#endif\n"
  },
  {
    "path": "VirtualUI/Source/VM Configuration/VMConfigurationViewModel.swift",
    "content": "//\n//  VMConfigurationViewModel.swift\n//  VirtualUI\n//\n//  Created by Guilherme Rambo on 18/07/22.\n//\n\nimport SwiftUI\nimport VirtualCore\n\npublic enum VMConfigurationContext: Int {\n    case preInstall\n    case postInstall\n}\n\npublic final class VMConfigurationViewModel: ObservableObject {\n    \n    @Published var config: VBMacConfiguration {\n        didSet {\n            /// Reset display preset when changing display settings.\n            /// This is so the warning goes away, if any warning is being shown.\n            if config.hardware.displayDevices != oldValue.hardware.displayDevices,\n               config.hardware.displayDevices.first != selectedDisplayPreset?.device\n            {\n                selectedDisplayPreset = nil\n            }\n        }\n    }\n    \n    @Published public internal(set) var supportState: VBMacConfiguration.SupportState = .supported\n\n    @Published public internal(set) var resolvedRestoreImage: ResolvedRestoreImage? {\n        didSet {\n            applyResolvedFeatureDefaultsIfNeeded()\n        }\n    }\n    \n    @Published var selectedDisplayPreset: VBDisplayPreset?\n    \n    @Published private(set) var vm: VBVirtualMachine\n\n    public let context: VMConfigurationContext\n    \n    public init(_ vm: VBVirtualMachine, context: VMConfigurationContext = .postInstall, resolvedRestoreImage: ResolvedRestoreImage? = nil) {\n        self.config = vm.configuration\n        self.vm = vm\n        self.context = context\n        self.resolvedRestoreImage = resolvedRestoreImage\n        \n        applyResolvedFeatureDefaultsIfNeeded()\n\n        Task { await validate() }\n    }\n\n    @discardableResult\n    public func validate() async -> VBMacConfiguration.SupportState {\n        let updatedState = await config.validate(for: vm, skipVirtualizationConfig: context == .preInstall)\n\n        await MainActor.run {\n            supportState = updatedState\n        }\n\n        return updatedState\n    }\n    \n    public func createImage(for device: VBStorageDevice) async throws {\n        guard let image = device.managedImage else {\n            throw Failure(\"Only managed disk images can be created.\")\n        }\n        \n        let settings = DiskImageGenerator.ImageSettings(for: image, in: vm)\n        \n        try await DiskImageGenerator.generateImage(with: settings)\n    }\n\n    public func updateBootStorageDevice(with image: VBManagedDiskImage) {\n        guard let idx = config.hardware.storageDevices.firstIndex(where: { $0.isBootVolume }) else {\n            fatalError(\"Missing boot device in VM configuration\")\n        }\n\n        var device = config.hardware.storageDevices[idx]\n        device.backing = .managedImage(image)\n        config.hardware.addOrUpdate(device)\n    }\n    \n}\n\n// MARK: - Feature Defaults\n\nprivate extension VMConfigurationViewModel {\n    func applyResolvedFeatureDefaultsIfNeeded() {\n        guard context == .preInstall else { return }\n        guard let resolvedRestoreImage else { return }\n\n        var updated = config\n\n        if resolvedRestoreImage.feature(id: CatalogFeatureID.guestApp)?.status.isUnsupported == true {\n            updated.guestAdditionsEnabled = false\n        }\n\n        if resolvedRestoreImage.feature(id: CatalogFeatureID.trackpad)?.status.isUnsupported == true,\n           updated.hardware.pointingDevice.kind == .trackpad\n        {\n            updated.hardware.pointingDevice.kind = .mouse\n        }\n\n        if resolvedRestoreImage.feature(id: CatalogFeatureID.macKeyboard)?.status.isUnsupported == true,\n           updated.hardware.keyboardDevice.kind == .mac\n        {\n            updated.hardware.keyboardDevice.kind = .generic\n        }\n\n        if resolvedRestoreImage.feature(id: CatalogFeatureID.displayResize)?.status.isUnsupported == true {\n            updated.hardware.displayDevices = updated.hardware.displayDevices.map { device in\n                var updatedDevice = device\n                updatedDevice.automaticallyReconfiguresDisplay = false\n                return updatedDevice\n            }\n        }\n\n        if resolvedRestoreImage.feature(id: CatalogFeatureID.rosettaSharing)?.status.isUnsupported == true {\n            updated.rosettaSharingEnabled = false\n        }\n\n        if updated != config {\n            config = updated\n        }\n    }\n}\n"
  },
  {
    "path": "VirtualUI/VirtualUI.h",
    "content": "//\n//  VirtualUI.h\n//  VirtualUI\n//\n//  Created by Guilherme Rambo on 17/07/22.\n//\n\n#import <Foundation/Foundation.h>\n\n//! Project version number for VirtualUI.\nFOUNDATION_EXPORT double VirtualUIVersionNumber;\n\n//! Project version string for VirtualUI.\nFOUNDATION_EXPORT const unsigned char VirtualUIVersionString[];\n\n// In this header, you should import all the public headers of your framework using statements like #import <VirtualUI/PublicHeader.h>\n\n#import <VirtualUI/NSStatusItem+.h>\n#import <VirtualUI/NSApplication+MenuBar.h>\n"
  },
  {
    "path": "VirtualWormhole/Source/Definitions/VirtualWormholeConstants.swift",
    "content": "//\n//  VirtualWormholeConstants.swift\n//  VirtualWormholeConstants\n//\n//  Created by Guilherme Rambo on 02/06/22.\n//\n\nimport Foundation\nimport OSLog\n\nstruct VirtualWormholeConstants {\n    static let subsystemName = \"codes.rambo.VirtualWormhole\"\n\n    static let verboseLoggingEnabled: Bool = {\n        #if DEBUG\n        return UserDefaults.standard.bool(forKey: \"WHVerbosePacketLogging\")\n        #else\n        return false\n        #endif\n    }()\n\n    static let payloadPropagationEnabled: Bool = {\n        !UserDefaults.standard.bool(forKey: \"WHDisablePayloadPropagation\")\n    }()\n\n    static let connectionTimeoutInNanoseconds: UInt64 = 15 * NSEC_PER_SEC\n\n    static let pingIntervalInSeconds: TimeInterval = 5.0\n}\n\nextension Logger {\n    init<T>(for type: T.Type) {\n        self.init(subsystem: VirtualWormholeConstants.subsystemName, category: String(describing: type))\n    }\n}\n\nprivate final class _VirtualWormholeStub { }\n\npublic extension Bundle {\n    static let virtualWormhole = Bundle(for: _VirtualWormholeStub.self)\n}\n\n"
  },
  {
    "path": "VirtualWormhole/Source/Services/Base/WormholeServiceClient.swift",
    "content": "//\n//  WormholeServiceClient.swift\n//  VirtualWormhole\n//\n//  Created by Guilherme Rambo on 10/03/23.\n//\n\nimport Foundation\n\npublic protocol WormholeServiceClient {\n    associatedtype ServiceType: WormholeService\n\n    init(with service: ServiceType)\n}\n"
  },
  {
    "path": "VirtualWormhole/Source/Services/Base/WormholeServiceProtocol.swift",
    "content": "//\n//  WormholeServiceProtocol.swift\n//  VirtualWormhole\n//\n//  Created by Guilherme Rambo on 02/06/22.\n//\n\nimport Foundation\nimport Virtualization\n\npublic protocol WormholeMultiplexer: AnyObject {\n\n    var side: WHConnectionSide { get }\n    \n    func send<T: WHPayload>(_ payload: T, to peerID: WHPeerID?) async\n\n    func stream<T: WHPayload>(for payloadType: T.Type) -> AsyncThrowingStream<(senderID: WHPeerID, payload: T), Error>\n    \n}\n\npublic protocol WormholeService: AnyObject {\n\n    static var id: String { get }\n    \n    init(with connection: WormholeMultiplexer)\n    \n    func activate()\n    \n}\n"
  },
  {
    "path": "VirtualWormhole/Source/Services/DarwinNotifications/SystemNotification.swift",
    "content": "//\n//  SystemNotification.swift\n//  VirtualWormhole\n//\n//  Created by Guilherme Rambo on 08/03/23.\n//\n\nimport Foundation\nimport notify\n\n/// Swift wrapper for the `notify_register_dispatch()` API.\npublic final class SystemNotification {\n\n    public struct LibNotifyError: LocalizedError {\n        public var errorDescription: String?\n\n        init(code: UInt32) {\n            let name: String\n            switch Int32(code) {\n            case NOTIFY_STATUS_OK: name = \"NOTIFY_STATUS_OK\"\n            case NOTIFY_STATUS_INVALID_NAME: name = \"NOTIFY_STATUS_INVALID_NAME\"\n            case NOTIFY_STATUS_INVALID_TOKEN: name = \"NOTIFY_STATUS_INVALID_TOKEN\"\n            case NOTIFY_STATUS_INVALID_PORT: name = \"NOTIFY_STATUS_INVALID_PORT\"\n            case NOTIFY_STATUS_INVALID_FILE: name = \"NOTIFY_STATUS_INVALID_FILE\"\n            case NOTIFY_STATUS_INVALID_SIGNAL: name = \"NOTIFY_STATUS_INVALID_SIGNAL\"\n            case NOTIFY_STATUS_INVALID_REQUEST: name = \"NOTIFY_STATUS_INVALID_REQUEST\"\n            case NOTIFY_STATUS_NOT_AUTHORIZED: name = \"NOTIFY_STATUS_NOT_AUTHORIZED\"\n            case NOTIFY_STATUS_OPT_DISABLE: name = \"NOTIFY_STATUS_OPT_DISABLE\"\n            case NOTIFY_STATUS_SERVER_NOT_FOUND: name = \"NOTIFY_STATUS_SERVER_NOT_FOUND\"\n            case NOTIFY_STATUS_NULL_INPUT: name = \"NOTIFY_STATUS_NULL_INPUT\"\n            default: name = \"unknown\"\n            }\n            self.errorDescription = \"Notify error \\(code) (\\(name))\"\n        }\n    }\n\n    public let name: String\n    private let queue: DispatchQueue\n    private let callback: (SystemNotification) -> Void\n    public private(set) var notificationToken: Int32 = 0\n    private var activated = false\n\n    public init(with name: String, queue: DispatchQueue = .main, callback: @escaping (SystemNotification) -> Void) {\n        self.name = name\n        self.queue = queue\n        self.callback = callback\n    }\n\n    public init(with name: String, queue: DispatchQueue = .main, callback: @escaping () -> Void) {\n        self.name = name\n        self.queue = queue\n        self.callback = { _ in callback() }\n    }\n\n    public func activate() throws {\n        guard !activated else { return }\n\n        let status = notify_register_dispatch(\n            name,\n            &notificationToken,\n            queue,\n            notificationReceived\n        )\n\n        guard status == NOTIFY_STATUS_OK else {\n            throw LibNotifyError(code: status)\n        }\n\n        activated = true\n    }\n\n    public func invalidate() {\n        guard activated else { return }\n\n        notify_cancel(notificationToken)\n        notificationToken = 0\n\n        activated = false\n    }\n\n    deinit { invalidate() }\n\n    private func notificationReceived(_ token: Int32) {\n        callback(self)\n    }\n\n    func getState() throws -> UInt64 {\n        var state: UInt64 = 0\n        let status = notify_get_state(notificationToken, &state)\n\n        guard status == NOTIFY_STATUS_OK else {\n            throw LibNotifyError(code: status)\n        }\n\n        return state\n    }\n\n}\n"
  },
  {
    "path": "VirtualWormhole/Source/Services/DarwinNotifications/WHDarwinNotificationsService.swift",
    "content": "//\n//  WHDarwinNotificationsService.swift\n//  VirtualWormhole\n//\n//  Created by Guilherme Rambo on 08/03/23.\n//\n\nimport Cocoa\nimport OSLog\nimport Combine\n\nenum DarwinNotificationMessage: WHPayload {\n    static let resendOnReconnect = true\n    \n    case post(String)\n    case subscribe(String)\n}\n\nfinal class WHDarwinNotificationsService: WormholeService {\n\n    static let id = \"darwinNotifications\"\n\n    private lazy var logger = Logger(for: Self.self)\n\n    private let peerPostedNotificationSubject = PassthroughSubject<(name: String, peerID: WHPeerID), Never>()\n\n    var onPeerNotificationReceived: AnyPublisher<(name: String, peerID: WHPeerID), Never> {\n        peerPostedNotificationSubject.eraseToAnyPublisher()\n    }\n\n    var connection: WormholeMultiplexer\n\n    init(with connection: WormholeMultiplexer) {\n        self.connection = connection\n    }\n\n    func activate() {\n        logger.debug(#function)\n\n        Task {\n            for try await message in connection.stream(for: DarwinNotificationMessage.self) {\n                handle(message.payload, from: message.senderID)\n            }\n        }\n    }\n\n    private func handle(_ message: DarwinNotificationMessage, from peerID: WHPeerID) {\n        logger.debug(\"Handle message: \\(String(describing: message))\")\n\n        switch message {\n        case .post(let name):\n            peerPostedNotificationSubject.send((name, peerID))\n        case .subscribe(let name):\n            createSubscription(for: name, peerID: peerID)\n        }\n    }\n\n    private var subscriptions = [SystemNotification]()\n\n    private func createSubscription(for name: String, peerID: WHPeerID) {\n        do {\n            let note = SystemNotification(with: name) { [weak self] in\n                guard let self = self else { return }\n                self.sendPostMessage(with: name, to: peerID)\n            }\n            subscriptions.append(note)\n\n            DistributedNotificationCenter.default().addObserver(forName: .init(name), object: nil, queue: nil) { [weak self] _ in\n                guard let self = self else { return }\n                self.sendPostMessage(with: name, to: peerID)\n            }\n\n            try note.activate()\n        } catch {\n            logger.error(\"Error creating notification subscription for \\\"\\(name)\\\": \\(error, privacy: .public)\")\n        }\n    }\n\n    private func sendPostMessage(with name: String, to peerID: WHPeerID) {\n        #if DEBUG\n        logger.debug(\"Posting \\(name, privacy: .public) to \\(peerID, privacy: .public)\")\n        #endif\n\n        Task {\n            await connection.send(DarwinNotificationMessage.post(name), to: peerID)\n        }\n    }\n\n}\n"
  },
  {
    "path": "VirtualWormhole/Source/Services/DefaultsImport/Implementation/DefaultsDomain+ExportImport.swift",
    "content": "//\n//  DefaultsDomain+ExportImport.swift\n//  VirtualWormhole\n//\n//  Created by Guilherme Rambo on 09/03/23.\n//\n\nimport Cocoa\nimport OSLog\n\npublic extension DefaultsDomainDescriptor {\n\n    func exportDefaults(to url: URL) async throws {\n        try await runDefaults(\"export\", domainName: id, plistPath: url.path)\n\n        try postProcessPlist(at: url)\n    }\n\n    func importDefaults(from url: URL) async throws {\n        try await runDefaults(\"import\", domainName: id, plistPath: url.path)\n\n        try await performRestartIfNeeded()\n    }\n\n    private func runDefaults(_ verb: String, domainName: String, plistPath: String) async throws {\n        let proc = Process()\n        proc.executableURL = URL(fileURLWithPath: \"/usr/bin/defaults\")\n        proc.arguments = [\n            verb,\n            domainName,\n            plistPath\n        ]\n\n        try proc.checkRun()\n    }\n\n    func performRestartIfNeeded() async throws {\n        guard let restart else { return }\n        \n        guard target.isRunning else { return }\n        \n        if restart.needsConfirmation {\n            let shouldRestart = await MainActor.run {\n                let alert = NSAlert()\n                alert.messageText = \"Restart \\(target.name)?\"\n                alert.informativeText = \"To apply the new settings, \\(target.name) must be restarted. Would you like to restart it now?\"\n                alert.addButton(withTitle: \"Restart Now\")\n                alert.addButton(withTitle: \"Later\")\n\n                return alert.runModal() == .alertFirstButtonReturn\n            }\n\n            guard shouldRestart else { return }\n        }\n\n        let proc = Process()\n        proc.executableURL = URL(fileURLWithPath: \"/bin/sh\")\n        proc.arguments = [\n            \"-c\",\n            restart.command\n        ]\n        try proc.checkRun()\n\n        guard restart.shouldRelaunch, let url = target.bundleURL else { return }\n\n        try await NSWorkspace.shared.openApplication(at: url, configuration: .init())\n    }\n\n    private func postProcessPlist(at url: URL) throws {\n        guard !ignoredKeyPaths.isEmpty else { return }\n\n        let data = try Data(contentsOf: url)\n        let plist = try PropertyListSerialization.propertyList(from: data, options: .mutableContainersAndLeaves, format: nil)\n        guard let dict = plist as? NSMutableDictionary else {\n            throw CocoaError(.coderReadCorrupt, userInfo: [NSLocalizedDescriptionKey: \"The exported defaults domain is not a valid property list.\"])\n        }\n\n        for key in ignoredKeyPaths {\n            dict.setValue(nil, forKeyPath: key)\n        }\n\n        let updatedPlist = try PropertyListSerialization.data(fromPropertyList: dict, format: .xml, options: 0)\n\n        try updatedPlist.write(to: url)\n    }\n\n}\n\nextension Pipe {\n    func readString() -> String? {\n        guard let data = try? fileHandleForReading.readToEnd() else { return nil }\n        guard !data.isEmpty else { return nil }\n        return String(decoding: data, as: UTF8.self)\n    }\n}\n\nextension Process {\n\n    private static let logger = Logger(subsystem: VirtualWormholeConstants.subsystemName, category: \"Process\")\n\n    @discardableResult\n    func checkRun(expectedStatus: Int32 = 0) throws -> Data? {\n        let errPipe = Pipe()\n        let outPipe = Pipe()\n        standardError = errPipe\n        standardOutput = outPipe\n\n        try run()\n        waitUntilExit()\n\n        let errStr = errPipe.readString()\n\n        guard terminationStatus == expectedStatus else {\n            var info: [String: Any] = [\n                NSLocalizedDescriptionKey: \"Command failed with exit code \\(terminationStatus)\"\n            ]\n            if let errStr {\n                Self.logger.error(\"Command \\(self.executableURL?.lastPathComponent ?? \"<nil>\", privacy: .public) failed with exit code \\(self.terminationStatus, privacy: .public): \\(errStr, privacy: .public)\")\n                info[NSLocalizedFailureReasonErrorKey] = errStr\n            }\n            throw CocoaError(.coderReadCorrupt, userInfo: info)\n        }\n\n        return try? outPipe.fileHandleForReading.readToEnd()\n    }\n\n}\n"
  },
  {
    "path": "VirtualWormhole/Source/Services/DefaultsImport/Implementation/DefaultsDomainDescriptor.swift",
    "content": "//\n//  DefaultsDomainDescriptor.swift\n//  VirtualWormhole\n//\n//  Created by Guilherme Rambo on 09/03/23.\n//\n\nimport Cocoa\nimport UniformTypeIdentifiers\n\npublic struct DefaultsDomainDescriptor: Identifiable, Codable {\n    public struct Target: Identifiable, Codable {\n        public var id: String { bundleIdentifier }\n        public var bundleIdentifier: String\n        public var name: String\n        public var isSystemService: Bool\n    }\n\n    public struct Restart: Codable {\n        public var command: String\n        public var needsConfirmation = true\n        public var shouldRelaunch = true\n    }\n\n    public var id: Target.ID { target.id }\n    public var target: Target\n    public var ignoredKeyPaths: [String] = []\n    public var restart: Restart?\n}\n\npublic extension DefaultsDomainDescriptor.Target {\n    var isRunning: Bool {\n        /// System services are not included in `NSRunningApplication.runningApplications`,\n        /// so just assume they're always running (which will be the case most of the time).\n        guard !isSystemService else { return true }\n        return NSRunningApplication.runningApplications(withBundleIdentifier: bundleIdentifier).contains(where: { !$0.isTerminated })\n    }\n\n    var bundleURL: URL? {\n        NSWorkspace.shared.urlForApplication(withBundleIdentifier: bundleIdentifier)\n    }\n\n    func iconImage(with size: CGSize = CGSize(width: 128, height: 128)) -> NSImage {\n        func getIcon() -> NSImage {\n            guard let url = bundleURL else {\n                return NSWorkspace.shared.icon(for: .application)\n            }\n\n            return NSWorkspace.shared.icon(forFile: url.path)\n        }\n\n        let image = getIcon()\n        image.size = size\n        return image\n    }\n}\n"
  },
  {
    "path": "VirtualWormhole/Source/Services/DefaultsImport/Implementation/DefaultsImportController.swift",
    "content": "//\n//  DefaultsImportController.swift\n//  VirtualWormhole\n//\n//  Created by Guilherme Rambo on 09/03/23.\n//\n\nimport Foundation\nimport OSLog\nimport Combine\n\npublic typealias DefaultsDomainCollection = [DefaultsDomainDescriptor.ID: DefaultsDomainDescriptor]\n\npublic final class DefaultsImportController: ObservableObject {\n\n    private lazy var logger = Logger(subsystem: VirtualWormholeConstants.subsystemName, category: String(describing: Self.self))\n\n    @Published public private(set) var sortedDomains = [DefaultsDomainDescriptor]()\n\n    @Published public private(set) var descriptors = DefaultsDomainCollection()\n\n    private lazy var cancellables = Set<AnyCancellable>()\n\n    public init() {\n        $descriptors\n            .map { $0.values.sorted(by: { $0.target.name.localizedStandardCompare($1.target.name) == .orderedAscending }) }\n            .assign(to: &$sortedDomains)\n\n        loadDomains()\n    }\n\n    private func loadDomains() {\n        do {\n            guard let url = Bundle.virtualWormhole.url(forResource: \"DefaultsDomains\", withExtension: \"plist\") else {\n                throw CocoaError(.fileNoSuchFile, userInfo: [NSLocalizedDescriptionKey: \"DefaultsDomains.plist missing from VirtualWormhole bundle\"])\n            }\n\n            let data = try Data(contentsOf: url)\n\n            let loadedDescriptors = try PropertyListDecoder().decode(DefaultsDomainCollection.self, from: data)\n\n            self.descriptors = loadedDescriptors\n        } catch {\n            logger.fault(\"Failed to load descriptors: \\(error, privacy: .public)\")\n            assertionFailure(\"Failed to load descriptors: \\(error)\")\n        }\n    }\n\n}\n"
  },
  {
    "path": "VirtualWormhole/Source/Services/DefaultsImport/Resources/DefaultsDomains.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>com.apple.Terminal</key>\n\t<dict>\n\t\t<key>target</key>\n\t\t<dict>\n\t\t\t<key>bundleIdentifier</key>\n\t\t\t<string>com.apple.Terminal</string>\n\t\t\t<key>name</key>\n\t\t\t<string>Terminal</string>\n\t\t\t<key>isSystemService</key>\n\t\t\t<false/>\n\t\t</dict>\n\t\t<key>ignoredKeyPaths</key>\n\t\t<array/>\n\t\t<key>restart</key>\n\t\t<dict>\n\t\t\t<key>command</key>\n\t\t\t<string>killall Terminal &amp;&amp; sleep 1</string>\n\t\t\t<key>needsConfirmation</key>\n\t\t\t<true/>\n\t\t\t<key>shouldRelaunch</key>\n\t\t\t<true/>\n\t\t</dict>\n\t</dict>\n\t<key>com.apple.Dock</key>\n\t<dict>\n\t\t<key>target</key>\n\t\t<dict>\n\t\t\t<key>bundleIdentifier</key>\n\t\t\t<string>com.apple.Dock</string>\n\t\t\t<key>name</key>\n\t\t\t<string>Dock</string>\n\t\t\t<key>isSystemService</key>\n\t\t\t<true/>\n\t\t</dict>\n\t\t<key>ignoredKeyPaths</key>\n\t\t<array>\n\t\t\t<string>persistent-apps</string>\n\t\t\t<string>persistent-others</string>\n\t\t\t<string>recent-apps</string>\n\t\t</array>\n\t\t<key>restart</key>\n\t\t<dict>\n\t\t\t<key>command</key>\n\t\t\t<string>killall Dock</string>\n\t\t\t<key>needsConfirmation</key>\n\t\t\t<false/>\n\t\t\t<key>shouldRelaunch</key>\n\t\t\t<false/>\n\t\t</dict>\n\t</dict>\n\t<key>com.apple.Finder</key>\n\t<dict>\n\t\t<key>target</key>\n\t\t<dict>\n\t\t\t<key>bundleIdentifier</key>\n\t\t\t<string>com.apple.Finder</string>\n\t\t\t<key>name</key>\n\t\t\t<string>Finder</string>\n\t\t\t<key>isSystemService</key>\n\t\t\t<true/>\n\t\t</dict>\n\t\t<key>ignoredKeyPaths</key>\n\t\t<array>\n\t\t\t<string>NSNavLastRootDirectory</string>\n\t\t\t<string>RecentMoveAndCopyDestinations</string>\n\t\t\t<string>NewWindowTargetPath</string>\n\t\t\t<string>FXConnectToLastURL</string>\n\t\t\t<string>GoToField</string>\n\t\t\t<string>GoToFieldHistory</string>\n\t\t</array>\n\t\t<key>restart</key>\n\t\t<dict>\n\t\t\t<key>command</key>\n\t\t\t<string>killall Finder</string>\n\t\t\t<key>needsConfirmation</key>\n\t\t\t<false/>\n\t\t\t<key>shouldRelaunch</key>\n\t\t\t<false/>\n\t\t</dict>\n\t</dict>\n</dict>\n</plist>\n"
  },
  {
    "path": "VirtualWormhole/Source/Services/DefaultsImport/WHDefaultsImportClient.swift",
    "content": "//\n//  WHDefaultsImportClient.swift\n//  VirtualWormhole\n//\n//  Created by Guilherme Rambo on 10/03/23.\n//\n\nimport Foundation\nimport OSLog\n\npublic final class WHDefaultsImportClient: WormholeServiceClient {\n\n    private lazy var logger = Logger(subsystem: VirtualWormholeConstants.subsystemName, category: String(describing: Self.self))\n\n    public typealias ServiceType = WHDefaultsImportService\n\n    let service: WHDefaultsImportService\n\n    public init(with service: WHDefaultsImportService) {\n        self.service = service\n    }\n\n    public func importDomain(with id: DefaultsDomainDescriptor.ID) async throws {\n        logger.debug(\"Requesting export for \\(id, privacy: .public)\")\n\n        let response = Task {\n            let stream = service.onDomainResponseReceived.filter({ $0.domainID == id }).values\n\n            for await response in stream {\n                switch response {\n                case .failure(_, let message):\n                    logger.error(\"Export request for \\(id, privacy: .public) resolved with error: \\(message, privacy: .public)\")\n\n                    throw CocoaError(.coderInvalidValue, userInfo: [NSLocalizedDescriptionKey: message])\n                case .success(let id, let data):\n                    logger.debug(\"Export request for \\(id, privacy: .public) resolved successfully\")\n\n                    try await performImport(for: id, with: data)\n                default:\n                    continue\n                }\n                break\n            }\n        }\n\n        await service.sendExportRequest(for: id)\n\n        logger.debug(\"Export request for \\(id, privacy: .public) sent, waiting for response\")\n\n        try await response.value\n    }\n\n    private func performImport(for domainID: String, with data: Data) async throws {\n        let domain = try service.fetchDescriptor(for: domainID)\n\n        let tempURL = service.temporaryURL(for: domainID)\n\n        try data.write(to: tempURL)\n\n        try await domain.importDefaults(from: tempURL)\n\n        try? FileManager.default.removeItem(at: tempURL)\n    }\n\n}\n"
  },
  {
    "path": "VirtualWormhole/Source/Services/DefaultsImport/WHDefaultsImportService.swift",
    "content": "//\n//  WHDefaultsImportService.swift\n//  VirtualWormhole\n//\n//  Created by Guilherme Rambo on 09/03/23.\n//\n\nimport Cocoa\nimport OSLog\nimport Combine\n\nenum DefaultsImportMessage: WHPayload {\n    /// Guest requesting domain export from host.\n    case request(domainID: String)\n    /// Host responding to guest request with domain ID and associated plist.\n    case success(domainID: String, plist: Data)\n    /// Host responding to guest request with domain ID and error message.\n    case failure(domainID: String, error: String)\n}\n\nextension DefaultsImportMessage {\n    var domainID: String {\n        switch self {\n        case .request(let domainID), .success(let domainID, _), .failure(let domainID, _):\n        return domainID\n        }\n    }\n}\n\npublic final class WHDefaultsImportService: WormholeService {\n\n    public static let id = \"defaultsImport\"\n\n    private lazy var logger = Logger(for: Self.self)\n\n    var connection: WormholeMultiplexer\n\n    public init(with connection: WormholeMultiplexer) {\n        self.connection = connection\n    }\n\n    public func activate() {\n        logger.debug(#function)\n\n        Task {\n            for try await message in connection.stream(for: DefaultsImportMessage.self) {\n                await handle(message.payload, from: message.senderID)\n            }\n        }\n    }\n\n    private lazy var controller = DefaultsImportController()\n\n    let onDomainResponseReceived = PassthroughSubject<DefaultsImportMessage, Never>()\n\n    func sendExportRequest(for domainID: String) async {\n        assert(connection.side == .guest, \"Requesting defaults export is only possible from guest to host\")\n\n        await connection.send(DefaultsImportMessage.request(domainID: domainID), to: nil)\n    }\n\n    private func handle(_ message: DefaultsImportMessage, from peerID: WHPeerID) async {\n        logger.debug(\"Handle message: \\(String(describing: message))\")\n\n        switch message {\n        case .request(let domainID):\n            await handleDomainRequest(for: domainID, from: peerID)\n        case .success, .failure:\n            await MainActor.run {\n                onDomainResponseReceived.send(message)\n            }\n        }\n    }\n\n    private func handleDomainRequest(for domainID: String, from peerID: WHPeerID) async {\n        do {\n            let data = try await fetchDomainData(for: domainID)\n\n            await connection.send(DefaultsImportMessage.success(domainID: domainID, plist: data), to: peerID)\n        } catch {\n            logger.error(\"Export failed: \\(error, privacy: .public)\")\n            \n            await connection.send(DefaultsImportMessage.failure(domainID: domainID, error: error.localizedDescription), to: peerID)\n        }\n    }\n\n    func fetchDescriptor(for domainID: String) throws -> DefaultsDomainDescriptor {\n        guard let domain = controller.descriptors[domainID] else {\n            throw CocoaError(.fileNoSuchFile, userInfo: [NSLocalizedDescriptionKey: \"Domain \\(domainID) not found.\"])\n        }\n        return domain\n    }\n\n    func temporaryURL(for domainID: String) -> URL {\n        URL(fileURLWithPath: NSTemporaryDirectory())\n            .appendingPathComponent(\"VirtualBuddyDefaultsExport-\\(domainID)-\\(Int(Date.now.timeIntervalSinceReferenceDate))\")\n            .appendingPathExtension(\"plist\")\n    }\n\n    private func fetchDomainData(for domainID: String) async throws -> Data {\n        let domain = try fetchDescriptor(for: domainID)\n\n        let tempURL = temporaryURL(for: domainID)\n\n        try await domain.exportDefaults(to: tempURL)\n\n        let result = try Data(contentsOf: tempURL)\n\n        try? FileManager.default.removeItem(at: tempURL)\n\n        return result\n    }\n\n}\n"
  },
  {
    "path": "VirtualWormhole/Source/Services/DesktopPicture/CGImage+FullyTransparent.swift",
    "content": "//\n//  CGImage+FullyTransparent.swift\n//  VirtualBuddy\n//\n//  Created by Guilherme Rambo on 19/06/25.\n//\n\nimport Cocoa\nimport CoreImage\nimport CoreImage.CIFilterBuiltins\n\nprivate let transparentCheckContext = CIContext(options: [.workingColorSpace: NSNull()])\n\nextension CGImage {\n    /// Returns `true` if the image is fully transparent or has width and height equal to zero.\n    func isFullyTransparent() -> Bool {\n        /// Zero size image is considered fully transparent.\n        guard width > 0, height > 0 else { return true }\n\n        /// If image has no alpha, then it can't be fully transparent.\n        switch alphaInfo {\n        case .none, .noneSkipFirst, .noneSkipLast:\n            return false\n        default:\n            break\n        }\n\n        let ciImage = CIImage(cgImage: self)\n\n        let filter = CIFilter.areaMaximumAlpha()\n        filter.inputImage = ciImage\n        filter.extent = ciImage.extent\n\n        guard let reduced = filter.outputImage else {\n            assertionFailure(\"Error reducing image area maximum alpha\")\n            return false\n        }\n\n        var alpha: UInt32 = 0\n\n        transparentCheckContext\n            .render(\n                reduced,\n                toBitmap: &alpha,\n                rowBytes: 4,\n                bounds: CGRect(x: 0, y: 0, width: 1, height: 1),\n                format: .A8,\n                colorSpace: nil\n            )\n\n        return alpha == 0\n    }\n\n    static func load(from url: URL) throws -> CGImage {\n        guard let source = CGImageSourceCreateWithURL(url as CFURL, nil) else {\n            throw CocoaError(.fileReadNoSuchFile)\n        }\n\n        guard let cgImage = CGImageSourceCreateImageAtIndex(source, 0, nil) else {\n            throw CocoaError(.coderInvalidValue)\n        }\n\n        return cgImage\n    }\n\n    static func load(from data: Data) throws -> CGImage {\n        guard let source = CGImageSourceCreateWithData(data as CFData, nil) else {\n            throw CocoaError(.coderValueNotFound)\n        }\n\n        guard let cgImage = CGImageSourceCreateImageAtIndex(source, 0, nil) else {\n            throw CocoaError(.coderInvalidValue)\n        }\n\n        return cgImage\n    }\n}\n"
  },
  {
    "path": "VirtualWormhole/Source/Services/DesktopPicture/NSImage+DesktopPicture.h",
    "content": "#import <Cocoa/Cocoa.h>\n\nNS_ASSUME_NONNULL_BEGIN\n\n@interface NSImage (DesktopPicture)\n\n@property (nonatomic, readonly, class) NSImage *_Nullable desktopPicture NS_SWIFT_UI_ACTOR;\n\n+ (void)desktopPictureForScreen:(NSScreen *)screen completion:(void(^)(NSImage *_Nullable desktopPicture))completion;\n\n@end\n\nNS_ASSUME_NONNULL_END\n"
  },
  {
    "path": "VirtualWormhole/Source/Services/DesktopPicture/NSImage+DesktopPicture.m",
    "content": "#import \"NSImage+DesktopPicture.h\"\n\n@interface NSScreen (DisplayInfo)\n\n@property (readonly) NSNumber *displayID;\n\n@end\n\n@implementation NSImage (DesktopPicture)\n\n+ (dispatch_queue_t)_vb_desktopPictureQueue\n{\n    static dispatch_once_t onceToken;\n    static dispatch_queue_t _queue;\n    dispatch_once(&onceToken, ^{\n        _queue = dispatch_queue_create(\"DesktopPicture\", dispatch_queue_attr_make_with_qos_class(NULL, QOS_CLASS_USER_INITIATED, 0));\n    });\n    return _queue;\n}\n\n+ (NSImage *)desktopPicture\n{\n    return [self _vb_desktopPictureForScreen:[NSScreen mainScreen]];\n}\n\n+ (void)desktopPictureForScreen:(NSScreen *)screen completion:(void(^)(NSImage *_Nullable desktopPicture))completion\n{\n    dispatch_async([self _vb_desktopPictureQueue], ^{\n        NSImage *picture = [self _vb_desktopPictureForScreen:screen];\n        dispatch_async(dispatch_get_main_queue(), ^{\n            if (completion) completion(picture);\n        });\n    });\n}\n\n+ (NSImage *)_vb_desktopPictureForScreen:(NSScreen *)screen\n{\n    if (!screen) return nil;\n\n    CFArrayRef windowList = CGWindowListCreate(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);\n    NSArray <NSDictionary *> *descriptions = (__bridge id)CGWindowListCreateDescriptionFromArray(windowList);\n\n    NSArray <NSDictionary <NSString *, id> *> *wallpaperWindows = [descriptions filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@\"(%K CONTAINS %@ OR %K CONTAINS %@) AND %K == %@\", kCGWindowName, @\"Wallpaper\", kCGWindowName, @\"Desktop Picture\", kCGWindowOwnerName, @\"Dock\"]];\n\n    CGDirectDisplayID displayID = screen.displayID.unsignedIntValue;\n    CGRect displayBounds = CGDisplayBounds(displayID);\n\n    NSDictionary *wallpaperWindow = nil;\n\n    for (NSDictionary *windowDict in wallpaperWindows) {\n        NSDictionary *boundsDict = windowDict[(__bridge id)kCGWindowBounds];\n        if (!boundsDict) continue;\n        CGRect windowRect = CGRectZero;\n        if (!CGRectMakeWithDictionaryRepresentation((__bridge CFDictionaryRef)boundsDict, &windowRect)) continue;\n\n        if (CGRectContainsPoint(displayBounds, windowRect.origin)) {\n            wallpaperWindow = windowDict;\n            break;\n        }\n    }\n\n    if (!wallpaperWindow) return nil;\n\n    NSNumber *windowNumber = [wallpaperWindow objectForKey:(__bridge id)kCGWindowNumber];\n    if (!windowNumber) return nil;\n\n    CGImageRef cgImage = CGWindowListCreateImage(displayBounds, kCGWindowListOptionIncludingWindow, (CGWindowID)[windowNumber unsignedIntValue], kCGWindowImageDefault);\n    if (!cgImage) return nil;\n\n    return [[NSImage alloc] initWithCGImage:cgImage size:NSMakeSize(CGImageGetWidth(cgImage), CGImageGetHeight(cgImage))];\n}\n\n@end\n\n@implementation NSScreen (DisplayInfo)\n\n- (NSNumber*)displayID\n{\n    return [[self deviceDescription] valueForKey:@\"NSScreenNumber\"];\n}\n\n@end\n"
  },
  {
    "path": "VirtualWormhole/Source/Services/DesktopPicture/WHDesktopPictureService.swift",
    "content": "//\n//  WHDesktopPictureService.swift\n//  VirtualBuddy\n//\n//  Created by Guilherme Rambo on 18/06/25.\n//\n\nimport Cocoa\nimport OSLog\nimport AVFoundation\nimport Combine\n\npublic struct DesktopPictureMessage: WHPayload {\n    public internal(set) var type: String\n    public internal(set) var content: Data\n\n    public static let resendOnReconnect = true\n}\n\nfinal class WHDesktopPictureService: WormholeService {\n\n    static let id = \"desktopPicture\"\n\n    private lazy var logger = Logger(for: Self.self)\n\n    var connection: WormholeMultiplexer\n\n    init(with connection: WormholeMultiplexer) {\n        self.connection = connection\n    }\n\n    static let imageProperties = [\n        kCGImageDestinationLossyCompressionQuality: 0.8,\n        kCGImageDestinationImageMaxPixelSize: 512\n    ] as CFDictionary\n\n    private let peerSentDesktopPictureSubject = PassthroughSubject<(message: DesktopPictureMessage, peerID: WHPeerID), Never>()\n\n    var onPeerPeerDesktopPictureReceived: AnyPublisher<(message: DesktopPictureMessage, peerID: WHPeerID), Never> {\n        peerSentDesktopPictureSubject.eraseToAnyPublisher()\n    }\n\n    func activate() {\n        logger.debug(#function)\n\n        Task {\n            for try await message in connection.stream(for: DesktopPictureMessage.self) {\n                logger.debug(\"Received desktop picture message with \\(message.payload.content.count) bytes of image data.\")\n\n                peerSentDesktopPictureSubject.send((message.payload, message.senderID))\n            }\n        }\n\n        guard connection.side == .guest else { return }\n\n        Task {\n            try? await Task.sleep(for: .seconds(2))\n\n            await sendDesktopPicture()\n        }\n    }\n\n    func sendDesktopPicture() async {\n        guard let image = await MainActor.run(body: { NSImage.desktopPicture }) else {\n            logger.error(\"Error getting desktop picture for main screen.\")\n            return\n        }\n\n        guard let cgImage = image.cgImage(forProposedRect: nil, context: nil, hints: nil) else {\n            logger.error(\"Error getting CGImage from desktop picture.\")\n            return\n        }\n\n        guard !cgImage.isFullyTransparent() else {\n            logger.warning(\"Skipping send desktop picture because it generated a fully transparent image.\")\n            return\n        }\n\n        guard let cfData = CFDataCreateMutable(kCFAllocatorDefault, 0) else {\n            logger.error(\"Failed to create CFMutableData\")\n            return\n        }\n        guard let destination = CGImageDestinationCreateWithData(cfData, AVFileType.heic.rawValue as CFString, 1, nil) else {\n            logger.error(\"Failed to create CGImageDestination\")\n            return\n        }\n\n        CGImageDestinationAddImage(destination, cgImage, Self.imageProperties)\n        CGImageDestinationFinalize(destination)\n\n        let payload = DesktopPictureMessage(type: AVFileType.heic.rawValue, content: cfData as Data)\n\n        logger.info(\"Sending payload with \\(payload.content.count) bytes\")\n\n        await connection.send(payload, to: nil)\n    }\n\n}\n\nextension NSImage: @retroactive @unchecked Sendable { }\n"
  },
  {
    "path": "VirtualWormhole/Source/Services/WHSharedClipboardService.swift",
    "content": "//\n//  WHSharedClipboardService.swift\n//  VirtualWormhole\n//\n//  Created by Guilherme Rambo on 02/06/22.\n//\n\nimport Cocoa\nimport OSLog\n\nstruct ClipboardData: Codable, Hashable {\n    var type: NSPasteboard.PasteboardType.RawValue\n    var value: Data\n}\n\nstruct ClipboardMessage: WHPayload, Hashable {\n    var timestamp: Date\n    var data: [ClipboardData]\n\n    static let propagateBetweenGuests = true\n}\n\nfinal class WHSharedClipboardService: WormholeService {\n\n    static let id = \"clipboard\"\n    \n    private lazy var logger = Logger(for: Self.self)\n\n    var connection: WormholeMultiplexer\n\n    init(with connection: WormholeMultiplexer) {\n        self.connection = connection\n    }\n    \n    private var previousMessage: ClipboardMessage?\n    \n    func activate() {\n        logger.debug(#function)\n\n        Task {\n            for try await message in connection.stream(for: ClipboardMessage.self) {\n                handle(message.payload)\n            }\n        }\n\n        startObservingClipboard()\n    }\n\n    private let pasteboard = NSPasteboard.general\n    \n    private func handle(_ message: ClipboardMessage) {\n        guard !message.data.isEmpty, message.data != previousMessage?.data else { return }\n        \n        logger.debug(\"Handle clipboard message: \\(String(describing: message))\")\n        \n        previousMessage = message\n        \n        pasteboard.read(from: message.data)\n\n        #if DEBUG\n        logger.debug(\"⏱️ Clipboard message roundtrip time: \\(String(format: \"%.03f\", Date.now.timeIntervalSince(message.timestamp)), privacy: .public)\")\n        #endif\n    }\n    \n    private var clipboardTimer: Timer?\n    \n    private func startObservingClipboard() {\n        clipboardTimer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true, block: { [weak self] _ in\n            self?.updateIfNeeded()\n        })\n    }\n    \n    private func updateIfNeeded() {\n        let currentData = ClipboardData.current\n        guard currentData != previousMessage?.data else { return }\n\n        #if DEBUG\n        logger.debug(\"Clipboard contents changed: \\(String(describing: currentData), privacy: .public)\")\n        #endif\n        \n        let message = ClipboardMessage(\n            timestamp: .now,\n            data: currentData\n        )\n        \n        previousMessage = message\n        \n        Task {\n            await connection.send(message, to: nil)\n        }\n    }\n\n}\n\nprivate extension ClipboardData {\n    static let supportedTypes: [NSPasteboard.PasteboardType] = [\n        .string,\n        .rtf,\n        .rtfd,\n        .pdf,\n        .png,\n        .tiff,\n    ]\n\n    static var current: [ClipboardData] {\n        guard let availableTypes = NSPasteboard.general.types else { return [] }\n\n        return supportedTypes.compactMap { type in\n            /// PNG and TIFF data are often present at the same time.\n            /// Ignore TIFF data and preserve only the PNG data when that's the case,\n            /// since TIFF data is usually much larger and unused.\n            if type == .tiff {\n                guard !availableTypes.contains(.png) else { return nil }\n            }\n            guard let data = NSPasteboard.general.data(forType: type) else {\n                return nil\n            }\n            return ClipboardData(type: type.rawValue, value: data)\n        }\n    }\n}\n\nprivate extension NSPasteboard {\n    func read(from data: [ClipboardData]) {\n        clearContents()\n\n        for item in data {\n            setData(item.value, forType: PasteboardType(rawValue: item.type))\n        }\n    }\n}\n"
  },
  {
    "path": "VirtualWormhole/Source/WireProtocol/WHPayload.swift",
    "content": "//\n//  WHPayload.swift\n//  VirtualWormhole\n//\n//  Created by Guilherme Rambo on 25/10/23.\n//\n\nimport Foundation\n\n/// Protocol adopted by types that can be sent over the guest <> host connection.\npublic protocol WHPayload: Codable, Sendable {\n    /// When `true`, the payload will be sent again if connection gets interrupted and re-established.\n    static var resendOnReconnect: Bool { get }\n\n    /// When `true`, the host will distribute the payload to all booted guests\n    /// upon receiving the payload from one of the guests.\n    static var propagateBetweenGuests: Bool { get }\n}\n\npublic extension WHPayload {\n    static var resendOnReconnect: Bool { false }\n    static var propagateBetweenGuests: Bool { false }\n}\n"
  },
  {
    "path": "VirtualWormhole/Source/WireProtocol/WHPing.swift",
    "content": "//\n//  WHPing.swift\n//  VirtualWormhole\n//\n//  Created by Guilherme Rambo on 08/03/23.\n//\n\nimport Foundation\n\nstruct WHPing: WHPayload {\n    var date = Date.now\n}\n\nstruct WHPong: WHPayload {\n    var date = Date.now\n}\n\nextension WormholePacket {\n    var isPing: Bool { payloadType == String(describing: WHPing.self) }\n    var isPong: Bool { payloadType == String(describing: WHPong.self) }\n}\n\nextension WormholePacket {\n    static var ping: WormholePacket {\n        get throws { try WormholePacket(WHPing()) }\n    }\n    static var pong: WormholePacket {\n        get throws { try WormholePacket(WHPong()) }\n    }\n}\n"
  },
  {
    "path": "VirtualWormhole/Source/WireProtocol/WormholePacket.swift",
    "content": "//\n//  WormholePacket.swift\n//  VirtualWormhole\n//\n//  Created by Guilherme Rambo on 08/03/23.\n//\n\nimport Foundation\n\nstruct WormholePacket {\n    var magic: UInt32 = Self.magicValue\n    var payloadType: String\n    var payloadLength: UInt64\n    var payload: Data\n}\n\nextension WormholePacket {\n\n    static let magicValue: UInt32 = 0x0DF0FECA\n    static let magicValueCompressed: UInt32 = 0x01F0FECA\n    static let maxUncompressedPayloadSize = 1000000\n    static let compressionAlgorithm = NSData.CompressionAlgorithm.lzma\n\n    /// The absolute minimum size an entire packet could be.\n    /// Any packet that's not at least this size has something wrong with it.\n    static let minimumSize: Int = {\n        MemoryLayout<UInt32>.size // magic\n        + 2 // payloadType // 1 byte for single character + null terminator\n        + MemoryLayout<UInt64>.size // payloadLength\n        + 1 // payload // at least 1 byte of payload data\n    }()\n}\n\n// MARK: - Encoding\n\nextension WormholePacket {\n\n    init<T: Codable>(_ payload: T) throws {\n        let data = try JSONEncoder.wormhole.encode(payload)\n        let typeName = String(describing: type(of: payload))\n\n        self.init(payloadType: typeName, payloadLength: UInt64(data.count), payload: data)\n    }\n\n    func encoded() throws -> Data {\n        var encodedMagic = magic\n        var encodedPayloadLength = payloadLength\n        var encodedPayload = payload\n\n        if payload.count >= Self.maxUncompressedPayloadSize {\n            encodedMagic = Self.magicValueCompressed\n            let compressedPayload = try (payload as NSData).compressed(using: Self.compressionAlgorithm)\n            encodedPayloadLength = UInt64(compressedPayload.count)\n            encodedPayload = compressedPayload as Data\n        }\n\n        return Data(bytes: &encodedMagic, count: MemoryLayout<UInt32>.size)\n        + Data(payloadType.utf8 + [0])\n        + Data(bytes: &encodedPayloadLength, count: MemoryLayout<UInt64>.size)\n        + encodedPayload\n    }\n\n}\n\n// MARK: - Decoding\n\nextension WormholePacket {\n\n    static func decode(from data: Data) throws -> WormholePacket {\n        guard data.count >= Self.minimumSize else {\n            throw CocoaError(.coderInvalidValue, userInfo: [NSLocalizedDescriptionKey: \"Packet data with length \\(data.count) is smaller than the minimum packet length\"])\n        }\n\n        return try data.withUnsafeBytes { buffer in\n            guard let pointer = buffer.baseAddress else {\n                throw CocoaError(.coderReadCorrupt, userInfo: [NSLocalizedDescriptionKey: \"Couldn't get buffer base address\"])\n            }\n\n            var byteOffset = 0\n\n            let magic = pointer.load(as: UInt32.self)\n\n            byteOffset += MemoryLayout<UInt32>.size\n\n            let strptr = pointer\n                .advanced(by: byteOffset)\n                .assumingMemoryBound(to: UInt8.self)\n\n            let payloadType = String(cString: strptr)\n\n            byteOffset += payloadType.count + 1\n\n            var payloadLength = pointer.loadUnaligned(fromByteOffset: byteOffset, as: UInt64.self)\n\n            byteOffset += MemoryLayout<UInt64>.size\n\n            guard UInt64(data.count) > payloadLength else {\n                throw CocoaError(.coderReadCorrupt, userInfo: [NSLocalizedDescriptionKey: \"Packet payload length \\(payloadLength) is out of bounds\"])\n            }\n\n            let upperBound = Int(byteOffset)+Int(truncatingIfNeeded: payloadLength)\n\n            guard data.count >= upperBound else {\n                throw CocoaError(.coderReadCorrupt, userInfo: [NSLocalizedDescriptionKey: \"Packet payload length \\(payloadLength) is out of bounds\"])\n            }\n\n            var payload = Data(data[byteOffset..<upperBound])\n\n            guard payload.count == Int(payloadLength) else {\n                throw CocoaError(.coderReadCorrupt, userInfo: [NSLocalizedDescriptionKey: \"Packet specified payload length \\(payloadLength), but payload has length \\(payload.count)\"])\n            }\n\n            if magic == Self.magicValueCompressed {\n                payload = try (payload as NSData).decompressed(using: Self.compressionAlgorithm) as Data\n                payloadLength = UInt64(payload.count)\n            }\n\n            return WormholePacket(\n                magic: magic,\n                payloadType: payloadType,\n                payloadLength: payloadLength,\n                payload: payload\n            )\n        }\n    }\n\n}\n\n// MARK: - Streaming\n\nimport OSLog\n\nextension WormholePacket {\n\n    static let logger = Logger(subsystem: VirtualWormholeConstants.subsystemName, category: \"WormholePacket\")\n\n    static func stream(from bytes: FileHandle.AsyncBytes) -> AsyncThrowingStream<WormholePacket, Error> {\n        AsyncThrowingStream { continuation in\n            Self.logger.debug(\"Activating stream\")\n\n            let task = Task {\n                do {\n                    var buffer = Data(capacity: WormholePacket.minimumSize)\n\n                    for try await byte in bytes {\n                        guard !Task.isCancelled else { break }\n\n//                        Self.logger.debug(\"RECV: \\(buffer.map({ String(format: \"%02X\", $0) }).joined())\")\n\n                        buffer.append(byte)\n\n                        guard buffer.count >= WormholePacket.minimumSize else { continue }\n\n                        if let packet = try? WormholePacket.decode(from: buffer) {\n                            continuation.yield(packet)\n                            buffer = Data(capacity: WormholePacket.minimumSize)\n                        }\n                    }\n\n                    Self.logger.debug(\"Stream ended/cancelled\")\n                } catch {\n                    Self.logger.error(\"Stream failed: \\(error, privacy: .public)\")\n\n                    continuation.finish(throwing: error)\n                }\n            }\n            \n            continuation.onTermination = { @Sendable _ in\n                task.cancel()\n            }\n        }\n    }\n\n}\n\nextension JSONDecoder {\n    static let wormhole = JSONDecoder()\n}\n\nextension JSONEncoder {\n    static let wormhole = JSONEncoder()\n}\n"
  },
  {
    "path": "VirtualWormhole/Source/WormholeManager.swift",
    "content": "//\n//  WormholeManager.swift\n//  VirtualWormhole\n//\n//  Created by Guilherme Rambo on 02/06/22.\n//\n\nimport Foundation\nimport Virtualization\nimport OSLog\n@preconcurrency import Combine\n\npublic typealias WHPeerID = String\n\npublic extension WHPeerID {\n    static let host = \"Host\"\n}\n\npublic enum WHConnectionSide: Hashable, CustomStringConvertible {\n    case host\n    case guest\n\n    public var description: String {\n        switch self {\n        case .host:\n            return \"Host\"\n        case .guest:\n            return \"Guest\"\n        }\n    }\n}\n\npublic final class WormholeManager: NSObject, ObservableObject, WormholeMultiplexer {\n\n    /// Singleton manager used by the VirtualBuddy app to talk\n    /// to VirtualBuddyGuest running in virtual machines.\n    public static let sharedHost = WormholeManager(for: .host)\n\n    /// Singleton manager used by the VirtualBuddyGuest app in a virtual machine\n    /// to talk to VirtualBuddy running in the host.\n    public static let sharedGuest = WormholeManager(for: .guest)\n\n    @Published private(set) var peers = [WHPeerID: WormholeChannel]()\n\n    @Published public private(set) var isConnected = false\n\n    private lazy var logger = Logger(for: Self.self)\n\n    let serviceTypes: [WormholeService.Type] = [\n        WHSharedClipboardService.self,\n        WHDarwinNotificationsService.self,\n        WHDefaultsImportService.self,\n        WHDesktopPictureService.self\n    ]\n    \n    var activeServices: [WormholeService] = []\n    \n    public let side: WHConnectionSide\n    \n    public init(for side: WHConnectionSide) {\n        self.side = side\n\n        super.init()\n    }\n\n    public func makeClient<C: WormholeServiceClient>(_ type: C.Type) throws -> C {\n        guard let service = activeServices.compactMap({ $0 as? C.ServiceType }).first else {\n            throw CocoaError(.coderInvalidValue, userInfo: [NSLocalizedDescriptionKey: \"Service unavailable.\"])\n        }\n        return C(with: service)\n    }\n\n    private var activated = false\n\n    private lazy var cancellables = Set<AnyCancellable>()\n\n    public func activate() {\n        guard !activated else { return }\n        activated = true\n        \n        logger.debug(\"Activate side \\(String(describing: self.side))\")\n\n        Task {\n            do {\n                try await activateGuestIfNeeded()\n            } catch {\n                logger.fault(\"Failed to register host peer: \\(error, privacy: .public)\")\n            }\n        }\n\n        activeServices = serviceTypes\n            .map { $0.init(with: self) }\n        activeServices.forEach { $0.activate() }\n\n        #if DEBUG\n        $peers.removeDuplicates(by: { $0.keys != $1.keys }).sink { [weak self] currentPeers in\n            guard let self = self else { return }\n            self.logger.debug(\"Peers: \\(currentPeers.keys.joined(separator: \", \"), privacy: .public)\")\n        }\n        .store(in: &cancellables)\n        #endif\n\n        Timer\n            .publish(every: VirtualWormholeConstants.pingIntervalInSeconds, tolerance: VirtualWormholeConstants.pingIntervalInSeconds * 0.5, on: .main, in: .common)\n            .autoconnect()\n            .sink { [weak self] _ in\n                guard let self = self else { return }\n\n                Task {\n                    await self.send(WHPing(), to: nil)\n                }\n            }\n            .store(in: &cancellables)\n    }\n\n    private let packetSubject = PassthroughSubject<(peerID: WHPeerID, packet: WormholePacket), Never>()\n\n    public func register(input: FileHandle, output: FileHandle, for peerID: WHPeerID) async {\n        if let existing = peers[peerID] {\n            await existing.invalidate()\n        }\n\n        let channel = await WormholeChannel(\n            input: input,\n            output: output,\n            peerID: peerID\n        ).onPacketReceived { [weak self] senderID, packet in\n            guard let self = self else { return }\n            self.packetSubject.send((senderID, packet))\n        }\n\n        /// When running in guest mode, observe the channel's connection state and bind it to the manager's state.\n        if self.side == .guest {\n            await channel.$isConnected.removeDuplicates().receive(on: DispatchQueue.main).sink { [weak self] isConnected in\n                guard let self = self else { return }\n                self.logger.notice(\"Connection to host changed state (isConnected = \\(isConnected, privacy: .public))\")\n                self.isConnected = isConnected\n            }.store(in: &cancellables)\n        }\n\n        peers[peerID] = channel\n\n        await channel.activate()\n    }\n\n    public func unregister(_ peerID: WHPeerID) async {\n        guard let channel = peers[peerID] else { return }\n\n        await channel.invalidate()\n\n        peers[peerID] = nil\n    }\n\n    public func send<T: WHPayload>(_ payload: T, to peerID: WHPeerID?) async {\n        guard !peers.isEmpty else { return }\n        \n        if side == .guest {\n            guard peerID == nil || peerID == .host else {\n                logger.fault(\"Guest can only send messages to host!\")\n                assertionFailure(\"Guest can only send messages to host!\")\n                return\n            }\n        }\n\n        do {\n            let packet = try WormholePacket(payload)\n\n            if let peerID {\n                guard let channel = peers[peerID] else {\n                    logger.error(\"Couldn't find channel for peer \\(peerID)\")\n                    return\n                }\n\n                /// Message will be repeated if other side disconnects and reconnects.\n                if T.resendOnReconnect {\n                    await channel.connected {\n                        do {\n                            /// Make sure there's a fresh packet every time the message is sent.\n                            let newPacket = try WormholePacket(payload)\n\n                            try await $0.send(newPacket)\n                        } catch {\n                            assertionFailure(\"Failed to send packet: \\(error)\")\n                        }\n                    }\n                } else {\n                    try await channel.send(packet)\n                }\n            } else {\n                for channel in peers.values {\n                    try await channel.send(packet)\n                }\n            }\n        } catch {\n            logger.fault(\"Failed to send packet: \\(error, privacy: .public)\")\n            assertionFailure(\"Failed to send packet: \\(error)\")\n        }\n    }\n\n    public func stream<T: WHPayload>(for payloadType: T.Type) -> AsyncThrowingStream<(senderID: WHPeerID, payload: T), Error> {\n        AsyncThrowingStream { [weak self] continuation in\n            guard let self = self else {\n                continuation.finish()\n                return\n            }\n\n            let typeName = String(describing: payloadType)\n\n            let cancellable = self.packetSubject\n                .filter { $0.packet.payloadType == typeName }\n                .sink { [weak self] peerID, packet in\n                    guard let self = self else { return }\n\n                    guard let decodedPayload = try? JSONDecoder().decode(payloadType, from: packet.payload) else { return }\n\n                    self.propagateIfNeeded(packet, type: payloadType, from: peerID)\n\n                    continuation.yield((peerID, decodedPayload))\n                }\n\n            continuation.onTermination = { @Sendable _ in\n                cancellable.cancel()\n            }\n        }\n    }\n\n    private func propagateIfNeeded<P: WHPayload>(_ packet: WormholePacket, type: P.Type, from senderID: WHPeerID) {\n        guard type.propagateBetweenGuests, VirtualWormholeConstants.payloadPropagationEnabled else { return }\n\n        let propagationChannels = self.peers.filter({ $0.key != senderID })\n        \n        Task {\n            for (id, channel) in propagationChannels {\n                do {\n                    if VirtualWormholeConstants.verboseLoggingEnabled {\n                        logger.debug(\"⬆️ PROPAGATE \\(packet.payloadType, privacy: .public) from \\(senderID, privacy: .public) to \\(id, privacy: .public)\")\n                    }\n                    try await channel.send(packet)\n                } catch {\n                    logger.error(\"Packet propagation to \\(id, privacy: .public) failed: \\(error, privacy: .public)\")\n                }\n            }\n        }\n    }\n\n    // MARK: - Ping\n\n    /// Waits for a connection with the given peer.\n    private func wait(for peerID: WHPeerID) async {\n        guard let channel = peers[peerID] else {\n            logger.error(\"Can't wait for peer \\(peerID) for which a channel doesn't exist\")\n            return\n        }\n\n        guard await channel.isConnected == false else { return }\n\n        for await state in await channel.$isConnected.values {\n            guard state else { continue }\n            break\n        }\n    }\n\n    /// Performs the specified asynchronous closure whenever the connection state for the peer changes\n    /// from not connected to connected. Also runs the closure if peer is already connected at the time of calling.\n    private func connected(to peerID: WHPeerID, perform block: @escaping (WormholeChannel) async -> Void) async {\n        guard let channel = peers[peerID] else {\n            logger.error(\"Can't wait for peer \\(peerID) for which a channel doesn't exist\")\n            return\n        }\n\n        await channel.connected(perform: block)\n    }\n\n    // MARK: - Service Interfaces\n\n    private func service<T: WormholeService>(_ serviceType: T.Type) -> T? {\n        activeServices.first(where: { type(of: $0).id == serviceType.id }) as? T\n    }\n\n    public func darwinNotifications(matching names: Set<String>, from peerID: WHPeerID) async throws -> AsyncStream<String> {\n        try ensurePeerAvailable(peerID)\n        \n        guard let notificationService = service(WHDarwinNotificationsService.self) else {\n            throw CocoaError(.coderValueNotFound, userInfo: [NSLocalizedDescriptionKey: \"Darwin notifications service not available\"])\n        }\n\n        try Task.checkCancellation()\n\n        for name in names {\n            await send(DarwinNotificationMessage.subscribe(name), to: peerID)\n        }\n\n        var iterator = notificationService.onPeerNotificationReceived.values\n            .filter { $0.peerID == peerID }\n            .map(\\.name)\n            .makeAsyncIterator()\n\n        return AsyncStream { await iterator.next() }\n    }\n\n    public func desktopPictureMessages(from peerID: WHPeerID) async throws -> AsyncStream<DesktopPictureMessage> {\n        try ensurePeerAvailable(peerID)\n\n        guard let desktopPictureService = service(WHDesktopPictureService.self) else {\n            throw CocoaError(.coderValueNotFound, userInfo: [NSLocalizedDescriptionKey: \"Desktop picture service not available\"])\n        }\n\n        try Task.checkCancellation()\n\n        var iterator = desktopPictureService.onPeerPeerDesktopPictureReceived.values\n            .filter { $0.peerID == peerID }\n            .map(\\.message)\n            .makeAsyncIterator()\n\n        return AsyncStream { await iterator.next() }\n    }\n\n    /// Can be called on the guest to force-send the current desktop picture.\n    public func sendDesktopPicture() async {\n        guard side == .guest else { return }\n\n        do {\n            guard let desktopPictureService = service(WHDesktopPictureService.self) else {\n                throw CocoaError(.coderValueNotFound, userInfo: [NSLocalizedDescriptionKey: \"Desktop picture service not available\"])\n            }\n\n            logger.debug(\"Sending desktop picture\")\n\n            await desktopPictureService.sendDesktopPicture()\n\n            logger.debug(\"Finished sending desktop picture\")\n        } catch {\n            logger.error(\"Send desktop picture failed - \\(error, privacy: .public)\")\n        }\n    }\n\n    private func ensurePeerAvailable(_ peerID: WHPeerID) throws {\n        guard peers[peerID] != nil else {\n            throw CocoaError(.coderValueNotFound, userInfo: [NSLocalizedDescriptionKey: \"Peer \\(peerID) is not registered\"])\n        }\n    }\n\n    // MARK: - Guest Mode\n\n    private let ttyPath = \"/dev/cu.virtio\"\n\n    private var hostOutputHandle: FileHandle {\n        get throws {\n            try FileHandle(forReadingFrom: URL(fileURLWithPath: ttyPath))\n        }\n    }\n\n    private var hostInputHandle: FileHandle {\n        get throws {\n            try FileHandle(forWritingTo: URL(fileURLWithPath: ttyPath))\n        }\n    }\n\n    private func activateGuestIfNeeded() async throws {\n        guard side == .guest else { return }\n\n        configureTTY()\n\n        logger.debug(\"Running in guest mode, registering host peer\")\n\n        let input = try hostOutputHandle\n        let output = try hostInputHandle\n\n        await register(input: input, output: output, for: .host)\n    }\n\n    private func configureTTY() {\n        do {\n            let proc = Process()\n            proc.executableURL = URL(fileURLWithPath: \"/bin/stty\")\n            proc.arguments = [\n                \"-f\",\n                ttyPath,\n                \"115200\"\n            ]\n            let errPipe = Pipe()\n            let outPipe = Pipe()\n            proc.standardError = errPipe\n            proc.standardOutput = outPipe\n\n            try proc.run()\n            proc.waitUntilExit()\n\n            if let errData = try? errPipe.fileHandleForReading.readToEnd(), !errData.isEmpty {\n                logger.debug(\"stty stdout: \\(String(decoding: errData, as: UTF8.self), privacy: .public)\")\n            }\n            if let outData = try? outPipe.fileHandleForReading.readToEnd(), !outData.isEmpty {\n                logger.debug(\"stty stderr: \\(String(decoding: outData, as: UTF8.self), privacy: .public)\")\n            }\n        } catch {\n            logger.error(\"stty error: \\(error, privacy: .public)\")\n        }\n    }\n\n}\n\n// MARK: - Channel Actor\n\nactor WormholeChannel: ObservableObject {\n\n    let input: FileHandle\n    let output: FileHandle\n    let peerID: WHPeerID\n    private let logger: Logger\n\n    init(input: FileHandle, output: FileHandle, peerID: WHPeerID) {\n        self.input = input\n        self.output = output\n        self.peerID = peerID\n        self.logger = Logger(subsystem: VirtualWormholeConstants.subsystemName, category: \"WormholeChannel-\\(peerID)\")\n    }\n\n    @Published private(set) var isConnected = false {\n        didSet {\n            guard isConnected != oldValue else { return }\n            logger.debug(\"isConnected = \\(self.isConnected, privacy: .public)\")\n        }\n    }\n\n    private let packetSubject = PassthroughSubject<WormholePacket, Never>()\n\n    private lazy var cancellables = Set<AnyCancellable>()\n\n    @discardableResult\n    func onPacketReceived(perform block: @escaping (WHPeerID, WormholePacket) -> Void) -> Self {\n        packetSubject.sink { [weak self] packet in\n            guard let self = self else { return }\n\n            block(self.peerID, packet)\n        }\n        .store(in: &cancellables)\n\n        return self\n    }\n\n    private var activated = false\n\n    func activate() {\n        guard !activated else { return }\n        activated = true\n\n        logger.debug(#function)\n\n        stream()\n    }\n\n    func invalidate() {\n        guard activated else { return }\n        activated = false\n\n        logger.debug(#function)\n\n        cancellables.removeAll()\n\n        timeoutTask?.cancel()\n\n        internalTasks.forEach { $0.cancel() }\n        internalTasks.removeAll()\n    }\n\n    func send(_ packet: WormholePacket) async throws {\n        let data = try packet.encoded()\n\n        if VirtualWormholeConstants.verboseLoggingEnabled {\n            if !packet.isPing, !packet.isPong {\n                logger.debug(\"⬆️ SEND \\(packet.payloadType, privacy: .public) (\\(packet.payload.count) bytes)\")\n                logger.debug(\"⏫ \\(data.map({ String(format: \"%02X\", $0) }).joined(), privacy: .public)\")\n            }\n        }\n\n        try output.write(contentsOf: data)\n    }\n\n    private var internalTasks = [Task<Void, Never>]()\n\n    private func stream() {\n        logger.debug(#function)\n\n        let streamingTask = Task {\n            do {\n                for try await packet in WormholePacket.stream(from: input.bytes) {\n                    if VirtualWormholeConstants.verboseLoggingEnabled {\n                        if !packet.isPing, !packet.isPong {\n                            logger.debug(\"⬇️ RECEIVE \\(packet.payloadType, privacy: .public) (\\(packet.payload.count) bytes)\")\n                            logger.debug(\"⏬ \\(packet.payload.map({ String(format: \"%02X\", $0) }).joined(), privacy: .public)\")\n                        }\n                    }\n                    \n                    guard !Task.isCancelled else { break }\n\n                    guard !packet.isPing && !packet.isPong else {\n                        await handlePingPong(packet)\n                        continue\n                    }\n\n                    packetSubject.send(packet)\n                }\n\n                logger.debug(\"⬇️ Packet streaming cancelled\")\n            } catch {\n                logger.error(\"⬇️ Serial read failure: \\(error, privacy: .public)\")\n\n                try? await Task.sleep(nanoseconds: 1 * NSEC_PER_SEC)\n\n                guard !Task.isCancelled else { return }\n\n                stream()\n            }\n        }\n        internalTasks.append(streamingTask)\n    }\n\n    private var timeoutTask: Task<Void, Never>?\n\n    private func handlePingPong(_ packet: WormholePacket) async {\n        self.isConnected = true\n\n        if packet.isPing {\n            if VirtualWormholeConstants.verboseLoggingEnabled {\n                logger.debug(\"🏓 Received ping\")\n            }\n\n            do {\n                try await send(.pong)\n            } catch {\n                logger.error(\"🏓 Pong send failure: \\(error, privacy: .public)\")\n            }\n        } else {\n            if VirtualWormholeConstants.verboseLoggingEnabled {\n                logger.debug(\"🏓 Received pong\")\n            }\n        }\n\n        timeoutTask?.cancel()\n\n        timeoutTask = Task {\n            try? await Task.sleep(nanoseconds: VirtualWormholeConstants.connectionTimeoutInNanoseconds)\n\n            guard !Task.isCancelled else { return }\n\n            logger.warning(\"🏓 Connection timed out\")\n\n            self.isConnected = false\n        }\n    }\n\n    func connected(perform block: @escaping (WormholeChannel) async -> Void) {\n        let task = Task { [weak self] in\n            guard let self = self else { return }\n\n            for await state in await self.$isConnected.removeDuplicates().values {\n                if state {\n                    await block(self)\n                }\n            }\n        }\n        internalTasks.append(task)\n    }\n\n}\n"
  },
  {
    "path": "VirtualWormhole/VirtualWormhole.h",
    "content": "//\n//  VirtualWormhole.h\n//  VirtualWormhole\n//\n//  Created by Guilherme Rambo on 02/06/22.\n//\n\n#import <Foundation/Foundation.h>\n\n//! Project version number for VirtualWormhole.\nFOUNDATION_EXPORT double VirtualWormholeVersionNumber;\n\n//! Project version string for VirtualWormhole.\nFOUNDATION_EXPORT const unsigned char VirtualWormholeVersionString[];\n\n// In this header, you should import all the public headers of your framework using statements like #import <VirtualWormhole/PublicHeader.h>\n\n#import <VirtualWormhole/NSImage+DesktopPicture.h>\n"
  },
  {
    "path": "VirtualWormholeTests/WormholePacketTests.swift",
    "content": "//\n//  VirtualWormholeTests.swift\n//  VirtualWormholeTests\n//\n//  Created by Guilherme Rambo on 08/03/23.\n//\n\nimport XCTest\n@testable import VirtualWormhole\n\nfinal class WormholePacketTests: XCTestCase {\n\n    func testPacketEncodingWithTestPayload() throws {\n        let payload = TestPayload()\n        let packet = try WormholePacket(payload)\n        let data = try packet.encoded()\n\n        XCTAssertEqual(data.hexDump, \"CAFEF00D546573745061796C6F6164003A000000000000007B226D657373616765223A2248656C6C6F2C20576F726C6421222C226E756D626572223A34322C2264617461223A227172764D3365375C2F227D\")\n    }\n\n    func testPacketDecodingWithTestPayload() throws {\n        let payload = TestPayload()\n        let packet = try WormholePacket(payload)\n        let data = try packet.encoded()\n\n        let decodedPacket = try WormholePacket.decode(from: data)\n\n        XCTAssertEqual(decodedPacket.magic, packet.magic)\n        XCTAssertEqual(decodedPacket.payloadType, packet.payloadType)\n        XCTAssertEqual(decodedPacket.payloadLength, packet.payloadLength)\n        XCTAssertEqual(decodedPacket.payload.count, packet.payload.count)\n        XCTAssertEqual(decodedPacket.payload, packet.payload)\n    }\n\n    func testPacketDecodingRespectsLength() throws {\n        let payload = TestPayload()\n\n        var packet = try WormholePacket(payload)\n        packet.payloadLength = 1\n\n        let data = try packet.encoded()\n\n        let decodedPacket = try WormholePacket.decode(from: data)\n        XCTAssertEqual(Int(packet.payloadLength), decodedPacket.payload.count)\n    }\n\n    func testPacketStreaming() async throws {\n        let handle = FileHandle.testStream\n\n        var packets = [WormholePacket]()\n\n        for try await packet in WormholePacket.stream(from: handle.bytes) {\n            packets.append(packet)\n\n            guard packets.count < 6 else { break }\n        }\n\n        XCTAssertEqual(packets.count, 6)\n\n        for packet in packets {\n            XCTAssertEqual(packet.magic, 0x0DF0FECA)\n            XCTAssertEqual(packet.payloadType, \"TestPayload\")\n            XCTAssertEqual(packet.payloadLength, 58)\n            XCTAssertEqual(packet.payload.count, 58)\n            XCTAssertEqual(packet.payload, Data(\"{\\\"message\\\":\\\"Hello, World!\\\",\\\"number\\\":42,\\\"data\\\":\\\"qrvM3e7\\\\/\\\"}\".utf8))\n        }\n    }\n\n    func testCompressedPacketEncodeDecode() async throws {\n        let payload = TestPayload(data: .empty(count: WormholePacket.maxUncompressedPayloadSize + 1))\n\n        let packet = try WormholePacket(payload)\n\n        let data = try packet.encoded()\n\n        let decodedPacket = try WormholePacket.decode(from: data)\n\n        XCTAssertEqual(decodedPacket.magic, WormholePacket.magicValueCompressed)\n        XCTAssertEqual(decodedPacket.payload, packet.payload)\n    }\n\n}\n\nstruct TestPayload: Codable {\n    var message = \"Hello, World!\"\n    var number = 42\n    var data = Data([0xAA,0xBB,0xCC,0xDD,0xEE,0xFF])\n}\n\nextension Data {\n    var hexDump: String {\n        map { String(format: \"%02X\", $0) }.joined()\n    }\n\n    static func empty(count: Int) -> Data {\n        let bytes = [UInt8](repeating: 0, count: count)\n        return Data(bytes)\n    }\n}\n\nextension FileHandle {\n    static var testStream: FileHandle {\n        guard let url = Bundle(for: WormholePacketTests.self).url(forResource: \"TestStream\", withExtension: \"bin\") else {\n            fatalError(\"Missing TestStream.bin in WormholeTests bundle!\")\n        }\n        return try! FileHandle(forReadingFrom: url)\n    }\n}\n"
  },
  {
    "path": "data/ipsws_v1.json",
    "content": "{\n  \"apiVersion\": 1,\n  \"channels\": [\n    {\n      \"id\": \"regular\",\n      \"name\": \"Release\",\n      \"note\": \"Public, stable releases.\",\n      \"icon\": \"checkmark.seal\"\n    },\n    {\n      \"id\": \"devbeta\",\n      \"name\": \"Developer Beta\",\n      \"note\": \"Betas meant for developer testing.\",\n      \"icon\": \"wrench.and.screwdriver\",\n      \"authentication\": {\n        \"name\": \"Developer Portal\",\n        \"url\": \"https://developer.apple.com/download\",\n        \"note\": \"Perform the authentication using the web view that will be opened. Your credentials will be sent directly to Apple and will not be stored locally or on any servers.\"\n      }\n    },\n    {\n      \"id\": \"pubbeta\",\n      \"name\": \"Public Beta\",\n      \"note\": \"Betas meant for customer testing.\",\n      \"icon\": \"sun.and.horizon\"\n    },\n    {\n      \"id\": \"seed\",\n      \"name\": \"Apple Seed\",\n      \"note\": \"Private betas for select customers.\",\n      \"icon\": \"tree\",\n      \"authentication\": {\n        \"name\": \"AppleSeed Portal\",\n        \"url\": \"https://appleseed.apple.com/\",\n        \"note\": \"Perform the authentication using the web view that will be opened. Your credentials will be sent directly to Apple and will not be stored locally or on any servers.\"\n      }\n    }\n  ],\n  \"groups\": [\n    {\n      \"id\": \"sequoia\",\n      \"name\": \"macOS Sequoia\",\n      \"majorVersion\": \"15.0\",\n      \"minHostVersion\": \"14.0\"\n    },\n    {\n      \"id\": \"sonoma\",\n      \"name\": \"macOS Sonoma\",\n      \"majorVersion\": \"14.0\",\n      \"minHostVersion\": \"13.0\"\n    },\n    {\n      \"id\": \"ventura\",\n      \"name\": \"macOS Ventura\",\n      \"majorVersion\": \"13.0\",\n      \"minHostVersion\": \"12.0\"\n    },\n    {\n      \"id\": \"monterey\",\n      \"name\": \"macOS Monterey\",\n      \"majorVersion\": \"12.0\",\n      \"minHostVersion\": \"12.3\"\n    }\n  ],\n  \"restoreImages\": [\n    {\n      \"group\": \"sequoia\",\n      \"name\": \"macOS 15.5\",\n      \"build\": \"24F74\",\n      \"url\": \"https://updates.cdn-apple.com/2025SpringFCS/fullrestores/082-44534/CE6C1054-99A3-4F67-A823-3EE9E6510CDE/UniversalMac_15.5_24F74_Restore.ipsw\",\n      \"channel\": \"regular\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sequoia\",\n      \"name\": \"macOS 15.5 Developer Beta 4\",\n      \"build\": \"24F5068b\",\n      \"url\": \"https://updates.cdn-apple.com/2025SpringSeed/fullrestores/082-42002/44CF75E3-7765-4356-A88C-5EEEC4471CA4/UniversalMac_15.5_24F5068b_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sequoia\",\n      \"name\": \"macOS 15.5 Developer Beta 3\",\n      \"build\": \"24F5053j\",\n      \"url\": \"https://updates.cdn-apple.com/2025SpringSeed/fullrestores/072-92980/8E6DFD3B-8438-423F-8FFE-A95234775E84/UniversalMac_15.5_24F5053j_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sequoia\",\n      \"name\": \"macOS 15.5 Developer Beta 2\",\n      \"build\": \"24F5053f\",\n      \"url\": \"https://updates.cdn-apple.com/2025SpringSeed/fullrestores/082-24561/B199D1B2-296D-43E5-A29A-5FC27796F36D/UniversalMac_15.5_24F5053f_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sequoia\",\n      \"name\": \"macOS 15.5 Developer Beta 1\",\n      \"build\": \"24F5042g\",\n      \"url\": \"https://updates.cdn-apple.com/2025SpringSeed/fullrestores/072-94893/B85463BB-3ED6-4A5A-BE78-92AD86B531D7/UniversalMac_15.5_24F5042g_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sequoia\",\n      \"name\": \"macOS 15.4.1\",\n      \"build\": \"24E263\",\n      \"url\": \"https://updates.cdn-apple.com/2025SpringFCS/fullrestores/082-23340/61923341-EE73-4C6E-BB3E-DAB3069548BF/UniversalMac_15.4.1_24E263_Restore.ipsw\",\n      \"channel\": \"regular\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sequoia\",\n      \"name\": \"macOS 15.4\",\n      \"build\": \"24E248\",\n      \"url\": \"https://updates.cdn-apple.com/2025SpringFCS/fullrestores/082-16517/AACDDC33-9683-4431-98AF-F04EF7C15EE3/UniversalMac_15.4_24E248_Restore.ipsw\",\n      \"channel\": \"regular\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sequoia\",\n      \"name\": \"macOS 15.4 RC 2\",\n      \"build\": \"24E247\",\n      \"url\": \"https://updates.cdn-apple.com/2025SpringFCS/fullrestores/082-13483/7D547A66-A1A6-48C1-A8E1-FE78559BDD34/UniversalMac_15.4_24E247_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sequoia\",\n      \"name\": \"macOS 15.4 RC\",\n      \"build\": \"24E246\",\n      \"url\": \"https://updates.cdn-apple.com/2025SpringFCS/fullrestores/082-13013/C3CAC9AF-B139-4924-BE1B-10ED6EF931A4/UniversalMac_15.4_24E246_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sequoia\",\n      \"name\": \"macOS 15.4 Developer Beta 4\",\n      \"build\": \"24E5238a\",\n      \"url\": \"https://updates.cdn-apple.com/2025WinterSeed/fullrestores/082-08323/FD7D7539-514F-4F2E-AD3B-4D6D0D248706/UniversalMac_15.4_24E5238a_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sequoia\",\n      \"name\": \"macOS 15.4 Developer Beta 3\",\n      \"build\": \"24E5228e\",\n      \"url\": \"https://updates.cdn-apple.com/2025WinterSeed/fullrestores/072-96972/D76A7AE6-3A35-4295-B102-E5FC5C84B4DC/UniversalMac_15.4_24E5228e_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sequoia\",\n      \"name\": \"macOS 15.4 Developer Beta 2\",\n      \"build\": \"24E5222f\",\n      \"url\": \"https://updates.cdn-apple.com/2025WinterSeed/fullrestores/072-90273/76B9EE73-0836-47FB-8CBA-E3E902ACD21A/UniversalMac_15.4_24E5222f_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sequoia\",\n      \"name\": \"macOS 15.4 Developer Beta 1\",\n      \"build\": \"24E5206s\",\n      \"url\": \"https://updates.cdn-apple.com/2025WinterSeed/fullrestores/072-92205/1FCDCB29-B8A3-4461-A98A-270F1D1121C8/UniversalMac_15.4_24E5206s_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sequoia\",\n      \"name\": \"macOS 15.3.2\",\n      \"build\": \"24D81\",\n      \"url\": \"https://updates.cdn-apple.com/2025WinterFCS/fullrestores/082-01504/828B8EF9-8134-49D5-B24A-0BA504FC5ECC/UniversalMac_15.3.2_24D81_Restore.ipsw\",\n      \"channel\": \"regular\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sequoia\",\n      \"name\": \"macOS 15.3.1\",\n      \"build\": \"24D70\",\n      \"url\": \"https://updates.cdn-apple.com/2025WinterFCS/fullrestores/072-70618/42F1A8CC-7E07-4329-958A-757FF600C303/UniversalMac_15.3.1_24D70_Restore.ipsw\",\n      \"channel\": \"regular\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sequoia\",\n      \"name\": \"macOS 15.3\",\n      \"build\": \"24D60\",\n      \"url\": \"https://updates.cdn-apple.com/2025WinterFCS/fullrestores/072-08269/7CAAB9F7-E970-428D-8764-4CD7BCD105CD/UniversalMac_15.3_24D60_Restore.ipsw\",\n      \"channel\": \"regular\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sequoia\",\n      \"name\": \"macOS 15.3 Developer Beta 3\",\n      \"build\": \"24D5055b\",\n      \"url\": \"https://updates.cdn-apple.com/2025WinterSeed/fullrestores/072-61910/64022727-D907-4A4F-963E-CADF5C74E1A7/UniversalMac_15.3_24D5055b_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sequoia\",\n      \"name\": \"macOS 15.3 Developer Beta 2\",\n      \"build\": \"24D5040f\",\n      \"url\": \"https://updates.cdn-apple.com/2025WinterSeed/fullrestores/072-51416/CB0B4DD0-1512-47FF-91B8-862ECB6CD2CF/UniversalMac_15.3_24D5040f_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sequoia\",\n      \"name\": \"macOS 15.3 Developer Beta 1\",\n      \"build\": \"24D5034f\",\n      \"url\": \"https://updates.cdn-apple.com/2024FallSeed/fullrestores/072-29298/D5353B95-E6B5-42DA-8DC3-664A28FFB1B9/UniversalMac_15.3_24D5034f_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sequoia\",\n      \"name\": \"macOS 15.2\",\n      \"build\": \"24C101\",\n      \"url\": \"https://updates.cdn-apple.com/2024FallFCS/fullrestores/072-44245/E811A1B0-28A9-4FCD-AE32-322E796F0EB8/UniversalMac_15.2_24C101_Restore.ipsw\",\n      \"channel\": \"regular\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sequoia\",\n      \"name\": \"macOS 15.2 RC 2\",\n      \"build\": \"24C100\",\n      \"url\": \"https://updates.cdn-apple.com/2024FallFCS/fullrestores/072-39903/D467F0BB-9D59-4DCF-A355-DD200CE808A8/UniversalMac_15.2_24C100_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sequoia\",\n      \"name\": \"macOS 15.2 RC\",\n      \"build\": \"24C98\",\n      \"url\": \"https://updates.cdn-apple.com/2024FallFCS/fullrestores/072-38683/C48192CB-7F42-4DD2-9EE6-0F6691DC088E/UniversalMac_15.2_24C98_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sequoia\",\n      \"name\": \"macOS 15.2 Developer Beta 4\",\n      \"build\": \"24C5089c\",\n      \"url\": \"https://updates.cdn-apple.com/2024FallSeed/fullrestores/072-35468/AF95DCD4-137A-4E7D-A5D8-E12009F4B0EF/UniversalMac_15.2_24C5089c_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sequoia\",\n      \"name\": \"macOS 15.2 Developer Beta 3\",\n      \"build\": \"24C5079e\",\n      \"url\": \"https://updates.cdn-apple.com/2024FallSeed/fullrestores/072-18002/44DBAF90-E56D-43E0-81F1-016B12E7FE27/UniversalMac_15.2_24C5079e_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sequoia\",\n      \"name\": \"macOS 15.2 Developer Beta 2\",\n      \"build\": \"24C5073e\",\n      \"url\": \"https://updates.cdn-apple.com/2024FallSeed/fullrestores/072-11046/F7A6FD60-0556-437D-88B6-6F2DD2CD8B96/UniversalMac_15.2_24C5073e_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sequoia\",\n      \"name\": \"macOS 15.1.1 (24B2091)\",\n      \"build\": \"24B2091\",\n      \"url\": \"https://updates.cdn-apple.com/2024FallFCS/fullrestores/072-29960/5EEC3C20-D7CB-4DD1-9CE4-7C177F531A41/UniversalMac_15.1.1_24B2091_Restore.ipsw\",\n      \"channel\": \"regular\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sequoia\",\n      \"name\": \"macOS 15.1.1\",\n      \"build\": \"24B91\",\n      \"url\": \"https://updates.cdn-apple.com/2024FallFCS/fullrestores/072-30094/44BD016F-6EE3-4EE5-8890-6F9AA008C537/UniversalMac_15.1.1_24B91_Restore.ipsw\",\n      \"channel\": \"regular\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sequoia\",\n      \"name\": \"macOS 15.1 (24B2083)\",\n      \"build\": \"24B2083\",\n      \"url\": \"https://updates.cdn-apple.com/2024FallFCS/fullrestores/072-12302/3786987A-AD94-4BFB-81B8-56D3841CA81B/UniversalMac_15.1_24B2083_Restore.ipsw\",\n      \"channel\": \"regular\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sequoia\",\n      \"name\": \"macOS 15.1\",\n      \"build\": \"24B83\",\n      \"url\": \"https://updates.cdn-apple.com/2024FallFCS/fullrestores/072-12340/78D28AC4-CCFC-45D2-BD27-1E5D915E43F9/UniversalMac_15.1_24B83_Restore.ipsw\",\n      \"channel\": \"regular\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sequoia\",\n      \"name\": \"macOS 15.1 RC\",\n      \"build\": \"24B82\",\n      \"url\": \"https://updates.cdn-apple.com/2024FallFCS/fullrestores/062-24344/4BBFA6BD-C58F-4C82-B793-6ECA98024379/UniversalMac_15.1_24B82_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sequoia\",\n      \"name\": \"macOS 15.1 Developer Beta 6\",\n      \"build\": \"24B5070a\",\n      \"url\": \"https://updates.cdn-apple.com/2024FallSeed/fullrestores/072-00383/7EB106DF-9B03-4C2A-ACD1-997B0BFF9364/UniversalMac_15.1_24B5070a_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sequoia\",\n      \"name\": \"macOS 15.1 Developer Beta 5\",\n      \"build\": \"24B5055e\",\n      \"url\": \"https://updates.cdn-apple.com/2024FallSeed/fullrestores/062-91081/FADE991A-C188-4D32-8F97-313F57E27359/UniversalMac_15.1_24B5055e_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sequoia\",\n      \"name\": \"macOS 15.1 Developer Beta 4\",\n      \"build\": \"24B5046f\",\n      \"url\": \"https://updates.cdn-apple.com/2024FallSeed/fullrestores/062-80453/61A07BD2-553D-425D-8B93-7E5A730AA4CA/UniversalMac_15.1_24B5046f_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sequoia\",\n      \"name\": \"macOS 15.1 Developer Beta 3\",\n      \"build\": \"24B5035e\",\n      \"url\": \"https://updates.cdn-apple.com/2024SummerSeed/fullrestores/062-74506/D50AC8F9-4795-4711-9C1A-907B6EB829A2/UniversalMac_15.1_24B5035e_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sequoia\",\n      \"name\": \"macOS 15.0.1\",\n      \"build\": \"24A348\",\n      \"url\": \"https://updates.cdn-apple.com/2024FallFCS/fullrestores/072-01423/566E5B4E-1100-4643-91B3-131247351844/UniversalMac_15.0.1_24A348_Restore.ipsw\",\n      \"channel\": \"regular\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sequoia\",\n      \"name\": \"macOS 15.0\",\n      \"build\": \"24A335\",\n      \"url\": \"https://updates.cdn-apple.com/2024FallFCS/fullrestores/062-78489/BDA44327-C79E-4608-A7E0-455A7E91911F/UniversalMac_15.0_24A335_Restore.ipsw\",\n      \"channel\": \"regular\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sequoia\",\n      \"name\": \"macOS 15.0 Developer Beta 8\",\n      \"build\": \"24A5331b\",\n      \"url\": \"https://updates.cdn-apple.com/2024SummerSeed/fullrestores/062-71949/A67919DD-2AAC-4324-99BF-70765065DD70/UniversalMac_15.0_24A5331b_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sequoia\",\n      \"name\": \"macOS 15.0 Developer Beta 7\",\n      \"build\": \"24A5327a\",\n      \"url\": \"https://updates.cdn-apple.com/2024SummerSeed/fullrestores/062-64520/F98C92F6-656A-46BB-A1DB-F447698DBB72/UniversalMac_15.0_24A5327a_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sequoia\",\n      \"name\": \"macOS 15.0 Developer Beta 6\",\n      \"build\": \"24A5320a\",\n      \"url\": \"https://updates.cdn-apple.com/2024SummerSeed/fullrestores/062-59123/7B77FE01-E040-4D4A-8E93-64BBF212A351/UniversalMac_15.0_24A5320a_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sequoia\",\n      \"name\": \"macOS 15.0 Developer Beta 5\",\n      \"build\": \"24A5309e\",\n      \"url\": \"https://updates.cdn-apple.com/2024SummerSeed/fullrestores/062-45756/5B063A0C-5ECA-434B-A462-CD1F65737105/UniversalMac_15.0_24A5309e_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sequoia\",\n      \"name\": \"macOS 15.0 Developer Beta 4\",\n      \"build\": \"24A5298h\",\n      \"url\": \"https://updates.cdn-apple.com/2024SummerSeed/fullrestores/062-39288/8A58D88A-ED62-4E46-A406-13F0294EB4F5/UniversalMac_15.0_24A5298h_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sequoia\",\n      \"name\": \"macOS 15.0 Developer Beta 3 (24A5289h)\",\n      \"build\": \"24A5289h\",\n      \"url\": \"https://updates.cdn-apple.com/2024SummerSeed/fullrestores/062-36090/83523325-6540-4A17-BA93-A5849E4E9AC2/UniversalMac_15.0_24A5289h_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sequoia\",\n      \"name\": \"macOS 15.0 Developer Beta 3\",\n      \"build\": \"24A5289g\",\n      \"url\": \"https://updates.cdn-apple.com/2024SummerSeed/fullrestores/062-33928/E22622FB-DAFC-4C64-8CC6-B7CAF89477F7/UniversalMac_15.0_24A5289g_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sequoia\",\n      \"name\": \"macOS 15.0 Developer Beta 2\",\n      \"build\": \"24A5279h\",\n      \"url\": \"https://updates.cdn-apple.com/2024SummerSeed/fullrestores/062-22022/AB066FFB-B7FE-4132-83AC-E58A323805C1/UniversalMac_15.0_24A5279h_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sequoia\",\n      \"name\": \"macOS 15.0 Developer Beta 1\",\n      \"build\": \"24A5264n\",\n      \"url\": \"https://updates.cdn-apple.com/2024SummerSeed/fullrestores/052-49083/ED8F54D6-A7BF-488A-85E5-617E08C41383/UniversalMac_15.0_24A5264n_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sonoma\",\n      \"name\": \"macOS 14.6.1\",\n      \"build\": \"23G93\",\n      \"url\": \"https://updates.cdn-apple.com/2024SummerFCS/fullrestores/062-52859/932E0A8F-6644-4759-82DA-F8FA8DEA806A/UniversalMac_14.6.1_23G93_Restore.ipsw\",\n      \"channel\": \"regular\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sonoma\",\n      \"name\": \"macOS 14.6\",\n      \"build\": \"23G80\",\n      \"url\": \"https://updates.cdn-apple.com/2024SummerFCS/fullrestores/052-69922/F5DA2B64-25EB-4370-9E89-FA5689859796/UniversalMac_14.6_23G80_Restore.ipsw\",\n      \"channel\": \"regular\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sonoma\",\n      \"name\": \"macOS 14.6 Developer Beta 4\",\n      \"build\": \"23G5075b\",\n      \"url\": \"https://updates.cdn-apple.com/2024SpringSeed/fullrestores/062-37092/7D35E462-CD30-4B65-A1D6-D2AF0DA9AED8/UniversalMac_14.6_23G5075b_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sonoma\",\n      \"name\": \"macOS 14.6 Developer Beta 3\",\n      \"build\": \"23G5066c\",\n      \"url\": \"https://updates.cdn-apple.com/2024SpringSeed/fullrestores/062-28262/A4400A1D-9F38-41F7-B1CD-B3CB782DA2A3/UniversalMac_14.6_23G5066c_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sonoma\",\n      \"name\": \"macOS 14.6 Developer Beta 2\",\n      \"build\": \"23G5061b\",\n      \"url\": \"https://updates.cdn-apple.com/2024SpringSeed/fullrestores/062-20934/B03E5DE3-1484-489F-AC53-956AC17DB7F0/UniversalMac_14.6_23G5061b_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sonoma\",\n      \"name\": \"macOS 14.6 Developer Beta 1\",\n      \"build\": \"23G5052d\",\n      \"url\": \"https://updates.cdn-apple.com/2024SpringSeed/fullrestores/052-91270/121EBE77-A313-4250-9F8D-55F2AABBCD4C/UniversalMac_14.6_23G5052d_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sonoma\",\n      \"name\": \"macOS 14.5\",\n      \"build\": \"23F79\",\n      \"url\": \"https://updates.cdn-apple.com/2024SpringFCS/fullrestores/062-01897/C874907B-9F82-4109-87EB-6B3C9BF1507D/UniversalMac_14.5_23F79_Restore.ipsw\",\n      \"channel\": \"regular\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sonoma\",\n      \"name\": \"macOS 14.5 Developer Beta 4\",\n      \"build\": \"23F5074a\",\n      \"url\": \"https://updates.cdn-apple.com/2024SpringSeed/fullrestores/052-93222/8E2C4935-6FAA-4624-88D5-008966BD1A4C/UniversalMac_14.5_23F5074a_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sonoma\",\n      \"name\": \"macOS 14.5 Developer Beta 3\",\n      \"build\": \"23F5064f\",\n      \"url\": \"https://updates.cdn-apple.com/2024SpringSeed/fullrestores/052-86827/74D9C6E6-396C-451B-8ACD-DB523D9FA93F/UniversalMac_14.5_23F5064f_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sonoma\",\n      \"name\": \"macOS 14.5 Developer Beta 2\",\n      \"build\": \"23F5059e\",\n      \"url\": \"https://updates.cdn-apple.com/2024SpringSeed/fullrestores/052-84344/599ECB0B-BF84-449F-B0D1-1428CFFAACC5/UniversalMac_14.5_23F5059e_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sonoma\",\n      \"name\": \"macOS 14.5 Developer Beta 1\",\n      \"build\": \"23F5049f\",\n      \"url\": \"https://updates.cdn-apple.com/2024SpringSeed/fullrestores/052-63630/11AFEFAF-A420-4CAE-84E7-D66530A7E9CA/UniversalMac_14.5_23F5049f_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sonoma\",\n      \"name\": \"macOS 14.4\",\n      \"build\": \"23E214\",\n      \"url\": \"https://updates.cdn-apple.com/2024WinterFCS/fullrestores/052-61990/47F0DD06-1106-4F2E-9CD6-AE6B361A0EC6/UniversalMac_14.4_23E214_Restore.ipsw\",\n      \"channel\": \"regular\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sonoma\",\n      \"name\": \"macOS 14.4 Developer Beta 5\",\n      \"build\": \"23E5211a\",\n      \"url\": \"https://updates.cdn-apple.com/2024WinterSeed/fullrestores/052-59970/42D9D34A-DD6E-40D5-AA2B-EEB8E65EE64E/UniversalMac_14.4_23E5211a_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sonoma\",\n      \"name\": \"macOS 14.4 Developer Beta 4\",\n      \"build\": \"23E5205c\",\n      \"url\": \"https://updates.cdn-apple.com/2024WinterSeed/fullrestores/052-58286/18DAC58E-4161-45D4-BE02-AE47B12B87B8/UniversalMac_14.4_23E5205c_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sonoma\",\n      \"name\": \"macOS 14.4 Developer Beta 3\",\n      \"build\": \"23E5196e\",\n      \"url\": \"https://updates.cdn-apple.com/2024WinterSeed/fullrestores/052-51632/C6A7A58A-3CB1-4271-9CED-1D5DAE9078CF/UniversalMac_14.4_23E5196e_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sonoma\",\n      \"name\": \"macOS 14.4 Developer Beta 2\",\n      \"build\": \"23E5191e\",\n      \"url\": \"https://updates.cdn-apple.com/2024WinterSeed/fullrestores/052-45324/985FEED9-1A9E-49EB-A904-467E5E7EEE9C/UniversalMac_14.4_23E5191e_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sonoma\",\n      \"name\": \"macOS 14.4 Developer Beta 1\",\n      \"build\": \"23E5180j\",\n      \"url\": \"https://updates.cdn-apple.com/2024WinterSeed/fullrestores/052-42583/F09F735D-D3C3-4AF7-9BEC-F9C5D3B84363/UniversalMac_14.4_23E5180j_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sonoma\",\n      \"name\": \"macOS 14.3\",\n      \"build\": \"23D56\",\n      \"url\": \"https://updates.cdn-apple.com/2024WinterFCS/fullrestores/042-78241/B45074EB-2891-4C05-BCA4-7463F3AC0982/UniversalMac_14.3_23D56_Restore.ipsw\",\n      \"channel\": \"regular\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sonoma\",\n      \"name\": \"macOS 14.3 Developer Beta 3\",\n      \"build\": \"23D5051b\",\n      \"url\": \"https://updates.cdn-apple.com/2023WinterSeed/fullrestores/052-27952/6B395980-45C8-4E7C-9252-9F4CE35C7EDB/UniversalMac_14.3_23D5051b_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sonoma\",\n      \"name\": \"macOS 14.3 Developer Beta 2\",\n      \"build\": \"23D5043d\",\n      \"url\": \"https://updates.cdn-apple.com/2023WinterSeed/fullrestores/052-23267/60655998-DD9A-40BF-BFAB-6D2A4442DE83/UniversalMac_14.3_23D5043d_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sonoma\",\n      \"name\": \"macOS 14.3 Developer Beta 1\",\n      \"build\": \"23D5033f\",\n      \"url\": \"https://updates.cdn-apple.com/2023WinterSeed/fullrestores/052-04657/4126C431-B3EA-42C2-BC36-ED83715B8700/UniversalMac_14.3_23D5033f_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sonoma\",\n      \"name\": \"macOS 14.2.1\",\n      \"build\": \"23C71\",\n      \"url\": \"https://updates.cdn-apple.com/2023FallFCS/fullrestores/052-22662/ECE59A41-DACC-4CA5-AB23-FDED1A4567DE/UniversalMac_14.2.1_23C71_Restore.ipsw\",\n      \"channel\": \"regular\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sonoma\",\n      \"name\": \"macOS 14.2\",\n      \"build\": \"23C64\",\n      \"url\": \"https://updates.cdn-apple.com/2023FallFCS/fullrestores/052-15117/DC2EE605-ABF3-41AE-9652-D137A8AA5907/UniversalMac_14.2_23C64_Restore.ipsw\",\n      \"channel\": \"regular\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sonoma\",\n      \"name\": \"macOS 14.2 RC\",\n      \"build\": \"23C63\",\n      \"url\": \"https://updates.cdn-apple.com/2023FallFCS/fullrestores/052-14744/7215DBB4-BEAD-4A9C-9202-276ABA6832D5/UniversalMac_14.2_23C63_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sonoma\",\n      \"name\": \"macOS 14.2 Developer Beta 4\",\n      \"build\": \"23C5055b\",\n      \"url\": \"https://updates.cdn-apple.com/2023WinterFCS/fullrestores/052-05962/A8344C85-06CE-43C7-9FF6-7B477A4DB8BA/UniversalMac_14.2_23C5055b_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sonoma\",\n      \"name\": \"macOS 14.2 Developer Beta 3\",\n      \"build\": \"23C5047e\",\n      \"url\": \"https://updates.cdn-apple.com/2023FallSeed/fullrestores/042-99526/0A9085CC-B36A-400A-86D8-B9FE23B1DA29/UniversalMac_14.2_23C5047e_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sonoma\",\n      \"name\": \"macOS 14.2 Developer Beta 2\",\n      \"build\": \"23C5041e\",\n      \"url\": \"https://updates.cdn-apple.com/2023FallSeed/fullrestores/042-92143/F642F928-DEE0-4C3F-A416-85745C778855/UniversalMac_14.2_23C5041e_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sonoma\",\n      \"name\": \"macOS 14.2 Developer Beta 1\",\n      \"build\": \"23C5030f\",\n      \"url\": \"https://updates.cdn-apple.com/2023FallSeed/fullrestores/042-71093/ECEFE157-E28B-40B4-9F21-CFD075129029/UniversalMac_14.2_23C5030f_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sonoma\",\n      \"name\": \"macOS 14.1.1\",\n      \"build\": \"23B81\",\n      \"url\": \"https://updates.cdn-apple.com/2023FallFCS/fullrestores/042-89681/55BD14DB-5535-4203-9359-E2C070E43FBE/UniversalMac_14.1.1_23B81_Restore.ipsw\",\n      \"channel\": \"regular\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sonoma\",\n      \"name\": \"macOS 14.1\",\n      \"build\": \"23B74\",\n      \"url\": \"https://updates.cdn-apple.com/2023FallFCS/fullrestores/042-86430/DBE44960-58A6-4715-948B-D64F33F769BD/UniversalMac_14.1_23B74_Restore.ipsw\",\n      \"channel\": \"regular\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sonoma\",\n      \"name\": \"macOS 14.1 RC\",\n      \"build\": \"23B73\",\n      \"url\": \"https://updates.cdn-apple.com/2023FallFCS/fullrestores/042-10900/347B734E-BC0B-41FA-9671-8000FCB5B0BB/UniversalMac_14.1_23B73_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sonoma\",\n      \"name\": \"macOS 14.1 Developer Beta 3\",\n      \"build\": \"23B5067a\",\n      \"url\": \"https://updates.cdn-apple.com/2023FallSeed/fullrestores/042-73325/B24FC9CF-34D1-44A6-B977-FA718FE83DEB/UniversalMac_14.1_23B5067a_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sonoma\",\n      \"name\": \"macOS 14.1 Developer Beta 2\",\n      \"build\": \"23B5056e\",\n      \"url\": \"https://updates.cdn-apple.com/2023FallSeed/fullrestores/042-65885/0FA6C4A7-C21A-4A5F-84C8-8FEE0D98A153/UniversalMac_14.1_23B5056e_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sonoma\",\n      \"name\": \"macOS 14.1 Developer Beta 1\",\n      \"build\": \"23B5046f\",\n      \"url\": \"https://updates.cdn-apple.com/2023FallSeed/fullrestores/042-60177/C4B6F5B3-8B66-461A-A048-4A9925F36FCD/UniversalMac_14.1_23B5046f_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sonoma\",\n      \"name\": \"macOS 14.0\",\n      \"build\": \"23A344\",\n      \"url\": \"https://updates.cdn-apple.com/2023FallFCS/fullrestores/042-54934/0E101AD6-3117-4B63-9BF1-143B6DB9270A/UniversalMac_14.0_23A344_Restore.ipsw\",\n      \"channel\": \"regular\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sonoma\",\n      \"name\": \"macOS 14.0 RC\",\n      \"build\": \"23A339\",\n      \"url\": \"https://updates.cdn-apple.com/2023FallFCS/fullrestores/002-81996/596571C1-9856-4BB3-B5BF-B5A48F4B406E/UniversalMac_14.0_23A339_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sonoma\",\n      \"name\": \"macOS 14.0 Developer Beta 7\",\n      \"build\": \"23A5337a\",\n      \"url\": \"https://updates.cdn-apple.com/2023SummerSeed/fullrestores/042-41500/D1789AEF-013B-4112-8A1E-401589023267/UniversalMac_14.0_23A5337a_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sonoma\",\n      \"name\": \"macOS 14.0 Developer Beta 6\",\n      \"build\": \"23A5328b\",\n      \"url\": \"https://updates.cdn-apple.com/2023SummerSeed/fullrestores/042-37824/AA6B32A0-3C2C-4BEB-95A1-64E601934330/UniversalMac_14.0_23A5328b_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sonoma\",\n      \"name\": \"macOS 14.0 Developer Beta 5\",\n      \"build\": \"23A5312d\",\n      \"url\": \"https://updates.cdn-apple.com/2023SummerSeed/fullrestores/042-27168/7E046825-8EBA-4AAE-8ECC-DDD51B9306D2/UniversalMac_14.0_23A5312d_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sonoma\",\n      \"name\": \"macOS 14.0 Developer Beta 4\",\n      \"build\": \"23A5301h\",\n      \"url\": \"https://updates.cdn-apple.com/2023SummerSeed/fullrestores/042-25548/9EA6EC3D-5A7D-4D53-A17C-70EE71393921/UniversalMac_14.0_23A5301h_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sonoma\",\n      \"name\": \"macOS 14.0 Developer Beta 3 (23A5286i)\",\n      \"build\": \"23A5286i\",\n      \"url\": \"https://updates.cdn-apple.com/2023SummerSeed/fullrestores/042-13887/3B4075C1-B695-49EA-82D9-4B720699D341/UniversalMac_14.0_23A5286i_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sonoma\",\n      \"name\": \"macOS 14.0 Developer Beta 3\",\n      \"build\": \"23A5286g\",\n      \"url\": \"https://updates.cdn-apple.com/2023SummerSeed/fullrestores/042-06324/379026FA-C14F-4095-99FD-19F607D10EBF/UniversalMac_14.0_23A5286g_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sonoma\",\n      \"name\": \"macOS 14.0 Developer Beta 2\",\n      \"build\": \"23A5276g\",\n      \"url\": \"https://updates.cdn-apple.com/2023SummerSeed/fullrestores/032-95861/67600C59-8516-4A46-B9D7-4007D395CEF5/UniversalMac_14.0_23A5276g_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"sonoma\",\n      \"name\": \"macOS 14.0 Developer Beta 1\",\n      \"build\": \"23A5257q\",\n      \"url\": \"https://updates.cdn-apple.com/2023SummerSeed/fullrestores/032-94355/CBE8CBE1-750D-487E-A393-B90FEF60CEBA/UniversalMac_14.0_23A5257q_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.6\",\n      \"build\": \"22G120\",\n      \"url\": \"https://updates.cdn-apple.com/2023FallFCS/fullrestores/042-55833/C0830847-A2F8-458F-B680-967991820931/UniversalMac_13.6_22G120_Restore.ipsw\",\n      \"channel\": \"regular\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.5.2\",\n      \"build\": \"22G91\",\n      \"url\": \"https://updates.cdn-apple.com/2023SummerFCS/fullrestores/042-43686/945D434B-DA5D-48DB-A558-F6D18D11AD69/UniversalMac_13.5.2_22G91_Restore.ipsw\",\n      \"channel\": \"regular\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.5.1\",\n      \"build\": \"22G90\",\n      \"url\": \"https://updates.cdn-apple.com/2023SummerFCS/fullrestores/042-25658/2D6BE8DB-5549-4F85-8C54-39FC23BABC68/UniversalMac_13.5.1_22G90_Restore.ipsw\",\n      \"channel\": \"regular\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.5\",\n      \"build\": \"22G74\",\n      \"url\": \"https://updates.cdn-apple.com/2023SummerFCS/fullrestores/032-69606/D3E05CDF-E105-434C-A4A1-4E3DC7668DD0/UniversalMac_13.5_22G74_Restore.ipsw\",\n      \"channel\": \"regular\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.5 Developer Beta 5\",\n      \"build\": \"22G5072a\",\n      \"url\": \"https://updates.cdn-apple.com/2023SpringSeed/fullrestores/042-09570/8DA0B0AA-6FD4-42C4-A54E-BC0D53B92AC0/UniversalMac_13.5_22G5072a_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.5 Developer Beta 4\",\n      \"build\": \"22G5059d\",\n      \"url\": \"https://updates.cdn-apple.com/2023SpringSeed/fullrestores/042-03209/55CBE04D-FD90-483B-A6D7-45E0FBC1C94F/UniversalMac_13.5_22G5059d_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.5 Developer Beta 3\",\n      \"build\": \"22G5048d\",\n      \"url\": \"https://updates.cdn-apple.com/2023SpringSeed/fullrestores/032-93679/1D39F2AC-8FD4-46A3-A159-478C76472B16/UniversalMac_13.5_22G5048d_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.5 Developer Beta 2\",\n      \"build\": \"22G5038d\",\n      \"url\": \"https://updates.cdn-apple.com/2023SpringSeed/fullrestores/032-92523/E476F5EC-D046-4A76-889B-F19DA354459E/UniversalMac_13.5_22G5038d_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.5 Developer Beta 1\",\n      \"build\": \"22G5027e\",\n      \"url\": \"https://updates.cdn-apple.com/2023SpringSeed/fullrestores/032-86178/CE6C5645-C5C3-41A9-B986-D5F0BD7BB10B/UniversalMac_13.5_22G5027e_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.4.1\",\n      \"build\": \"22F82\",\n      \"url\": \"https://updates.cdn-apple.com/2023SpringFCS/fullrestores/042-01877/2F49A9FE-7033-41D0-9D0C-64EFCE6B4C22/UniversalMac_13.4.1_22F82_Restore.ipsw\",\n      \"channel\": \"regular\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.4.1 (WWDC23 M2 Macs)\",\n      \"build\": \"22F2083\",\n      \"url\": \"https://updates.cdn-apple.com/2023SpringFCS/fullrestores/042-01864/A8378F91-BA71-40DF-8F0D-606A16F1836B/UniversalMac_13.4.1_22F2083_Restore.ipsw\",\n      \"channel\": \"regular\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.4\",\n      \"build\": \"22F66\",\n      \"url\": \"https://updates.cdn-apple.com/2023SpringFCS/fullrestores/032-84884/F97A22EE-9B5E-4FD5-94C1-B39DCEE8D80F/UniversalMac_13.4_22F66_Restore.ipsw\",\n      \"channel\": \"regular\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.4 RC 2\",\n      \"build\": \"22F63\",\n      \"url\": \"https://updates.cdn-apple.com/2023SpringFCS/fullrestores/032-83954/6E06237C-1B56-4932-A8E1-3A07A3EE03A8/UniversalMac_13.4_22F63_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.4 RC\",\n      \"build\": \"22F62\",\n      \"url\": \"https://updates.cdn-apple.com/2023SpringFCS/fullrestores/032-44024/731F1533-53BE-4CEB-AA05-74F333CA904A/UniversalMac_13.4_22F62_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.4 Developer Beta 4\",\n      \"build\": \"22F5059b\",\n      \"url\": \"https://updates.cdn-apple.com/2023SpringSeed/fullrestores/032-79565/BA9CBFB7-152C-4FB6-B0B3-47769997BFA1/UniversalMac_13.4_22F5059b_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.4 Developer Beta 3\",\n      \"build\": \"22F5049e\",\n      \"url\": \"https://updates.cdn-apple.com/2023SpringSeed/fullrestores/032-76661/573284E7-4A4A-440C-AC01-6065C7A8E667/UniversalMac_13.4_22F5049e_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.4 Developer Beta 2\",\n      \"build\": \"22F5037d\",\n      \"url\": \"https://updates.cdn-apple.com/2023SpringSeed/fullrestores/032-69885/ECFA1532-C633-4ACE-9D2C-3B5FD19510D4/UniversalMac_13.4_22F5037d_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.4 Developer Beta 1\",\n      \"build\": \"22F5027f\",\n      \"url\": \"https://updates.cdn-apple.com/2023SpringSeed/fullrestores/032-69187/B11709E0-1CF5-4460-A069-D12E1243E2AD/UniversalMac_13.4_22F5027f_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.3.1\",\n      \"build\": \"22E261\",\n      \"url\": \"https://updates.cdn-apple.com/2023WinterFCS/fullrestores/032-66602/418BC37A-FCD9-400A-B4FA-022A19576CD4/UniversalMac_13.3.1_22E261_Restore.ipsw\",\n      \"channel\": \"regular\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.3\",\n      \"build\": \"22E252\",\n      \"url\": \"https://updates.cdn-apple.com/2023WinterSeed/fullrestores/002-75537/8250FA0E-0962-46D6-8A90-57A390B9FFD7/UniversalMac_13.3_22E252_Restore.ipsw\",\n      \"channel\": \"regular\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.3 Developer Beta 4\",\n      \"build\": \"22E5246b\",\n      \"url\": \"https://updates.cdn-apple.com/2023WinterSeed/fullrestores/032-63669/7C0F9BA8-35C0-457F-AF56-6943D58A2CDB/UniversalMac_13.3_22E5246b_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.3 Developer Beta 3\",\n      \"build\": \"22E5236f\",\n      \"url\": \"https://updates.cdn-apple.com/2023WinterSeed/fullrestores/032-60411/1DDA996F-B620-4770-8FFE-87AB2043784D/UniversalMac_13.3_22E5236f_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.3 Developer Beta 2\",\n      \"build\": \"22E5230e\",\n      \"url\": \"https://updates.cdn-apple.com/2023WinterSeed/fullrestores/032-54760/AE02E378-FD59-474D-93AB-C52617103C72/UniversalMac_13.3_22E5230e_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.3 Developer Beta 1\",\n      \"build\": \"22E5219e\",\n      \"url\": \"https://updates.cdn-apple.com/2023WinterSeed/fullrestores/032-01932/676E0981-4535-4942-A4AE-E14C604CE719/UniversalMac_13.3_22E5219e_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.2.1\",\n      \"build\": \"22D68\",\n      \"url\": \"https://updates.cdn-apple.com/2023WinterFCS/fullrestores/032-48346/EFF99C1E-C408-4E7A-A448-12E1468AF06C/UniversalMac_13.2.1_22D68_Restore.ipsw\",\n      \"channel\": \"regular\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.2\",\n      \"build\": \"22D49\",\n      \"url\": \"https://updates.cdn-apple.com/2023WinterFCS/fullrestores/032-35688/0350BB21-2B4B-4850-BF77-70B830283B28/UniversalMac_13.2_22D49_Restore.ipsw\",\n      \"channel\": \"regular\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.2 Developer Beta 2\",\n      \"build\": \"22D5038i\",\n      \"url\": \"https://updates.cdn-apple.com/2023WinterSeed/fullrestores/032-33181/62ECE236-5806-4136-AD08-EDC026FD80A5/UniversalMac_13.2_22D5038i_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.2 Developer Beta 1\",\n      \"build\": \"22D5027d\",\n      \"url\": \"https://updates.cdn-apple.com/2023WinterSeed/fullrestores/032-12640/6B472BA3-E678-4251-92D1-7AA23B66F53E/UniversalMac_13.2_22D5027d_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.1\",\n      \"build\": \"22C65\",\n      \"url\": \"https://updates.cdn-apple.com/2022FallFCS/fullrestores/012-60270/0A7F49BA-FC31-4AD9-8E45-49B1FB9128A6/UniversalMac_13.1_22C65_Restore.ipsw\",\n      \"channel\": \"regular\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.1 Developer Beta 4\",\n      \"build\": \"22C5059b\",\n      \"url\": \"https://updates.cdn-apple.com/2022FallSeed/fullrestores/032-08112/957EA73A-7C95-4B3C-B99C-2C2C47555832/UniversalMac_13.1_22C5059b_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.1 Developer Beta 3\",\n      \"build\": \"22C5050e\",\n      \"url\": \"https://updates.cdn-apple.com/2022FallSeed/fullrestores/032-06252/946CBF92-8F27-49B1-A692-81F54C73D2F0/UniversalMac_13.1_22C5050e_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.1 Developer Beta 2\",\n      \"build\": \"22C5044e\",\n      \"url\": \"https://updates.cdn-apple.com/2022FallSeed/fullrestores/032-02019/670C9BA6-67EB-4AE6-A02E-88976F6F3118/UniversalMac_13.1_22C5044e_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.1 Developer Beta 1\",\n      \"build\": \"22C5033e\",\n      \"url\": \"https://updates.cdn-apple.com/2022FallSeed/fullrestores/012-82062/10E6B723-51B8-4B2C-BA3B-12A18ED4E719/UniversalMac_13.1_22C5033e_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.0.1\",\n      \"build\": \"22A400\",\n      \"url\": \"https://updates.cdn-apple.com/2022FallFCS/fullrestores/012-93802/A7270B0F-05F8-43D1-A9AD-40EF5699E82C/UniversalMac_13.0.1_22A400_Restore.ipsw\",\n      \"channel\": \"regular\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.0\",\n      \"build\": \"22A380\",\n      \"url\": \"https://updates.cdn-apple.com/2022FallFCS/fullrestores/012-92188/2C38BCD1-2BFF-4A10-B358-94E8E28BE805/UniversalMac_13.0_22A380_Restore.ipsw\",\n      \"channel\": \"regular\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.0 RC\",\n      \"build\": \"22A379\",\n      \"url\": \"https://updates.cdn-apple.com/2022FallFCS/fullrestores/071-08994/1118ADF4-1CC9-4554-9333-B1F64CF0C820/UniversalMac_13.0_22A379_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.0 Developer Beta 11\",\n      \"build\": \"22A5373b\",\n      \"url\": \"https://updates.cdn-apple.com/2022SummerSeed/fullrestores/012-84563/2FC38C63-3213-4BB6-8E41-2B066332CBE6/UniversalMac_13.0_22A5373b_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.0 Developer Beta 10\",\n      \"build\": \"22A5365d\",\n      \"url\": \"https://updates.cdn-apple.com/2022SummerSeed/fullrestores/012-83054/16ECAA12-3A1B-4663-B49B-B1563ECD4314/UniversalMac_13.0_22A5365d_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.0 Developer Beta 9\",\n      \"build\": \"22A5358e\",\n      \"url\": \"https://updates.cdn-apple.com/2022SummerSeed/fullrestores/012-71790/AF5A04A6-FF20-44C1-9BFF-43081BDB4D8C/UniversalMac_13.0_22A5358e_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.0 Developer Beta 8\",\n      \"build\": \"22A5352e\",\n      \"url\": \"https://updates.cdn-apple.com/2022SummerSeed/fullrestores/012-70113/6F1F08B7-9A1B-48A9-93DB-55EE21121C87/UniversalMac_13.0_22A5352e_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.0 Developer Beta 7\",\n      \"build\": \"22A5342f\",\n      \"url\": \"https://updates.cdn-apple.com/2022SummerSeed/fullrestores/012-66750/108EF06D-FBEE-4910-BA83-56A5C9B54110/UniversalMac_13.0_22A5342f_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.0 Developer Beta 6\",\n      \"build\": \"22A5331f\",\n      \"url\": \"https://updates.cdn-apple.com/2022SummerSeed/fullrestores/012-61458/80300AD0-69E5-4429-AE3E-A936CA83B5FC/UniversalMac_13.0_22A5331f_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.0 Developer Beta 5\",\n      \"build\": \"22A5321d\",\n      \"url\": \"https://updates.cdn-apple.com/2022SummerSeed/fullrestores/012-51397/8EF0874D-388A-4F62-B58A-89F968DD3082/UniversalMac_13.0_22A5321d_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.0 Developer Beta 4\",\n      \"build\": \"22A5311f\",\n      \"url\": \"https://updates.cdn-apple.com/2022SummerSeed/fullrestores/012-43316/6CE4D83A-E44C-4DD1-B47F-DE168355662E/UniversalMac_13.0_22A5311f_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.0 Developer Beta 3 (22A5295i)\",\n      \"build\": \"22A5295i\",\n      \"url\": \"https://updates.cdn-apple.com/2022SummerSeed/fullrestores/012-38309/6EDC76A0-4432-4C64-83C5-F43C885A75D6/UniversalMac_13.0_22A5295i_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.0 Developer Beta 3\",\n      \"build\": \"22A5295h\",\n      \"url\": \"https://updates.cdn-apple.com/2022SummerSeed/fullrestores/012-34274/130176F5-C4CB-4664-A2F0-F29CA1281694/UniversalMac_13.0_22A5295h_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.0 Developer Beta 2\",\n      \"build\": \"22A5286j\",\n      \"url\": \"https://updates.cdn-apple.com/2022SummerSeed/fullrestores/012-30346/9DD787A7-044B-4650-86D4-84E80B6B9C36/UniversalMac_13.0_22A5286j_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"ventura\",\n      \"name\": \"macOS 13.0 Developer Beta 1\",\n      \"build\": \"22A5266r\",\n      \"url\": \"https://developer.apple.com/services-account/download?path=%2FWWDC_2022%2FmacOS_13_beta%2FUniversalMac_13.0_22A5266r_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": true\n    },\n    {\n      \"group\": \"monterey\",\n      \"name\": \"macOS 12.6.1\",\n      \"build\": \"21G217\",\n      \"url\": \"https://updates.cdn-apple.com/2022FallFCS/fullrestores/012-66032/8D8D90C6-A876-4FFF-BBF4-D158939B3841/UniversalMac_12.6.1_21G217_Restore.ipsw\",\n      \"channel\": \"regular\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"monterey\",\n      \"name\": \"macOS 12.6\",\n      \"build\": \"21G115\",\n      \"url\": \"https://updates.cdn-apple.com/2022FallFCS/fullrestores/012-40537/0EC7C669-13E9-49FB-BD64-9EECC1D174B2/UniversalMac_12.6_21G115_Restore.ipsw\",\n      \"channel\": \"regular\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"monterey\",\n      \"name\": \"macOS 12.5.1\",\n      \"build\": \"21G83\",\n      \"url\": \"https://updates.cdn-apple.com/2022SummerFCS/fullrestores/012-51674/A7019DDB-3355-470F-A355-4162A187AB6C/UniversalMac_12.5.1_21G83_Restore.ipsw\",\n      \"channel\": \"regular\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"monterey\",\n      \"name\": \"macOS 12.5\",\n      \"build\": \"21G72\",\n      \"url\": \"https://updates.cdn-apple.com/2022SummerFCS/fullrestores/012-42731/BD9917E0-262C-41C5-A69F-AC316A534A39/UniversalMac_12.5_21G72_Restore.ipsw\",\n      \"channel\": \"regular\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"monterey\",\n      \"name\": \"macOS 12.5 RC\",\n      \"build\": \"21G69\",\n      \"url\": \"https://updates.cdn-apple.com/2022SummerFCS/fullrestores/012-40368/5DD0A524-140A-46AF-91ED-5F28EA9DEC01/UniversalMac_12.5_21G69_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"monterey\",\n      \"name\": \"macOS 12.5 Developer Beta 5\",\n      \"build\": \"21G5063a\",\n      \"url\": \"https://updates.cdn-apple.com/2022FallSeed/fullrestores/012-36748/52342C55-6598-4A86-AAB8-8901145792C8/UniversalMac_12.5_21G5063a_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"monterey\",\n      \"name\": \"macOS 12.5 Developer Beta 4\",\n      \"build\": \"21G5056b\",\n      \"url\": \"https://updates.cdn-apple.com/2022FallSeed/fullrestores/012-26441/AE0AC638-2773-49D3-BF84-950B10BF39E9/UniversalMac_12.5_21G5056b_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"monterey\",\n      \"name\": \"macOS 12.5 Developer Beta 3\",\n      \"build\": \"21G5046c\",\n      \"url\": \"https://updates.cdn-apple.com/2022FallSeed/fullrestores/012-18271/FFF202B2-E4B6-4A3E-9681-42A0F3F81B11/UniversalMac_12.5_21G5046c_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"monterey\",\n      \"name\": \"macOS 12.5 Developer Beta 2\",\n      \"build\": \"21G5037d\",\n      \"url\": \"https://updates.cdn-apple.com/2022FallSeed/fullrestores/012-10648/1CC63FC5-5A22-4A5A-9A7B-C19C8C4A6731/UniversalMac_12.5_21G5037d_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"monterey\",\n      \"name\": \"macOS 12.5 Developer Beta 1\",\n      \"build\": \"21G5027d\",\n      \"url\": \"https://updates.cdn-apple.com/2022FallSeed/fullrestores/002-93712/5F234425-6096-43FC-B518-1E9D7B4D0254/UniversalMac_12.5_21G5027d_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"monterey\",\n      \"name\": \"macOS 12.4\",\n      \"build\": \"21F79\",\n      \"url\": \"https://updates.cdn-apple.com/2022SpringFCS/fullrestores/012-06874/9CECE956-D945-45E2-93E9-4FFDC81BB49A/UniversalMac_12.4_21F79_Restore.ipsw\",\n      \"channel\": \"regular\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"monterey\",\n      \"name\": \"macOS 12.4 Developer Beta 4\",\n      \"build\": \"21F5071b\",\n      \"url\": \"https://updates.cdn-apple.com/2022SpringSeed/fullrestores/002-95106/0F7A6388-C4B5-4B8E-B8B2-F62C030699D0/UniversalMac_12.4_21F5071b_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"monterey\",\n      \"name\": \"macOS 12.4 Developer Beta 3\",\n      \"build\": \"21F5063e\",\n      \"url\": \"https://updates.cdn-apple.com/2022SpringSeed/fullrestores/002-90009/DA6BD192-1698-48B3-AB6D-9D3A045ED1B1/UniversalMac_12.4_21F5063e_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"monterey\",\n      \"name\": \"macOS 12.4 Developer Beta 2\",\n      \"build\": \"21F5058e\",\n      \"url\": \"https://updates.cdn-apple.com/2022SpringSeed/fullrestores/002-87587/BC2EBE80-F0F4-4B56-BCDC-340E0AD8E985/UniversalMac_12.4_21F5058e_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"monterey\",\n      \"name\": \"macOS 12.4 Developer Beta 1\",\n      \"build\": \"21F5048e\",\n      \"url\": \"https://updates.cdn-apple.com/2022SpringSeed/fullrestores/002-85721/A21FF659-8493-4A16-A989-2C3141F48D8C/UniversalMac_12.4_21F5048e_Restore.ipsw\",\n      \"channel\": \"devbeta\",\n      \"needsCookie\": false\n    },\n    {\n      \"group\": \"monterey\",\n      \"name\": \"macOS 12.3.1\",\n      \"build\": \"21E258\",\n      \"url\": \"https://updates.cdn-apple.com/2022SpringFCS/fullrestores/002-79219/851BEDF0-19DB-4040-B765-0F4089D1530D/UniversalMac_12.3.1_21E258_Restore.ipsw\",\n      \"channel\": \"regular\",\n      \"needsCookie\": false\n    }\n  ]\n}\n"
  },
  {
    "path": "data/ipsws_v2.json",
    "content": "{\n  \"apiVersion\" : 2,\n  \"channels\" : [\n    {\n      \"icon\" : \"checkmark.seal\",\n      \"id\" : \"regular\",\n      \"name\" : \"Release\",\n      \"note\" : \"Public, stable releases.\"\n    },\n    {\n      \"icon\" : \"wrench.and.screwdriver\",\n      \"id\" : \"devbeta\",\n      \"name\" : \"Developer Beta\",\n      \"note\" : \"Betas meant for developer testing.\"\n    }\n  ],\n  \"deviceSupportVersions\" : [\n    {\n      \"id\" : \"device_support_macOS26_beta\",\n      \"instructions\" : \"Your Mac needs an update to install virtual machines that use this version of macOS.\\n\\nHere’s how you can install the update:\\n\\n1. Head over to the [Apple Developer downloads page](https:\\/\\/developer.apple.com\\/download\\/).\\n2. Under “Operating Systems”, find “Device Support for macOS 26”.\\n3. Click the link below that item.\\n4. Double-click the downloaded DMG and open the installer. Follow the instructions.\\n5. Quit and relaunch VirtualBuddy. Then, try again.\\n\\nInstalling the latest Xcode beta will also install the required update.\",\n      \"mobileDeviceMinVersion\" : \"1810.0.0\",\n      \"osVersion\" : \"26.0.0\",\n      \"title\" : \"Device Support Update Required\"\n    }\n  ],\n  \"features\" : [\n    {\n      \"detail\" : \"For previous versions, you can enable file sharing in System Preferences > Sharing.\",\n      \"id\" : \"file_sharing\",\n      \"minVersionGuest\" : \"13.0.0\",\n      \"minVersionHost\" : \"13.0.0\",\n      \"name\" : \"File sharing\",\n      \"unsupportedPlatform\" : false\n    },\n    {\n      \"id\" : \"guest_app\",\n      \"minVersionGuest\" : \"13.0.0\",\n      \"minVersionHost\" : \"13.0.0\",\n      \"name\" : \"VirtualBuddyGuest app\",\n      \"unsupportedPlatform\" : false\n    },\n    {\n      \"id\" : \"trackpad\",\n      \"minVersionGuest\" : \"13.0.0\",\n      \"minVersionHost\" : \"13.0.0\",\n      \"name\" : \"Trackpad\",\n      \"unsupportedPlatform\" : false\n    },\n    {\n      \"id\" : \"mac_keyboard\",\n      \"minVersionGuest\" : \"13.0.0\",\n      \"minVersionHost\" : \"13.0.0\",\n      \"name\" : \"Mac keyboard\",\n      \"unsupportedPlatform\" : false\n    },\n    {\n      \"id\" : \"state_restoration\",\n      \"minVersionGuest\" : \"14.0.0\",\n      \"minVersionHost\" : \"14.0.0\",\n      \"name\" : \"State restoration\",\n      \"unsupportedPlatform\" : false\n    },\n    {\n      \"id\" : \"display_resize\",\n      \"minVersionGuest\" : \"14.0.0\",\n      \"minVersionHost\" : \"14.0.0\",\n      \"name\" : \"Automatic display configuration\",\n      \"unsupportedPlatform\" : false\n    },\n    {\n      \"id\" : \"rosetta_sharing\",\n      \"minVersionGuest\" : \"14.0.0\",\n      \"minVersionHost\" : \"14.0.0\",\n      \"name\" : \"Rosetta Sharing\",\n      \"unsupportedPlatform\" : true\n    }\n  ],\n  \"groups\" : [\n    {\n      \"darkImage\" : {\n        \"id\" : \"tahoe\",\n        \"thumbnail\" : {\n          \"blurHash\" : \"UfG0bS?9S1Rn~Pr@R.ofs+S6jFt3EBIZt5oJ\",\n          \"height\" : 720,\n          \"url\" : \"https:\\/\\/api.virtualbuddy.app\\/v2\\/images\\/tahoe-dark-thumbnail.heic\",\n          \"width\" : 720\n        },\n        \"url\" : \"https:\\/\\/api.virtualbuddy.app\\/v2\\/images\\/tahoe-dark.heic\"\n      },\n      \"id\" : \"tahoe\",\n      \"image\" : {\n        \"id\" : \"tahoe\",\n        \"thumbnail\" : {\n          \"blurHash\" : \"UfG0bS?9S1Rn~Pr@R.ofs+S6jFt3EBIZt5oJ\",\n          \"height\" : 720,\n          \"url\" : \"https:\\/\\/api.virtualbuddy.app\\/v2\\/images\\/tahoe-thumbnail.heic\",\n          \"width\" : 720\n        },\n        \"url\" : \"https:\\/\\/api.virtualbuddy.app\\/v2\\/images\\/tahoe.heic\"\n      },\n      \"majorVersion\" : \"26.0.0\",\n      \"name\" : \"macOS Tahoe\"\n    },\n    {\n      \"darkImage\" : {\n        \"id\" : \"sequoia\",\n        \"thumbnail\" : {\n          \"blurHash\" : \"eN86q8M1Hbyya7t-g$MpnTx,b;k9X8Vgr?osbHenWEeYoGj@aNaPah\",\n          \"height\" : 399,\n          \"url\" : \"https:\\/\\/api.virtualbuddy.app\\/v2\\/images\\/sequoia-dark-thumbnail.heic\",\n          \"width\" : 720\n        },\n        \"url\" : \"https:\\/\\/api.virtualbuddy.app\\/v2\\/images\\/sequoia-dark.heic\"\n      },\n      \"id\" : \"sequoia\",\n      \"image\" : {\n        \"id\" : \"sequoia\",\n        \"thumbnail\" : {\n          \"blurHash\" : \"egK9[+5Y0:}iNe-S=rI^SixW$JoIwbNfOYocn$NeWXNfShfkS5S5WX\",\n          \"height\" : 399,\n          \"url\" : \"https:\\/\\/api.virtualbuddy.app\\/v2\\/images\\/sequoia-thumbnail.heic\",\n          \"width\" : 720\n        },\n        \"url\" : \"https:\\/\\/api.virtualbuddy.app\\/v2\\/images\\/sequoia.heic\"\n      },\n      \"majorVersion\" : \"15.0.0\",\n      \"name\" : \"macOS Sequoia\"\n    },\n    {\n      \"darkImage\" : {\n        \"id\" : \"sonoma\",\n        \"thumbnail\" : {\n          \"blurHash\" : \"ec8rzYaIWBj?a}iqosaxj?fkRFa2axayj[t%ofV[ayf6V]pGkBf5fi\",\n          \"height\" : 720,\n          \"url\" : \"https:\\/\\/api.virtualbuddy.app\\/v2\\/images\\/sonoma-dark-thumbnail.heic\",\n          \"width\" : 720\n        },\n        \"url\" : \"https:\\/\\/api.virtualbuddy.app\\/v2\\/images\\/sonoma-dark.heic\"\n      },\n      \"id\" : \"sonoma\",\n      \"image\" : {\n        \"id\" : \"sonoma\",\n        \"thumbnail\" : {\n          \"blurHash\" : \"e:Hx.{wFRlbFaxm#xToba{j?tmetnOjsa#J,tRsln$a#NINff+oIj?\",\n          \"height\" : 720,\n          \"url\" : \"https:\\/\\/api.virtualbuddy.app\\/v2\\/images\\/sonoma-thumbnail.heic\",\n          \"width\" : 720\n        },\n        \"url\" : \"https:\\/\\/api.virtualbuddy.app\\/v2\\/images\\/sonoma.heic\"\n      },\n      \"majorVersion\" : \"14.0.0\",\n      \"name\" : \"macOS Sonoma\"\n    },\n    {\n      \"darkImage\" : {\n        \"id\" : \"ventura\",\n        \"thumbnail\" : {\n          \"blurHash\" : \"enHk%=ocoeW:Nb-8xEODaya#1fWWJAWExDEjR-jGazoJWCagw^s-Wp\",\n          \"height\" : 720,\n          \"url\" : \"https:\\/\\/api.virtualbuddy.app\\/v2\\/images\\/ventura-dark-thumbnail.heic\",\n          \"width\" : 720\n        },\n        \"url\" : \"https:\\/\\/api.virtualbuddy.app\\/v2\\/images\\/ventura-dark.heic\"\n      },\n      \"id\" : \"ventura\",\n      \"image\" : {\n        \"id\" : \"ventura\",\n        \"thumbnail\" : {\n          \"blurHash\" : \"eqOd?[TcFgR.a}LM$yWFs+oJ2{N{Iuj[jZJrj]slS5a#NNafw@ocoI\",\n          \"height\" : 720,\n          \"url\" : \"https:\\/\\/api.virtualbuddy.app\\/v2\\/images\\/ventura-thumbnail.heic\",\n          \"width\" : 720\n        },\n        \"url\" : \"https:\\/\\/api.virtualbuddy.app\\/v2\\/images\\/ventura.heic\"\n      },\n      \"majorVersion\" : \"13.0.0\",\n      \"name\" : \"macOS Ventura\"\n    },\n    {\n      \"darkImage\" : {\n        \"id\" : \"monterey\",\n        \"thumbnail\" : {\n          \"blurHash\" : \"eb6$?*bER}o3j?oToNjqa^jsS7oOoMa_axoOWqa}oNjrbJa~a#a}oM\",\n          \"height\" : 720,\n          \"url\" : \"https:\\/\\/api.virtualbuddy.app\\/v2\\/images\\/monterey-dark-thumbnail.heic\",\n          \"width\" : 720\n        },\n        \"url\" : \"https:\\/\\/api.virtualbuddy.app\\/v2\\/images\\/monterey-dark.heic\"\n      },\n      \"id\" : \"monterey\",\n      \"image\" : {\n        \"id\" : \"monterey\",\n        \"thumbnail\" : {\n          \"blurHash\" : \"euJi.3I.R6snaz~UjLa1fOafPQxBoLaiaeXVX4g2o0jZoaW?X9bXoL\",\n          \"height\" : 720,\n          \"url\" : \"https:\\/\\/api.virtualbuddy.app\\/v2\\/images\\/monterey-thumbnail.heic\",\n          \"width\" : 720\n        },\n        \"url\" : \"https:\\/\\/api.virtualbuddy.app\\/v2\\/images\\/monterey.heic\"\n      },\n      \"majorVersion\" : \"12.0.0\",\n      \"name\" : \"macOS Monterey\"\n    }\n  ],\n  \"minAppVersion\" : \"2.0.0\",\n  \"requirementSets\" : [\n    {\n      \"id\" : \"min_host_12\",\n      \"minCPUCount\" : 2,\n      \"minMemorySizeMB\" : 4096,\n      \"minVersionHost\" : \"12.0.0\"\n    },\n    {\n      \"id\" : \"min_host_13\",\n      \"minCPUCount\" : 2,\n      \"minMemorySizeMB\" : 4096,\n      \"minVersionHost\" : \"13.0.0\"\n    }\n  ],\n  \"restoreImages\" : [\n    {\n      \"build\" : \"25E243\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 19734806625,\n      \"group\" : \"tahoe\",\n      \"id\" : \"25E243\",\n      \"mobileDeviceMinVersion\" : \"1827.100.14\",\n      \"name\" : \"macOS 26.4 RC\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2026WinterFCS\\/fullrestores\\/122-08082\\/B9E8695D-7851-435A-8897-6D84A80F4F84\\/UniversalMac_26.4_25E243_Restore.ipsw\",\n      \"version\" : \"26.4.0\"\n    },\n    {\n      \"build\" : \"25E5233c\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 19731757690,\n      \"group\" : \"tahoe\",\n      \"id\" : \"25E5233c\",\n      \"mobileDeviceMinVersion\" : \"1827.100.14\",\n      \"name\" : \"macOS 26.4 Developer Beta 4\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2026WinterSeed\\/fullrestores\\/047-94466\\/DDE04370-287E-4321-8FAB-8656591CAF70\\/UniversalMac_26.4_25E5233c_Restore.ipsw\",\n      \"version\" : \"26.4.0\"\n    },\n    {\n      \"build\" : \"25E5223i\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 19222852846,\n      \"group\" : \"tahoe\",\n      \"id\" : \"25E5223i\",\n      \"mobileDeviceMinVersion\" : \"1827.100.14\",\n      \"name\" : \"macOS 26.4 Developer Beta 3\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2026WinterSeed\\/fullrestores\\/047-83754\\/2EAF0D9F-ADAF-402B-9C29-874D9B1E6378\\/UniversalMac_26.4_25E5223i_Restore.ipsw\",\n      \"version\" : \"26.4.0\"\n    },\n    {\n      \"build\" : \"25E5218f\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 19289481885,\n      \"group\" : \"tahoe\",\n      \"id\" : \"25E5218f\",\n      \"mobileDeviceMinVersion\" : \"1827.100.14\",\n      \"name\" : \"macOS 26.4 Developer Beta 2\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2026WinterSeed\\/fullrestores\\/047-83734\\/6F2EB9BB-586D-4CCA-8C8D-28A02E38AAA2\\/UniversalMac_26.4_25E5218f_Restore.ipsw\",\n      \"version\" : \"26.4.0\"\n    },\n    {\n      \"build\" : \"25E5207k\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 19289462929,\n      \"group\" : \"tahoe\",\n      \"id\" : \"25E5207k\",\n      \"mobileDeviceMinVersion\" : \"1810.0.0\",\n      \"name\" : \"macOS 26.4 Developer Beta 1\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2026WinterSeed\\/fullrestores\\/047-01191\\/B8945931-E74E-43AC-9370-BFCA2E35392E\\/UniversalMac_26.4_25E5207k_Restore.ipsw\",\n      \"version\" : \"26.4.0\"\n    },\n    {\n      \"build\" : \"25D2140\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 19330789958,\n      \"group\" : \"tahoe\",\n      \"id\" : \"25D2140\",\n      \"mobileDeviceMinVersion\" : \"1810.0.0\",\n      \"name\" : \"macOS 26.3.2\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2026WinterFCS\\/fullrestores\\/047-94879\\/40A2B65E-4E49-4EAA-8BEC-62A305007488\\/UniversalMac_26.3.2_25D2140_Restore.ipsw\",\n      \"version\" : \"26.3.2\"\n    },\n    {\n      \"build\" : \"25D2128\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 19330833456,\n      \"group\" : \"tahoe\",\n      \"id\" : \"25D2128\",\n      \"mobileDeviceMinVersion\" : \"1810.0.0\",\n      \"name\" : \"macOS 26.3.1\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2026WinterFCS\\/fullrestores\\/047-88313\\/2E098049-1731-4415-A206-546D09301973\\/UniversalMac_26.3.1_25D2128_Restore.ipsw\",\n      \"version\" : \"26.3.1\"\n    },\n    {\n      \"build\" : \"25D125\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 18877622182,\n      \"group\" : \"tahoe\",\n      \"id\" : \"25D125\",\n      \"mobileDeviceMinVersion\" : \"1810.0.0\",\n      \"name\" : \"macOS 26.3\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2026WinterFCS\\/fullrestores\\/047-60229\\/6D5DBEA5-75A0-4BEF-ACC9-5ACF9B8DF6B7\\/UniversalMac_26.3_25D125_Restore.ipsw\",\n      \"version\" : \"26.3.0\"\n    },\n    {\n      \"build\" : \"25D122\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 18877545740,\n      \"group\" : \"tahoe\",\n      \"id\" : \"25D122\",\n      \"mobileDeviceMinVersion\" : \"1810.0.0\",\n      \"name\" : \"macOS 26.3 RC\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2026WinterFCS\\/fullrestores\\/082-57168\\/EE47B566-E405-4A7C-B99E-5BBCA418BF73\\/UniversalMac_26.3_25D122_Restore.ipsw\",\n      \"version\" : \"26.3.0\"\n    },\n    {\n      \"build\" : \"25D5112c\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 18873995756,\n      \"group\" : \"tahoe\",\n      \"id\" : \"25D5112c\",\n      \"mobileDeviceMinVersion\" : \"1810.0.0\",\n      \"name\" : \"macOS 26.3 Developer Beta 3\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2026WinterSeed\\/fullrestores\\/047-27561\\/D17A405D-58FC-4CD3-9CBD-2FF572E5B45C\\/UniversalMac_26.3_25D5112c_Restore.ipsw\",\n      \"version\" : \"26.3.0\"\n    },\n    {\n      \"build\" : \"25D5101c\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 18806520160,\n      \"group\" : \"tahoe\",\n      \"id\" : \"25D5101c\",\n      \"mobileDeviceMinVersion\" : \"1810.0.0\",\n      \"name\" : \"macOS 26.3 Developer Beta 2\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2026WinterSeed\\/fullrestores\\/047-11932\\/34528828-0245-4624-9D72-AFB752B2BEA2\\/UniversalMac_26.3_25D5101c_Restore.ipsw\",\n      \"version\" : \"26.3.0\"\n    },\n    {\n      \"build\" : \"25D5087f\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 18789116337,\n      \"group\" : \"tahoe\",\n      \"id\" : \"25D5087f\",\n      \"mobileDeviceMinVersion\" : \"1810.0.0\",\n      \"name\" : \"macOS 26.3 Developer Beta 1\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025FallSeed\\/fullrestores\\/089-61648\\/B3DA1DA6-F227-4898-87F1-D6F07AEDC708\\/UniversalMac_26.3_25D5087f_Restore.ipsw\",\n      \"version\" : \"26.3.0\"\n    },\n    {\n      \"build\" : \"25C56\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 18741508230,\n      \"group\" : \"tahoe\",\n      \"id\" : \"25C56\",\n      \"mobileDeviceMinVersion\" : \"1810.0.0\",\n      \"name\" : \"macOS 26.2\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025FallFCS\\/fullrestores\\/093-37399\\/E144C918-CF99-4BBC-B1D0-3E739B9A3F2D\\/UniversalMac_26.2_25C56_Restore.ipsw\",\n      \"version\" : \"26.2.0\"\n    },\n    {\n      \"build\" : \"25C5048a\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 18721157967,\n      \"group\" : \"tahoe\",\n      \"id\" : \"25C5048a\",\n      \"mobileDeviceMinVersion\" : \"1810.0.0\",\n      \"name\" : \"macOS 26.2 Developer Beta 3\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025FallSeed\\/fullrestores\\/089-47061\\/439CA6BD-EFD9-4FFF-9C04-6A63838AB6A9\\/UniversalMac_26.2_25C5048a_Restore.ipsw\",\n      \"version\" : \"26.2.0\"\n    },\n    {\n      \"build\" : \"25C5037j\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 18720456518,\n      \"group\" : \"tahoe\",\n      \"id\" : \"25C5037j\",\n      \"mobileDeviceMinVersion\" : \"1810.0.0\",\n      \"name\" : \"macOS 26.2 Developer Beta 2\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025FallSeed\\/fullrestores\\/089-37244\\/F3E98428-E443-4226-849D-48649C50B10B\\/UniversalMac_26.2_25C5037j_Restore.ipsw\",\n      \"version\" : \"26.2.0\"\n    },\n    {\n      \"build\" : \"25C5031i\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 18688911675,\n      \"group\" : \"tahoe\",\n      \"id\" : \"25C5031i\",\n      \"mobileDeviceMinVersion\" : \"1810.0.0\",\n      \"name\" : \"macOS 26.2 Developer Beta 1\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025FallSeed\\/fullrestores\\/089-21529\\/D038FF08-78B0-4FFE-9E39-2F5A64AE46AB\\/UniversalMac_26.2_25C5031i_Restore.ipsw\",\n      \"version\" : \"26.2.0\"\n    },\n    {\n      \"build\" : \"25B78\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 18718884780,\n      \"group\" : \"tahoe\",\n      \"id\" : \"25B78\",\n      \"mobileDeviceMinVersion\" : \"1810.0.0\",\n      \"name\" : \"macOS 26.1\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025FallFCS\\/fullrestores\\/089-04148\\/791B6F00-A30B-4EB0-B2E3-257167F7715B\\/UniversalMac_26.1_25B78_Restore.ipsw\",\n      \"version\" : \"26.1.0\"\n    },\n    {\n      \"build\" : \"25B77\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 18718876862,\n      \"group\" : \"tahoe\",\n      \"id\" : \"25B77\",\n      \"mobileDeviceMinVersion\" : \"1810.0.0\",\n      \"name\" : \"macOS 26.1 RC\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025FallFCS\\/fullrestores\\/082-71650\\/9F912A37-12E8-4AC7-9ABE-F12B1A38D1B1\\/UniversalMac_26.1_25B77_Restore.ipsw\",\n      \"version\" : \"26.1.0\"\n    },\n    {\n      \"build\" : \"25B5072a\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 18580910549,\n      \"group\" : \"tahoe\",\n      \"id\" : \"25B5072a\",\n      \"mobileDeviceMinVersion\" : \"1810.0.0\",\n      \"name\" : \"macOS 26.1 Developer Beta 4\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025FallSeed\\/fullrestores\\/093-85256\\/C134DA7F-3E56-47A6-9E51-A83457E42A15\\/UniversalMac_26.1_25B5072a_Restore.ipsw\",\n      \"version\" : \"26.1.0\"\n    },\n    {\n      \"build\" : \"25B5062e\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 18573497955,\n      \"group\" : \"tahoe\",\n      \"id\" : \"25B5062e\",\n      \"mobileDeviceMinVersion\" : \"1810.0.0\",\n      \"name\" : \"macOS 26.1 Developer Beta 3\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025FallSeed\\/fullrestores\\/093-84191\\/18D74779-3C02-4F5B-AADC-77259C29ADA1\\/UniversalMac_26.1_25B5062e_Restore.ipsw\",\n      \"version\" : \"26.1.0\"\n    },\n    {\n      \"build\" : \"25B5057f\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 18573453168,\n      \"group\" : \"tahoe\",\n      \"id\" : \"25B5057f\",\n      \"mobileDeviceMinVersion\" : \"1810.0.0\",\n      \"name\" : \"macOS 26.1 Developer Beta 2\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025FallSeed\\/fullrestores\\/093-73644\\/970FD02E-AB88-4138-B23A-B8B490E7A2D6\\/UniversalMac_26.1_25B5057f_Restore.ipsw\",\n      \"version\" : \"26.1.0\"\n    },\n    {\n      \"build\" : \"25B5042k\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 19012528170,\n      \"group\" : \"tahoe\",\n      \"id\" : \"25B5042k\",\n      \"mobileDeviceMinVersion\" : \"1810.0.0\",\n      \"name\" : \"macOS 26.1 Developer Beta 1\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025FallSeed\\/fullrestores\\/082-91406\\/9745D030-E51F-497A-9E26-1B40A07FE9AE\\/UniversalMac_26.1_25B5042k_Restore.ipsw\",\n      \"version\" : \"26.1.0\"\n    },\n    {\n      \"build\" : \"25A362\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 18267682409,\n      \"group\" : \"tahoe\",\n      \"id\" : \"25A362\",\n      \"mobileDeviceMinVersion\" : \"1810.0.0\",\n      \"name\" : \"macOS 26.0.1\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025FallFCS\\/fullrestores\\/093-50898\\/60AE7E97-3E60-441B-9B34-E603C694C5C1\\/UniversalMac_26.0.1_25A362_Restore.ipsw\",\n      \"version\" : \"26.0.1\"\n    },\n    {\n      \"build\" : \"25A354\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 18267586270,\n      \"group\" : \"tahoe\",\n      \"id\" : \"25A354\",\n      \"mobileDeviceMinVersion\" : \"1810.0.0\",\n      \"name\" : \"macOS 26.0\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025FallFCS\\/fullrestores\\/093-37622\\/CE01FAB2-7F26-48EE-AEE4-5E57A7F6D8BB\\/UniversalMac_26.0_25A354_Restore.ipsw\",\n      \"version\" : \"26.0.0\"\n    },\n    {\n      \"build\" : \"25A353\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 18250895075,\n      \"group\" : \"tahoe\",\n      \"id\" : \"25A353\",\n      \"mobileDeviceMinVersion\" : \"1810.0.0\",\n      \"name\" : \"macOS 26.0 RC\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025FallFCS\\/fullrestores\\/093-37294\\/119120C1-6306-4287-AC2B-0AF964CD0B3C\\/UniversalMac_26.0_25A353_Restore.ipsw\",\n      \"version\" : \"26.0.0\"\n    },\n    {\n      \"build\" : \"25A5351b\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 18110649555,\n      \"group\" : \"tahoe\",\n      \"id\" : \"25A5351b\",\n      \"mobileDeviceMinVersion\" : \"1810.0.0\",\n      \"name\" : \"macOS 26.0 Developer Beta 9\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025SummerSeed\\/fullrestores\\/093-28610\\/036AE4A3-8A7D-44F1-A26F-ED754C346A74\\/UniversalMac_26.0_25A5351b_Restore.ipsw\",\n      \"version\" : \"26.0.0\"\n    },\n    {\n      \"build\" : \"25A5349a\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 18110732600,\n      \"group\" : \"tahoe\",\n      \"id\" : \"25A5349a\",\n      \"mobileDeviceMinVersion\" : \"1810.0.0\",\n      \"name\" : \"macOS 26.0 Developer Beta 8\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025SummerSeed\\/fullrestores\\/093-26148\\/FDF54F82-98DF-4285-A0DB-CB30F4F93C71\\/UniversalMac_26.0_25A5349a_Restore.ipsw\",\n      \"version\" : \"26.0.0\"\n    },\n    {\n      \"build\" : \"25A5346a\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 18110708260,\n      \"group\" : \"tahoe\",\n      \"id\" : \"25A5346a\",\n      \"mobileDeviceMinVersion\" : \"1810.0.0\",\n      \"name\" : \"macOS 26.0 Developer Beta 7\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025SummerSeed\\/fullrestores\\/093-15545\\/4C191A6F-E63B-4A9F-8597-6B1880EE09CC\\/UniversalMac_26.0_25A5346a_Restore.ipsw\",\n      \"version\" : \"26.0.0\"\n    },\n    {\n      \"build\" : \"25A5338b\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 18110631556,\n      \"group\" : \"tahoe\",\n      \"id\" : \"25A5338b\",\n      \"mobileDeviceMinVersion\" : \"1810.0.0\",\n      \"name\" : \"macOS 26.0 Developer Beta 6\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025SummerSeed\\/fullrestores\\/093-05416\\/26D38526-AE12-4E87-8542-D71CA2EA1A0F\\/UniversalMac_26.0_25A5338b_Restore.ipsw\",\n      \"version\" : \"26.0.0\"\n    },\n    {\n      \"build\" : \"25A5327h\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 17958910966,\n      \"group\" : \"tahoe\",\n      \"id\" : \"25A5327h\",\n      \"mobileDeviceMinVersion\" : \"1810.0.0\",\n      \"name\" : \"macOS 26.0 Developer Beta 5\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025SummerSeed\\/fullrestores\\/093-03417\\/C35F3BA3-6DCD-473C-9266-A3BF390520BE\\/UniversalMac_26.0_25A5327h_Restore.ipsw\",\n      \"version\" : \"26.0.0\"\n    },\n    {\n      \"build\" : \"25A5316i\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 18615400612,\n      \"group\" : \"tahoe\",\n      \"id\" : \"25A5316i\",\n      \"mobileDeviceMinVersion\" : \"1810.0.0\",\n      \"name\" : \"macOS 26.0 Developer Beta 4\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025SummerSeed\\/fullrestores\\/082-89871\\/D95E6C0D-147C-4F0D-878B-A4534D9B4763\\/UniversalMac_26.0_25A5316i_Restore.ipsw\",\n      \"version\" : \"26.0.0\"\n    },\n    {\n      \"build\" : \"25A5306g\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 18833175626,\n      \"group\" : \"tahoe\",\n      \"id\" : \"25A5306g\",\n      \"mobileDeviceMinVersion\" : \"1810.0.0\",\n      \"name\" : \"macOS 26.0 Developer Beta 3\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025SummerSeed\\/fullrestores\\/082-72248\\/F32F08F8-FC66-4D24-847B-F03C6CF7C410\\/UniversalMac_26.0_25A5306g_Restore.ipsw\",\n      \"version\" : \"26.0.0\"\n    },\n    {\n      \"build\" : \"24G90\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 16814137790,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24G90\",\n      \"mobileDeviceMinVersion\" : \"1774.0.0\",\n      \"name\" : \"macOS 15.6.1\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025SummerFCS\\/fullrestores\\/093-10809\\/CFD6DD38-DAF0-40DA-854F-31AAD1294C6F\\/UniversalMac_15.6.1_24G90_Restore.ipsw\",\n      \"version\" : \"15.6.1\"\n    },\n    {\n      \"build\" : \"24G84\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 16814076831,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24G84\",\n      \"mobileDeviceMinVersion\" : \"1774.0.0\",\n      \"name\" : \"macOS 15.6\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025SummerFCS\\/fullrestores\\/082-08674\\/51294E4D-A273-44BE-A280-A69FC347FB87\\/UniversalMac_15.6_24G84_Restore.ipsw\",\n      \"version\" : \"15.6.0\"\n    },\n    {\n      \"build\" : \"24G5074c\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 16877900839,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24G5074c\",\n      \"mobileDeviceMinVersion\" : \"1774.0.0\",\n      \"name\" : \"macOS 15.6 Developer Beta 3\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025SpringSeed\\/fullrestores\\/082-70173\\/CA418AAE-B538-4B79-AA99-303369C6A512\\/UniversalMac_15.6_24G5074c_Restore.ipsw\",\n      \"version\" : \"15.6.0\"\n    },\n    {\n      \"build\" : \"24G5065c\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 16877786738,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24G5065c\",\n      \"mobileDeviceMinVersion\" : \"1774.0.0\",\n      \"name\" : \"macOS 15.6 Developer Beta 2\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025SpringSeed\\/fullrestores\\/082-62583\\/9DB8F3CA-FCE0-45B5-8C17-6A4BF5E5A706\\/UniversalMac_15.6_24G5065c_Restore.ipsw\",\n      \"version\" : \"15.6.0\"\n    },\n    {\n      \"build\" : \"25A5295e\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 18740071846,\n      \"group\" : \"tahoe\",\n      \"id\" : \"25A5295e\",\n      \"mobileDeviceMinVersion\" : \"1810.0.0\",\n      \"name\" : \"macOS 26.0 Developer Beta 2\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025SummerSeed\\/fullrestores\\/082-58035\\/4434D4B0-E61D-483F-87A0-4643F9D4750D\\/UniversalMac_26.0_25A5295e_Restore.ipsw\",\n      \"version\" : \"26.0.0\"\n    },\n    {\n      \"build\" : \"25A5279m\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 18287329125,\n      \"group\" : \"tahoe\",\n      \"id\" : \"25A5279m\",\n      \"mobileDeviceMinVersion\" : \"1810.0.0\",\n      \"name\" : \"macOS 26.0 Developer Beta 1\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025SummerSeed\\/fullrestores\\/082-55592\\/95F6DA16-4116-491E-B332-7165C051E1C5\\/UniversalMac_26.0_25A5279m_Restore.ipsw\",\n      \"version\" : \"26.0.0\"\n    },\n    {\n      \"build\" : \"24G5054d\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 16877678522,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24G5054d\",\n      \"mobileDeviceMinVersion\" : \"1774.0.0\",\n      \"name\" : \"macOS 15.6 Developer Beta 1\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025SpringSeed\\/fullrestores\\/082-29051\\/6E674436-7F60-4CEC-B318-C0062D336517\\/UniversalMac_15.6_24G5054d_Restore.ipsw\",\n      \"version\" : \"15.6.0\"\n    },\n    {\n      \"build\" : \"24F74\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 16879691985,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24F74\",\n      \"mobileDeviceMinVersion\" : \"1774.0.0\",\n      \"name\" : \"macOS 15.5\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025SpringFCS\\/fullrestores\\/082-44534\\/CE6C1054-99A3-4F67-A823-3EE9E6510CDE\\/UniversalMac_15.5_24F74_Restore.ipsw\",\n      \"version\" : \"15.5.0\"\n    },\n    {\n      \"build\" : \"24F5068b\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 16876550506,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24F5068b\",\n      \"mobileDeviceMinVersion\" : \"1774.0.0\",\n      \"name\" : \"macOS 15.5 Developer Beta 4\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025SpringSeed\\/fullrestores\\/082-42002\\/44CF75E3-7765-4356-A88C-5EEEC4471CA4\\/UniversalMac_15.5_24F5068b_Restore.ipsw\",\n      \"version\" : \"15.5.0\"\n    },\n    {\n      \"build\" : \"24F5053j\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 16809364641,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24F5053j\",\n      \"mobileDeviceMinVersion\" : \"1774.0.0\",\n      \"name\" : \"macOS 15.5 Developer Beta 3\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025SpringSeed\\/fullrestores\\/072-92980\\/8E6DFD3B-8438-423F-8FFE-A95234775E84\\/UniversalMac_15.5_24F5053j_Restore.ipsw\",\n      \"version\" : \"15.5.0\"\n    },\n    {\n      \"build\" : \"24F5053f\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 16876267533,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24F5053f\",\n      \"mobileDeviceMinVersion\" : \"1774.0.0\",\n      \"name\" : \"macOS 15.5 Developer Beta 2\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025SpringSeed\\/fullrestores\\/082-24561\\/B199D1B2-296D-43E5-A29A-5FC27796F36D\\/UniversalMac_15.5_24F5053f_Restore.ipsw\",\n      \"version\" : \"15.5.0\"\n    },\n    {\n      \"build\" : \"24F5042g\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 16875810487,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24F5042g\",\n      \"mobileDeviceMinVersion\" : \"1774.0.0\",\n      \"name\" : \"macOS 15.5 Developer Beta 1\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025SpringSeed\\/fullrestores\\/072-94893\\/B85463BB-3ED6-4A5A-BE78-92AD86B531D7\\/UniversalMac_15.5_24F5042g_Restore.ipsw\",\n      \"version\" : \"15.5.0\"\n    },\n    {\n      \"build\" : \"24E263\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 16808445859,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24E263\",\n      \"mobileDeviceMinVersion\" : \"1774.0.0\",\n      \"name\" : \"macOS 15.4.1\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025SpringFCS\\/fullrestores\\/082-23340\\/61923341-EE73-4C6E-BB3E-DAB3069548BF\\/UniversalMac_15.4.1_24E263_Restore.ipsw\",\n      \"version\" : \"15.4.1\"\n    },\n    {\n      \"build\" : \"24E248\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 16808408047,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24E248\",\n      \"mobileDeviceMinVersion\" : \"1774.0.0\",\n      \"name\" : \"macOS 15.4\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025SpringFCS\\/fullrestores\\/082-16517\\/AACDDC33-9683-4431-98AF-F04EF7C15EE3\\/UniversalMac_15.4_24E248_Restore.ipsw\",\n      \"version\" : \"15.4.0\"\n    },\n    {\n      \"build\" : \"24E247\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 16808392577,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24E247\",\n      \"mobileDeviceMinVersion\" : \"1774.0.0\",\n      \"name\" : \"macOS 15.4 RC 2\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025SpringFCS\\/fullrestores\\/082-13483\\/7D547A66-A1A6-48C1-A8E1-FE78559BDD34\\/UniversalMac_15.4_24E247_Restore.ipsw\",\n      \"version\" : \"15.4.0\"\n    },\n    {\n      \"build\" : \"24E246\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 16808427372,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24E246\",\n      \"mobileDeviceMinVersion\" : \"1774.0.0\",\n      \"name\" : \"macOS 15.4 RC\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025SpringFCS\\/fullrestores\\/082-13013\\/C3CAC9AF-B139-4924-BE1B-10ED6EF931A4\\/UniversalMac_15.4_24E246_Restore.ipsw\",\n      \"version\" : \"15.4.0\"\n    },\n    {\n      \"build\" : \"24E5238a\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 16805266642,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24E5238a\",\n      \"mobileDeviceMinVersion\" : \"1774.0.0\",\n      \"name\" : \"macOS 15.4 Developer Beta 4\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025WinterSeed\\/fullrestores\\/082-08323\\/FD7D7539-514F-4F2E-AD3B-4D6D0D248706\\/UniversalMac_15.4_24E5238a_Restore.ipsw\",\n      \"version\" : \"15.4.0\"\n    },\n    {\n      \"build\" : \"24E5228e\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 16805273503,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24E5228e\",\n      \"mobileDeviceMinVersion\" : \"1774.0.0\",\n      \"name\" : \"macOS 15.4 Developer Beta 3\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025WinterSeed\\/fullrestores\\/072-96972\\/D76A7AE6-3A35-4295-B102-E5FC5C84B4DC\\/UniversalMac_15.4_24E5228e_Restore.ipsw\",\n      \"version\" : \"15.4.0\"\n    },\n    {\n      \"build\" : \"24E5222f\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 16602610887,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24E5222f\",\n      \"mobileDeviceMinVersion\" : \"1774.0.0\",\n      \"name\" : \"macOS 15.4 Developer Beta 2\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025WinterSeed\\/fullrestores\\/072-90273\\/76B9EE73-0836-47FB-8CBA-E3E902ACD21A\\/UniversalMac_15.4_24E5222f_Restore.ipsw\",\n      \"version\" : \"15.4.0\"\n    },\n    {\n      \"build\" : \"24E5206s\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 16598479538,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24E5206s\",\n      \"mobileDeviceMinVersion\" : \"1774.0.0\",\n      \"name\" : \"macOS 15.4 Developer Beta 1\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025WinterSeed\\/fullrestores\\/072-92205\\/1FCDCB29-B8A3-4461-A98A-270F1D1121C8\\/UniversalMac_15.4_24E5206s_Restore.ipsw\",\n      \"version\" : \"15.4.0\"\n    },\n    {\n      \"build\" : \"24D81\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 16532705221,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24D81\",\n      \"mobileDeviceMinVersion\" : \"1754.0.0\",\n      \"name\" : \"macOS 15.3.2\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025WinterFCS\\/fullrestores\\/082-01504\\/828B8EF9-8134-49D5-B24A-0BA504FC5ECC\\/UniversalMac_15.3.2_24D81_Restore.ipsw\",\n      \"version\" : \"15.3.2\"\n    },\n    {\n      \"build\" : \"24D70\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 16532837554,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24D70\",\n      \"mobileDeviceMinVersion\" : \"1754.0.0\",\n      \"name\" : \"macOS 15.3.1\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025WinterFCS\\/fullrestores\\/072-70618\\/42F1A8CC-7E07-4329-958A-757FF600C303\\/UniversalMac_15.3.1_24D70_Restore.ipsw\",\n      \"version\" : \"15.3.1\"\n    },\n    {\n      \"build\" : \"24D60\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 16532796141,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24D60\",\n      \"mobileDeviceMinVersion\" : \"1754.0.0\",\n      \"name\" : \"macOS 15.3\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025WinterFCS\\/fullrestores\\/072-08269\\/7CAAB9F7-E970-428D-8764-4CD7BCD105CD\\/UniversalMac_15.3_24D60_Restore.ipsw\",\n      \"version\" : \"15.3.0\"\n    },\n    {\n      \"build\" : \"24D5055b\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 16529416640,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24D5055b\",\n      \"mobileDeviceMinVersion\" : \"1754.0.0\",\n      \"name\" : \"macOS 15.3 Developer Beta 3\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025WinterSeed\\/fullrestores\\/072-61910\\/64022727-D907-4A4F-963E-CADF5C74E1A7\\/UniversalMac_15.3_24D5055b_Restore.ipsw\",\n      \"version\" : \"15.3.0\"\n    },\n    {\n      \"build\" : \"24D5040f\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 16529508256,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24D5040f\",\n      \"mobileDeviceMinVersion\" : \"1754.0.0\",\n      \"name\" : \"macOS 15.3 Developer Beta 2\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2025WinterSeed\\/fullrestores\\/072-51416\\/CB0B4DD0-1512-47FF-91B8-862ECB6CD2CF\\/UniversalMac_15.3_24D5040f_Restore.ipsw\",\n      \"version\" : \"15.3.0\"\n    },\n    {\n      \"build\" : \"24D5034f\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 16528933369,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24D5034f\",\n      \"mobileDeviceMinVersion\" : \"1754.0.0\",\n      \"name\" : \"macOS 15.3 Developer Beta 1\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024FallSeed\\/fullrestores\\/072-29298\\/D5353B95-E6B5-42DA-8DC3-664A28FFB1B9\\/UniversalMac_15.3_24D5034f_Restore.ipsw\",\n      \"version\" : \"15.3.0\"\n    },\n    {\n      \"build\" : \"24C101\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 16532456817,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24C101\",\n      \"mobileDeviceMinVersion\" : \"1754.0.0\",\n      \"name\" : \"macOS 15.2\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024FallFCS\\/fullrestores\\/072-44245\\/E811A1B0-28A9-4FCD-AE32-322E796F0EB8\\/UniversalMac_15.2_24C101_Restore.ipsw\",\n      \"version\" : \"15.2.0\"\n    },\n    {\n      \"build\" : \"24C100\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 16532519706,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24C100\",\n      \"mobileDeviceMinVersion\" : \"1754.0.0\",\n      \"name\" : \"macOS 15.2 RC 2\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024FallFCS\\/fullrestores\\/072-39903\\/D467F0BB-9D59-4DCF-A355-DD200CE808A8\\/UniversalMac_15.2_24C100_Restore.ipsw\",\n      \"version\" : \"15.2.0\"\n    },\n    {\n      \"build\" : \"24C98\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 16532504882,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24C98\",\n      \"mobileDeviceMinVersion\" : \"1754.0.0\",\n      \"name\" : \"macOS 15.2 RC\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024FallFCS\\/fullrestores\\/072-38683\\/C48192CB-7F42-4DD2-9EE6-0F6691DC088E\\/UniversalMac_15.2_24C98_Restore.ipsw\",\n      \"version\" : \"15.2.0\"\n    },\n    {\n      \"build\" : \"24C5089c\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 16529207615,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24C5089c\",\n      \"mobileDeviceMinVersion\" : \"1754.0.0\",\n      \"name\" : \"macOS 15.2 Developer Beta 4\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024FallSeed\\/fullrestores\\/072-35468\\/AF95DCD4-137A-4E7D-A5D8-E12009F4B0EF\\/UniversalMac_15.2_24C5089c_Restore.ipsw\",\n      \"version\" : \"15.2.0\"\n    },\n    {\n      \"build\" : \"24C5079e\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 16529245021,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24C5079e\",\n      \"mobileDeviceMinVersion\" : \"1754.0.0\",\n      \"name\" : \"macOS 15.2 Developer Beta 3\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024FallSeed\\/fullrestores\\/072-18002\\/44DBAF90-E56D-43E0-81F1-016B12E7FE27\\/UniversalMac_15.2_24C5079e_Restore.ipsw\",\n      \"version\" : \"15.2.0\"\n    },\n    {\n      \"build\" : \"24C5073e\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 16529788046,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24C5073e\",\n      \"mobileDeviceMinVersion\" : \"1754.0.0\",\n      \"name\" : \"macOS 15.2 Developer Beta 2\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024FallSeed\\/fullrestores\\/072-11046\\/F7A6FD60-0556-437D-88B6-6F2DD2CD8B96\\/UniversalMac_15.2_24C5073e_Restore.ipsw\",\n      \"version\" : \"15.2.0\"\n    },\n    {\n      \"build\" : \"24B2091\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 15516666958,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24B2091\",\n      \"mobileDeviceMinVersion\" : \"1754.0.0\",\n      \"name\" : \"macOS 15.1.1 (24B2091)\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024FallFCS\\/fullrestores\\/072-29960\\/5EEC3C20-D7CB-4DD1-9CE4-7C177F531A41\\/UniversalMac_15.1.1_24B2091_Restore.ipsw\",\n      \"version\" : \"15.1.1\"\n    },\n    {\n      \"build\" : \"24B91\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 15755042223,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24B91\",\n      \"mobileDeviceMinVersion\" : \"1754.0.0\",\n      \"name\" : \"macOS 15.1.1\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024FallFCS\\/fullrestores\\/072-30094\\/44BD016F-6EE3-4EE5-8890-6F9AA008C537\\/UniversalMac_15.1.1_24B91_Restore.ipsw\",\n      \"version\" : \"15.1.1\"\n    },\n    {\n      \"build\" : \"24B2083\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 15516638342,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24B2083\",\n      \"mobileDeviceMinVersion\" : \"1754.0.0\",\n      \"name\" : \"macOS 15.1 (24B2083)\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024FallFCS\\/fullrestores\\/072-12302\\/3786987A-AD94-4BFB-81B8-56D3841CA81B\\/UniversalMac_15.1_24B2083_Restore.ipsw\",\n      \"version\" : \"15.1.0\"\n    },\n    {\n      \"build\" : \"24B83\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 15738221177,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24B83\",\n      \"mobileDeviceMinVersion\" : \"1754.0.0\",\n      \"name\" : \"macOS 15.1\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024FallFCS\\/fullrestores\\/072-12340\\/78D28AC4-CCFC-45D2-BD27-1E5D915E43F9\\/UniversalMac_15.1_24B83_Restore.ipsw\",\n      \"version\" : \"15.1.0\"\n    },\n    {\n      \"build\" : \"24B82\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 15755058094,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24B82\",\n      \"mobileDeviceMinVersion\" : \"1754.0.0\",\n      \"name\" : \"macOS 15.1 RC\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024FallFCS\\/fullrestores\\/062-24344\\/4BBFA6BD-C58F-4C82-B793-6ECA98024379\\/UniversalMac_15.1_24B82_Restore.ipsw\",\n      \"version\" : \"15.1.0\"\n    },\n    {\n      \"build\" : \"24B5070a\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 15734880172,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24B5070a\",\n      \"mobileDeviceMinVersion\" : \"1754.0.0\",\n      \"name\" : \"macOS 15.1 Developer Beta 6\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024FallSeed\\/fullrestores\\/072-00383\\/7EB106DF-9B03-4C2A-ACD1-997B0BFF9364\\/UniversalMac_15.1_24B5070a_Restore.ipsw\",\n      \"version\" : \"15.1.0\"\n    },\n    {\n      \"build\" : \"24B5055e\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 15734924985,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24B5055e\",\n      \"mobileDeviceMinVersion\" : \"1754.0.0\",\n      \"name\" : \"macOS 15.1 Developer Beta 5\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024FallSeed\\/fullrestores\\/062-91081\\/FADE991A-C188-4D32-8F97-313F57E27359\\/UniversalMac_15.1_24B5055e_Restore.ipsw\",\n      \"version\" : \"15.1.0\"\n    },\n    {\n      \"build\" : \"24B5046f\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 15870172423,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24B5046f\",\n      \"mobileDeviceMinVersion\" : \"1754.0.0\",\n      \"name\" : \"macOS 15.1 Developer Beta 4\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024FallSeed\\/fullrestores\\/062-80453\\/61A07BD2-553D-425D-8B93-7E5A730AA4CA\\/UniversalMac_15.1_24B5046f_Restore.ipsw\",\n      \"version\" : \"15.1.0\"\n    },\n    {\n      \"build\" : \"24B5035e\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 15869388891,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24B5035e\",\n      \"mobileDeviceMinVersion\" : \"1754.0.0\",\n      \"name\" : \"macOS 15.1 Developer Beta 3\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024SummerSeed\\/fullrestores\\/062-74506\\/D50AC8F9-4795-4711-9C1A-907B6EB829A2\\/UniversalMac_15.1_24B5035e_Restore.ipsw\",\n      \"version\" : \"15.1.0\"\n    },\n    {\n      \"build\" : \"24A348\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 15737037399,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24A348\",\n      \"mobileDeviceMinVersion\" : \"1754.0.0\",\n      \"name\" : \"macOS 15.0.1\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024FallFCS\\/fullrestores\\/072-01423\\/566E5B4E-1100-4643-91B3-131247351844\\/UniversalMac_15.0.1_24A348_Restore.ipsw\",\n      \"version\" : \"15.0.1\"\n    },\n    {\n      \"build\" : \"24A335\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 15736994120,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24A335\",\n      \"mobileDeviceMinVersion\" : \"1754.0.0\",\n      \"name\" : \"macOS 15.0\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024FallFCS\\/fullrestores\\/062-78489\\/BDA44327-C79E-4608-A7E0-455A7E91911F\\/UniversalMac_15.0_24A335_Restore.ipsw\",\n      \"version\" : \"15.0.0\"\n    },\n    {\n      \"build\" : \"24A5331b\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 15598810418,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24A5331b\",\n      \"mobileDeviceMinVersion\" : \"1754.0.0\",\n      \"name\" : \"macOS 15.0 Developer Beta 8\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024SummerSeed\\/fullrestores\\/062-71949\\/A67919DD-2AAC-4324-99BF-70765065DD70\\/UniversalMac_15.0_24A5331b_Restore.ipsw\",\n      \"version\" : \"15.0.0\"\n    },\n    {\n      \"build\" : \"24A5327a\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 15598903779,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24A5327a\",\n      \"mobileDeviceMinVersion\" : \"1754.0.0\",\n      \"name\" : \"macOS 15.0 Developer Beta 7\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024SummerSeed\\/fullrestores\\/062-64520\\/F98C92F6-656A-46BB-A1DB-F447698DBB72\\/UniversalMac_15.0_24A5327a_Restore.ipsw\",\n      \"version\" : \"15.0.0\"\n    },\n    {\n      \"build\" : \"24A5320a\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 15598703819,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24A5320a\",\n      \"mobileDeviceMinVersion\" : \"1754.0.0\",\n      \"name\" : \"macOS 15.0 Developer Beta 6\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024SummerSeed\\/fullrestores\\/062-59123\\/7B77FE01-E040-4D4A-8E93-64BBF212A351\\/UniversalMac_15.0_24A5320a_Restore.ipsw\",\n      \"version\" : \"15.0.0\"\n    },\n    {\n      \"build\" : \"24A5309e\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 16020119097,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24A5309e\",\n      \"mobileDeviceMinVersion\" : \"1754.0.0\",\n      \"name\" : \"macOS 15.0 Developer Beta 5\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024SummerSeed\\/fullrestores\\/062-45756\\/5B063A0C-5ECA-434B-A462-CD1F65737105\\/UniversalMac_15.0_24A5309e_Restore.ipsw\",\n      \"version\" : \"15.0.0\"\n    },\n    {\n      \"build\" : \"24A5298h\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 15892020340,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24A5298h\",\n      \"mobileDeviceMinVersion\" : \"1754.0.0\",\n      \"name\" : \"macOS 15.0 Developer Beta 4\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024SummerSeed\\/fullrestores\\/062-39288\\/8A58D88A-ED62-4E46-A406-13F0294EB4F5\\/UniversalMac_15.0_24A5298h_Restore.ipsw\",\n      \"version\" : \"15.0.0\"\n    },\n    {\n      \"build\" : \"24A5289h\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 15756935317,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24A5289h\",\n      \"mobileDeviceMinVersion\" : \"1742.0.0\",\n      \"name\" : \"macOS 15.0 Developer Beta 3 (24A5289h)\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024SummerSeed\\/fullrestores\\/062-36090\\/83523325-6540-4A17-BA93-A5849E4E9AC2\\/UniversalMac_15.0_24A5289h_Restore.ipsw\",\n      \"version\" : \"15.0.0\"\n    },\n    {\n      \"build\" : \"24A5289g\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 15756393703,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24A5289g\",\n      \"mobileDeviceMinVersion\" : \"1742.0.0\",\n      \"name\" : \"macOS 15.0 Developer Beta 3\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024SummerSeed\\/fullrestores\\/062-33928\\/E22622FB-DAFC-4C64-8CC6-B7CAF89477F7\\/UniversalMac_15.0_24A5289g_Restore.ipsw\",\n      \"version\" : \"15.0.0\"\n    },\n    {\n      \"build\" : \"24A5279h\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 16230968934,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24A5279h\",\n      \"mobileDeviceMinVersion\" : \"1742.0.0\",\n      \"name\" : \"macOS 15.0 Developer Beta 2\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024SummerSeed\\/fullrestores\\/062-22022\\/AB066FFB-B7FE-4132-83AC-E58A323805C1\\/UniversalMac_15.0_24A5279h_Restore.ipsw\",\n      \"version\" : \"15.0.0\"\n    },\n    {\n      \"build\" : \"24A5264n\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 15824875957,\n      \"group\" : \"sequoia\",\n      \"id\" : \"24A5264n\",\n      \"mobileDeviceMinVersion\" : \"1742.0.0\",\n      \"name\" : \"macOS 15.0 Developer Beta 1\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024SummerSeed\\/fullrestores\\/052-49083\\/ED8F54D6-A7BF-488A-85E5-617E08C41383\\/UniversalMac_15.0_24A5264n_Restore.ipsw\",\n      \"version\" : \"15.0.0\"\n    },\n    {\n      \"build\" : \"23G93\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 14790601661,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23G93\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.6.1\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024SummerFCS\\/fullrestores\\/062-52859\\/932E0A8F-6644-4759-82DA-F8FA8DEA806A\\/UniversalMac_14.6.1_23G93_Restore.ipsw\",\n      \"version\" : \"14.6.1\"\n    },\n    {\n      \"build\" : \"23G80\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 14790718754,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23G80\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.6\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024SummerFCS\\/fullrestores\\/052-69922\\/F5DA2B64-25EB-4370-9E89-FA5689859796\\/UniversalMac_14.6_23G80_Restore.ipsw\",\n      \"version\" : \"14.6.0\"\n    },\n    {\n      \"build\" : \"23G5075b\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 14634053502,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23G5075b\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.6 Developer Beta 4\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024SpringSeed\\/fullrestores\\/062-37092\\/7D35E462-CD30-4B65-A1D6-D2AF0DA9AED8\\/UniversalMac_14.6_23G5075b_Restore.ipsw\",\n      \"version\" : \"14.6.0\"\n    },\n    {\n      \"build\" : \"23G5066c\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 14631054851,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23G5066c\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.6 Developer Beta 3\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024SpringSeed\\/fullrestores\\/062-28262\\/A4400A1D-9F38-41F7-B1CD-B3CB782DA2A3\\/UniversalMac_14.6_23G5066c_Restore.ipsw\",\n      \"version\" : \"14.6.0\"\n    },\n    {\n      \"build\" : \"23G5061b\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 14647192213,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23G5061b\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.6 Developer Beta 2\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024SpringSeed\\/fullrestores\\/062-20934\\/B03E5DE3-1484-489F-AC53-956AC17DB7F0\\/UniversalMac_14.6_23G5061b_Restore.ipsw\",\n      \"version\" : \"14.6.0\"\n    },\n    {\n      \"build\" : \"23G5052d\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 14640728273,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23G5052d\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.6 Developer Beta 1\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024SpringSeed\\/fullrestores\\/052-91270\\/121EBE77-A313-4250-9F8D-55F2AABBCD4C\\/UniversalMac_14.6_23G5052d_Restore.ipsw\",\n      \"version\" : \"14.6.0\"\n    },\n    {\n      \"build\" : \"23F79\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 14801044197,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23F79\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.5\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024SpringFCS\\/fullrestores\\/062-01897\\/C874907B-9F82-4109-87EB-6B3C9BF1507D\\/UniversalMac_14.5_23F79_Restore.ipsw\",\n      \"version\" : \"14.5.0\"\n    },\n    {\n      \"build\" : \"23F5074a\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 14631266856,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23F5074a\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.5 Developer Beta 4\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024SpringSeed\\/fullrestores\\/052-93222\\/8E2C4935-6FAA-4624-88D5-008966BD1A4C\\/UniversalMac_14.5_23F5074a_Restore.ipsw\",\n      \"version\" : \"14.5.0\"\n    },\n    {\n      \"build\" : \"23F5064f\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 14612241697,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23F5064f\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.5 Developer Beta 3\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024SpringSeed\\/fullrestores\\/052-86827\\/74D9C6E6-396C-451B-8ACD-DB523D9FA93F\\/UniversalMac_14.5_23F5064f_Restore.ipsw\",\n      \"version\" : \"14.5.0\"\n    },\n    {\n      \"build\" : \"23F5059e\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 14625397506,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23F5059e\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.5 Developer Beta 2\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024SpringSeed\\/fullrestores\\/052-84344\\/599ECB0B-BF84-449F-B0D1-1428CFFAACC5\\/UniversalMac_14.5_23F5059e_Restore.ipsw\",\n      \"version\" : \"14.5.0\"\n    },\n    {\n      \"build\" : \"23F5049f\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 14617671816,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23F5049f\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.5 Developer Beta 1\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024SpringSeed\\/fullrestores\\/052-63630\\/11AFEFAF-A420-4CAE-84E7-D66530A7E9CA\\/UniversalMac_14.5_23F5049f_Restore.ipsw\",\n      \"version\" : \"14.5.0\"\n    },\n    {\n      \"build\" : \"23E224\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 14744062393,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23E224\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.4.1\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024WinterFCS\\/fullrestores\\/052-77579\\/4569734E-120C-4F31-AD08-FC1FF825D059\\/UniversalMac_14.4.1_23E224_Restore.ipsw\",\n      \"version\" : \"14.4.1\"\n    },\n    {\n      \"build\" : \"23E214\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 14744505377,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23E214\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.4\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024WinterFCS\\/fullrestores\\/052-61990\\/47F0DD06-1106-4F2E-9CD6-AE6B361A0EC6\\/UniversalMac_14.4_23E214_Restore.ipsw\",\n      \"version\" : \"14.4.0\"\n    },\n    {\n      \"build\" : \"23E5211a\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 14553892980,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23E5211a\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.4 Developer Beta 5\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024WinterSeed\\/fullrestores\\/052-59970\\/42D9D34A-DD6E-40D5-AA2B-EEB8E65EE64E\\/UniversalMac_14.4_23E5211a_Restore.ipsw\",\n      \"version\" : \"14.4.0\"\n    },\n    {\n      \"build\" : \"23E5205c\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 14556773482,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23E5205c\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.4 Developer Beta 4\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024WinterSeed\\/fullrestores\\/052-58286\\/18DAC58E-4161-45D4-BE02-AE47B12B87B8\\/UniversalMac_14.4_23E5205c_Restore.ipsw\",\n      \"version\" : \"14.4.0\"\n    },\n    {\n      \"build\" : \"23E5196e\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 14552980775,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23E5196e\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.4 Developer Beta 3\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024WinterSeed\\/fullrestores\\/052-51632\\/C6A7A58A-3CB1-4271-9CED-1D5DAE9078CF\\/UniversalMac_14.4_23E5196e_Restore.ipsw\",\n      \"version\" : \"14.4.0\"\n    },\n    {\n      \"build\" : \"23E5191e\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 14560421496,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23E5191e\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.4 Developer Beta 2\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024WinterSeed\\/fullrestores\\/052-45324\\/985FEED9-1A9E-49EB-A904-467E5E7EEE9C\\/UniversalMac_14.4_23E5191e_Restore.ipsw\",\n      \"version\" : \"14.4.0\"\n    },\n    {\n      \"build\" : \"23E5180j\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 14526761461,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23E5180j\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.4 Developer Beta 1\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024WinterSeed\\/fullrestores\\/052-42583\\/F09F735D-D3C3-4AF7-9BEC-F9C5D3B84363\\/UniversalMac_14.4_23E5180j_Restore.ipsw\",\n      \"version\" : \"14.4.0\"\n    },\n    {\n      \"build\" : \"23D60\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 14503964718,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23D60\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.3.1\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024WinterFCS\\/fullrestores\\/052-40770\\/72916BCC-D357-422D-A4A2-EF1DEDF6968C\\/UniversalMac_14.3.1_23D60_Restore.ipsw\",\n      \"version\" : \"14.3.1\"\n    },\n    {\n      \"build\" : \"23D56\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 14505910832,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23D56\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.3\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2024WinterFCS\\/fullrestores\\/042-78241\\/B45074EB-2891-4C05-BCA4-7463F3AC0982\\/UniversalMac_14.3_23D56_Restore.ipsw\",\n      \"version\" : \"14.3.0\"\n    },\n    {\n      \"build\" : \"23D5051b\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 14345532149,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23D5051b\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.3 Developer Beta 3\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023WinterSeed\\/fullrestores\\/052-27952\\/6B395980-45C8-4E7C-9252-9F4CE35C7EDB\\/UniversalMac_14.3_23D5051b_Restore.ipsw\",\n      \"version\" : \"14.3.0\"\n    },\n    {\n      \"build\" : \"23D5043d\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 14354286748,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23D5043d\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.3 Developer Beta 2\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023WinterSeed\\/fullrestores\\/052-23267\\/60655998-DD9A-40BF-BFAB-6D2A4442DE83\\/UniversalMac_14.3_23D5043d_Restore.ipsw\",\n      \"version\" : \"14.3.0\"\n    },\n    {\n      \"build\" : \"23D5033f\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 14349582906,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23D5033f\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.3 Developer Beta 1\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023WinterSeed\\/fullrestores\\/052-04657\\/4126C431-B3EA-42C2-BC36-ED83715B8700\\/UniversalMac_14.3_23D5033f_Restore.ipsw\",\n      \"version\" : \"14.3.0\"\n    },\n    {\n      \"build\" : \"23C71\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 14489499738,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23C71\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.2.1\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023FallFCS\\/fullrestores\\/052-22662\\/ECE59A41-DACC-4CA5-AB23-FDED1A4567DE\\/UniversalMac_14.2.1_23C71_Restore.ipsw\",\n      \"version\" : \"14.2.1\"\n    },\n    {\n      \"build\" : \"23C64\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 14488791535,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23C64\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.2\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023FallFCS\\/fullrestores\\/052-15117\\/DC2EE605-ABF3-41AE-9652-D137A8AA5907\\/UniversalMac_14.2_23C64_Restore.ipsw\",\n      \"version\" : \"14.2.0\"\n    },\n    {\n      \"build\" : \"23C63\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 14489489499,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23C63\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.2 RC\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023FallFCS\\/fullrestores\\/052-14744\\/7215DBB4-BEAD-4A9C-9202-276ABA6832D5\\/UniversalMac_14.2_23C63_Restore.ipsw\",\n      \"version\" : \"14.2.0\"\n    },\n    {\n      \"build\" : \"23C5055b\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 14332799879,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23C5055b\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.2 Developer Beta 4\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023WinterFCS\\/fullrestores\\/052-05962\\/A8344C85-06CE-43C7-9FF6-7B477A4DB8BA\\/UniversalMac_14.2_23C5055b_Restore.ipsw\",\n      \"version\" : \"14.2.0\"\n    },\n    {\n      \"build\" : \"23C5047e\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 14324106377,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23C5047e\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.2 Developer Beta 3\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023FallSeed\\/fullrestores\\/042-99526\\/0A9085CC-B36A-400A-86D8-B9FE23B1DA29\\/UniversalMac_14.2_23C5047e_Restore.ipsw\",\n      \"version\" : \"14.2.0\"\n    },\n    {\n      \"build\" : \"23C5041e\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 14343066933,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23C5041e\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.2 Developer Beta 2\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023FallSeed\\/fullrestores\\/042-92143\\/F642F928-DEE0-4C3F-A416-85745C778855\\/UniversalMac_14.2_23C5041e_Restore.ipsw\",\n      \"version\" : \"14.2.0\"\n    },\n    {\n      \"build\" : \"23C5030f\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 14015403903,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23C5030f\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.2 Developer Beta 1\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023FallSeed\\/fullrestores\\/042-71093\\/ECEFE157-E28B-40B4-9F21-CFD075129029\\/UniversalMac_14.2_23C5030f_Restore.ipsw\",\n      \"version\" : \"14.2.0\"\n    },\n    {\n      \"build\" : \"23B92\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 13980816431,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23B92\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.1.2\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023FallFCS\\/fullrestores\\/052-09443\\/E8752548-0B80-480C-9FB4-67246672C1B5\\/UniversalMac_14.1.2_23B92_Restore.ipsw\",\n      \"version\" : \"14.1.2\"\n    },\n    {\n      \"build\" : \"23B81\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 13981156491,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23B81\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.1.1\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023FallFCS\\/fullrestores\\/042-89681\\/55BD14DB-5535-4203-9359-E2C070E43FBE\\/UniversalMac_14.1.1_23B81_Restore.ipsw\",\n      \"version\" : \"14.1.1\"\n    },\n    {\n      \"build\" : \"23B74\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 13981550665,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23B74\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.1\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023FallFCS\\/fullrestores\\/042-86430\\/DBE44960-58A6-4715-948B-D64F33F769BD\\/UniversalMac_14.1_23B74_Restore.ipsw\",\n      \"version\" : \"14.1.0\"\n    },\n    {\n      \"build\" : \"23B73\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 13981665474,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23B73\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.1 RC\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023FallFCS\\/fullrestores\\/042-10900\\/347B734E-BC0B-41FA-9671-8000FCB5B0BB\\/UniversalMac_14.1_23B73_Restore.ipsw\",\n      \"version\" : \"14.1.0\"\n    },\n    {\n      \"build\" : \"23B5067a\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 13964726406,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23B5067a\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.1 Developer Beta 3\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023FallSeed\\/fullrestores\\/042-73325\\/B24FC9CF-34D1-44A6-B977-FA718FE83DEB\\/UniversalMac_14.1_23B5067a_Restore.ipsw\",\n      \"version\" : \"14.1.0\"\n    },\n    {\n      \"build\" : \"23B5056e\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 13913662198,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23B5056e\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.1 Developer Beta 2\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023FallSeed\\/fullrestores\\/042-65885\\/0FA6C4A7-C21A-4A5F-84C8-8FEE0D98A153\\/UniversalMac_14.1_23B5056e_Restore.ipsw\",\n      \"version\" : \"14.1.0\"\n    },\n    {\n      \"build\" : \"23B5046f\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 13909776599,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23B5046f\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.1 Developer Beta 1\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023FallSeed\\/fullrestores\\/042-60177\\/C4B6F5B3-8B66-461A-A048-4A9925F36FCD\\/UniversalMac_14.1_23B5046f_Restore.ipsw\",\n      \"version\" : \"14.1.0\"\n    },\n    {\n      \"build\" : \"23A344\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 13905898651,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23A344\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.0\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023FallFCS\\/fullrestores\\/042-54934\\/0E101AD6-3117-4B63-9BF1-143B6DB9270A\\/UniversalMac_14.0_23A344_Restore.ipsw\",\n      \"version\" : \"14.0.0\"\n    },\n    {\n      \"build\" : \"23A339\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 13905892847,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23A339\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.0 RC\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023FallFCS\\/fullrestores\\/002-81996\\/596571C1-9856-4BB3-B5BF-B5A48F4B406E\\/UniversalMac_14.0_23A339_Restore.ipsw\",\n      \"version\" : \"14.0.0\"\n    },\n    {\n      \"build\" : \"23A5337a\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 13817165626,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23A5337a\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.0 Developer Beta 7\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023SummerSeed\\/fullrestores\\/042-41500\\/D1789AEF-013B-4112-8A1E-401589023267\\/UniversalMac_14.0_23A5337a_Restore.ipsw\",\n      \"version\" : \"14.0.0\"\n    },\n    {\n      \"build\" : \"23A5328b\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 13818265607,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23A5328b\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.0 Developer Beta 6\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023SummerSeed\\/fullrestores\\/042-37824\\/AA6B32A0-3C2C-4BEB-95A1-64E601934330\\/UniversalMac_14.0_23A5328b_Restore.ipsw\",\n      \"version\" : \"14.0.0\"\n    },\n    {\n      \"build\" : \"23A5312d\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 13902876317,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23A5312d\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.0 Developer Beta 5\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023SummerSeed\\/fullrestores\\/042-27168\\/7E046825-8EBA-4AAE-8ECC-DDD51B9306D2\\/UniversalMac_14.0_23A5312d_Restore.ipsw\",\n      \"version\" : \"14.0.0\"\n    },\n    {\n      \"build\" : \"23A5301h\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 13966115260,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23A5301h\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.0 Developer Beta 4\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023SummerSeed\\/fullrestores\\/042-25548\\/9EA6EC3D-5A7D-4D53-A17C-70EE71393921\\/UniversalMac_14.0_23A5301h_Restore.ipsw\",\n      \"version\" : \"14.0.0\"\n    },\n    {\n      \"build\" : \"23A5286i\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 14040882686,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23A5286i\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.0 Developer Beta 3 (23A5286i)\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023SummerSeed\\/fullrestores\\/042-13887\\/3B4075C1-B695-49EA-82D9-4B720699D341\\/UniversalMac_14.0_23A5286i_Restore.ipsw\",\n      \"version\" : \"14.0.0\"\n    },\n    {\n      \"build\" : \"23A5286g\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 14039361424,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23A5286g\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.0 Developer Beta 3\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023SummerSeed\\/fullrestores\\/042-06324\\/379026FA-C14F-4095-99FD-19F607D10EBF\\/UniversalMac_14.0_23A5286g_Restore.ipsw\",\n      \"version\" : \"14.0.0\"\n    },\n    {\n      \"build\" : \"23A5276g\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 14011239154,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23A5276g\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.0 Developer Beta 2\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023SummerSeed\\/fullrestores\\/032-95861\\/67600C59-8516-4A46-B9D7-4007D395CEF5\\/UniversalMac_14.0_23A5276g_Restore.ipsw\",\n      \"version\" : \"14.0.0\"\n    },\n    {\n      \"build\" : \"23A5257q\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 14073497027,\n      \"group\" : \"sonoma\",\n      \"id\" : \"23A5257q\",\n      \"mobileDeviceMinVersion\" : \"1600.0.0\",\n      \"name\" : \"macOS 14.0 Developer Beta 1\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023SummerSeed\\/fullrestores\\/032-94355\\/CBE8CBE1-750D-487E-A393-B90FEF60CEBA\\/UniversalMac_14.0_23A5257q_Restore.ipsw\",\n      \"version\" : \"14.0.0\"\n    },\n    {\n      \"build\" : \"22G120\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 12893555341,\n      \"group\" : \"ventura\",\n      \"id\" : \"22G120\",\n      \"mobileDeviceMinVersion\" : \"1400.0.0\",\n      \"name\" : \"macOS 13.6\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023FallFCS\\/fullrestores\\/042-55833\\/C0830847-A2F8-458F-B680-967991820931\\/UniversalMac_13.6_22G120_Restore.ipsw\",\n      \"version\" : \"13.6.0\"\n    },\n    {\n      \"build\" : \"22G91\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 12892043230,\n      \"group\" : \"ventura\",\n      \"id\" : \"22G91\",\n      \"mobileDeviceMinVersion\" : \"1400.0.0\",\n      \"name\" : \"macOS 13.5.2\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023SummerFCS\\/fullrestores\\/042-43686\\/945D434B-DA5D-48DB-A558-F6D18D11AD69\\/UniversalMac_13.5.2_22G91_Restore.ipsw\",\n      \"version\" : \"13.5.2\"\n    },\n    {\n      \"build\" : \"22G90\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 12892897195,\n      \"group\" : \"ventura\",\n      \"id\" : \"22G90\",\n      \"mobileDeviceMinVersion\" : \"1400.0.0\",\n      \"name\" : \"macOS 13.5.1\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023SummerFCS\\/fullrestores\\/042-25658\\/2D6BE8DB-5549-4F85-8C54-39FC23BABC68\\/UniversalMac_13.5.1_22G90_Restore.ipsw\",\n      \"version\" : \"13.5.1\"\n    },\n    {\n      \"build\" : \"22G74\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 12893195726,\n      \"group\" : \"ventura\",\n      \"id\" : \"22G74\",\n      \"mobileDeviceMinVersion\" : \"1400.0.0\",\n      \"name\" : \"macOS 13.5\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023SummerFCS\\/fullrestores\\/032-69606\\/D3E05CDF-E105-434C-A4A1-4E3DC7668DD0\\/UniversalMac_13.5_22G74_Restore.ipsw\",\n      \"version\" : \"13.5.0\"\n    },\n    {\n      \"build\" : \"22G5072a\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 12876188620,\n      \"group\" : \"ventura\",\n      \"id\" : \"22G5072a\",\n      \"mobileDeviceMinVersion\" : \"1400.0.0\",\n      \"name\" : \"macOS 13.5 Developer Beta 5\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023SpringSeed\\/fullrestores\\/042-09570\\/8DA0B0AA-6FD4-42C4-A54E-BC0D53B92AC0\\/UniversalMac_13.5_22G5072a_Restore.ipsw\",\n      \"version\" : \"13.5.0\"\n    },\n    {\n      \"build\" : \"22G5059d\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 12881087818,\n      \"group\" : \"ventura\",\n      \"id\" : \"22G5059d\",\n      \"mobileDeviceMinVersion\" : \"1400.0.0\",\n      \"name\" : \"macOS 13.5 Developer Beta 4\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023SpringSeed\\/fullrestores\\/042-03209\\/55CBE04D-FD90-483B-A6D7-45E0FBC1C94F\\/UniversalMac_13.5_22G5059d_Restore.ipsw\",\n      \"version\" : \"13.5.0\"\n    },\n    {\n      \"build\" : \"22G5048d\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 12874423585,\n      \"group\" : \"ventura\",\n      \"id\" : \"22G5048d\",\n      \"mobileDeviceMinVersion\" : \"1400.0.0\",\n      \"name\" : \"macOS 13.5 Developer Beta 3\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023SpringSeed\\/fullrestores\\/032-93679\\/1D39F2AC-8FD4-46A3-A159-478C76472B16\\/UniversalMac_13.5_22G5048d_Restore.ipsw\",\n      \"version\" : \"13.5.0\"\n    },\n    {\n      \"build\" : \"22G5038d\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 12727337348,\n      \"group\" : \"ventura\",\n      \"id\" : \"22G5038d\",\n      \"mobileDeviceMinVersion\" : \"1400.0.0\",\n      \"name\" : \"macOS 13.5 Developer Beta 2\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023SpringSeed\\/fullrestores\\/032-92523\\/E476F5EC-D046-4A76-889B-F19DA354459E\\/UniversalMac_13.5_22G5038d_Restore.ipsw\",\n      \"version\" : \"13.5.0\"\n    },\n    {\n      \"build\" : \"22G5027e\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 12729623010,\n      \"group\" : \"ventura\",\n      \"id\" : \"22G5027e\",\n      \"mobileDeviceMinVersion\" : \"1400.0.0\",\n      \"name\" : \"macOS 13.5 Developer Beta 1\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023SpringSeed\\/fullrestores\\/032-86178\\/CE6C5645-C5C3-41A9-B986-D5F0BD7BB10B\\/UniversalMac_13.5_22G5027e_Restore.ipsw\",\n      \"version\" : \"13.5.0\"\n    },\n    {\n      \"build\" : \"22F82\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 12739802320,\n      \"group\" : \"ventura\",\n      \"id\" : \"22F82\",\n      \"mobileDeviceMinVersion\" : \"1400.0.0\",\n      \"name\" : \"macOS 13.4.1\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023SpringFCS\\/fullrestores\\/042-01877\\/2F49A9FE-7033-41D0-9D0C-64EFCE6B4C22\\/UniversalMac_13.4.1_22F82_Restore.ipsw\",\n      \"version\" : \"13.4.1\"\n    },\n    {\n      \"build\" : \"22F66\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 12739995153,\n      \"group\" : \"ventura\",\n      \"id\" : \"22F66\",\n      \"mobileDeviceMinVersion\" : \"1400.0.0\",\n      \"name\" : \"macOS 13.4\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023SpringFCS\\/fullrestores\\/032-84884\\/F97A22EE-9B5E-4FD5-94C1-B39DCEE8D80F\\/UniversalMac_13.4_22F66_Restore.ipsw\",\n      \"version\" : \"13.4.0\"\n    },\n    {\n      \"build\" : \"22F63\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 12739473501,\n      \"group\" : \"ventura\",\n      \"id\" : \"22F63\",\n      \"mobileDeviceMinVersion\" : \"1400.0.0\",\n      \"name\" : \"macOS 13.4 RC 2\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023SpringFCS\\/fullrestores\\/032-83954\\/6E06237C-1B56-4932-A8E1-3A07A3EE03A8\\/UniversalMac_13.4_22F63_Restore.ipsw\",\n      \"version\" : \"13.4.0\"\n    },\n    {\n      \"build\" : \"22F62\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 12738586980,\n      \"group\" : \"ventura\",\n      \"id\" : \"22F62\",\n      \"mobileDeviceMinVersion\" : \"1400.0.0\",\n      \"name\" : \"macOS 13.4 RC\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023SpringFCS\\/fullrestores\\/032-44024\\/731F1533-53BE-4CEB-AA05-74F333CA904A\\/UniversalMac_13.4_22F62_Restore.ipsw\",\n      \"version\" : \"13.4.0\"\n    },\n    {\n      \"build\" : \"22F5059b\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 12721895514,\n      \"group\" : \"ventura\",\n      \"id\" : \"22F5059b\",\n      \"mobileDeviceMinVersion\" : \"1400.0.0\",\n      \"name\" : \"macOS 13.4 Developer Beta 4\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023SpringSeed\\/fullrestores\\/032-79565\\/BA9CBFB7-152C-4FB6-B0B3-47769997BFA1\\/UniversalMac_13.4_22F5059b_Restore.ipsw\",\n      \"version\" : \"13.4.0\"\n    },\n    {\n      \"build\" : \"22F5049e\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 12720838737,\n      \"group\" : \"ventura\",\n      \"id\" : \"22F5049e\",\n      \"mobileDeviceMinVersion\" : \"1400.0.0\",\n      \"name\" : \"macOS 13.4 Developer Beta 3\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023SpringSeed\\/fullrestores\\/032-76661\\/573284E7-4A4A-440C-AC01-6065C7A8E667\\/UniversalMac_13.4_22F5049e_Restore.ipsw\",\n      \"version\" : \"13.4.0\"\n    },\n    {\n      \"build\" : \"22F5037d\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 12743516873,\n      \"group\" : \"ventura\",\n      \"id\" : \"22F5037d\",\n      \"mobileDeviceMinVersion\" : \"1400.0.0\",\n      \"name\" : \"macOS 13.4 Developer Beta 2\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023SpringSeed\\/fullrestores\\/032-69885\\/ECFA1532-C633-4ACE-9D2C-3B5FD19510D4\\/UniversalMac_13.4_22F5037d_Restore.ipsw\",\n      \"version\" : \"13.4.0\"\n    },\n    {\n      \"build\" : \"22F5027f\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 12728262691,\n      \"group\" : \"ventura\",\n      \"id\" : \"22F5027f\",\n      \"mobileDeviceMinVersion\" : \"1400.0.0\",\n      \"name\" : \"macOS 13.4 Developer Beta 1\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023SpringSeed\\/fullrestores\\/032-69187\\/B11709E0-1CF5-4460-A069-D12E1243E2AD\\/UniversalMac_13.4_22F5027f_Restore.ipsw\",\n      \"version\" : \"13.4.0\"\n    },\n    {\n      \"build\" : \"22E261\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 12726460903,\n      \"group\" : \"ventura\",\n      \"id\" : \"22E261\",\n      \"mobileDeviceMinVersion\" : \"1400.0.0\",\n      \"name\" : \"macOS 13.3.1\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023WinterFCS\\/fullrestores\\/032-66602\\/418BC37A-FCD9-400A-B4FA-022A19576CD4\\/UniversalMac_13.3.1_22E261_Restore.ipsw\",\n      \"version\" : \"13.3.1\"\n    },\n    {\n      \"build\" : \"22E252\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 12727918028,\n      \"group\" : \"ventura\",\n      \"id\" : \"22E252\",\n      \"mobileDeviceMinVersion\" : \"1400.0.0\",\n      \"name\" : \"macOS 13.3\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023WinterSeed\\/fullrestores\\/002-75537\\/8250FA0E-0962-46D6-8A90-57A390B9FFD7\\/UniversalMac_13.3_22E252_Restore.ipsw\",\n      \"version\" : \"13.3.0\"\n    },\n    {\n      \"build\" : \"22E5246b\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 12713804212,\n      \"group\" : \"ventura\",\n      \"id\" : \"22E5246b\",\n      \"mobileDeviceMinVersion\" : \"1400.0.0\",\n      \"name\" : \"macOS 13.3 Developer Beta 4\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023WinterSeed\\/fullrestores\\/032-63669\\/7C0F9BA8-35C0-457F-AF56-6943D58A2CDB\\/UniversalMac_13.3_22E5246b_Restore.ipsw\",\n      \"version\" : \"13.3.0\"\n    },\n    {\n      \"build\" : \"22E5236f\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 12711164370,\n      \"group\" : \"ventura\",\n      \"id\" : \"22E5236f\",\n      \"mobileDeviceMinVersion\" : \"1400.0.0\",\n      \"name\" : \"macOS 13.3 Developer Beta 3\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023WinterSeed\\/fullrestores\\/032-60411\\/1DDA996F-B620-4770-8FFE-87AB2043784D\\/UniversalMac_13.3_22E5236f_Restore.ipsw\",\n      \"version\" : \"13.3.0\"\n    },\n    {\n      \"build\" : \"22E5230e\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 12702079514,\n      \"group\" : \"ventura\",\n      \"id\" : \"22E5230e\",\n      \"mobileDeviceMinVersion\" : \"1400.0.0\",\n      \"name\" : \"macOS 13.3 Developer Beta 2\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023WinterSeed\\/fullrestores\\/032-54760\\/AE02E378-FD59-474D-93AB-C52617103C72\\/UniversalMac_13.3_22E5230e_Restore.ipsw\",\n      \"version\" : \"13.3.0\"\n    },\n    {\n      \"build\" : \"22E5219e\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 12690065479,\n      \"group\" : \"ventura\",\n      \"id\" : \"22E5219e\",\n      \"mobileDeviceMinVersion\" : \"1400.0.0\",\n      \"name\" : \"macOS 13.3 Developer Beta 1\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023WinterSeed\\/fullrestores\\/032-01932\\/676E0981-4535-4942-A4AE-E14C604CE719\\/UniversalMac_13.3_22E5219e_Restore.ipsw\",\n      \"version\" : \"13.3.0\"\n    },\n    {\n      \"build\" : \"22D68\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 12494476408,\n      \"group\" : \"ventura\",\n      \"id\" : \"22D68\",\n      \"mobileDeviceMinVersion\" : \"1400.0.0\",\n      \"name\" : \"macOS 13.2.1\",\n      \"requirements\" : \"min_host_12\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023WinterFCS\\/fullrestores\\/032-48346\\/EFF99C1E-C408-4E7A-A448-12E1468AF06C\\/UniversalMac_13.2.1_22D68_Restore.ipsw\",\n      \"version\" : \"13.2.1\"\n    },\n    {\n      \"build\" : \"22D49\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 12494248692,\n      \"group\" : \"ventura\",\n      \"id\" : \"22D49\",\n      \"mobileDeviceMinVersion\" : \"1400.0.0\",\n      \"name\" : \"macOS 13.2\",\n      \"requirements\" : \"min_host_12\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023WinterFCS\\/fullrestores\\/032-35688\\/0350BB21-2B4B-4850-BF77-70B830283B28\\/UniversalMac_13.2_22D49_Restore.ipsw\",\n      \"version\" : \"13.2.0\"\n    },\n    {\n      \"build\" : \"22D5038i\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 12269915297,\n      \"group\" : \"ventura\",\n      \"id\" : \"22D5038i\",\n      \"mobileDeviceMinVersion\" : \"1400.0.0\",\n      \"name\" : \"macOS 13.2 Developer Beta 2\",\n      \"requirements\" : \"min_host_12\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023WinterSeed\\/fullrestores\\/032-33181\\/62ECE236-5806-4136-AD08-EDC026FD80A5\\/UniversalMac_13.2_22D5038i_Restore.ipsw\",\n      \"version\" : \"13.2.0\"\n    },\n    {\n      \"build\" : \"22D5027d\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 12278161108,\n      \"group\" : \"ventura\",\n      \"id\" : \"22D5027d\",\n      \"mobileDeviceMinVersion\" : \"1400.0.0\",\n      \"name\" : \"macOS 13.2 Developer Beta 1\",\n      \"requirements\" : \"min_host_12\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2023WinterSeed\\/fullrestores\\/032-12640\\/6B472BA3-E678-4251-92D1-7AA23B66F53E\\/UniversalMac_13.2_22D5027d_Restore.ipsw\",\n      \"version\" : \"13.2.0\"\n    },\n    {\n      \"build\" : \"22C65\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 12279576859,\n      \"group\" : \"ventura\",\n      \"id\" : \"22C65\",\n      \"mobileDeviceMinVersion\" : \"1400.0.0\",\n      \"name\" : \"macOS 13.1\",\n      \"requirements\" : \"min_host_12\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2022FallFCS\\/fullrestores\\/012-60270\\/0A7F49BA-FC31-4AD9-8E45-49B1FB9128A6\\/UniversalMac_13.1_22C65_Restore.ipsw\",\n      \"version\" : \"13.1.0\"\n    },\n    {\n      \"build\" : \"22C5059b\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 12261527012,\n      \"group\" : \"ventura\",\n      \"id\" : \"22C5059b\",\n      \"mobileDeviceMinVersion\" : \"1400.0.0\",\n      \"name\" : \"macOS 13.1 Developer Beta 4\",\n      \"requirements\" : \"min_host_12\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2022FallSeed\\/fullrestores\\/032-08112\\/957EA73A-7C95-4B3C-B99C-2C2C47555832\\/UniversalMac_13.1_22C5059b_Restore.ipsw\",\n      \"version\" : \"13.1.0\"\n    },\n    {\n      \"build\" : \"22C5050e\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 12258951167,\n      \"group\" : \"ventura\",\n      \"id\" : \"22C5050e\",\n      \"mobileDeviceMinVersion\" : \"1400.0.0\",\n      \"name\" : \"macOS 13.1 Developer Beta 3\",\n      \"requirements\" : \"min_host_12\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2022FallSeed\\/fullrestores\\/032-06252\\/946CBF92-8F27-49B1-A692-81F54C73D2F0\\/UniversalMac_13.1_22C5050e_Restore.ipsw\",\n      \"version\" : \"13.1.0\"\n    },\n    {\n      \"build\" : \"22C5044e\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 11918902102,\n      \"group\" : \"ventura\",\n      \"id\" : \"22C5044e\",\n      \"mobileDeviceMinVersion\" : \"1400.0.0\",\n      \"name\" : \"macOS 13.1 Developer Beta 2\",\n      \"requirements\" : \"min_host_12\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2022FallSeed\\/fullrestores\\/032-02019\\/670C9BA6-67EB-4AE6-A02E-88976F6F3118\\/UniversalMac_13.1_22C5044e_Restore.ipsw\",\n      \"version\" : \"13.1.0\"\n    },\n    {\n      \"build\" : \"22C5033e\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 11886880578,\n      \"group\" : \"ventura\",\n      \"id\" : \"22C5033e\",\n      \"mobileDeviceMinVersion\" : \"1400.0.0\",\n      \"name\" : \"macOS 13.1 Developer Beta 1\",\n      \"requirements\" : \"min_host_12\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2022FallSeed\\/fullrestores\\/012-82062\\/10E6B723-51B8-4B2C-BA3B-12A18ED4E719\\/UniversalMac_13.1_22C5033e_Restore.ipsw\",\n      \"version\" : \"13.1.0\"\n    },\n    {\n      \"build\" : \"22A400\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 12197932579,\n      \"group\" : \"ventura\",\n      \"id\" : \"22A400\",\n      \"mobileDeviceMinVersion\" : \"1400.0.0\",\n      \"name\" : \"macOS 13.0.1\",\n      \"requirements\" : \"min_host_12\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2022FallFCS\\/fullrestores\\/012-93802\\/A7270B0F-05F8-43D1-A9AD-40EF5699E82C\\/UniversalMac_13.0.1_22A400_Restore.ipsw\",\n      \"version\" : \"13.0.1\"\n    },\n    {\n      \"build\" : \"22A380\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 12197669257,\n      \"group\" : \"ventura\",\n      \"id\" : \"22A380\",\n      \"mobileDeviceMinVersion\" : \"1400.0.0\",\n      \"name\" : \"macOS 13.0\",\n      \"requirements\" : \"min_host_12\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2022FallFCS\\/fullrestores\\/012-92188\\/2C38BCD1-2BFF-4A10-B358-94E8E28BE805\\/UniversalMac_13.0_22A380_Restore.ipsw\",\n      \"version\" : \"13.0.0\"\n    },\n    {\n      \"build\" : \"22A379\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 12197156665,\n      \"group\" : \"ventura\",\n      \"id\" : \"22A379\",\n      \"mobileDeviceMinVersion\" : \"1400.0.0\",\n      \"name\" : \"macOS 13.0 RC\",\n      \"requirements\" : \"min_host_12\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2022FallFCS\\/fullrestores\\/071-08994\\/1118ADF4-1CC9-4554-9333-B1F64CF0C820\\/UniversalMac_13.0_22A379_Restore.ipsw\",\n      \"version\" : \"13.0.0\"\n    },\n    {\n      \"build\" : \"22A5373b\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 12173276873,\n      \"group\" : \"ventura\",\n      \"id\" : \"22A5373b\",\n      \"mobileDeviceMinVersion\" : \"1400.0.0\",\n      \"name\" : \"macOS 13.0 Developer Beta 11\",\n      \"requirements\" : \"min_host_12\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2022SummerSeed\\/fullrestores\\/012-84563\\/2FC38C63-3213-4BB6-8E41-2B066332CBE6\\/UniversalMac_13.0_22A5373b_Restore.ipsw\",\n      \"version\" : \"13.0.0\"\n    },\n    {\n      \"build\" : \"22A5365d\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 12175865747,\n      \"group\" : \"ventura\",\n      \"id\" : \"22A5365d\",\n      \"mobileDeviceMinVersion\" : \"1400.0.0\",\n      \"name\" : \"macOS 13.0 Developer Beta 10\",\n      \"requirements\" : \"min_host_12\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2022SummerSeed\\/fullrestores\\/012-83054\\/16ECAA12-3A1B-4663-B49B-B1563ECD4314\\/UniversalMac_13.0_22A5365d_Restore.ipsw\",\n      \"version\" : \"13.0.0\"\n    },\n    {\n      \"build\" : \"22A5358e\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 12211984012,\n      \"group\" : \"ventura\",\n      \"id\" : \"22A5358e\",\n      \"mobileDeviceMinVersion\" : \"1400.0.0\",\n      \"name\" : \"macOS 13.0 Developer Beta 9\",\n      \"requirements\" : \"min_host_12\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2022SummerSeed\\/fullrestores\\/012-71790\\/AF5A04A6-FF20-44C1-9BFF-43081BDB4D8C\\/UniversalMac_13.0_22A5358e_Restore.ipsw\",\n      \"version\" : \"13.0.0\"\n    },\n    {\n      \"build\" : \"22A5352e\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 12202491122,\n      \"group\" : \"ventura\",\n      \"id\" : \"22A5352e\",\n      \"mobileDeviceMinVersion\" : \"1400.0.0\",\n      \"name\" : \"macOS 13.0 Developer Beta 8\",\n      \"requirements\" : \"min_host_12\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2022SummerSeed\\/fullrestores\\/012-70113\\/6F1F08B7-9A1B-48A9-93DB-55EE21121C87\\/UniversalMac_13.0_22A5352e_Restore.ipsw\",\n      \"version\" : \"13.0.0\"\n    },\n    {\n      \"build\" : \"22A5342f\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 12036111685,\n      \"group\" : \"ventura\",\n      \"id\" : \"22A5342f\",\n      \"mobileDeviceMinVersion\" : \"1400.0.0\",\n      \"name\" : \"macOS 13.0 Developer Beta 7\",\n      \"requirements\" : \"min_host_12\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2022SummerSeed\\/fullrestores\\/012-66750\\/108EF06D-FBEE-4910-BA83-56A5C9B54110\\/UniversalMac_13.0_22A5342f_Restore.ipsw\",\n      \"version\" : \"13.0.0\"\n    },\n    {\n      \"build\" : \"22A5331f\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 12020352498,\n      \"group\" : \"ventura\",\n      \"id\" : \"22A5331f\",\n      \"mobileDeviceMinVersion\" : \"1400.0.0\",\n      \"name\" : \"macOS 13.0 Developer Beta 6\",\n      \"requirements\" : \"min_host_12\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2022SummerSeed\\/fullrestores\\/012-61458\\/80300AD0-69E5-4429-AE3E-A936CA83B5FC\\/UniversalMac_13.0_22A5331f_Restore.ipsw\",\n      \"version\" : \"13.0.0\"\n    },\n    {\n      \"build\" : \"22A5321d\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 12013062572,\n      \"group\" : \"ventura\",\n      \"id\" : \"22A5321d\",\n      \"mobileDeviceMinVersion\" : \"1400.0.0\",\n      \"name\" : \"macOS 13.0 Developer Beta 5\",\n      \"requirements\" : \"min_host_12\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2022SummerSeed\\/fullrestores\\/012-51397\\/8EF0874D-388A-4F62-B58A-89F968DD3082\\/UniversalMac_13.0_22A5321d_Restore.ipsw\",\n      \"version\" : \"13.0.0\"\n    },\n    {\n      \"build\" : \"22A5311f\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 12177142220,\n      \"group\" : \"ventura\",\n      \"id\" : \"22A5311f\",\n      \"mobileDeviceMinVersion\" : \"1400.0.0\",\n      \"name\" : \"macOS 13.0 Developer Beta 4\",\n      \"requirements\" : \"min_host_12\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2022SummerSeed\\/fullrestores\\/012-43316\\/6CE4D83A-E44C-4DD1-B47F-DE168355662E\\/UniversalMac_13.0_22A5311f_Restore.ipsw\",\n      \"version\" : \"13.0.0\"\n    },\n    {\n      \"build\" : \"22A5295i\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 12110805518,\n      \"group\" : \"ventura\",\n      \"id\" : \"22A5295i\",\n      \"mobileDeviceMinVersion\" : \"1400.0.0\",\n      \"name\" : \"macOS 13.0 Developer Beta 3 (22A5295i)\",\n      \"requirements\" : \"min_host_12\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2022SummerSeed\\/fullrestores\\/012-38309\\/6EDC76A0-4432-4C64-83C5-F43C885A75D6\\/UniversalMac_13.0_22A5295i_Restore.ipsw\",\n      \"version\" : \"13.0.0\"\n    },\n    {\n      \"build\" : \"22A5295h\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 12109750298,\n      \"group\" : \"ventura\",\n      \"id\" : \"22A5295h\",\n      \"mobileDeviceMinVersion\" : \"1400.0.0\",\n      \"name\" : \"macOS 13.0 Developer Beta 3\",\n      \"requirements\" : \"min_host_12\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2022SummerSeed\\/fullrestores\\/012-34274\\/130176F5-C4CB-4664-A2F0-F29CA1281694\\/UniversalMac_13.0_22A5295h_Restore.ipsw\",\n      \"version\" : \"13.0.0\"\n    },\n    {\n      \"build\" : \"22A5286j\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 12103954377,\n      \"group\" : \"ventura\",\n      \"id\" : \"22A5286j\",\n      \"mobileDeviceMinVersion\" : \"1351.0.0\",\n      \"name\" : \"macOS 13.0 Developer Beta 2\",\n      \"requirements\" : \"min_host_12\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2022SummerSeed\\/fullrestores\\/012-30346\\/9DD787A7-044B-4650-86D4-84E80B6B9C36\\/UniversalMac_13.0_22A5286j_Restore.ipsw\",\n      \"version\" : \"13.0.0\"\n    },\n    {\n      \"build\" : \"21G217\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 14086558415,\n      \"group\" : \"monterey\",\n      \"id\" : \"21G217\",\n      \"mobileDeviceMinVersion\" : \"1351.0.0\",\n      \"name\" : \"macOS 12.6.1\",\n      \"requirements\" : \"min_host_12\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2022FallFCS\\/fullrestores\\/012-66032\\/8D8D90C6-A876-4FFF-BBF4-D158939B3841\\/UniversalMac_12.6.1_21G217_Restore.ipsw\",\n      \"version\" : \"12.6.1\"\n    },\n    {\n      \"build\" : \"21G115\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 14081739387,\n      \"group\" : \"monterey\",\n      \"id\" : \"21G115\",\n      \"mobileDeviceMinVersion\" : \"1351.0.0\",\n      \"name\" : \"macOS 12.6\",\n      \"requirements\" : \"min_host_12\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2022FallFCS\\/fullrestores\\/012-40537\\/0EC7C669-13E9-49FB-BD64-9EECC1D174B2\\/UniversalMac_12.6_21G115_Restore.ipsw\",\n      \"version\" : \"12.6.0\"\n    },\n    {\n      \"build\" : \"21G83\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 14088266331,\n      \"group\" : \"monterey\",\n      \"id\" : \"21G83\",\n      \"mobileDeviceMinVersion\" : \"1351.0.0\",\n      \"name\" : \"macOS 12.5.1\",\n      \"requirements\" : \"min_host_12\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2022SummerFCS\\/fullrestores\\/012-51674\\/A7019DDB-3355-470F-A355-4162A187AB6C\\/UniversalMac_12.5.1_21G83_Restore.ipsw\",\n      \"version\" : \"12.5.1\"\n    },\n    {\n      \"build\" : \"21G72\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 14084058716,\n      \"group\" : \"monterey\",\n      \"id\" : \"21G72\",\n      \"mobileDeviceMinVersion\" : \"1351.0.0\",\n      \"name\" : \"macOS 12.5\",\n      \"requirements\" : \"min_host_12\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2022SummerFCS\\/fullrestores\\/012-42731\\/BD9917E0-262C-41C5-A69F-AC316A534A39\\/UniversalMac_12.5_21G72_Restore.ipsw\",\n      \"version\" : \"12.5.0\"\n    },\n    {\n      \"build\" : \"21G69\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 14085080132,\n      \"group\" : \"monterey\",\n      \"id\" : \"21G69\",\n      \"mobileDeviceMinVersion\" : \"1351.0.0\",\n      \"name\" : \"macOS 12.5 RC\",\n      \"requirements\" : \"min_host_12\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2022SummerFCS\\/fullrestores\\/012-40368\\/5DD0A524-140A-46AF-91ED-5F28EA9DEC01\\/UniversalMac_12.5_21G69_Restore.ipsw\",\n      \"version\" : \"12.5.0\"\n    },\n    {\n      \"build\" : \"21G5063a\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 13938297123,\n      \"group\" : \"monterey\",\n      \"id\" : \"21G5063a\",\n      \"mobileDeviceMinVersion\" : \"1351.0.0\",\n      \"name\" : \"macOS 12.5 Developer Beta 5\",\n      \"requirements\" : \"min_host_12\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2022FallSeed\\/fullrestores\\/012-36748\\/52342C55-6598-4A86-AAB8-8901145792C8\\/UniversalMac_12.5_21G5063a_Restore.ipsw\",\n      \"version\" : \"12.5.0\"\n    },\n    {\n      \"build\" : \"21G5056b\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 13934172302,\n      \"group\" : \"monterey\",\n      \"id\" : \"21G5056b\",\n      \"mobileDeviceMinVersion\" : \"1351.0.0\",\n      \"name\" : \"macOS 12.5 Developer Beta 4\",\n      \"requirements\" : \"min_host_12\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2022FallSeed\\/fullrestores\\/012-26441\\/AE0AC638-2773-49D3-BF84-950B10BF39E9\\/UniversalMac_12.5_21G5056b_Restore.ipsw\",\n      \"version\" : \"12.5.0\"\n    },\n    {\n      \"build\" : \"21G5046c\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 13868797741,\n      \"group\" : \"monterey\",\n      \"id\" : \"21G5046c\",\n      \"mobileDeviceMinVersion\" : \"1351.0.0\",\n      \"name\" : \"macOS 12.5 Developer Beta 3\",\n      \"requirements\" : \"min_host_12\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2022FallSeed\\/fullrestores\\/012-18271\\/FFF202B2-E4B6-4A3E-9681-42A0F3F81B11\\/UniversalMac_12.5_21G5046c_Restore.ipsw\",\n      \"version\" : \"12.5.0\"\n    },\n    {\n      \"build\" : \"21G5037d\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 13829637373,\n      \"group\" : \"monterey\",\n      \"id\" : \"21G5037d\",\n      \"mobileDeviceMinVersion\" : \"1351.0.0\",\n      \"name\" : \"macOS 12.5 Developer Beta 2\",\n      \"requirements\" : \"min_host_12\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2022FallSeed\\/fullrestores\\/012-10648\\/1CC63FC5-5A22-4A5A-9A7B-C19C8C4A6731\\/UniversalMac_12.5_21G5037d_Restore.ipsw\",\n      \"version\" : \"12.5.0\"\n    },\n    {\n      \"build\" : \"21G5027d\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 13831129170,\n      \"group\" : \"monterey\",\n      \"id\" : \"21G5027d\",\n      \"mobileDeviceMinVersion\" : \"1351.0.0\",\n      \"name\" : \"macOS 12.5 Developer Beta 1\",\n      \"requirements\" : \"min_host_12\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2022FallSeed\\/fullrestores\\/002-93712\\/5F234425-6096-43FC-B518-1E9D7B4D0254\\/UniversalMac_12.5_21G5027d_Restore.ipsw\",\n      \"version\" : \"12.5.0\"\n    },\n    {\n      \"build\" : \"21F79\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 13837340777,\n      \"group\" : \"monterey\",\n      \"id\" : \"21F79\",\n      \"mobileDeviceMinVersion\" : \"1351.0.0\",\n      \"name\" : \"macOS 12.4\",\n      \"requirements\" : \"min_host_12\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2022SpringFCS\\/fullrestores\\/012-06874\\/9CECE956-D945-45E2-93E9-4FFDC81BB49A\\/UniversalMac_12.4_21F79_Restore.ipsw\",\n      \"version\" : \"12.4.0\"\n    },\n    {\n      \"build\" : \"21F5071b\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 13819236155,\n      \"group\" : \"monterey\",\n      \"id\" : \"21F5071b\",\n      \"mobileDeviceMinVersion\" : \"1351.0.0\",\n      \"name\" : \"macOS 12.4 Developer Beta 4\",\n      \"requirements\" : \"min_host_12\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2022SpringSeed\\/fullrestores\\/002-95106\\/0F7A6388-C4B5-4B8E-B8B2-F62C030699D0\\/UniversalMac_12.4_21F5071b_Restore.ipsw\",\n      \"version\" : \"12.4.0\"\n    },\n    {\n      \"build\" : \"21F5063e\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 13818052632,\n      \"group\" : \"monterey\",\n      \"id\" : \"21F5063e\",\n      \"mobileDeviceMinVersion\" : \"1351.0.0\",\n      \"name\" : \"macOS 12.4 Developer Beta 3\",\n      \"requirements\" : \"min_host_12\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2022SpringSeed\\/fullrestores\\/002-90009\\/DA6BD192-1698-48B3-AB6D-9D3A045ED1B1\\/UniversalMac_12.4_21F5063e_Restore.ipsw\",\n      \"version\" : \"12.4.0\"\n    },\n    {\n      \"build\" : \"21F5058e\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 13832392408,\n      \"group\" : \"monterey\",\n      \"id\" : \"21F5058e\",\n      \"mobileDeviceMinVersion\" : \"1351.0.0\",\n      \"name\" : \"macOS 12.4 Developer Beta 2\",\n      \"requirements\" : \"min_host_12\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2022SpringSeed\\/fullrestores\\/002-87587\\/BC2EBE80-F0F4-4B56-BCDC-340E0AD8E985\\/UniversalMac_12.4_21F5058e_Restore.ipsw\",\n      \"version\" : \"12.4.0\"\n    },\n    {\n      \"build\" : \"21F5048e\",\n      \"channel\" : \"devbeta\",\n      \"downloadSize\" : 13812557221,\n      \"group\" : \"monterey\",\n      \"id\" : \"21F5048e\",\n      \"mobileDeviceMinVersion\" : \"1351.0.0\",\n      \"name\" : \"macOS 12.4 Developer Beta 1\",\n      \"requirements\" : \"min_host_12\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2022SpringSeed\\/fullrestores\\/002-85721\\/A21FF659-8493-4A16-A989-2C3141F48D8C\\/UniversalMac_12.4_21F5048e_Restore.ipsw\",\n      \"version\" : \"12.4.0\"\n    },\n    {\n      \"build\" : \"21E258\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 13875260060,\n      \"group\" : \"monterey\",\n      \"id\" : \"21E258\",\n      \"mobileDeviceMinVersion\" : \"1351.0.0\",\n      \"name\" : \"macOS 12.3.1\",\n      \"requirements\" : \"min_host_12\",\n      \"url\" : \"https:\\/\\/updates.cdn-apple.com\\/2022SpringFCS\\/fullrestores\\/002-79219\\/851BEDF0-19DB-4040-B765-0F4089D1530D\\/UniversalMac_12.3.1_21E258_Restore.ipsw\",\n      \"version\" : \"12.3.1\"\n    }\n  ]\n}"
  },
  {
    "path": "data/linux_v1.json",
    "content": "{\n  \"apiVersion\": 1,\n  \"channels\": [\n      {\n        \"id\": \"stable\",\n        \"name\": \"Stable\",\n        \"note\": \"Public, stable releases.\",\n        \"icon\": \"checkmark.seal\"\n      },\n      {\n        \"id\": \"daily\",\n        \"name\": \"Daily\",\n        \"note\": \"Daily builds meant for testing.\",\n        \"icon\": \"wrench.and.screwdriver\"\n      }\n    ],\n  \"groups\": [\n    {\n      \"id\": \"ubuntu\",\n      \"name\": \"Ubuntu\",\n      \"majorVersion\": \"22.0\",\n      \"minHostVersion\": \"13.0\"\n    }\n  ],\n  \"restoreImages\": [\n    {\n      \"group\": \"ubuntu\",\n      \"name\": \"Jammy Jellyfish Daily Build\",\n      \"build\": \"22.04.3 LTS\",\n      \"url\": \"https://cdimage.ubuntu.com/jammy/daily-live/current/jammy-desktop-arm64.iso\",\n      \"channel\": \"daily\"\n    }\n  ]\n}"
  },
  {
    "path": "data/linux_v2.json",
    "content": "{\n  \"apiVersion\" : 2,\n  \"channels\" : [\n    {\n      \"icon\" : \"checkmark.seal\",\n      \"id\" : \"regular\",\n      \"name\" : \"Release\",\n      \"note\" : \"Public, stable releases.\"\n    },\n        {\n            \"icon\": \"checkmark.seal\",\n            \"id\": \"lts\",\n            \"name\": \"LTS\",\n            \"note\": \"Long-term support releases\"\n        },\n    {\n      \"icon\" : \"wrench.and.screwdriver\",\n      \"id\" : \"daily\",\n      \"name\" : \"Daily\",\n      \"note\" : \"Daily builds meant for testing.\"\n    }\n  ],\n  \"features\" : [\n    {\n      \"id\" : \"file_sharing\",\n      \"minVersionGuest\" : \"0.0.0\",\n      \"minVersionHost\" : \"0.0.0\",\n      \"name\" : \"File sharing\",\n      \"unsupportedPlatform\" : true\n    },\n    {\n      \"id\" : \"guest_app\",\n      \"minVersionGuest\" : \"0.0.0\",\n      \"minVersionHost\" : \"0.0.0\",\n      \"name\" : \"VirtualBuddyGuest app\",\n      \"unsupportedPlatform\" : true\n    },\n    {\n      \"id\" : \"trackpad\",\n      \"minVersionGuest\" : \"0.0.0\",\n      \"minVersionHost\" : \"0.0.0\",\n      \"name\" : \"Trackpad\",\n      \"unsupportedPlatform\" : true\n    },\n    {\n      \"id\" : \"mac_keyboard\",\n      \"minVersionGuest\" : \"0.0.0\",\n      \"minVersionHost\" : \"0.0.0\",\n      \"name\" : \"Mac keyboard\",\n      \"unsupportedPlatform\" : true\n    },\n    {\n      \"id\" : \"state_restoration\",\n      \"minVersionGuest\" : \"0.0.0\",\n      \"minVersionHost\" : \"0.0.0\",\n      \"name\" : \"State restoration\",\n      \"unsupportedPlatform\" : true\n    },\n    {\n      \"id\" : \"display_resize\",\n      \"minVersionGuest\" : \"0.0.0\",\n      \"minVersionHost\" : \"0.0.0\",\n      \"name\" : \"Automatic display configuration\",\n      \"unsupportedPlatform\" : true\n    },\n    {\n      \"id\" : \"rosetta_sharing\",\n      \"minVersionGuest\" : \"0.0.0\",\n      \"minVersionHost\" : \"14.0.0\",\n      \"name\" : \"Rosetta Sharing\",\n      \"unsupportedPlatform\" : false\n    }\n  ],\n    \"deviceSupportVersions\": [\n    ],\n  \"groups\" : [\n    {\n      \"darkImage\" : {\n        \"id\" : \"jammyjellyfish\",\n        \"thumbnail\" : {\n          \"blurHash\" : \"UIE0281ds7Ey1a$MWYaysSjvoMjsWoWUo2jv\",\n          \"height\" : 405,\n          \"url\" : \"https:\\/\\/api.virtualbuddy.app\\/v2\\/images\\/jammyjellyfish-dark-thumbnail.heic\",\n          \"width\" : 720\n        },\n        \"url\" : \"https:\\/\\/api.virtualbuddy.app\\/v2\\/images\\/jammyjellyfish-dark.heic\"\n      },\n      \"id\" : \"ubuntu\",\n      \"image\" : {\n        \"id\" : \"jammyjellyfish\",\n        \"thumbnail\" : {\n          \"blurHash\" : \"UIE0281ds7Ey1a$MWYaysSjvoMjsWoWUo2jv\",\n          \"height\" : 405,\n          \"url\" : \"https:\\/\\/api.virtualbuddy.app\\/v2\\/images\\/jammyjellyfish-thumbnail.heic\",\n          \"width\" : 720\n        },\n        \"url\" : \"https:\\/\\/api.virtualbuddy.app\\/v2\\/images\\/jammyjellyfish.heic\"\n      },\n      \"majorVersion\" : \"22.0.0\",\n      \"name\" : \"Ubuntu\"\n    },\n    {\n      \"darkImage\" : {\n        \"id\" : \"debian\",\n        \"thumbnail\" : {\n          \"blurHash\" : \"U96%Xrw$I%oLS,NWR#ohRyR}bGohW8s?oNWU\",\n          \"height\" : 720,\n          \"url\" : \"https:\\/\\/api.virtualbuddy.app\\/v2\\/images\\/debian-dark-thumbnail.heic\",\n          \"width\" : 720\n        },\n        \"url\" : \"https:\\/\\/api.virtualbuddy.app\\/v2\\/images\\/debian-dark.heic\"\n      },\n      \"id\" : \"debian\",\n      \"image\" : {\n        \"id\" : \"debian\",\n        \"thumbnail\" : {\n          \"blurHash\" : \"U96%Xrw$I%oLS,NWR#ohRyR}bGohW8s?oNWU\",\n          \"height\" : 720,\n          \"url\" : \"https:\\/\\/api.virtualbuddy.app\\/v2\\/images\\/debian-thumbnail.heic\",\n          \"width\" : 720\n        },\n        \"url\" : \"https:\\/\\/api.virtualbuddy.app\\/v2\\/images\\/debian.heic\"\n      },\n      \"majorVersion\" : \"12.0.0\",\n      \"name\" : \"Debian\"\n    },\n    {\n      \"darkImage\" : {\n        \"id\" : \"fedora\",\n        \"thumbnail\" : {\n          \"blurHash\" : \"UQ6+b#kCMcflyGaeR4f5R4ayozj[Mbofx^a}\",\n          \"height\" : 720,\n          \"url\" : \"https:\\/\\/api.virtualbuddy.app\\/v2\\/images\\/fedora-dark-thumbnail.heic\",\n          \"width\" : 720\n        },\n        \"url\" : \"https:\\/\\/api.virtualbuddy.app\\/v2\\/images\\/fedora-dark.heic\"\n      },\n      \"id\" : \"fedora\",\n      \"image\" : {\n        \"id\" : \"fedora\",\n        \"thumbnail\" : {\n          \"blurHash\" : \"UE6.Evf5D6j[*0ahMdf8Htf*xufQD5kC%fbG\",\n          \"height\" : 720,\n          \"url\" : \"https:\\/\\/api.virtualbuddy.app\\/v2\\/images\\/fedora-thumbnail.heic\",\n          \"width\" : 720\n        },\n        \"url\" : \"https:\\/\\/api.virtualbuddy.app\\/v2\\/images\\/fedora.heic\"\n      },\n      \"majorVersion\" : \"41.0.0\",\n      \"name\" : \"Fedora\"\n    },\n    {\n      \"darkImage\" : {\n        \"id\" : \"kali\",\n        \"thumbnail\" : {\n          \"blurHash\" : \"UD7CB$.AnftURMkEo$ozMwRPRjawWBa#f-ae\",\n          \"height\" : 405,\n          \"url\" : \"https:\\/\\/api.virtualbuddy.app\\/v2\\/images\\/kali-dark-thumbnail.heic\",\n          \"width\" : 720\n        },\n        \"url\" : \"https:\\/\\/api.virtualbuddy.app\\/v2\\/images\\/kali-dark.heic\"\n      },\n      \"id\" : \"kali\",\n      \"image\" : {\n        \"id\" : \"kali\",\n        \"thumbnail\" : {\n          \"blurHash\" : \"UD7CB$.AnftURMkEo$ozMwRPRjawWBa#f-ae\",\n          \"height\" : 405,\n          \"url\" : \"https:\\/\\/api.virtualbuddy.app\\/v2\\/images\\/kali-thumbnail.heic\",\n          \"width\" : 720\n        },\n        \"url\" : \"https:\\/\\/api.virtualbuddy.app\\/v2\\/images\\/kali.heic\"\n      },\n      \"majorVersion\" : \"2025.0.0\",\n      \"name\" : \"Kali\"\n    }\n  ],\n  \"minAppVersion\" : \"2.0.0\",\n  \"requirementSets\" : [\n    {\n      \"id\" : \"min_host_13\",\n      \"minCPUCount\" : 1,\n      \"minMemorySizeMB\" : 1024,\n      \"minVersionHost\" : \"13.0.0\"\n    }\n  ],\n  \"restoreImages\" : [\n        {\n            \"build\": \"25.10\",\n            \"channel\": \"regular\",\n            \"downloadSize\": 4981665792,\n            \"group\": \"ubuntu\",\n            \"id\": \"25.10_Desktop\",\n            \"mobileDeviceMinVersion\": \"0.0.0\",\n            \"name\": \"Ubuntu Desktop 25.10\",\n            \"requirements\": \"min_host_13\",\n            \"url\": \"https://cdimage.ubuntu.com/releases/25.10/release/ubuntu-25.10-desktop-arm64.iso\",\n            \"version\": \"25.10\"\n        },\n    {\n      \"build\" : \"24.04.4 LTS\",\n      \"channel\" : \"lts\",\n      \"downloadSize\" : 3059724288,\n      \"group\" : \"ubuntu\",\n      \"id\" : \"24.04.4 LTS\",\n      \"mobileDeviceMinVersion\" : \"0.0.0\",\n      \"name\" : \"Ubuntu Server 24.04.4 LTS\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https://cdimage.ubuntu.com/releases/24.04/release/ubuntu-24.04.4-live-server-arm64.iso\",\n      \"version\" : \"24.04.4\"\n    },\n        {\n            \"build\": \"25.10\",\n            \"channel\": \"regular\",\n            \"downloadSize\": 2394763264,\n            \"group\": \"ubuntu\",\n            \"id\": \"25.10_Server\",\n            \"mobileDeviceMinVersion\": \"0.0.0\",\n            \"name\": \"Ubuntu Server 25.10\",\n            \"requirements\": \"min_host_13\",\n            \"url\": \"https://cdimage.ubuntu.com/releases/25.10/release/ubuntu-25.10-live-server-arm64.iso\",\n            \"version\": \"25.10\"\n        },\n    {\n      \"build\" : \"42.1.1\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 1006553088,\n      \"group\" : \"fedora\",\n      \"id\" : \"42.1.1\",\n      \"mobileDeviceMinVersion\" : \"0.0.0\",\n      \"name\" : \"Fedora 42.1.1 Net Install\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/download.fedoraproject.org\\/pub\\/fedora\\/linux\\/releases\\/42\\/Everything\\/aarch64\\/iso\\/Fedora-Everything-netinst-aarch64-42-1.1.iso\",\n      \"version\" : \"42.1.1\"\n    },\n    {\n      \"build\" : \"2025.2\",\n      \"channel\" : \"regular\",\n      \"downloadSize\" : 3767808000,\n      \"group\" : \"kali\",\n      \"id\" : \"2025.2\",\n      \"mobileDeviceMinVersion\" : \"0.0.0\",\n      \"name\" : \"Kali Linux 2025.2 Full\",\n      \"requirements\" : \"min_host_13\",\n      \"url\" : \"https:\\/\\/cdimage.kali.org\\/kali-2025.2\\/kali-linux-2025.2-installer-arm64.iso\",\n      \"version\" : \"2025.2.0\"\n    },\n        {\n            \"build\": \"12.11\",\n            \"channel\": \"regular\",\n            \"downloadSize\": 3993284608,\n            \"group\": \"debian\",\n            \"id\": \"12.11\",\n            \"mobileDeviceMinVersion\": \"0.0.0\",\n            \"name\": \"Debian 12.11\",\n            \"requirements\": \"min_host_13\",\n            \"url\": \"https://cdimage.debian.org/debian-cd/current/arm64/iso-dvd/debian-12.11.0-arm64-DVD-1.iso\",\n            \"version\": \"12.11\"\n        }\n  ]\n}\n"
  }
]