diff --git a/Common/Database/Model/Cheat.swift b/Common/Database/Model/Cheat.swift
index 4adfc68..8e9baff 100644
--- a/Common/Database/Model/Cheat.swift
+++ b/Common/Database/Model/Cheat.swift
@@ -19,6 +19,7 @@ extension Cheat
case name
case code
case type
+ case enabled
case creationDate
case modifiedDate
@@ -55,6 +56,7 @@ class Cheat: NSManagedObject, CheatProtocol
@NSManaged var name: String?
@NSManaged var code: String
@NSManaged var modifiedDate: NSDate
+ @NSManaged var enabled: Bool
@NSManaged private(set) var identifier: String
@NSManaged private(set) var creationDate: NSDate
diff --git a/Common/Database/Model/Model.xcdatamodeld/Model.xcdatamodel/contents b/Common/Database/Model/Model.xcdatamodeld/Model.xcdatamodel/contents
index 6e77d59..b03bbf7 100644
--- a/Common/Database/Model/Model.xcdatamodeld/Model.xcdatamodel/contents
+++ b/Common/Database/Model/Model.xcdatamodeld/Model.xcdatamodel/contents
@@ -3,6 +3,7 @@
+
@@ -68,9 +69,9 @@
+
-
\ No newline at end of file
diff --git a/Delta/Base.lproj/PauseMenu.storyboard b/Delta/Base.lproj/PauseMenu.storyboard
index 352117b..d4bfab4 100644
--- a/Delta/Base.lproj/PauseMenu.storyboard
+++ b/Delta/Base.lproj/PauseMenu.storyboard
@@ -203,7 +203,9 @@
+
+
@@ -214,7 +216,7 @@
-
+
diff --git a/Delta/Emulation/EmulationViewController.swift b/Delta/Emulation/EmulationViewController.swift
index 860ce35..cbed37c 100644
--- a/Delta/Emulation/EmulationViewController.swift
+++ b/Delta/Emulation/EmulationViewController.swift
@@ -106,17 +106,23 @@ class EmulationViewController: UIViewController
self.deferredPreparationHandler?()
self.deferredPreparationHandler = nil
+ // Yes, order DOES matter here, in order to prevent audio from being slightly delayed after peeking with 3D Touch (ugh so tired of that issue)
+ switch self.emulatorCore.state
+ {
+ case .Stopped:
+ self.emulatorCore.startEmulation()
+ self.updateCheats()
+
+ case .Running: break
+ case .Paused:
+ self.updateCheats()
+ self.emulatorCore.resumeEmulation()
+ }
+
// Toggle audioManager.enabled to reset the audio buffer and ensure the audio isn't delayed from the beginning
// This is especially noticeable when peeking a game
self.emulatorCore.audioManager.enabled = false
self.emulatorCore.audioManager.enabled = true
-
- switch self.emulatorCore.state
- {
- case .Stopped: self.emulatorCore.startEmulation()
- case .Running: break
- case .Paused: self.emulatorCore.resumeEmulation()
- }
}
override func viewDidLayoutSubviews()
@@ -181,22 +187,22 @@ class EmulationViewController: UIViewController
// Specifically, if you pause a game, open the save states menu, go back, return to menu, select a new game, then try to pause it, it will crash
// As a dirty workaround, we just use a weak reference, and force unwrap it if needed
- let saveStateItem = PauseItem(image: UIImage(named: "SmallPause")!, text: NSLocalizedString("Save State", comment: ""), action: { [weak self] _ in
- pauseViewController.presentSaveStateViewControllerWithMode(.Saving, delegate: self!)
+ let saveStateItem = PauseItem(image: UIImage(named: "SmallPause")!, text: NSLocalizedString("Save State", comment: ""), action: { [unowned self] _ in
+ pauseViewController.presentSaveStateViewControllerWithMode(.Saving, delegate: self)
})
- let loadStateItem = PauseItem(image: UIImage(named: "SmallPause")!, text: NSLocalizedString("Load State", comment: ""), action: { [weak self] _ in
- pauseViewController.presentSaveStateViewControllerWithMode(.Loading, delegate: self!)
+ let loadStateItem = PauseItem(image: UIImage(named: "SmallPause")!, text: NSLocalizedString("Load State", comment: ""), action: { [unowned self] _ in
+ pauseViewController.presentSaveStateViewControllerWithMode(.Loading, delegate: self)
})
- let cheatCodesItem = PauseItem(image: UIImage(named: "SmallPause")!, text: NSLocalizedString("Cheat Codes", comment: ""), action: { _ in
+ let cheatCodesItem = PauseItem(image: UIImage(named: "SmallPause")!, text: NSLocalizedString("Cheat Codes", comment: ""), action: { [unowned self] _ 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
- self?.emulatorCore.fastForwarding = item.selected
+ var fastForwardItem = PauseItem(image: UIImage(named: "FastForward")!, text: NSLocalizedString("Fast Forward", comment: ""), action: { [unowned self] item in
+ self.emulatorCore.fastForwarding = item.selected
})
fastForwardItem.selected = self.emulatorCore.fastForwarding
@@ -324,6 +330,8 @@ extension EmulationViewController: SaveStatesViewControllerDelegate
{
self.emulatorCore.loadSaveState(saveState)
+ self.updateCheats()
+
self.pauseViewController?.dismiss()
}
}
@@ -336,6 +344,70 @@ extension EmulationViewController: CheatsViewControllerDelegate
{
return self.emulatorCore.game as! Game
}
+
+ func cheatsViewController(cheatsViewController: CheatsViewController, didActivateCheat cheat: Cheat) throws
+ {
+ try self.emulatorCore.activateCheat(cheat)
+ }
+
+ func cheatsViewController(cheatsViewController: CheatsViewController, didDeactivateCheat cheat: Cheat) throws
+ {
+ try self.emulatorCore.deactivateCheat(cheat)
+ }
+
+ private func updateCheats()
+ {
+ let backgroundContext = DatabaseManager.sharedManager.backgroundManagedObjectContext()
+ backgroundContext.performBlockAndWait {
+
+ let running = (self.emulatorCore.state == .Running)
+
+ if running
+ {
+ // Core MUST be paused when activating cheats, or else race conditions could crash the core
+ self.emulatorCore.pauseEmulation()
+ }
+
+ let predicate = NSPredicate(format: "%K == %@", Cheat.Attributes.game.rawValue, self.emulatorCore.game as! Game)
+
+ let cheats = Cheat.instancesWithPredicate(predicate, inManagedObjectContext: backgroundContext, type: Cheat.self)
+ for cheat in cheats
+ {
+ do
+ {
+ if cheat.enabled
+ {
+ try self.emulatorCore.activateCheat(cheat)
+ }
+ else
+ {
+ try self.emulatorCore.deactivateCheat(cheat)
+ }
+ }
+ catch EmulatorCore.CheatError.invalid
+ {
+ print("Invalid cheat:", cheat.name, cheat.code)
+ }
+ catch EmulatorCore.CheatError.doesNotExist
+ {
+ // Ignore this error, because we could be deactivating a cheat that hasn't yet been activated
+ print("Cheat does not exist:", cheat.name, cheat.code)
+ }
+ catch let error as NSError
+ {
+ print("Unknown Cheat Error:", error, cheat.name, cheat.code)
+ }
+
+ }
+
+ if running
+ {
+ self.emulatorCore.resumeEmulation()
+ }
+
+ }
+
+ }
}
//MARK: - -
diff --git a/Delta/Pause Menu/Cheats/CheatsViewController.swift b/Delta/Pause Menu/Cheats/CheatsViewController.swift
index d1c6a59..7567bbb 100644
--- a/Delta/Pause Menu/Cheats/CheatsViewController.swift
+++ b/Delta/Pause Menu/Cheats/CheatsViewController.swift
@@ -9,11 +9,15 @@
import UIKit
import CoreData
+import DeltaCore
+
import Roxas
protocol CheatsViewControllerDelegate: class
{
func cheatsViewControllerActiveGame(saveStatesViewController: CheatsViewController) -> Game
+ func cheatsViewController(cheatsViewController: CheatsViewController, didActivateCheat cheat: Cheat) throws
+ func cheatsViewController(cheatsViewController: CheatsViewController, didDeactivateCheat cheat: Cheat) throws
}
class CheatsViewController: UITableViewController
@@ -95,19 +99,158 @@ private extension CheatsViewController
}
}
+//MARK: - Managing Cheats -
+/// Managing Cheats
+private extension CheatsViewController
+{
+ @IBAction func addCheat()
+ {
+ let backgroundContext = DatabaseManager.sharedManager.backgroundManagedObjectContext()
+ backgroundContext.performBlock {
+
+ var game = self.delegate.cheatsViewControllerActiveGame(self)
+ game = backgroundContext.objectWithID(game.objectID) as! Game
+
+ let cheat = Cheat.insertIntoManagedObjectContext(backgroundContext)
+ cheat.game = game
+ cheat.name = "Unlimited Jumps"
+ cheat.code = "3E2C-AF6F"
+ cheat.type = .gameGenie
+
+ do
+ {
+ try self.delegate.cheatsViewController(self, didActivateCheat: cheat)
+ backgroundContext.saveWithErrorLogging()
+ }
+ catch EmulatorCore.CheatError.invalid
+ {
+ dispatch_async(dispatch_get_main_queue()) {
+
+ let alertController = UIAlertController(title: NSLocalizedString("Invalid Cheat", comment: ""), message: NSLocalizedString("Please make sure you typed the cheat code in correctly and try again.", comment: ""), preferredStyle: .Alert)
+ alertController.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .Default, handler: nil))
+ self.presentViewController(alertController, animated: true, completion: nil)
+
+ }
+
+ print("Invalid cheat:", cheat.name, cheat.code)
+ }
+ catch let error as NSError
+ {
+ print("Unknown Cheat Error:", error, cheat.name, cheat.code)
+ }
+
+
+ }
+ }
+
+ func deleteCheat(cheat: Cheat)
+ {
+ let _ = try? self.delegate.cheatsViewController(self, didDeactivateCheat: cheat)
+
+ let backgroundContext = DatabaseManager.sharedManager.backgroundManagedObjectContext()
+ backgroundContext.performBlock {
+ let temporaryCheat = backgroundContext.objectWithID(cheat.objectID)
+ backgroundContext.deleteObject(temporaryCheat)
+ backgroundContext.saveWithErrorLogging()
+ }
+ }
+}
+
+//MARK: - Content -
+/// Content
+private extension CheatsViewController
+{
+ func configure(cell cell: UITableViewCell, forIndexPath indexPath: NSIndexPath)
+ {
+ let cheat = self.fetchedResultsController.objectAtIndexPath(indexPath) as! Cheat
+ cell.textLabel?.text = cheat.name
+ cell.textLabel?.font = UIFont.boldSystemFontOfSize(cell.textLabel!.font.pointSize)
+ cell.accessoryType = cheat.enabled ? .Checkmark : .None
+ }
+}
+
extension CheatsViewController
{
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int
{
- // #warning Incomplete implementation, return the number of sections
- return 0
+ let numberOfSections = self.fetchedResultsController.sections!.count
+ return numberOfSections
}
- override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
- // #warning Incomplete implementation, return the number of rows
- return 0
+ override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
+ {
+ let section = self.fetchedResultsController.sections![section]
+ return section.numberOfObjects
+ }
+
+ override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
+ {
+ let cell = tableView.dequeueReusableCellWithIdentifier(RSTGenericCellIdentifier, forIndexPath: indexPath)
+ self.configure(cell: cell, forIndexPath: indexPath)
+ return cell
+ }
+}
+
+extension CheatsViewController
+{
+ override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
+ {
+ let cheat = self.fetchedResultsController.objectAtIndexPath(indexPath) as! Cheat
+
+ let backgroundContext = DatabaseManager.sharedManager.backgroundManagedObjectContext()
+ backgroundContext.performBlockAndWait {
+ let temporaryCheat = backgroundContext.objectWithID(cheat.objectID) as! Cheat
+ temporaryCheat.enabled = !temporaryCheat.enabled
+
+ do
+ {
+ if temporaryCheat.enabled
+ {
+ try self.delegate.cheatsViewController(self, didActivateCheat: temporaryCheat)
+ }
+ else
+ {
+ try self.delegate.cheatsViewController(self, didDeactivateCheat: temporaryCheat)
+ }
+ }
+ catch EmulatorCore.CheatError.invalid
+ {
+ print("Invalid cheat:", cheat.name, cheat.code)
+ }
+ catch EmulatorCore.CheatError.doesNotExist
+ {
+ print("Cheat does not exist:", cheat.name, cheat.code)
+ }
+ catch let error as NSError
+ {
+ print("Unknown Cheat Error:", error, cheat.name, cheat.code)
+ }
+
+ backgroundContext.saveWithErrorLogging()
+ }
+
+ self.tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .None)
+ }
+
+ override func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]?
+ {
+ let deleteAction = UITableViewRowAction(style: .Destructive, title: NSLocalizedString("Delete", comment: "")) { (action, indexPath) in
+ let cheat = self.fetchedResultsController.objectAtIndexPath(indexPath) as! Cheat
+ self.deleteCheat(cheat)
+ }
+
+ let editAction = UITableViewRowAction(style: .Normal, title: NSLocalizedString("Edit", comment: "")) { (action, indexPath) in
+
+ }
+
+ return [deleteAction, editAction]
+ }
+
+ override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath)
+ {
+ // This method intentionally left blank because someone decided it was a Good Idea™ to require this method be implemented to use UITableViewRowActions
}
}