diff --git a/Cores/DeltaCore b/Cores/DeltaCore index 4406d30..29abe10 160000 --- a/Cores/DeltaCore +++ b/Cores/DeltaCore @@ -1 +1 @@ -Subproject commit 4406d30cec9e252979a87966bcce5c5d3711b985 +Subproject commit 29abe10afb7f720e40b9345bc88f321c518efbf6 diff --git a/Cores/SNESDeltaCore b/Cores/SNESDeltaCore index 015d8b1..87d9cfa 160000 --- a/Cores/SNESDeltaCore +++ b/Cores/SNESDeltaCore @@ -1 +1 @@ -Subproject commit 015d8b1a7fc364a9887a2bb2d76a9215f7ead19c +Subproject commit 87d9cfa5574fe42db453f9b6b08b3e7190a4b7d2 diff --git a/Delta/Base.lproj/Main.storyboard b/Delta/Base.lproj/Main.storyboard index 8057667..72af4c2 100644 --- a/Delta/Base.lproj/Main.storyboard +++ b/Delta/Base.lproj/Main.storyboard @@ -1,7 +1,7 @@ - + - + @@ -93,7 +93,7 @@ - + diff --git a/Delta/Emulation/EmulationViewController.swift b/Delta/Emulation/EmulationViewController.swift index 93bf495..4e6a4aa 100644 --- a/Delta/Emulation/EmulationViewController.swift +++ b/Delta/Emulation/EmulationViewController.swift @@ -23,10 +23,23 @@ class EmulationViewController: UIViewController guard oldValue != game else { return } self.emulatorCore = SNESEmulatorCore(game: game) + + } + } + private(set) var emulatorCore: EmulatorCore! { + didSet + { self.preferredContentSize = self.emulatorCore.preferredRenderingSize } } - private(set) var emulatorCore: EmulatorCore! + + // If non-nil, will override the default preview action items returned in previewActionItems() + var overridePreviewActionItems: [UIPreviewActionItem]? + + // Annoying iOS gotcha: if the previewingContext(_:viewControllerForLocation:) callback takes too long, the peek/preview starts, but fails to actually present the view controller + // To workaround, we have this closure to defer work for Peeking/Popping until the view controller appears + // Hacky, but works + var deferredPreparationHandler: (Void -> Void)? //MARK: - Private Properties @IBOutlet private var controllerView: ControllerView! @@ -90,7 +103,14 @@ class EmulationViewController: UIViewController { super.viewDidAppear(animated) - self.emulatorCore.startEmulation() + self.deferredPreparationHandler?() + + switch self.emulatorCore.state + { + case .Stopped: self.emulatorCore.startEmulation() + case .Running: break + case .Paused: self.emulatorCore.resumeEmulation() + } } override func viewDidLayoutSubviews() @@ -180,12 +200,25 @@ class EmulationViewController: UIViewController self.pauseViewController = nil self.emulatorCore.resumeEmulation() + + // Temporarily disable audioManager to prevent delayed audio bug when using 3D Touch Peek & Pop + self.emulatorCore.audioManager.enabled = false + + // Re-enable after delay + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(0.1 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) { + self.emulatorCore.audioManager.enabled = true + } } //MARK: - 3D Touch - /// 3D Touch override func previewActionItems() -> [UIPreviewActionItem] { + if let previewActionItems = self.overridePreviewActionItems + { + return previewActionItems + } + let presentingViewController = self.presentingViewController let launchGameAction = UIPreviewAction(title: NSLocalizedString("Launch \(self.game.name)", comment: ""), style: .Default) { (action, viewController) in @@ -227,9 +260,9 @@ private extension EmulationViewController /// Save States extension EmulationViewController: SaveStatesViewControllerDelegate { - func saveStatesViewControllerActiveGame(saveStatesViewController: SaveStatesViewController) -> Game + func saveStatesViewControllerActiveEmulatorCore(saveStatesViewController: SaveStatesViewController) -> EmulatorCore { - return self.game + return self.emulatorCore } func saveStatesViewController(saveStatesViewController: SaveStatesViewController, updateSaveState saveState: SaveState) @@ -276,7 +309,7 @@ extension EmulationViewController: SaveStatesViewControllerDelegate } } - func saveStatesViewController(saveStatesViewController: SaveStatesViewController, loadSaveState saveState: SaveState) + func saveStatesViewController(saveStatesViewController: SaveStatesViewController, loadSaveState saveState: SaveStateType) { self.emulatorCore.loadSaveState(saveState) diff --git a/Delta/Pause Menu/Save States/SaveStatesViewController.swift b/Delta/Pause Menu/Save States/SaveStatesViewController.swift index 04a023a..68ede08 100644 --- a/Delta/Pause Menu/Save States/SaveStatesViewController.swift +++ b/Delta/Pause Menu/Save States/SaveStatesViewController.swift @@ -14,9 +14,9 @@ import Roxas protocol SaveStatesViewControllerDelegate: class { - func saveStatesViewControllerActiveGame(saveStatesViewController: SaveStatesViewController) -> Game + func saveStatesViewControllerActiveEmulatorCore(saveStatesViewController: SaveStatesViewController) -> EmulatorCore func saveStatesViewController(saveStatesViewController: SaveStatesViewController, updateSaveState saveState: SaveState) - func saveStatesViewController(saveStatesViewController: SaveStatesViewController, loadSaveState saveState: SaveState) + func saveStatesViewController(saveStatesViewController: SaveStatesViewController, loadSaveState saveState: SaveStateType) } extension SaveStatesViewController @@ -56,6 +56,8 @@ class SaveStatesViewController: UICollectionViewController private let imageOperationQueue = RSTOperationQueue() private let imageCache = NSCache() + private var currentGameState: SaveStateType? + private let dateFormatter: NSDateFormatter required init?(coder aDecoder: NSCoder) @@ -109,6 +111,8 @@ extension SaveStatesViewController let longPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: "handleLongPressGesture:") self.collectionView?.addGestureRecognizer(longPressGestureRecognizer) + self.registerForPreviewingWithDelegate(self, sourceView: self.collectionView!) + self.updateBackgroundView() } @@ -130,6 +134,13 @@ extension SaveStatesViewController super.viewWillAppear(animated) } + + override func viewDidDisappear(animated: Bool) + { + super.viewDidDisappear(animated) + + self.resetEmulatorCoreIfNeeded() + } override func didReceiveMemoryWarning() { @@ -143,7 +154,7 @@ private extension SaveStatesViewController func updateFetchedResultsController() { - let game = self.delegate.saveStatesViewControllerActiveGame(self) + let game = self.delegate.saveStatesViewControllerActiveEmulatorCore(self).game as! Game let fetchRequest = SaveState.fetchRequest() fetchRequest.returnsObjectsAsFaults = false @@ -264,7 +275,7 @@ private extension SaveStatesViewController let backgroundContext = DatabaseManager.sharedManager.backgroundManagedObjectContext() backgroundContext.performBlock { - var game = self.delegate.saveStatesViewControllerActiveGame(self) + var game = self.delegate.saveStatesViewControllerActiveEmulatorCore(self).game as! Game game = backgroundContext.objectWithID(game.objectID) as! Game let saveState = SaveState.insertIntoManagedObjectContext(backgroundContext) @@ -324,6 +335,39 @@ private extension SaveStatesViewController } } + //MARK: - Emulator - + + func resetEmulatorCoreIfNeeded() + { + // Kinda hacky, but isMovingFromParentViewController only returns yes when popping off navigation controller, and not being dismissed modally + // Because of this, this is only run when the user returns to PauseMenuViewController, and not when they choose a save state to load + guard let saveState = self.currentGameState where self.isMovingFromParentViewController() else { return } + + // We stopped emulation for 3D Touch, so now we must resume emulation and load the save state we made to make it seem like it was never stopped + let emulatorCore = self.delegate.saveStatesViewControllerActiveEmulatorCore(self) + + // Temporarily disable video rendering to prevent flickers + emulatorCore.videoManager.enabled = false + + // Load the save state we stored a reference to + emulatorCore.startEmulation() + emulatorCore.pauseEmulation() + emulatorCore.loadSaveState(saveState) + + // Re-enable video rendering + emulatorCore.videoManager.enabled = true + + // Remove temporary save state file + do + { + try NSFileManager.defaultManager().removeItemAtURL(saveState.fileURL) + } + catch let error as NSError + { + print(error) + } + } + //MARK: - Convenience Methods - func correctedSectionForSectionIndex(section: Int) -> Section @@ -336,6 +380,79 @@ private extension SaveStatesViewController } } +//MARK: - - +extension SaveStatesViewController: UIViewControllerPreviewingDelegate +{ + func previewingContext(previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? + { + guard let indexPath = self.collectionView?.indexPathForItemAtPoint(location), layoutAttributes = self.collectionViewLayout.layoutAttributesForItemAtIndexPath(indexPath) else { return nil } + + previewingContext.sourceRect = layoutAttributes.frame + + let emulatorCore = self.delegate.saveStatesViewControllerActiveEmulatorCore(self) + let storyboard = UIStoryboard(name: "Main", bundle: nil) + + let emulationViewController = storyboard.instantiateViewControllerWithIdentifier("emulationViewController") as! EmulationViewController + emulationViewController.game = emulatorCore.game as! Game + emulationViewController.overridePreviewActionItems = [] + emulationViewController.deferredPreparationHandler = { [unowned emulationViewController] in + + // Store reference to current game state before we stop emulation so we can resume it if user decides to not load a save state + if self.currentGameState == nil + { + emulatorCore.saveSaveState() { saveState in + + let fileURL = NSFileManager.uniqueTemporaryURL() + + do + { + try NSFileManager.defaultManager().moveItemAtURL(saveState.fileURL, toURL: fileURL) + } + catch let error as NSError + { + print(error) + } + + self.currentGameState = DeltaCore.SaveState(name: nil, fileURL: fileURL) + } + } + + emulatorCore.stopEmulation() + + + let saveState = self.fetchedResultsController.objectAtIndexPath(indexPath) as! SaveState + + emulationViewController.emulatorCore.startEmulation() + emulationViewController.emulatorCore.pauseEmulation() + emulationViewController.emulatorCore.loadSaveState(saveState) + } + + return emulationViewController + } + + func previewingContext(previewingContext: UIViewControllerPreviewing, commitViewController viewControllerToCommit: UIViewController) + { + let emulationViewController = viewControllerToCommit as! EmulationViewController + + emulationViewController.emulatorCore.pauseEmulation() + emulationViewController.emulatorCore.saveSaveState() { saveState in + + emulationViewController.emulatorCore.stopEmulation() + + let emulatorCore = self.delegate.saveStatesViewControllerActiveEmulatorCore(self) + + emulatorCore.audioManager.stop() + + emulatorCore.startEmulation() + emulatorCore.pauseEmulation() + + self.delegate.saveStatesViewController(self, loadSaveState: saveState) + + emulatorCore.videoManager.enabled = true + } + } +} + //MARK: - - extension SaveStatesViewController {