From 9dcb88b2de1ab31d02aa03bef586e58e53f498e9 Mon Sep 17 00:00:00 2001 From: Riley Testut Date: Fri, 20 May 2016 18:36:43 -0500 Subject: [PATCH] Presents empty CheatsViewController for Cheats pause item --- Common/Database/Model/Cheat.swift | 102 +++++++++++++++ Common/Database/Model/Game.swift | 2 + .../Model.xcdatamodel/contents | 20 ++- ...etchedResultsController+Conveniences.swift | 26 ++++ Cores/DeltaCore | 2 +- Delta.xcodeproj/project.pbxproj | 20 +++ Delta/Base.lproj/PauseMenu.storyboard | 43 +++++- Delta/Emulation/EmulationViewController.swift | 15 ++- .../Cheats/CheatsViewController.swift | 122 ++++++++++++++++++ Delta/Pause Menu/PauseViewController.swift | 17 ++- .../SaveStatesViewController.swift | 12 +- 11 files changed, 361 insertions(+), 20 deletions(-) create mode 100644 Common/Database/Model/Cheat.swift create mode 100644 Common/Extensions/NSFetchedResultsController+Conveniences.swift create mode 100644 Delta/Pause Menu/Cheats/CheatsViewController.swift diff --git a/Common/Database/Model/Cheat.swift b/Common/Database/Model/Cheat.swift new file mode 100644 index 0000000..4adfc68 --- /dev/null +++ b/Common/Database/Model/Cheat.swift @@ -0,0 +1,102 @@ +// +// Cheat.swift +// Delta +// +// Created by Riley Testut on 5/19/16. +// Copyright © 2016 Riley Testut. All rights reserved. +// + +import Foundation +import CoreData + +import DeltaCore + +extension Cheat +{ + enum Attributes: String + { + case identifier + case name + case code + case type + case creationDate + case modifiedDate + + case game + } +} + +extension CheatType +{ + var rawValue: Int16 + { + switch self + { + case .actionReplay: return 0 + case .gameGenie: return 1 + } + } + + init?(rawValue: Int16) + { + switch rawValue + { + case 0: self = .actionReplay + case 1: self = .gameGenie + default: return nil + } + } +} + +@objc(Cheat) +class Cheat: NSManagedObject, CheatProtocol +{ + //TODO: Change type to String! when Swift 3 allows it + @NSManaged var name: String? + @NSManaged var code: String + @NSManaged var modifiedDate: NSDate + + @NSManaged private(set) var identifier: String + @NSManaged private(set) var creationDate: NSDate + + // Must be optional relationship to satisfy weird Core Data requirement + // https://forums.developer.apple.com/thread/20535 + @NSManaged var game: Game! + + var type: CheatType + { + get + { + self.willAccessValueForKey(Attributes.type.rawValue) + let type = CheatType(rawValue: self.primitiveType.shortValue)! + self.didAccessValueForKey(Attributes.type.rawValue) + return type + } + set + { + self.willChangeValueForKey(Attributes.type.rawValue) + self.primitiveType = NSNumber(short: newValue.rawValue) + self.didChangeValueForKey(Attributes.type.rawValue) + } + } +} + +extension Cheat +{ + @NSManaged private var primitiveIdentifier: String + @NSManaged private var primitiveCreationDate: NSDate + @NSManaged private var primitiveModifiedDate: NSDate + @NSManaged private var primitiveType: NSNumber + + override func awakeFromInsert() + { + super.awakeFromInsert() + + let identifier = NSUUID().UUIDString + let date = NSDate() + + self.primitiveIdentifier = identifier + self.primitiveCreationDate = date + self.primitiveModifiedDate = date + } +} diff --git a/Common/Database/Model/Game.swift b/Common/Database/Model/Game.swift index 7946c8c..fedaac1 100644 --- a/Common/Database/Model/Game.swift +++ b/Common/Database/Model/Game.swift @@ -25,7 +25,9 @@ extension Game case typeIdentifier case gameCollections + case saveStates case previewSaveState + case cheats } } diff --git a/Common/Database/Model/Model.xcdatamodeld/Model.xcdatamodel/contents b/Common/Database/Model/Model.xcdatamodeld/Model.xcdatamodel/contents index 26a7b76..6e77d59 100644 --- a/Common/Database/Model/Model.xcdatamodeld/Model.xcdatamodel/contents +++ b/Common/Database/Model/Model.xcdatamodeld/Model.xcdatamodel/contents @@ -1,5 +1,19 @@ + + + + + + + + + + + + + + @@ -14,6 +28,7 @@ + @@ -53,8 +68,9 @@ - - + + + \ No newline at end of file diff --git a/Common/Extensions/NSFetchedResultsController+Conveniences.swift b/Common/Extensions/NSFetchedResultsController+Conveniences.swift new file mode 100644 index 0000000..23d6606 --- /dev/null +++ b/Common/Extensions/NSFetchedResultsController+Conveniences.swift @@ -0,0 +1,26 @@ +// +// NSFetchedResultsController+Conveniences.swift +// Delta +// +// Created by Riley Testut on 5/20/16. +// Copyright © 2016 Riley Testut. All rights reserved. +// + +import CoreData + +extension NSFetchedResultsController +{ + func performFetchIfNeeded() + { + guard self.fetchedObjects == nil else { return } + + do + { + try self.performFetch() + } + catch let error as NSError + { + print(error) + } + } +} diff --git a/Cores/DeltaCore b/Cores/DeltaCore index 29abe10..67ef536 160000 --- a/Cores/DeltaCore +++ b/Cores/DeltaCore @@ -1 +1 @@ -Subproject commit 29abe10afb7f720e40b9345bc88f321c518efbf6 +Subproject commit 67ef5360d0c1ac20ffc584bd908cca634183380b diff --git a/Delta.xcodeproj/project.pbxproj b/Delta.xcodeproj/project.pbxproj index db4ea79..40d2e3d 100644 --- a/Delta.xcodeproj/project.pbxproj +++ b/Delta.xcodeproj/project.pbxproj @@ -42,6 +42,7 @@ BF4566E91BC090B6007BFA1A /* Model.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = BF4566E61BC090B6007BFA1A /* Model.xcdatamodeld */; }; BF5E7F441B9A650B00AE44F8 /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF5E7F431B9A650B00AE44F8 /* SettingsViewController.swift */; }; BF5E7F461B9A652600AE44F8 /* Settings.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BF5E7F451B9A652600AE44F8 /* Settings.storyboard */; }; + BF65E8631CEE5C6A00CD3247 /* Cheat.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF65E8621CEE5C6A00CD3247 /* Cheat.swift */; }; BF6BB23F1BB73FE800CCF94A /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6BB23E1BB73FE800CCF94A /* AppDelegate.swift */; }; BF6BB2411BB73FE800CCF94A /* GameSelectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6BB2401BB73FE800CCF94A /* GameSelectionViewController.swift */; }; BF6BB2441BB73FE800CCF94A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BF6BB2421BB73FE800CCF94A /* Main.storyboard */; }; @@ -74,6 +75,8 @@ BFC134E21AAD82470087AD7B /* SNESDeltaCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BFC134E01AAD82460087AD7B /* SNESDeltaCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; BFC2731A1BE6152200D22B05 /* GameCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC273171BE6152200D22B05 /* GameCollection.swift */; }; BFC2731B1BE6152200D22B05 /* GameCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC273171BE6152200D22B05 /* GameCollection.swift */; }; + BFC9B7391CEFCD34008629BB /* CheatsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC9B7381CEFCD34008629BB /* CheatsViewController.swift */; }; + BFC9B73B1CEFD438008629BB /* NSFetchedResultsController+Conveniences.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC9B73A1CEFD438008629BB /* NSFetchedResultsController+Conveniences.swift */; }; BFDB28451BC9DA7B001D0C83 /* GamePickerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDB28441BC9DA7B001D0C83 /* GamePickerController.swift */; }; BFDE393C1BC0CEDF003F72E8 /* Game.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDE39391BC0CEDF003F72E8 /* Game.swift */; }; BFDE393D1BC0CEDF003F72E8 /* Game.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDE39391BC0CEDF003F72E8 /* Game.swift */; }; @@ -147,6 +150,7 @@ BF4566E71BC090B6007BFA1A /* Model.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Model.xcdatamodel; sourceTree = ""; }; BF5E7F431B9A650B00AE44F8 /* SettingsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = ""; }; BF5E7F451B9A652600AE44F8 /* Settings.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Settings.storyboard; sourceTree = ""; }; + BF65E8621CEE5C6A00CD3247 /* Cheat.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Cheat.swift; sourceTree = ""; }; BF6BB23C1BB73FE800CCF94A /* Delta.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Delta.app; sourceTree = BUILT_PRODUCTS_DIR; }; BF6BB23E1BB73FE800CCF94A /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; BF6BB2401BB73FE800CCF94A /* GameSelectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameSelectionViewController.swift; sourceTree = ""; }; @@ -168,6 +172,8 @@ BFB141171BE46934004FBF46 /* GameCollectionViewDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GameCollectionViewDataSource.swift; path = "Collection View/GameCollectionViewDataSource.swift"; sourceTree = ""; }; BFC134E01AAD82460087AD7B /* SNESDeltaCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SNESDeltaCore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; BFC273171BE6152200D22B05 /* GameCollection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameCollection.swift; sourceTree = ""; }; + BFC9B7381CEFCD34008629BB /* CheatsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CheatsViewController.swift; path = "Pause Menu/Cheats/CheatsViewController.swift"; sourceTree = ""; }; + BFC9B73A1CEFD438008629BB /* NSFetchedResultsController+Conveniences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSFetchedResultsController+Conveniences.swift"; sourceTree = ""; }; BFDB28441BC9DA7B001D0C83 /* GamePickerController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GamePickerController.swift; sourceTree = ""; }; BFDE39391BC0CEDF003F72E8 /* Game.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Game.swift; sourceTree = ""; }; BFEC732C1AAECC4A00650035 /* Roxas.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Roxas.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -275,6 +281,7 @@ BFDE39391BC0CEDF003F72E8 /* Game.swift */, BFC273171BE6152200D22B05 /* GameCollection.swift */, BF1FB1831C5EE643007E2494 /* SaveState.swift */, + BF65E8621CEE5C6A00CD3247 /* Cheat.swift */, ); path = Model; sourceTree = ""; @@ -305,6 +312,7 @@ children = ( BF762EAA1BC1B076002C8866 /* NSManagedObject+Conveniences.swift */, BF172AEA1C68986300C26774 /* NSManagedObjectContext+Conveniences.swift */, + BFC9B73A1CEFD438008629BB /* NSFetchedResultsController+Conveniences.swift */, ); path = Extensions; sourceTree = ""; @@ -317,6 +325,7 @@ BF7AE8041C2E858400B1B5BC /* PauseMenuViewController.swift */, BF353FF81C5D870B00C1184C /* PauseItem.swift */, BF3540031C5DA6D800C1184C /* Save States */, + BFC9B7371CEFCD08008629BB /* Cheats */, BF353FFB1C5DA2F600C1184C /* Presentation Controller */, BF912E481C5CB5D50041527C /* Segues */, ); @@ -375,6 +384,14 @@ path = Settings; sourceTree = ""; }; + BFC9B7371CEFCD08008629BB /* Cheats */ = { + isa = PBXGroup; + children = ( + BFC9B7381CEFCD34008629BB /* CheatsViewController.swift */, + ); + name = Cheats; + sourceTree = ""; + }; BFDB28431BC9D9D1001D0C83 /* Importing */ = { isa = PBXGroup; children = ( @@ -719,11 +736,14 @@ BFF1E5641BE04CAF000E9EF6 /* BoxArtImageView.swift in Sources */, BF2B98E61C97E32F00F6D57D /* SaveStatesCollectionHeaderView.swift in Sources */, BF762EAB1BC1B076002C8866 /* NSManagedObject+Conveniences.swift in Sources */, + BFC9B7391CEFCD34008629BB /* CheatsViewController.swift in Sources */, BF353FFF1C5DA3C500C1184C /* PausePresentationController.swift in Sources */, BF7AE80A1C2E8C7600B1B5BC /* UIColor+Delta.swift in Sources */, BF762E9E1BC19D31002C8866 /* DatabaseManager.swift in Sources */, BF090CF41B490D8300DCAB45 /* UIDevice+Vibration.m in Sources */, + BFC9B73B1CEFD438008629BB /* NSFetchedResultsController+Conveniences.swift in Sources */, BF797A2D1C2D339F00F1A000 /* UILabel+FontSize.swift in Sources */, + BF65E8631CEE5C6A00CD3247 /* Cheat.swift in Sources */, BF3540021C5DA3D500C1184C /* PauseStoryboardSegue.swift in Sources */, BF0CDDAD1C8155D200640168 /* LoadImageOperation.swift in Sources */, BF107EC41BF413F000E0C32C /* GamesViewController.swift in Sources */, diff --git a/Delta/Base.lproj/PauseMenu.storyboard b/Delta/Base.lproj/PauseMenu.storyboard index 702513e..352117b 100644 --- a/Delta/Base.lproj/PauseMenu.storyboard +++ b/Delta/Base.lproj/PauseMenu.storyboard @@ -1,7 +1,7 @@ - + - + @@ -66,7 +66,8 @@ - + + @@ -187,5 +188,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Delta/Emulation/EmulationViewController.swift b/Delta/Emulation/EmulationViewController.swift index 96ead10..860ce35 100644 --- a/Delta/Emulation/EmulationViewController.swift +++ b/Delta/Emulation/EmulationViewController.swift @@ -189,7 +189,10 @@ class EmulationViewController: UIViewController pauseViewController.presentSaveStateViewControllerWithMode(.Loading, delegate: self!) }) - let cheatCodesItem = PauseItem(image: UIImage(named: "SmallPause")!, text: NSLocalizedString("Cheat Codes", comment: ""), action: dismissAction) + let cheatCodesItem = PauseItem(image: UIImage(named: "SmallPause")!, text: NSLocalizedString("Cheat Codes", comment: ""), action: { _ in + pauseViewController.presentCheatsViewController(delegate: self) + }) + let sustainButtonItem = PauseItem(image: UIImage(named: "SmallPause")!, text: NSLocalizedString("Sustain Button", comment: ""), action: dismissAction) var fastForwardItem = PauseItem(image: UIImage(named: "FastForward")!, text: NSLocalizedString("Fast Forward", comment: ""), action: { [weak self] item in @@ -325,6 +328,16 @@ extension EmulationViewController: SaveStatesViewControllerDelegate } } +//MARK: - Cheats +/// Cheats +extension EmulationViewController: CheatsViewControllerDelegate +{ + func cheatsViewControllerActiveGame(cheatsViewController: CheatsViewController) -> Game + { + return self.emulatorCore.game as! Game + } +} + //MARK: - - /// extension EmulationViewController: GameControllerReceiverType diff --git a/Delta/Pause Menu/Cheats/CheatsViewController.swift b/Delta/Pause Menu/Cheats/CheatsViewController.swift new file mode 100644 index 0000000..d1c6a59 --- /dev/null +++ b/Delta/Pause Menu/Cheats/CheatsViewController.swift @@ -0,0 +1,122 @@ +// +// CheatsViewController.swift +// Delta +// +// Created by Riley Testut on 5/20/16. +// Copyright © 2016 Riley Testut. All rights reserved. +// + +import UIKit +import CoreData + +import Roxas + +protocol CheatsViewControllerDelegate: class +{ + func cheatsViewControllerActiveGame(saveStatesViewController: CheatsViewController) -> Game +} + +class CheatsViewController: UITableViewController +{ + weak var delegate: CheatsViewControllerDelegate! { + didSet { + self.updateFetchedResultsController() + } + } + + private var backgroundView: RSTBackgroundView! + + private var fetchedResultsController: NSFetchedResultsController! +} + +extension CheatsViewController +{ + override func viewDidLoad() + { + super.viewDidLoad() + + self.title = NSLocalizedString("Cheats", comment: "") + + self.backgroundView = RSTBackgroundView(frame: self.view.bounds) + self.backgroundView.hidden = false + self.backgroundView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight] + self.backgroundView.textLabel.text = NSLocalizedString("No Cheats", comment: "") + self.backgroundView.textLabel.textColor = UIColor.whiteColor() + self.backgroundView.detailTextLabel.text = NSLocalizedString("You can add a new cheat by pressing the + button in the top right.", comment: "") + self.backgroundView.detailTextLabel.textColor = UIColor.whiteColor() + self.tableView.backgroundView = self.backgroundView + } + + override func viewWillAppear(animated: Bool) + { + self.fetchedResultsController.performFetchIfNeeded() + + self.updateBackgroundView() + + super.viewWillAppear(animated) + } + + override func didReceiveMemoryWarning() + { + super.didReceiveMemoryWarning() + // Dispose of any resources that can be recreated. + } +} + +private extension CheatsViewController +{ + //MARK: - Update - + + func updateFetchedResultsController() + { + let game = self.delegate.cheatsViewControllerActiveGame(self) + + let fetchRequest = Cheat.fetchRequest() + fetchRequest.returnsObjectsAsFaults = false + fetchRequest.predicate = NSPredicate(format: "%K == %@", Cheat.Attributes.game.rawValue, game) + fetchRequest.sortDescriptors = [NSSortDescriptor(key: Cheat.Attributes.name.rawValue, ascending: true)] + + self.fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: DatabaseManager.sharedManager.managedObjectContext, sectionNameKeyPath: nil, cacheName: nil) + self.fetchedResultsController.delegate = self + } + + func updateBackgroundView() + { + if let fetchedObjects = self.fetchedResultsController.fetchedObjects where fetchedObjects.count > 0 + { + self.tableView.separatorStyle = .SingleLine + self.backgroundView.hidden = true + } + else + { + self.tableView.separatorStyle = .None + self.backgroundView.hidden = false + } + } +} + +extension CheatsViewController +{ + // MARK: - Table view data source + + override func numberOfSectionsInTableView(tableView: UITableView) -> Int + { + // #warning Incomplete implementation, return the number of sections + return 0 + } + + override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + // #warning Incomplete implementation, return the number of rows + return 0 + } +} + +//MARK: - - +extension CheatsViewController: NSFetchedResultsControllerDelegate +{ + func controllerDidChangeContent(controller: NSFetchedResultsController) + { + self.tableView.reloadData() + self.updateBackgroundView() + } +} diff --git a/Delta/Pause Menu/PauseViewController.swift b/Delta/Pause Menu/PauseViewController.swift index 5f12e4a..21698bd 100644 --- a/Delta/Pause Menu/PauseViewController.swift +++ b/Delta/Pause Menu/PauseViewController.swift @@ -19,6 +19,8 @@ class PauseViewController: UIViewController, PauseInfoProvidable private weak var saveStatesViewControllerDelegate: SaveStatesViewControllerDelegate? private var saveStatesViewControllerMode = SaveStatesViewController.Mode.Saving + private weak var cheatsViewControllerDelegate: CheatsViewControllerDelegate? + /// UIViewController override var preferredContentSize: CGSize { set { } @@ -86,11 +88,15 @@ extension PauseViewController // Keep navigation bar outside the UIVisualEffectView's self.view.addSubview(self.pauseNavigationController.navigationBar) - case "saveState": + case "saveStates": let saveStatesViewController = segue.destinationViewController as! SaveStatesViewController saveStatesViewController.delegate = self.saveStatesViewControllerDelegate saveStatesViewController.mode = self.saveStatesViewControllerMode + case "cheats": + let cheatsViewController = segue.destinationViewController as! CheatsViewController + cheatsViewController.delegate = self.cheatsViewControllerDelegate + default: break } } @@ -108,7 +114,14 @@ extension PauseViewController self.saveStatesViewControllerMode = mode self.saveStatesViewControllerDelegate = delegate - self.performSegueWithIdentifier("saveState", sender: self) + self.performSegueWithIdentifier("saveStates", sender: self) + } + + func presentCheatsViewController(delegate delegate: CheatsViewControllerDelegate) + { + self.cheatsViewControllerDelegate = delegate + + self.performSegueWithIdentifier("cheats", sender: self) } } diff --git a/Delta/Pause Menu/Save States/SaveStatesViewController.swift b/Delta/Pause Menu/Save States/SaveStatesViewController.swift index 468efff..86ae238 100644 --- a/Delta/Pause Menu/Save States/SaveStatesViewController.swift +++ b/Delta/Pause Menu/Save States/SaveStatesViewController.swift @@ -119,17 +119,7 @@ extension SaveStatesViewController override func viewWillAppear(animated: Bool) { - if self.fetchedResultsController.fetchedObjects == nil - { - do - { - try self.fetchedResultsController.performFetch() - } - catch let error as NSError - { - print(error) - } - } + self.fetchedResultsController.performFetchIfNeeded() self.updateBackgroundView()