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()