Refactors previewing GameViewController logic into new PreviewGameViewController
This commit is contained in:
parent
b017be6368
commit
3d16fed35a
@ -1 +1 @@
|
|||||||
Subproject commit d9b61b60c23286a4758bab06ed4a89de641f2628
|
Subproject commit d4895367427ffed6ef1fd04e60868cf799fb3371
|
||||||
@ -13,6 +13,8 @@
|
|||||||
BF090CF41B490D8300DCAB45 /* UIDevice+Vibration.m in Sources */ = {isa = PBXBuildFile; fileRef = BF090CF31B490D8300DCAB45 /* UIDevice+Vibration.m */; };
|
BF090CF41B490D8300DCAB45 /* UIDevice+Vibration.m in Sources */ = {isa = PBXBuildFile; fileRef = BF090CF31B490D8300DCAB45 /* UIDevice+Vibration.m */; };
|
||||||
BF0CDDAD1C8155D200640168 /* LoadImageOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0CDDAC1C8155D200640168 /* LoadImageOperation.swift */; };
|
BF0CDDAD1C8155D200640168 /* LoadImageOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0CDDAC1C8155D200640168 /* LoadImageOperation.swift */; };
|
||||||
BF107EC41BF413F000E0C32C /* GamesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF107EC31BF413F000E0C32C /* GamesViewController.swift */; };
|
BF107EC41BF413F000E0C32C /* GamesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF107EC31BF413F000E0C32C /* GamesViewController.swift */; };
|
||||||
|
BF13A7561D5D29B0000BB055 /* PreviewGameViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF13A7551D5D29B0000BB055 /* PreviewGameViewController.swift */; };
|
||||||
|
BF13A7581D5D2FD9000BB055 /* EmulatorCore+Cheats.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF13A7571D5D2FD9000BB055 /* EmulatorCore+Cheats.swift */; };
|
||||||
BF172AEB1C68986300C26774 /* NSManagedObjectContext+Conveniences.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF172AEA1C68986300C26774 /* NSManagedObjectContext+Conveniences.swift */; };
|
BF172AEB1C68986300C26774 /* NSManagedObjectContext+Conveniences.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF172AEA1C68986300C26774 /* NSManagedObjectContext+Conveniences.swift */; };
|
||||||
BF1FB1861C5EE643007E2494 /* SaveState.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1FB1831C5EE643007E2494 /* SaveState.swift */; };
|
BF1FB1861C5EE643007E2494 /* SaveState.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1FB1831C5EE643007E2494 /* SaveState.swift */; };
|
||||||
BF27CC8E1BC9FEA200A20D89 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BF6BB2451BB73FE800CCF94A /* Assets.xcassets */; };
|
BF27CC8E1BC9FEA200A20D89 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BF6BB2451BB73FE800CCF94A /* Assets.xcassets */; };
|
||||||
@ -99,6 +101,8 @@
|
|||||||
BF090CF31B490D8300DCAB45 /* UIDevice+Vibration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIDevice+Vibration.m"; sourceTree = "<group>"; };
|
BF090CF31B490D8300DCAB45 /* UIDevice+Vibration.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIDevice+Vibration.m"; sourceTree = "<group>"; };
|
||||||
BF0CDDAC1C8155D200640168 /* LoadImageOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = LoadImageOperation.swift; path = Components/LoadImageOperation.swift; sourceTree = "<group>"; };
|
BF0CDDAC1C8155D200640168 /* LoadImageOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = LoadImageOperation.swift; path = Components/LoadImageOperation.swift; sourceTree = "<group>"; };
|
||||||
BF107EC31BF413F000E0C32C /* GamesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GamesViewController.swift; sourceTree = "<group>"; };
|
BF107EC31BF413F000E0C32C /* GamesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GamesViewController.swift; sourceTree = "<group>"; };
|
||||||
|
BF13A7551D5D29B0000BB055 /* PreviewGameViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreviewGameViewController.swift; sourceTree = "<group>"; };
|
||||||
|
BF13A7571D5D2FD9000BB055 /* EmulatorCore+Cheats.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "EmulatorCore+Cheats.swift"; sourceTree = "<group>"; };
|
||||||
BF172AEA1C68986300C26774 /* NSManagedObjectContext+Conveniences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSManagedObjectContext+Conveniences.swift"; sourceTree = "<group>"; };
|
BF172AEA1C68986300C26774 /* NSManagedObjectContext+Conveniences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSManagedObjectContext+Conveniences.swift"; sourceTree = "<group>"; };
|
||||||
BF1FB1831C5EE643007E2494 /* SaveState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SaveState.swift; sourceTree = "<group>"; };
|
BF1FB1831C5EE643007E2494 /* SaveState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SaveState.swift; sourceTree = "<group>"; };
|
||||||
BF27CC861BC9E3C600A20D89 /* Delta.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = Delta.entitlements; sourceTree = "<group>"; };
|
BF27CC861BC9E3C600A20D89 /* Delta.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = Delta.entitlements; sourceTree = "<group>"; };
|
||||||
@ -189,6 +193,7 @@
|
|||||||
BF797A2C1C2D339F00F1A000 /* UILabel+FontSize.swift */,
|
BF797A2C1C2D339F00F1A000 /* UILabel+FontSize.swift */,
|
||||||
BF7AE8091C2E8C7600B1B5BC /* UIColor+Delta.swift */,
|
BF7AE8091C2E8C7600B1B5BC /* UIColor+Delta.swift */,
|
||||||
BFCEA67D1D56FF640061A534 /* UIViewControllerContextTransitioning+Conveniences.swift */,
|
BFCEA67D1D56FF640061A534 /* UIViewControllerContextTransitioning+Conveniences.swift */,
|
||||||
|
BF13A7571D5D2FD9000BB055 /* EmulatorCore+Cheats.swift */,
|
||||||
);
|
);
|
||||||
path = Extensions;
|
path = Extensions;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -412,6 +417,7 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
BF63BDE91D389EEB00FCB040 /* GameViewController.swift */,
|
BF63BDE91D389EEB00FCB040 /* GameViewController.swift */,
|
||||||
|
BF13A7551D5D29B0000BB055 /* PreviewGameViewController.swift */,
|
||||||
);
|
);
|
||||||
path = Emulation;
|
path = Emulation;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -573,6 +579,7 @@
|
|||||||
BF7AE8241C2E984300B1B5BC /* GridCollectionViewLayout.swift in Sources */,
|
BF7AE8241C2E984300B1B5BC /* GridCollectionViewLayout.swift in Sources */,
|
||||||
BF1FB1861C5EE643007E2494 /* SaveState.swift in Sources */,
|
BF1FB1861C5EE643007E2494 /* SaveState.swift in Sources */,
|
||||||
BFA0D1271D3AE1F600565894 /* GameViewController.swift in Sources */,
|
BFA0D1271D3AE1F600565894 /* GameViewController.swift in Sources */,
|
||||||
|
BF13A7581D5D2FD9000BB055 /* EmulatorCore+Cheats.swift in Sources */,
|
||||||
BF31878B1D489AAA00BD020D /* CheatValidator.swift in Sources */,
|
BF31878B1D489AAA00BD020D /* CheatValidator.swift in Sources */,
|
||||||
BFB141181BE46934004FBF46 /* GameCollectionViewDataSource.swift in Sources */,
|
BFB141181BE46934004FBF46 /* GameCollectionViewDataSource.swift in Sources */,
|
||||||
BFFC46201D59823500AF2CC6 /* InitialGamesStoryboardSegue.swift in Sources */,
|
BFFC46201D59823500AF2CC6 /* InitialGamesStoryboardSegue.swift in Sources */,
|
||||||
@ -605,6 +612,7 @@
|
|||||||
BF0CDDAD1C8155D200640168 /* LoadImageOperation.swift in Sources */,
|
BF0CDDAD1C8155D200640168 /* LoadImageOperation.swift in Sources */,
|
||||||
BF107EC41BF413F000E0C32C /* GamesViewController.swift in Sources */,
|
BF107EC41BF413F000E0C32C /* GamesViewController.swift in Sources */,
|
||||||
BF172AEB1C68986300C26774 /* NSManagedObjectContext+Conveniences.swift in Sources */,
|
BF172AEB1C68986300C26774 /* NSManagedObjectContext+Conveniences.swift in Sources */,
|
||||||
|
BF13A7561D5D29B0000BB055 /* PreviewGameViewController.swift in Sources */,
|
||||||
BFCEA67E1D56FF640061A534 /* UIViewControllerContextTransitioning+Conveniences.swift in Sources */,
|
BFCEA67E1D56FF640061A534 /* UIViewControllerContextTransitioning+Conveniences.swift in Sources */,
|
||||||
BFFC461E1D59823500AF2CC6 /* GamesPresentationController.swift in Sources */,
|
BFFC461E1D59823500AF2CC6 /* GamesPresentationController.swift in Sources */,
|
||||||
BF5E7F441B9A650B00AE44F8 /* SettingsViewController.swift in Sources */,
|
BF5E7F441B9A650B00AE44F8 /* SettingsViewController.swift in Sources */,
|
||||||
|
|||||||
@ -39,19 +39,10 @@ class GameViewController: DeltaCore.GameViewController
|
|||||||
self.emulatorCore?.removeObserver(self, forKeyPath: #keyPath(EmulatorCore.state), context: &kvoContext)
|
self.emulatorCore?.removeObserver(self, forKeyPath: #keyPath(EmulatorCore.state), context: &kvoContext)
|
||||||
}
|
}
|
||||||
didSet {
|
didSet {
|
||||||
guard let emulatorCore = self.emulatorCore else { return }
|
self.emulatorCore?.addObserver(self, forKeyPath: #keyPath(EmulatorCore.state), options: [.old], context: &kvoContext)
|
||||||
self.preferredContentSize = emulatorCore.preferredRenderingSize
|
|
||||||
|
|
||||||
emulatorCore.addObserver(self, forKeyPath: #keyPath(EmulatorCore.state), options: [.old], context: &kvoContext)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If non-nil, will override the default preview action items returned in previewActionItems()
|
|
||||||
var overridePreviewActionItems: [UIPreviewActionItem]?
|
|
||||||
|
|
||||||
// Set to true to handle automatically updating auto save state
|
|
||||||
var updatesAutoSaveState = false
|
|
||||||
|
|
||||||
//MARK: - Private Properties -
|
//MARK: - Private Properties -
|
||||||
private var pauseViewController: PauseViewController?
|
private var pauseViewController: PauseViewController?
|
||||||
private var pausingGameController: GameController?
|
private var pausingGameController: GameController?
|
||||||
@ -86,26 +77,6 @@ class GameViewController: DeltaCore.GameViewController
|
|||||||
private var sustainButtonsBlurView: UIVisualEffectView!
|
private var sustainButtonsBlurView: UIVisualEffectView!
|
||||||
private var sustainButtonsBackgroundView: RSTBackgroundView!
|
private var sustainButtonsBackgroundView: RSTBackgroundView!
|
||||||
|
|
||||||
override var previewActionItems: [UIPreviewActionItem]
|
|
||||||
{
|
|
||||||
if let previewActionItems = self.overridePreviewActionItems
|
|
||||||
{
|
|
||||||
return previewActionItems
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let game = self.game as? Game else { return [] }
|
|
||||||
|
|
||||||
let presentingViewController = self.presentingViewController
|
|
||||||
|
|
||||||
let launchGameAction = UIPreviewAction(title: NSLocalizedString("Launch \(game.name)", comment: ""), style: .default) { (action, viewController) in
|
|
||||||
// Delaying until next run loop prevents self from being dismissed immediately
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
presentingViewController?.present(viewController, animated: true, completion: nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [launchGameAction]
|
|
||||||
}
|
|
||||||
|
|
||||||
required init()
|
required init()
|
||||||
{
|
{
|
||||||
self.reactivateSustainedInputsQueue = OperationQueue()
|
self.reactivateSustainedInputsQueue = OperationQueue()
|
||||||
@ -349,7 +320,7 @@ extension GameViewController
|
|||||||
|
|
||||||
if previousState == .stopped
|
if previousState == .stopped
|
||||||
{
|
{
|
||||||
self.updateCheats()
|
self.emulatorCore?.updateCheats()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -397,8 +368,6 @@ extension GameViewController: SaveStatesViewControllerDelegate
|
|||||||
{
|
{
|
||||||
private func updateAutoSaveState()
|
private func updateAutoSaveState()
|
||||||
{
|
{
|
||||||
guard self.updatesAutoSaveState else { return }
|
|
||||||
|
|
||||||
// If pausedSaveState exists and has already been saved, don't update auto save state
|
// If pausedSaveState exists and has already been saved, don't update auto save state
|
||||||
// This prevents us from filling our auto save state slots with the same save state
|
// This prevents us from filling our auto save state slots with the same save state
|
||||||
let savedPausedSaveState = self.pausedSaveState?.isSaved ?? false
|
let savedPausedSaveState = self.pausedSaveState?.isSaved ?? false
|
||||||
@ -566,7 +535,7 @@ extension GameViewController: SaveStatesViewControllerDelegate
|
|||||||
print(error)
|
print(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.updateCheats()
|
self.emulatorCore?.updateCheats()
|
||||||
|
|
||||||
self.pauseViewController?.dismiss()
|
self.pauseViewController?.dismiss()
|
||||||
}
|
}
|
||||||
@ -578,67 +547,13 @@ extension GameViewController: CheatsViewControllerDelegate
|
|||||||
{
|
{
|
||||||
func cheatsViewController(_ cheatsViewController: CheatsViewController, activateCheat cheat: Cheat)
|
func cheatsViewController(_ cheatsViewController: CheatsViewController, activateCheat cheat: Cheat)
|
||||||
{
|
{
|
||||||
self.activate(cheat)
|
self.emulatorCore?.activateCheatWithErrorLogging(cheat)
|
||||||
}
|
}
|
||||||
|
|
||||||
func cheatsViewController(_ cheatsViewController: CheatsViewController, deactivateCheat cheat: Cheat)
|
func cheatsViewController(_ cheatsViewController: CheatsViewController, deactivateCheat cheat: Cheat)
|
||||||
{
|
{
|
||||||
self.emulatorCore?.deactivate(cheat)
|
self.emulatorCore?.deactivate(cheat)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func activate(_ cheat: Cheat)
|
|
||||||
{
|
|
||||||
do
|
|
||||||
{
|
|
||||||
try self.emulatorCore?.activate(cheat)
|
|
||||||
}
|
|
||||||
catch EmulatorCore.CheatError.invalid
|
|
||||||
{
|
|
||||||
print("Invalid cheat:", cheat.name, cheat.code)
|
|
||||||
}
|
|
||||||
catch let error as NSError
|
|
||||||
{
|
|
||||||
print("Unknown Cheat Error:", error, cheat.name, cheat.code)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func updateCheats()
|
|
||||||
{
|
|
||||||
guard let game = self.game as? Game else { return }
|
|
||||||
|
|
||||||
let running = (self.emulatorCore?.state == .running)
|
|
||||||
|
|
||||||
if running
|
|
||||||
{
|
|
||||||
// Core MUST be paused when activating cheats, or else race conditions could crash the core
|
|
||||||
self.pauseEmulation()
|
|
||||||
}
|
|
||||||
|
|
||||||
let backgroundContext = DatabaseManager.shared.newBackgroundContext()
|
|
||||||
backgroundContext.performAndWait {
|
|
||||||
|
|
||||||
let predicate = NSPredicate(format: "%K == %@", Cheat.Attributes.game.rawValue, game)
|
|
||||||
|
|
||||||
let cheats = Cheat.instancesWithPredicate(predicate, inManagedObjectContext: backgroundContext, type: Cheat.self)
|
|
||||||
for cheat in cheats
|
|
||||||
{
|
|
||||||
if cheat.enabled
|
|
||||||
{
|
|
||||||
self.activate(cheat)
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self.emulatorCore?.deactivate(cheat)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if running
|
|
||||||
{
|
|
||||||
self.resumeEmulation()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//MARK: - Sustain Buttons -
|
//MARK: - Sustain Buttons -
|
||||||
|
|||||||
176
Delta/Emulation/PreviewGameViewController.swift
Normal file
176
Delta/Emulation/PreviewGameViewController.swift
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
//
|
||||||
|
// PreviewGameViewController.swift
|
||||||
|
// Delta
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 8/11/16.
|
||||||
|
// Copyright © 2016 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
import DeltaCore
|
||||||
|
|
||||||
|
private var kvoContext = 0
|
||||||
|
|
||||||
|
class PreviewGameViewController: DeltaCore.GameViewController
|
||||||
|
{
|
||||||
|
// If non-nil, will override the default preview action items returned in previewActionItems()
|
||||||
|
var overridePreviewActionItems: [UIPreviewActionItem]?
|
||||||
|
|
||||||
|
// Save state to be loaded upon preview
|
||||||
|
var previewSaveState: SaveStateProtocol?
|
||||||
|
|
||||||
|
// Initial image to be shown while loading
|
||||||
|
var previewImage: UIImage? {
|
||||||
|
didSet {
|
||||||
|
self.updatePreviewImage()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var emulatorCoreQueue = DispatchQueue(label: "com.rileytestut.Delta.PreviewGameViewController.emulatorCoreQueue", qos: .userInitiated)
|
||||||
|
|
||||||
|
override var game: GameProtocol? {
|
||||||
|
willSet {
|
||||||
|
self.emulatorCore?.removeObserver(self, forKeyPath: #keyPath(EmulatorCore.state), context: &kvoContext)
|
||||||
|
}
|
||||||
|
didSet {
|
||||||
|
guard let emulatorCore = self.emulatorCore else {
|
||||||
|
self.preferredContentSize = CGSize.zero
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
emulatorCore.addObserver(self, forKeyPath: #keyPath(EmulatorCore.state), options: [.old], context: &kvoContext)
|
||||||
|
|
||||||
|
self.preferredContentSize = emulatorCore.preferredRenderingSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override var previewActionItems: [UIPreviewActionItem] {
|
||||||
|
guard let previewActionItems = self.overridePreviewActionItems else { return [] }
|
||||||
|
return previewActionItems
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit
|
||||||
|
{
|
||||||
|
self.emulatorCore?.removeObserver(self, forKeyPath: #keyPath(EmulatorCore.state), context: &kvoContext)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//MARK: - UIViewController -
|
||||||
|
/// UIViewController
|
||||||
|
extension PreviewGameViewController
|
||||||
|
{
|
||||||
|
override func viewDidLoad()
|
||||||
|
{
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
self.controllerView.isHidden = true
|
||||||
|
|
||||||
|
// Temporarily prevent emulatorCore from updating gameView to prevent flicker of black, or other visual glitches
|
||||||
|
self.emulatorCore?.remove(self.gameView)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewDidAppear(_ animated: Bool)
|
||||||
|
{
|
||||||
|
super.viewDidAppear(animated)
|
||||||
|
|
||||||
|
self.emulatorCoreQueue.async {
|
||||||
|
self.emulatorCore?.resume()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewWillDisappear(_ animated: Bool)
|
||||||
|
{
|
||||||
|
super.viewWillDisappear(animated)
|
||||||
|
|
||||||
|
// Pause in viewWillDisappear and not viewDidDisappear like DeltaCore.GameViewController so the audio cuts off earlier if being dismissed
|
||||||
|
self.emulatorCore?.pause()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewDidLayoutSubviews()
|
||||||
|
{
|
||||||
|
super.viewDidLayoutSubviews()
|
||||||
|
|
||||||
|
// Need to update in viewDidLayoutSubviews() to ensure bounds of gameView are not CGRect.zero
|
||||||
|
self.updatePreviewImage()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func didReceiveMemoryWarning()
|
||||||
|
{
|
||||||
|
super.didReceiveMemoryWarning()
|
||||||
|
// Dispose of any resources that can be recreated.
|
||||||
|
}
|
||||||
|
|
||||||
|
override func observeValue(forKeyPath keyPath: String?, of object: AnyObject?, change: [NSKeyValueChangeKey : AnyObject]?, context: UnsafeMutablePointer<Void>?)
|
||||||
|
{
|
||||||
|
guard context == &kvoContext else { return super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) }
|
||||||
|
|
||||||
|
guard
|
||||||
|
let rawValue = change?[.oldKey] as? Int,
|
||||||
|
let previousState = EmulatorCore.State(rawValue: rawValue),
|
||||||
|
let state = self.emulatorCore?.state
|
||||||
|
else { return }
|
||||||
|
|
||||||
|
if previousState == .stopped, state == .running
|
||||||
|
{
|
||||||
|
self.emulatorCoreQueue.sync {
|
||||||
|
if self.isAppearing
|
||||||
|
{
|
||||||
|
// Pause to prevent it from starting before visible (in case user peeked slowly)
|
||||||
|
self.emulatorCore?.pause()
|
||||||
|
}
|
||||||
|
|
||||||
|
self.preparePreview()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//MARK: - Private -
|
||||||
|
private extension PreviewGameViewController
|
||||||
|
{
|
||||||
|
func updatePreviewImage()
|
||||||
|
{
|
||||||
|
if let previewImage = self.previewImage
|
||||||
|
{
|
||||||
|
self.gameView?.inputImage = CIImage(image: previewImage)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
self.gameView?.inputImage = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func preparePreview()
|
||||||
|
{
|
||||||
|
var previewSaveState = self.previewSaveState
|
||||||
|
|
||||||
|
if let saveState = self.previewSaveState as? SaveState
|
||||||
|
{
|
||||||
|
saveState.managedObjectContext?.performAndWait {
|
||||||
|
previewSaveState = DeltaCore.SaveState(fileURL: saveState.fileURL, gameType: saveState.gameType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let saveState = previewSaveState
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
try self.emulatorCore?.load(saveState)
|
||||||
|
}
|
||||||
|
catch EmulatorCore.SaveStateError.doesNotExist
|
||||||
|
{
|
||||||
|
print("Save State does not exist.")
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
print(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.emulatorCore?.updateCheats()
|
||||||
|
|
||||||
|
// Re-enable emulatorCore to update gameView again
|
||||||
|
self.emulatorCore?.add(self.gameView)
|
||||||
|
}
|
||||||
|
}
|
||||||
66
Delta/Extensions/EmulatorCore+Cheats.swift
Normal file
66
Delta/Extensions/EmulatorCore+Cheats.swift
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
//
|
||||||
|
// EmulatorCore+Cheats.swift
|
||||||
|
// Delta
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 8/11/16.
|
||||||
|
// Copyright © 2016 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import DeltaCore
|
||||||
|
|
||||||
|
extension EmulatorCore
|
||||||
|
{
|
||||||
|
func activateCheatWithErrorLogging(_ cheat: Cheat)
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
try self.activate(cheat)
|
||||||
|
}
|
||||||
|
catch EmulatorCore.CheatError.invalid
|
||||||
|
{
|
||||||
|
print("Invalid cheat:", cheat.name, cheat.code)
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
print("Unknown Cheat Error:", error, cheat.name, cheat.code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateCheats()
|
||||||
|
{
|
||||||
|
guard let game = self.game as? Game else { return }
|
||||||
|
|
||||||
|
let running = (self.state == .running)
|
||||||
|
|
||||||
|
if running
|
||||||
|
{
|
||||||
|
// Core MUST be paused when activating cheats, or else race conditions could crash the core
|
||||||
|
self.pause()
|
||||||
|
}
|
||||||
|
|
||||||
|
let backgroundContext = DatabaseManager.shared.newBackgroundContext()
|
||||||
|
backgroundContext.performAndWait {
|
||||||
|
|
||||||
|
let predicate = NSPredicate(format: "%K == %@", Cheat.Attributes.game.rawValue, game)
|
||||||
|
|
||||||
|
let cheats = Cheat.instancesWithPredicate(predicate, inManagedObjectContext: backgroundContext, type: Cheat.self)
|
||||||
|
for cheat in cheats
|
||||||
|
{
|
||||||
|
if cheat.enabled
|
||||||
|
{
|
||||||
|
self.activateCheatWithErrorLogging(cheat)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
self.deactivate(cheat)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if running
|
||||||
|
{
|
||||||
|
self.resume()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -43,6 +43,5 @@ class LaunchViewController: UIViewController
|
|||||||
guard segue.identifier == "embedGameViewController" else { return }
|
guard segue.identifier == "embedGameViewController" else { return }
|
||||||
|
|
||||||
self.gameViewController = segue.destination as! GameViewController
|
self.gameViewController = segue.destination as! GameViewController
|
||||||
self.gameViewController.updatesAutoSaveState = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -65,8 +65,6 @@ class SaveStatesViewController: UICollectionViewController
|
|||||||
|
|
||||||
private let dateFormatter: DateFormatter
|
private let dateFormatter: DateFormatter
|
||||||
|
|
||||||
private let previewGameViewController = GameViewController()
|
|
||||||
|
|
||||||
required init?(coder aDecoder: NSCoder)
|
required init?(coder aDecoder: NSCoder)
|
||||||
{
|
{
|
||||||
self.dateFormatter = DateFormatter()
|
self.dateFormatter = DateFormatter()
|
||||||
@ -123,8 +121,7 @@ extension SaveStatesViewController
|
|||||||
let longPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(SaveStatesViewController.handleLongPressGesture(_:)))
|
let longPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(SaveStatesViewController.handleLongPressGesture(_:)))
|
||||||
self.collectionView?.addGestureRecognizer(longPressGestureRecognizer)
|
self.collectionView?.addGestureRecognizer(longPressGestureRecognizer)
|
||||||
|
|
||||||
// Pre-initialize previewGameViewController with game and start/pause emulation to ensure previewingContext(_:viewControllerForLocation:) callback doesn't take too long + break 3D Touch animation
|
self.prepareEmulatorCoreSaveState()
|
||||||
self.preparePreviewGameViewController()
|
|
||||||
|
|
||||||
self.registerForPreviewing(with: self, sourceView: self.collectionView!)
|
self.registerForPreviewing(with: self, sourceView: self.collectionView!)
|
||||||
|
|
||||||
@ -286,23 +283,13 @@ private extension SaveStatesViewController
|
|||||||
|
|
||||||
func updateSaveState(_ saveState: SaveState)
|
func updateSaveState(_ saveState: SaveState)
|
||||||
{
|
{
|
||||||
// Stop previewGameViewController.emulatorCore, and switch to self.emulatorCore
|
// Switch back to self.emulatorCore
|
||||||
self.prepareEmulatorCore()
|
self.prepareEmulatorCore()
|
||||||
|
|
||||||
saveState.managedObjectContext?.performAndWait {
|
saveState.managedObjectContext?.performAndWait {
|
||||||
self.delegate?.saveStatesViewController(self, updateSaveState: saveState)
|
self.delegate?.saveStatesViewController(self, updateSaveState: saveState)
|
||||||
saveState.managedObjectContext?.saveWithErrorLogging()
|
saveState.managedObjectContext?.saveWithErrorLogging()
|
||||||
}
|
}
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
// Only restart self.previewGameViewController.emulatorCore if we're not being dismissed
|
|
||||||
if !self.isDisappearing
|
|
||||||
{
|
|
||||||
self.emulatorCore?.stop()
|
|
||||||
self.previewGameViewController.emulatorCore?.start()
|
|
||||||
self.previewGameViewController.emulatorCore?.pause()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadSaveState(_ saveState: SaveStateProtocol)
|
func loadSaveState(_ saveState: SaveStateProtocol)
|
||||||
@ -492,9 +479,14 @@ private extension SaveStatesViewController
|
|||||||
|
|
||||||
func resetEmulatorCoreIfNeeded()
|
func resetEmulatorCoreIfNeeded()
|
||||||
{
|
{
|
||||||
guard let saveState = self.emulatorCoreSaveState else { return }
|
// 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
|
||||||
|
if self.isMovingFromParentViewController
|
||||||
|
{
|
||||||
|
self.prepareEmulatorCore()
|
||||||
|
}
|
||||||
|
|
||||||
defer
|
if let saveState = self.emulatorCoreSaveState
|
||||||
{
|
{
|
||||||
// Remove temporary save state file
|
// Remove temporary save state file
|
||||||
do
|
do
|
||||||
@ -506,76 +498,57 @@ private extension SaveStatesViewController
|
|||||||
print(error)
|
print(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.previewGameViewController.emulatorCore?.stop()
|
|
||||||
|
|
||||||
// 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
|
|
||||||
if self.isMovingFromParentViewController
|
|
||||||
{
|
|
||||||
self.prepareEmulatorCore()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func prepareEmulatorCore()
|
func prepareEmulatorCore()
|
||||||
{
|
{
|
||||||
self.previewGameViewController.emulatorCore?.stop()
|
|
||||||
|
|
||||||
// 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
|
// 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
|
||||||
if let emulatorCore = self.emulatorCore
|
guard let emulatorCore = self.emulatorCore else { return }
|
||||||
|
|
||||||
|
// Temporarily disable video rendering to prevent flickers
|
||||||
|
emulatorCore.videoManager.isEnabled = false
|
||||||
|
|
||||||
|
// Load the save state we stored a reference to
|
||||||
|
emulatorCore.start()
|
||||||
|
emulatorCore.pause()
|
||||||
|
|
||||||
|
if let saveState = self.emulatorCoreSaveState
|
||||||
{
|
{
|
||||||
// Temporarily disable video rendering to prevent flickers
|
do
|
||||||
emulatorCore.videoManager.isEnabled = false
|
|
||||||
|
|
||||||
// Load the save state we stored a reference to
|
|
||||||
emulatorCore.start()
|
|
||||||
emulatorCore.pause()
|
|
||||||
|
|
||||||
if let saveState = self.emulatorCoreSaveState
|
|
||||||
{
|
{
|
||||||
do
|
try emulatorCore.load(saveState)
|
||||||
{
|
}
|
||||||
try emulatorCore.load(saveState)
|
catch EmulatorCore.SaveStateError.doesNotExist
|
||||||
}
|
{
|
||||||
catch EmulatorCore.SaveStateError.doesNotExist
|
print("Save State does not exist.")
|
||||||
{
|
}
|
||||||
print("Save State does not exist.")
|
catch let error as NSError
|
||||||
}
|
{
|
||||||
catch let error as NSError
|
print(error)
|
||||||
{
|
|
||||||
print(error)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-enable video rendering
|
|
||||||
emulatorCore.videoManager.isEnabled = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Re-enable video rendering
|
||||||
|
emulatorCore.videoManager.isEnabled = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//MARK: - 3D Touch -
|
//MARK: - 3D Touch -
|
||||||
extension SaveStatesViewController: UIViewControllerPreviewingDelegate
|
extension SaveStatesViewController: UIViewControllerPreviewingDelegate
|
||||||
{
|
{
|
||||||
private func preparePreviewGameViewController()
|
private func prepareEmulatorCoreSaveState()
|
||||||
{
|
{
|
||||||
if let emulatorCore = self.emulatorCore
|
guard let emulatorCore = self.emulatorCore else { return }
|
||||||
|
|
||||||
|
// Store reference to current game state before we stop emulation so we can resume it if user decides to not load a save state
|
||||||
|
|
||||||
|
let fileURL = FileManager.uniqueTemporaryURL()
|
||||||
|
self.emulatorCoreSaveState = emulatorCore.saveSaveState(to: fileURL)
|
||||||
|
|
||||||
|
if self.emulatorCoreSaveState != nil
|
||||||
{
|
{
|
||||||
// Store reference to current game state before we stop emulation so we can resume it if user decides to not load a save state
|
|
||||||
|
|
||||||
let fileURL = FileManager.uniqueTemporaryURL()
|
|
||||||
self.emulatorCoreSaveState = emulatorCore.saveSaveState(to: fileURL)
|
|
||||||
|
|
||||||
guard self.emulatorCoreSaveState != nil else { return }
|
|
||||||
|
|
||||||
emulatorCore.stop()
|
emulatorCore.stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
self.previewGameViewController.loadViewIfNeeded()
|
|
||||||
self.previewGameViewController.controllerView.isHidden = true
|
|
||||||
|
|
||||||
self.previewGameViewController.game = self.game
|
|
||||||
self.previewGameViewController.emulatorCore?.start()
|
|
||||||
self.previewGameViewController.emulatorCore?.pause()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController?
|
func previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController?
|
||||||
@ -589,37 +562,28 @@ extension SaveStatesViewController: UIViewControllerPreviewingDelegate
|
|||||||
previewingContext.sourceRect = layoutAttributes.frame
|
previewingContext.sourceRect = layoutAttributes.frame
|
||||||
|
|
||||||
let saveState = self.fetchedResultsController.object(at: indexPath) as! SaveState
|
let saveState = self.fetchedResultsController.object(at: indexPath) as! SaveState
|
||||||
|
let actions = self.actionsForSaveState(saveState)?.lazy.filter{ $0.style != .cancel }.map{ $0.previewAction } ?? []
|
||||||
|
let previewImage = self.imageCache.object(forKey: saveState.imageFileURL) ?? UIImage(contentsOfFile: saveState.imageFileURL.path)
|
||||||
|
|
||||||
do
|
let previewGameViewController = PreviewGameViewController()
|
||||||
{
|
previewGameViewController.game = self.game
|
||||||
try self.previewGameViewController.emulatorCore?.load(saveState)
|
previewGameViewController.overridePreviewActionItems = actions
|
||||||
|
previewGameViewController.previewSaveState = saveState
|
||||||
|
previewGameViewController.previewImage = previewImage
|
||||||
|
|
||||||
let actions = self.actionsForSaveState(saveState)?.lazy.filter{ $0.style != .cancel }.map{ $0.previewAction } ?? []
|
return previewGameViewController
|
||||||
self.previewGameViewController.overridePreviewActionItems = Array(actions)
|
|
||||||
|
|
||||||
return self.previewGameViewController
|
|
||||||
}
|
|
||||||
catch EmulatorCore.SaveStateError.doesNotExist
|
|
||||||
{
|
|
||||||
print("Save State \(saveState.name) does not exist.")
|
|
||||||
}
|
|
||||||
catch let error as NSError
|
|
||||||
{
|
|
||||||
print(error)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func previewingContext(_ previewingContext: UIViewControllerPreviewing, commit viewControllerToCommit: UIViewController)
|
func previewingContext(_ previewingContext: UIViewControllerPreviewing, commit viewControllerToCommit: UIViewController)
|
||||||
{
|
{
|
||||||
let gameViewController = viewControllerToCommit as! GameViewController
|
let gameViewController = viewControllerToCommit as! PreviewGameViewController
|
||||||
|
|
||||||
gameViewController.emulatorCore?.pause()
|
gameViewController.emulatorCore?.pause()
|
||||||
|
|
||||||
let fileURL = FileManager.uniqueTemporaryURL()
|
let fileURL = FileManager.uniqueTemporaryURL()
|
||||||
if let saveState = gameViewController.emulatorCore?.saveSaveState(to: fileURL)
|
if let saveState = gameViewController.emulatorCore?.saveSaveState(to: fileURL)
|
||||||
{
|
{
|
||||||
|
gameViewController.emulatorCore?.stop()
|
||||||
|
|
||||||
self.loadSaveState(saveState)
|
self.loadSaveState(saveState)
|
||||||
|
|
||||||
do
|
do
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user