StikDebug/StikDebug/Device/MountingProgress.swift
2026-09-27 13:16:09 -06:00

126 lines
3.4 KiB
Swift

//
// MountingProgress.swift
// StikDebug
//
import Foundation
import idevice
final class MountingProgress: ObservableObject {
static let shared = MountingProgress()
@Published private(set) var mountingThread: Thread?
@Published private(set) var coolisMounted: Bool = false
private let mountCheckLock = NSLock()
private var mountCheckInProgress = false
private let mountLock = NSLock()
private var mountInProgress = false
private init() {}
func checkforMounted() {
guard TunnelManager.shared.isConnected else { return }
mountCheckLock.lock()
guard !mountCheckInProgress else {
mountCheckLock.unlock()
return
}
mountCheckInProgress = true
mountCheckLock.unlock()
DispatchQueue.global(qos: .utility).async {
let status = checkMountStatus()
self.mountCheckLock.lock()
self.mountCheckInProgress = false
self.mountCheckLock.unlock()
DispatchQueue.main.async {
if status != .unreachable {
self.coolisMounted = status == .mounted
}
}
}
}
func pubMount() {
guard TunnelManager.shared.isConnected, DeveloperDiskImageService.filesAreReady else { return }
mountLock.lock()
guard !mountInProgress else {
mountLock.unlock()
return
}
mountInProgress = true
mountLock.unlock()
let thread = Thread { [weak self] in
self?.mount()
}
thread.qualityOfService = .background
thread.name = "mounting"
DispatchQueue.main.async {
self.mountingThread = thread
}
thread.start()
}
private func mount() {
switch checkMountStatus() {
case .mounted:
finishMount {
self.coolisMounted = true
}
return
case .unreachable:
finishMount()
return
case .notMounted:
break
}
guard isPairing() else {
finishMount()
return
}
let mountError = mountDeveloperDiskImage(from: DeveloperDiskImageService.directoryURL.path)
let mounted = mountError == nil || checkMountStatus() == .mounted
finishMount {
if mounted {
self.coolisMounted = true
self.checkforMounted()
} else if let mountError {
LogManager.shared.addErrorLog("Failed to mount DDI: \(mountError)")
showAlert(title: "DDI Mount Failed", message: mountError, showOk: true, showTryAgain: true) { shouldTryAgain in
if shouldTryAgain {
self.pubMount()
}
}
}
}
}
private func finishMount(_ completion: @escaping () -> Void = {}) {
DispatchQueue.main.async {
self.mountingThread = nil
self.mountLock.lock()
self.mountInProgress = false
self.mountLock.unlock()
completion()
}
}
}
func isPairing() -> Bool {
let pairingPath = PairingFileStore.prepareURL().path
var pairingFile: RpPairingFileHandle?
let error = rp_pairing_file_read(pairingPath, &pairingFile)
if error != nil {
return false
}
rp_pairing_file_free(pairingFile)
return true
}