Adds back support for presenting CheatsViewController from Pause Menu

This commit is contained in:
Riley Testut 2016-07-27 04:08:08 -05:00
parent 8c3d072a5f
commit a5c50b41bc
8 changed files with 221 additions and 76 deletions

View File

@ -7,7 +7,7 @@
<attribute name="identifier" attributeType="String" syncable="YES"/> <attribute name="identifier" attributeType="String" syncable="YES"/>
<attribute name="modifiedDate" attributeType="Date" usesScalarValueType="NO" syncable="YES"/> <attribute name="modifiedDate" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
<attribute name="name" attributeType="String" syncable="YES"/> <attribute name="name" attributeType="String" syncable="YES"/>
<attribute name="type" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/> <attribute name="type" attributeType="String" syncable="YES"/>
<relationship name="game" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Game" inverseName="cheats" inverseEntity="Game" syncable="YES"/> <relationship name="game" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Game" inverseName="cheats" inverseEntity="Game" syncable="YES"/>
<uniquenessConstraints> <uniquenessConstraints>
<uniquenessConstraint> <uniquenessConstraint>

@ -1 +1 @@
Subproject commit d819614e5fe422aa4975f70e3d4f1d2dc97b9c24 Subproject commit 7149d73128c0f5c95b3e58990f9d185cf33277df

View File

@ -19,6 +19,7 @@
BF27CC8E1BC9FEA200A20D89 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BF6BB2451BB73FE800CCF94A /* Assets.xcassets */; }; BF27CC8E1BC9FEA200A20D89 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BF6BB2451BB73FE800CCF94A /* Assets.xcassets */; };
BF27CC971BCC890700A20D89 /* GamesCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF27CC961BCC890700A20D89 /* GamesCollectionViewController.swift */; }; BF27CC971BCC890700A20D89 /* GamesCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF27CC961BCC890700A20D89 /* GamesCollectionViewController.swift */; };
BF2B98E61C97E32F00F6D57D /* SaveStatesCollectionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF2B98E51C97E32F00F6D57D /* SaveStatesCollectionHeaderView.swift */; }; BF2B98E61C97E32F00F6D57D /* SaveStatesCollectionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF2B98E51C97E32F00F6D57D /* SaveStatesCollectionHeaderView.swift */; };
BF31878B1D489AAA00BD020D /* CheatValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF31878A1D489AAA00BD020D /* CheatValidator.swift */; };
BF34FA071CF0F510006624C7 /* EditCheatViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF34FA061CF0F510006624C7 /* EditCheatViewController.swift */; }; BF34FA071CF0F510006624C7 /* EditCheatViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF34FA061CF0F510006624C7 /* EditCheatViewController.swift */; };
BF34FA111CF1899D006624C7 /* CheatTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF34FA101CF1899D006624C7 /* CheatTextView.swift */; }; BF34FA111CF1899D006624C7 /* CheatTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF34FA101CF1899D006624C7 /* CheatTextView.swift */; };
BF353FF21C5D7FB000C1184C /* PauseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF353FF11C5D7FB000C1184C /* PauseViewController.swift */; }; BF353FF21C5D7FB000C1184C /* PauseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF353FF11C5D7FB000C1184C /* PauseViewController.swift */; };
@ -102,6 +103,7 @@
BF27CC941BCB7B7A00A20D89 /* GameController.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameController.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS9.0.sdk/System/Library/Frameworks/GameController.framework; sourceTree = DEVELOPER_DIR; }; BF27CC941BCB7B7A00A20D89 /* GameController.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameController.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS9.0.sdk/System/Library/Frameworks/GameController.framework; sourceTree = DEVELOPER_DIR; };
BF27CC961BCC890700A20D89 /* GamesCollectionViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GamesCollectionViewController.swift; sourceTree = "<group>"; }; BF27CC961BCC890700A20D89 /* GamesCollectionViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GamesCollectionViewController.swift; sourceTree = "<group>"; };
BF2B98E51C97E32F00F6D57D /* SaveStatesCollectionHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SaveStatesCollectionHeaderView.swift; path = "Pause Menu/Save States/SaveStatesCollectionHeaderView.swift"; sourceTree = "<group>"; }; BF2B98E51C97E32F00F6D57D /* SaveStatesCollectionHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SaveStatesCollectionHeaderView.swift; path = "Pause Menu/Save States/SaveStatesCollectionHeaderView.swift"; sourceTree = "<group>"; };
BF31878A1D489AAA00BD020D /* CheatValidator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CheatValidator.swift; path = "Pause Menu/Cheats/CheatValidator.swift"; sourceTree = "<group>"; };
BF34FA061CF0F510006624C7 /* EditCheatViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = EditCheatViewController.swift; path = "Pause Menu/Cheats/EditCheatViewController.swift"; sourceTree = "<group>"; }; BF34FA061CF0F510006624C7 /* EditCheatViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = EditCheatViewController.swift; path = "Pause Menu/Cheats/EditCheatViewController.swift"; sourceTree = "<group>"; };
BF34FA101CF1899D006624C7 /* CheatTextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CheatTextView.swift; path = "Pause Menu/Cheats/CheatTextView.swift"; sourceTree = "<group>"; }; BF34FA101CF1899D006624C7 /* CheatTextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CheatTextView.swift; path = "Pause Menu/Cheats/CheatTextView.swift"; sourceTree = "<group>"; };
BF353FF11C5D7FB000C1184C /* PauseViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PauseViewController.swift; path = "Pause Menu/PauseViewController.swift"; sourceTree = "<group>"; }; BF353FF11C5D7FB000C1184C /* PauseViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PauseViewController.swift; path = "Pause Menu/PauseViewController.swift"; sourceTree = "<group>"; };
@ -320,6 +322,7 @@
BFC9B7381CEFCD34008629BB /* CheatsViewController.swift */, BFC9B7381CEFCD34008629BB /* CheatsViewController.swift */,
BF34FA061CF0F510006624C7 /* EditCheatViewController.swift */, BF34FA061CF0F510006624C7 /* EditCheatViewController.swift */,
BF34FA101CF1899D006624C7 /* CheatTextView.swift */, BF34FA101CF1899D006624C7 /* CheatTextView.swift */,
BF31878A1D489AAA00BD020D /* CheatValidator.swift */,
); );
name = Cheats; name = Cheats;
sourceTree = "<group>"; sourceTree = "<group>";
@ -542,6 +545,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 */,
BF31878B1D489AAA00BD020D /* CheatValidator.swift in Sources */,
BFB141181BE46934004FBF46 /* GameCollectionViewDataSource.swift in Sources */, BFB141181BE46934004FBF46 /* GameCollectionViewDataSource.swift in Sources */,
BFA2315C1CED10BE0011E35A /* Action.swift in Sources */, BFA2315C1CED10BE0011E35A /* Action.swift in Sources */,
BFAA1FF41B8AD7F900495943 /* ControllersSettingsViewController.swift in Sources */, BFAA1FF41B8AD7F900495943 /* ControllersSettingsViewController.swift in Sources */,

View File

@ -10,13 +10,20 @@ import UIKit
import DeltaCore import DeltaCore
private var kvoContext = 0
class GameViewController: DeltaCore.GameViewController class GameViewController: DeltaCore.GameViewController
{ {
/// Assumed to be Delta.Game instance /// Assumed to be Delta.Game instance
override var game: GameProtocol? { override var game: GameProtocol? {
willSet {
self.emulatorCore?.removeObserver(self, forKeyPath: #keyPath(EmulatorCore.state), context: &kvoContext)
}
didSet { didSet {
guard let emulatorCore = self.emulatorCore else { return } guard let emulatorCore = self.emulatorCore else { return }
self.preferredContentSize = emulatorCore.preferredRenderingSize self.preferredContentSize = emulatorCore.preferredRenderingSize
emulatorCore.addObserver(self, forKeyPath: #keyPath(EmulatorCore.state), options: [.old], context: &kvoContext)
} }
} }
@ -51,6 +58,11 @@ class GameViewController: DeltaCore.GameViewController
NotificationCenter.default.addObserver(self, selector: #selector(GameViewController.updateControllers), name: .externalControllerDidDisconnect, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(GameViewController.updateControllers), name: .externalControllerDidDisconnect, object: nil)
} }
deinit
{
self.emulatorCore?.removeObserver(self, forKeyPath: #keyPath(EmulatorCore.state), context: &kvoContext)
}
// MARK: GameControllerReceiver - // MARK: GameControllerReceiver -
override func gameController(_ gameController: GameController, didActivate input: Input) override func gameController(_ gameController: GameController, didActivate input: Input)
{ {
@ -116,6 +128,7 @@ extension GameViewController
pauseViewController.pauseText = (self.game as? Game)?.name ?? NSLocalizedString("Delta", comment: "") pauseViewController.pauseText = (self.game as? Game)?.name ?? NSLocalizedString("Delta", comment: "")
pauseViewController.emulatorCore = self.emulatorCore pauseViewController.emulatorCore = self.emulatorCore
pauseViewController.saveStatesViewControllerDelegate = self pauseViewController.saveStatesViewControllerDelegate = self
pauseViewController.cheatsViewControllerDelegate = self
self.pauseViewController = pauseViewController self.pauseViewController = pauseViewController
} }
@ -135,6 +148,20 @@ extension GameViewController
} }
} }
} }
// MARK: - KVO -
/// KVO
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) else { return }
if previousState == .stopped
{
self.updateCheats()
}
}
} }
//MARK: Controllers - //MARK: Controllers -
@ -244,10 +271,81 @@ extension GameViewController: SaveStatesViewControllerDelegate
print(error) print(error)
} }
self.updateCheats()
self.pauseViewController?.dismiss() self.pauseViewController?.dismiss()
} }
} }
//MARK: - Cheats
/// Cheats
extension GameViewController: CheatsViewControllerDelegate
{
func cheatsViewController(_ cheatsViewController: CheatsViewController, activateCheat cheat: Cheat)
{
self.activate(cheat)
}
func cheatsViewController(_ cheatsViewController: CheatsViewController, deactivateCheat cheat: 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.sharedManager.backgroundManagedObjectContext()
backgroundContext.performAndWait {
let predicate = Predicate(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: GameViewControllerDelegate - //MARK: GameViewControllerDelegate -
/// GameViewControllerDelegate /// GameViewControllerDelegate
extension GameViewController: GameViewControllerDelegate extension GameViewController: GameViewControllerDelegate

View File

@ -0,0 +1,59 @@
//
// CheatValidator.swift
// Delta
//
// Created by Riley Testut on 7/27/16.
// Copyright © 2016 Riley Testut. All rights reserved.
//
import Foundation
import DeltaCore
extension CheatValidator
{
enum Error: ErrorProtocol
{
case invalidCode
case invalidName
case duplicateName
case duplicateCode
}
}
struct CheatValidator
{
let format: CheatFormat
let managedObjectContext: NSManagedObjectContext
func validate(_ cheat: Cheat) throws
{
guard let name = cheat.name else { throw Error.invalidName }
let code = cheat.code
// Find all cheats that are for the same game, don't have the same identifier as the current cheat, but have either the same name or code
let predicate = Predicate(format: "%K == %@ AND %K != %@ AND (%K == %@ OR %K == %@)", Cheat.Attributes.game.rawValue, cheat.game, Cheat.Attributes.identifier.rawValue, cheat.identifier, Cheat.Attributes.code.rawValue, code, Cheat.Attributes.name.rawValue, name)
let cheats = Cheat.instancesWithPredicate(predicate, inManagedObjectContext: self.managedObjectContext, type: Cheat.self)
for cheat in cheats
{
if cheat.name == name
{
throw Error.duplicateName
}
else if cheat.code == code
{
throw Error.duplicateCode
}
}
// Remove newline characters (code should already be formatted)
let sanitizedCode = (cheat.code as NSString).replacingOccurrences(of: "\n", with: "")
if sanitizedCode.characters.count % self.format.format.characters.count != 0
{
throw Error.invalidCode
}
}
}

View File

@ -15,19 +15,20 @@ import Roxas
protocol CheatsViewControllerDelegate: class protocol CheatsViewControllerDelegate: class
{ {
func cheatsViewControllerActiveEmulatorCore(_ saveStatesViewController: CheatsViewController) -> EmulatorCore func cheatsViewController(_ cheatsViewController: CheatsViewController, activateCheat cheat: Cheat)
func cheatsViewController(_ cheatsViewController: CheatsViewController, didActivateCheat cheat: Cheat) throws func cheatsViewController(_ cheatsViewController: CheatsViewController, deactivateCheat cheat: Cheat)
func cheatsViewController(_ cheatsViewController: CheatsViewController, didDeactivateCheat cheat: Cheat)
} }
class CheatsViewController: UITableViewController class CheatsViewController: UITableViewController
{ {
weak var delegate: CheatsViewControllerDelegate! { var game: Game! {
didSet { didSet {
self.updateFetchedResultsController() self.updateFetchedResultsController()
} }
} }
weak var delegate: CheatsViewControllerDelegate?
private var backgroundView: RSTBackgroundView! private var backgroundView: RSTBackgroundView!
private var fetchedResultsController: NSFetchedResultsController<NSFetchRequestResult>! private var fetchedResultsController: NSFetchedResultsController<NSFetchRequestResult>!
@ -83,11 +84,9 @@ private extension CheatsViewController
{ {
func updateFetchedResultsController() func updateFetchedResultsController()
{ {
let game = self.delegate.cheatsViewControllerActiveEmulatorCore(self).game as! Game
let fetchRequest = Cheat.rst_fetchRequest() let fetchRequest = Cheat.rst_fetchRequest()
fetchRequest.returnsObjectsAsFaults = false fetchRequest.returnsObjectsAsFaults = false
fetchRequest.predicate = Predicate(format: "%K == %@", Cheat.Attributes.game.rawValue, game) fetchRequest.predicate = Predicate(format: "%K == %@", Cheat.Attributes.game.rawValue, self.game)
fetchRequest.sortDescriptors = [SortDescriptor(key: Cheat.Attributes.name.rawValue, ascending: true)] fetchRequest.sortDescriptors = [SortDescriptor(key: Cheat.Attributes.name.rawValue, ascending: true)]
self.fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: DatabaseManager.sharedManager.managedObjectContext, sectionNameKeyPath: nil, cacheName: nil) self.fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: DatabaseManager.sharedManager.managedObjectContext, sectionNameKeyPath: nil, cacheName: nil)
@ -121,7 +120,7 @@ private extension CheatsViewController
func deleteCheat(_ cheat: Cheat) func deleteCheat(_ cheat: Cheat)
{ {
self.delegate.cheatsViewController(self, didDeactivateCheat: cheat) self.delegate?.cheatsViewController(self, deactivateCheat: cheat)
let backgroundContext = DatabaseManager.sharedManager.backgroundManagedObjectContext() let backgroundContext = DatabaseManager.sharedManager.backgroundManagedObjectContext()
backgroundContext.perform { backgroundContext.perform {
@ -145,12 +144,11 @@ private extension CheatsViewController
} }
func makeEditCheatViewController(cheat: Cheat?) -> EditCheatViewController func makeEditCheatViewController(cheat: Cheat?) -> EditCheatViewController
{ {
let editCheatViewController = self.storyboard!.instantiateViewController(withIdentifier: "editCheatViewController") as! EditCheatViewController let editCheatViewController = self.storyboard!.instantiateViewController(withIdentifier: "editCheatViewController") as! EditCheatViewController
editCheatViewController.delegate = self editCheatViewController.delegate = self
editCheatViewController.supportedCheatFormats = self.delegate.cheatsViewControllerActiveEmulatorCore(self).configuration.supportedCheatFormats
editCheatViewController.cheat = cheat editCheatViewController.cheat = cheat
editCheatViewController.game = self.delegate.cheatsViewControllerActiveEmulatorCore(self).game as! Game editCheatViewController.game = self.game
return editCheatViewController return editCheatViewController
} }
@ -193,25 +191,13 @@ extension CheatsViewController
if temporaryCheat.enabled if temporaryCheat.enabled
{ {
do self.delegate?.cheatsViewController(self, activateCheat: temporaryCheat)
{
try self.delegate.cheatsViewController(self, didActivateCheat: temporaryCheat)
}
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)
}
} }
else else
{ {
self.delegate.cheatsViewController(self, didDeactivateCheat: temporaryCheat) self.delegate?.cheatsViewController(self, deactivateCheat: temporaryCheat)
} }
backgroundContext.saveWithErrorLogging() backgroundContext.saveWithErrorLogging()
} }
@ -266,9 +252,9 @@ extension CheatsViewController: UIViewControllerPreviewingDelegate
//MARK: - <EditCheatViewControllerDelegate> - //MARK: - <EditCheatViewControllerDelegate> -
extension CheatsViewController: EditCheatViewControllerDelegate extension CheatsViewController: EditCheatViewControllerDelegate
{ {
func editCheatViewController(_ editCheatViewController: EditCheatViewController, activateCheat cheat: Cheat, previousCheat: Cheat?) throws func editCheatViewController(_ editCheatViewController: EditCheatViewController, activateCheat cheat: Cheat, previousCheat: Cheat?)
{ {
try self.delegate.cheatsViewController(self, didActivateCheat: cheat) self.delegate?.cheatsViewController(self, activateCheat: cheat)
if let previousCheat = previousCheat if let previousCheat = previousCheat
{ {
@ -278,14 +264,14 @@ extension CheatsViewController: EditCheatViewControllerDelegate
guard previousCheat.code != code else { return } guard previousCheat.code != code else { return }
self.delegate.cheatsViewController(self, didDeactivateCheat: previousCheat) self.delegate?.cheatsViewController(self, deactivateCheat: previousCheat)
}) })
} }
} }
func editCheatViewController(_ editCheatViewController: EditCheatViewController, deactivateCheat cheat: Cheat) func editCheatViewController(_ editCheatViewController: EditCheatViewController, deactivateCheat cheat: Cheat)
{ {
self.delegate.cheatsViewController(self, didDeactivateCheat: cheat) self.delegate?.cheatsViewController(self, deactivateCheat: cheat)
} }
} }

View File

@ -14,19 +14,12 @@ import Roxas
protocol EditCheatViewControllerDelegate: class protocol EditCheatViewControllerDelegate: class
{ {
func editCheatViewController(_ editCheatViewController: EditCheatViewController, activateCheat cheat: Cheat, previousCheat: Cheat?) throws func editCheatViewController(_ editCheatViewController: EditCheatViewController, activateCheat cheat: Cheat, previousCheat: Cheat?)
func editCheatViewController(_ editCheatViewController: EditCheatViewController, deactivateCheat cheat: Cheat) func editCheatViewController(_ editCheatViewController: EditCheatViewController, deactivateCheat cheat: Cheat)
} }
private extension EditCheatViewController private extension EditCheatViewController
{ {
enum ValidationError: ErrorProtocol
{
case invalidCode
case duplicateName
case duplicateCode
}
enum Section: Int enum Section: Int
{ {
case name case name
@ -37,11 +30,18 @@ private extension EditCheatViewController
class EditCheatViewController: UITableViewController class EditCheatViewController: UITableViewController
{ {
weak var delegate: EditCheatViewControllerDelegate? var game: Game! {
didSet {
let deltaCore = Delta.core(for: self.game.type)!
self.supportedCheatFormats = deltaCore.emulatorConfiguration.supportedCheatFormats
}
}
var cheat: Cheat? var cheat: Cheat?
var game: Game!
var supportedCheatFormats: [CheatFormat]! weak var delegate: EditCheatViewControllerDelegate?
private var supportedCheatFormats: [CheatFormat]!
private var selectedCheatFormat: CheatFormat { private var selectedCheatFormat: CheatFormat {
let cheatFormat = self.supportedCheatFormats[self.typeSegmentedControl.selectedSegmentIndex] let cheatFormat = self.supportedCheatFormats[self.typeSegmentedControl.selectedSegmentIndex]
@ -271,63 +271,51 @@ private extension EditCheatViewController
do do
{ {
try self.validateCheat(self.mutableCheat) try self.validateCheat(self.mutableCheat)
self.delegate?.editCheatViewController(self, activateCheat: self.mutableCheat, previousCheat: self.cheat)
self.mutableCheat.managedObjectContext?.saveWithErrorLogging() self.mutableCheat.managedObjectContext?.saveWithErrorLogging()
self.performSegue(withIdentifier: "unwindEditCheatSegue", sender: sender) self.performSegue(withIdentifier: "unwindEditCheatSegue", sender: sender)
} }
catch ValidationError.invalidCode catch CheatValidator.Error.invalidCode
{ {
self.presentErrorAlert(title: NSLocalizedString("Invalid Code", comment: ""), message: NSLocalizedString("Please make sure you typed the cheat code in correctly and try again.", comment: "")) { self.presentErrorAlert(title: NSLocalizedString("Invalid Code", comment: ""), message: NSLocalizedString("Please make sure you typed the cheat code in correctly and try again.", comment: "")) {
self.codeTextView.becomeFirstResponder() self.codeTextView.becomeFirstResponder()
} }
} }
catch ValidationError.duplicateCode catch CheatValidator.Error.invalidName
{
self.presentErrorAlert(title: NSLocalizedString("Invalid Name", comment: ""), message: NSLocalizedString("Please rename this cheat and try again.", comment: "")) {
self.codeTextView.becomeFirstResponder()
}
}
catch CheatValidator.Error.duplicateCode
{ {
self.presentErrorAlert(title: NSLocalizedString("Duplicate Code", comment: ""), message: NSLocalizedString("A cheat already exists with this code. Please type in a different code and try again.", comment: "")) { self.presentErrorAlert(title: NSLocalizedString("Duplicate Code", comment: ""), message: NSLocalizedString("A cheat already exists with this code. Please type in a different code and try again.", comment: "")) {
self.codeTextView.becomeFirstResponder() self.codeTextView.becomeFirstResponder()
} }
} }
catch ValidationError.duplicateName catch CheatValidator.Error.duplicateName
{ {
self.presentErrorAlert(title: NSLocalizedString("Duplicate Name", comment: ""), message: NSLocalizedString("A cheat already exists with this name. Please rename this cheat and try again.", comment: "")) { self.presentErrorAlert(title: NSLocalizedString("Duplicate Name", comment: ""), message: NSLocalizedString("A cheat already exists with this name. Please rename this cheat and try again.", comment: "")) {
self.nameTextField.becomeFirstResponder() self.nameTextField.becomeFirstResponder()
} }
} }
catch let error as NSError catch
{ {
print(error) print(error)
self.presentErrorAlert(title: NSLocalizedString("Unknown Error", comment: ""), message: NSLocalizedString("An error occured. Please make sure you typed the cheat code in correctly and try again.", comment: "")) {
self.codeTextView.becomeFirstResponder()
}
} }
} }
} }
func validateCheat(_ cheat: Cheat) throws func validateCheat(_ cheat: Cheat) throws
{ {
let name = cheat.name! let validator = CheatValidator(format: self.selectedCheatFormat, managedObjectContext: self.managedObjectContext)
let code = cheat.code try validator.validate(cheat)
// Find all cheats that are for the same game, don't have the same identifier as the current cheat, but have either the same name or code
let predicate = Predicate(format: "%K == %@ AND %K != %@ AND (%K == %@ OR %K == %@)", Cheat.Attributes.game.rawValue, cheat.game, Cheat.Attributes.identifier.rawValue, cheat.identifier, Cheat.Attributes.code.rawValue, code, Cheat.Attributes.name.rawValue, name)
let cheats = Cheat.instancesWithPredicate(predicate, inManagedObjectContext: self.managedObjectContext, type: Cheat.self)
for cheat in cheats
{
if cheat.name == name
{
throw ValidationError.duplicateName
}
else if cheat.code == code
{
throw ValidationError.duplicateCode
}
}
do
{
try self.delegate?.editCheatViewController(self, activateCheat: cheat, previousCheat: self.cheat)
}
catch
{
throw ValidationError.invalidCode
}
} }
@IBAction func textFieldDidEndEditing(_ sender: UITextField) @IBAction func textFieldDidEndEditing(_ sender: UITextField)

View File

@ -32,10 +32,12 @@ class PauseViewController: UIViewController, PauseInfoProviding
/// PauseInfoProviding /// PauseInfoProviding
var pauseText: String? var pauseText: String?
/// Cheats
weak var cheatsViewControllerDelegate: CheatsViewControllerDelegate?
/// Save States /// Save States
weak var saveStatesViewControllerDelegate: SaveStatesViewControllerDelegate? weak var saveStatesViewControllerDelegate: SaveStatesViewControllerDelegate?
// Hopefully this can be removed once SE-0116 is implemented
private var saveStatesViewControllerMode = SaveStatesViewController.Mode.loading private var saveStatesViewControllerMode = SaveStatesViewController.Mode.loading
private var pauseNavigationController: UINavigationController! private var pauseNavigationController: UINavigationController!
@ -104,7 +106,12 @@ extension PauseViewController
saveStatesViewController.game = self.emulatorCore?.game as? Game saveStatesViewController.game = self.emulatorCore?.game as? Game
saveStatesViewController.emulatorCore = self.emulatorCore saveStatesViewController.emulatorCore = self.emulatorCore
saveStatesViewController.mode = self.saveStatesViewControllerMode saveStatesViewController.mode = self.saveStatesViewControllerMode
case "cheats":
let cheatsViewController = segue.destinationViewController as! CheatsViewController
cheatsViewController.delegate = self.cheatsViewControllerDelegate
cheatsViewController.game = self.emulatorCore?.game as? Game
default: break default: break
} }
} }
@ -150,7 +157,10 @@ private extension PauseViewController
self.performSegue(withIdentifier: "saveStates", sender: self) self.performSegue(withIdentifier: "saveStates", sender: self)
}) })
self.cheatCodesItem = PauseItem(image: UIImage(named: "SmallPause")!, text: NSLocalizedString("Cheat Codes", comment: ""), action: { _ in }) self.cheatCodesItem = PauseItem(image: UIImage(named: "SmallPause")!, text: NSLocalizedString("Cheat Codes", comment: ""), action: { [unowned self] _ in
self.performSegue(withIdentifier: "cheats", sender: self)
})
self.sustainButtonsItem = PauseItem(image: UIImage(named: "SmallPause")!, text: NSLocalizedString("Sustain Buttons", comment: ""), action: { _ in }) self.sustainButtonsItem = PauseItem(image: UIImage(named: "SmallPause")!, text: NSLocalizedString("Sustain Buttons", comment: ""), action: { _ in })
self.fastForwardItem = PauseItem(image: UIImage(named: "FastForward")!, text: NSLocalizedString("Fast Forward", comment: ""), action: { _ in }) self.fastForwardItem = PauseItem(image: UIImage(named: "FastForward")!, text: NSLocalizedString("Fast Forward", comment: ""), action: { _ in })
} }