From 0d9596e7e459134a7a220f0ee83f0f12b2272d78 Mon Sep 17 00:00:00 2001 From: "Michael J. Miller" Date: Mon, 28 Jan 2019 23:47:58 -0700 Subject: [PATCH 01/12] quicksave icon --- Carthage/Checkouts/SteamController | 2 +- .../button-save-pressed.imageset/Contents.json | 16 ++++++++++++++++ .../save-pressed.png | Bin 0 -> 12053 bytes .../button-save.imageset/Contents.json | 16 ++++++++++++++++ .../button-save.imageset/save.png | Bin 0 -> 12053 bytes 5 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 Provenance/Resources/Assets.xcassets/button-save-pressed.imageset/Contents.json create mode 100644 Provenance/Resources/Assets.xcassets/button-save-pressed.imageset/save-pressed.png create mode 100644 Provenance/Resources/Assets.xcassets/button-save.imageset/Contents.json create mode 100644 Provenance/Resources/Assets.xcassets/button-save.imageset/save.png diff --git a/Carthage/Checkouts/SteamController b/Carthage/Checkouts/SteamController index b26dda8147..80c984953e 160000 --- a/Carthage/Checkouts/SteamController +++ b/Carthage/Checkouts/SteamController @@ -1 +1 @@ -Subproject commit b26dda814768b4a60396d6a1028dbc304f5448fb +Subproject commit 80c984953eb68e3d340ff7ba36a9042470ae57bf diff --git a/Provenance/Resources/Assets.xcassets/button-save-pressed.imageset/Contents.json b/Provenance/Resources/Assets.xcassets/button-save-pressed.imageset/Contents.json new file mode 100644 index 0000000000..e8bb53e882 --- /dev/null +++ b/Provenance/Resources/Assets.xcassets/button-save-pressed.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "save-pressed.png" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "template-rendering-intent" : "template", + "preserves-vector-representation" : true + } +} \ No newline at end of file diff --git a/Provenance/Resources/Assets.xcassets/button-save-pressed.imageset/save-pressed.png b/Provenance/Resources/Assets.xcassets/button-save-pressed.imageset/save-pressed.png new file mode 100644 index 0000000000000000000000000000000000000000..03d92e50e131e7d87b917e7aa9e7765f66e83f4a GIT binary patch literal 12053 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 F{T(GLaxee@ literal 0 HcmV?d00001 From 599d0226c4ff3d596e667ba9e270b3f2a6109add Mon Sep 17 00:00:00 2001 From: "Michael J. Miller" Date: Tue, 29 Jan 2019 00:08:39 -0700 Subject: [PATCH 02/12] Initial working quicksave --- .../Emulator/PVEmulatorViewController.swift | 145 +++++++++++------- .../PVEmulatorViewController~iOS.swift | 37 ++++- 2 files changed, 116 insertions(+), 66 deletions(-) diff --git a/Provenance/Emulator/PVEmulatorViewController.swift b/Provenance/Emulator/PVEmulatorViewController.swift index 9a5e1e8c51..3c6e3fd788 100644 --- a/Provenance/Emulator/PVEmulatorViewController.swift +++ b/Provenance/Emulator/PVEmulatorViewController.swift @@ -76,6 +76,7 @@ final class PVEmulatorViewController: PVEmulatorViewControllerRootClass, PVAudio var batterySavesPath: URL { return PVEmulatorConfiguration.batterySavesPath(forGame: game) } var BIOSPath: URL { return PVEmulatorConfiguration.biosPath(forGame: game) } var menuButton: MenuButton? + var quicksaveButton: MenuButton? private(set) lazy var glViewController: PVGLViewController = PVGLViewController(emulatorCore: core) private(set) lazy var controllerViewController: (UIViewController & StartSelectDelegate)? = PVCoreFactory.controllerViewController(forSystem: game.system, core: core) @@ -173,60 +174,76 @@ final class PVEmulatorViewController: PVEmulatorViewControllerRootClass, PVAudio } } - private func initNotifcationObservers() { - NotificationCenter.default.addObserver(self, selector: #selector(PVEmulatorViewController.appWillEnterForeground(_:)), name: UIApplication.willEnterForegroundNotification, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(PVEmulatorViewController.appDidEnterBackground(_:)), name: UIApplication.didEnterBackgroundNotification, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(PVEmulatorViewController.appWillResignActive(_:)), name: UIApplication.willResignActiveNotification, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(PVEmulatorViewController.appDidBecomeActive(_:)), name: UIApplication.didBecomeActiveNotification, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(PVEmulatorViewController.controllerDidConnect(_:)), name: .GCControllerDidConnect, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(PVEmulatorViewController.controllerDidDisconnect(_:)), name: .GCControllerDidDisconnect, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(PVEmulatorViewController.screenDidConnect(_:)), name: UIScreen.didConnectNotification, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(PVEmulatorViewController.screenDidDisconnect(_:)), name: UIScreen.didDisconnectNotification, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(PVEmulatorViewController.handleControllerManagerControllerReassigned(_:)), name: .PVControllerManagerControllerReassigned, object: nil) - } - - private func initCore() { - core.audioDelegate = self - core.saveStatesPath = saveStatePath.path - core.batterySavesPath = batterySavesPath.path - core.biosPath = BIOSPath.path - core.controller1 = PVControllerManager.shared.player1 - core.controller2 = PVControllerManager.shared.player2 - core.controller3 = PVControllerManager.shared.player3 - core.controller4 = PVControllerManager.shared.player4 - - let md5Hash: String = game.md5Hash - core.romMD5 = md5Hash - core.romSerial = game.romSerial - } - - private func initMenuButton() { - // controllerViewController = PVCoreFactory.controllerViewController(forSystem: game.system, core: core) - if let aController = controllerViewController { - addChild(aController) - } - if let aView = controllerViewController?.view { - view.addSubview(aView) - } - controllerViewController?.didMove(toParent: self) - + private func initNotifcationObservers() { + NotificationCenter.default.addObserver(self, selector: #selector(PVEmulatorViewController.appWillEnterForeground(_:)), name: UIApplication.willEnterForegroundNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(PVEmulatorViewController.appDidEnterBackground(_:)), name: UIApplication.didEnterBackgroundNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(PVEmulatorViewController.appWillResignActive(_:)), name: UIApplication.willResignActiveNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(PVEmulatorViewController.appDidBecomeActive(_:)), name: UIApplication.didBecomeActiveNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(PVEmulatorViewController.controllerDidConnect(_:)), name: .GCControllerDidConnect, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(PVEmulatorViewController.controllerDidDisconnect(_:)), name: .GCControllerDidDisconnect, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(PVEmulatorViewController.screenDidConnect(_:)), name: UIScreen.didConnectNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(PVEmulatorViewController.screenDidDisconnect(_:)), name: UIScreen.didDisconnectNotification, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(PVEmulatorViewController.handleControllerManagerControllerReassigned(_:)), name: .PVControllerManagerControllerReassigned, object: nil) + } + + private func initCore() { + core.audioDelegate = self + core.saveStatesPath = self.saveStatePath.path + core.batterySavesPath = batterySavesPath.path + core.biosPath = BIOSPath.path + core.controller1 = PVControllerManager.shared.player1 + core.controller2 = PVControllerManager.shared.player2 + core.controller3 = PVControllerManager.shared.player3 + core.controller4 = PVControllerManager.shared.player4 + + let md5Hash: String = game.md5Hash + core.romMD5 = md5Hash + core.romSerial = game.romSerial + } + + private func initMenuButton() { + // controllerViewController = PVCoreFactory.controllerViewController(forSystem: game.system, core: core) + if let aController = controllerViewController { + addChild(aController) + } + if let aView = controllerViewController?.view { + view.addSubview(aView) + } + controllerViewController?.didMove(toParent: self) + + let alpha: CGFloat = PVSettingsModel.shared.controllerOpacity + menuButton = MenuButton(type: .custom) + menuButton?.autoresizingMask = [.flexibleLeftMargin, .flexibleRightMargin, .flexibleBottomMargin] + menuButton?.setImage(UIImage(named: "button-menu"), for: .normal) + menuButton?.setImage(UIImage(named: "button-menu-pressed"), for: .highlighted) + // Commenting out title label for now (menu has changed to graphic only) + //[self.menuButton setTitle:@"Menu" forState:UIControlStateNormal]; + //menuButton?.titleLabel?.font = UIFont.systemFont(ofSize: 12) + //menuButton?.setTitleColor(UIColor.white, for: .normal) + menuButton?.layer.shadowOffset = CGSize(width: 0, height: 1) + menuButton?.layer.shadowRadius = 3.0 + menuButton?.layer.shadowColor = UIColor.black.cgColor + menuButton?.layer.shadowOpacity = 0.75 + menuButton?.tintColor = UIColor.white + menuButton?.alpha = alpha + menuButton?.addTarget(self, action: #selector(PVEmulatorViewController.showMenu(_:)), for: .touchUpInside) + view.addSubview(menuButton!) + } + + private func initQuicksaveButton() { let alpha: CGFloat = PVSettingsModel.shared.controllerOpacity - menuButton = MenuButton(type: .custom) - menuButton?.autoresizingMask = [.flexibleLeftMargin, .flexibleRightMargin, .flexibleBottomMargin] - menuButton?.setImage(UIImage(named: "button-menu"), for: .normal) - menuButton?.setImage(UIImage(named: "button-menu-pressed"), for: .highlighted) - // Commenting out title label for now (menu has changed to graphic only) - // [self.menuButton setTitle:@"Menu" forState:UIControlStateNormal]; - // menuButton?.titleLabel?.font = UIFont.systemFont(ofSize: 12) - // menuButton?.setTitleColor(UIColor.white, for: .normal) - menuButton?.layer.shadowOffset = CGSize(width: 0, height: 1) - menuButton?.layer.shadowRadius = 3.0 - menuButton?.layer.shadowColor = UIColor.black.cgColor - menuButton?.layer.shadowOpacity = 0.75 - menuButton?.tintColor = UIColor.white - menuButton?.alpha = alpha - menuButton?.addTarget(self, action: #selector(PVEmulatorViewController.showMenu(_:)), for: .touchUpInside) - view.addSubview(menuButton!) + quicksaveButton = MenuButton(type: .custom) + quicksaveButton?.autoresizingMask = [.flexibleLeftMargin, .flexibleRightMargin, .flexibleBottomMargin] + quicksaveButton?.setImage(UIImage(named: "button-save"), for: .normal) + quicksaveButton?.setImage(UIImage(named: "button-save-pressed"), for: .highlighted) + quicksaveButton?.layer.shadowOffset = CGSize(width: 0, height: 1) + quicksaveButton?.layer.shadowRadius = 3.0 + quicksaveButton?.layer.shadowColor = UIColor.black.cgColor + quicksaveButton?.layer.shadowOpacity = 0.75 + quicksaveButton?.tintColor = UIColor.white + quicksaveButton?.alpha = alpha + quicksaveButton?.addTarget(self, action: #selector(PVEmulatorViewController.quicksave(_:)), for: .touchUpInside) + view.addSubview(quicksaveButton!) } private func initFPSLabel() { @@ -331,9 +348,10 @@ final class PVEmulatorViewController: PVEmulatorViewControllerRootClass, PVAudio } glViewController.didMove(toParent: self) } - #if os(iOS) + #if os(iOS) initMenuButton() - #endif + initQuicksaveButton() + #endif if PVSettingsModel.shared.showFPSCount { initFPSLabel() @@ -342,6 +360,7 @@ final class PVEmulatorViewController: PVEmulatorViewControllerRootClass, PVAudio #if !targetEnvironment(simulator) if !GCController.controllers().isEmpty { menuButton?.isHidden = true + quicksaveButton?.isHidden = true } #endif @@ -463,9 +482,10 @@ final class PVEmulatorViewController: PVEmulatorViewControllerRootClass, PVAudio override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() - #if os(iOS) - layoutMenuButton() - #endif + #if os(iOS) + layoutMenuButton() + layoutQuicksaveButton() + #endif } #if os(iOS) @@ -478,6 +498,15 @@ final class PVEmulatorViewController: PVEmulatorViewControllerRootClass, PVAudio menuButton.frame = frame } } + func layoutQuicksaveButton() { + if let quicksaveButton = self.quicksaveButton { + let height: CGFloat = 42 + let width: CGFloat = 42 + quicksaveButton.imageView?.contentMode = .center + let frame = CGRect(x: self.view.frame.size.width - safeAreaInsets.right - width, y: safeAreaInsets.top + 5, width: width, height: height) + quicksaveButton.frame = frame + } + } #endif func documentsPath() -> String? { #if os(tvOS) diff --git a/Provenance/Emulator/PVEmulatorViewController~iOS.swift b/Provenance/Emulator/PVEmulatorViewController~iOS.swift index cc3873f718..3bb238c312 100644 --- a/Provenance/Emulator/PVEmulatorViewController~iOS.swift +++ b/Provenance/Emulator/PVEmulatorViewController~iOS.swift @@ -434,14 +434,35 @@ extension PVEmulatorViewController { self.isShowingMenu = false self.enableContorllerInput(false) } + } + + present(actionSheet, animated: true, completion: {() -> Void in + PVControllerManager.shared.iCadeController?.refreshListener() + }) + } + + @objc func quicksave(_ sender: Any?) { + + let image = captureScreenshot() + + if let latestManualSaveState = game.saveStates.sorted(byKeyPath: "date", ascending: true).last { + + createNewSaveState(auto: false, screenshot: image) { result in + switch result { + case .success: + do { + try PVSaveState.delete(latestManualSaveState) + } catch { + self.presentError("Error deleting previous save after writing quicksave: \(error)") + } + case .error(let error): + self.presentError("Error writing quicksave: \(error)") + } + } } - - present(actionSheet, animated: true, completion: { () -> Void in - PVControllerManager.shared.iCadeController?.refreshListener() - }) } - - // override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) { - // super.dismiss(animated: flag, completion: completion) - // } + + // override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) { + // super.dismiss(animated: flag, completion: completion) + // } } From 25845678dd5a707152e42b5141f49c4c26152bcd Mon Sep 17 00:00:00 2001 From: "Michael J. Miller" Date: Tue, 29 Jan 2019 08:27:53 -0700 Subject: [PATCH 03/12] Adding toggle for quicksave --- PVSupport/Settings/PVSettingsModel.swift | 1 + Provenance/Emulator/PVEmulatorViewController.swift | 6 ++++-- Provenance/Settings/PVSettingsViewController.swift | 1 + 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/PVSupport/Settings/PVSettingsModel.swift b/PVSupport/Settings/PVSettingsModel.swift index c097b4660c..144e7eb547 100644 --- a/PVSupport/Settings/PVSettingsModel.swift +++ b/PVSupport/Settings/PVSettingsModel.swift @@ -242,6 +242,7 @@ extension MirroredSettings { public dynamic var askToAutoLoad = true public dynamic var autoLoadSaves = false + public dynamic var showQuicksaveButton = false public dynamic var disableAutoLock = false public dynamic var buttonVibration = true diff --git a/Provenance/Emulator/PVEmulatorViewController.swift b/Provenance/Emulator/PVEmulatorViewController.swift index 3c6e3fd788..000552bc9d 100644 --- a/Provenance/Emulator/PVEmulatorViewController.swift +++ b/Provenance/Emulator/PVEmulatorViewController.swift @@ -484,10 +484,12 @@ final class PVEmulatorViewController: PVEmulatorViewControllerRootClass, PVAudio super.viewDidLayoutSubviews() #if os(iOS) layoutMenuButton() - layoutQuicksaveButton() + + if PVSettingsModel.shared.showQuicksaveButton { + layoutQuicksaveButton() + } #endif } - #if os(iOS) func layoutMenuButton() { if let menuButton = self.menuButton { diff --git a/Provenance/Settings/PVSettingsViewController.swift b/Provenance/Settings/PVSettingsViewController.swift index 04e6d968b6..21f42ca2d7 100644 --- a/Provenance/Settings/PVSettingsViewController.swift +++ b/Provenance/Settings/PVSettingsViewController.swift @@ -97,6 +97,7 @@ final class PVSettingsViewController: PVQuickTableViewController { PVSettingsSwitchRow(text: "Timed Auto Saves", key: \PVSettingsModel.timedAutoSaves), PVSettingsSwitchRow(text: "Auto Load Saves", key: \PVSettingsModel.autoLoadSaves), PVSettingsSwitchRow(text: "Ask to Load Saves", key: \PVSettingsModel.askToAutoLoad), + PVSettingsSwitchRow(text: "Show Quicksave Button", key: \PVSettingsModel.showQuicksaveButton) ] let savesSection = Section(title: "Saves", rows: saveRows) From f8b1f3b119e103cb6961b680f83860c32df8b665 Mon Sep 17 00:00:00 2001 From: "Michael J. Miller" Date: Sat, 9 Feb 2019 17:51:12 -0700 Subject: [PATCH 04/12] Adding quicksave to the menu --- Provenance/Emulator/PVEmulatorViewController~iOS.swift | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Provenance/Emulator/PVEmulatorViewController~iOS.swift b/Provenance/Emulator/PVEmulatorViewController~iOS.swift index 3bb238c312..0d9bbe457b 100644 --- a/Provenance/Emulator/PVEmulatorViewController~iOS.swift +++ b/Provenance/Emulator/PVEmulatorViewController~iOS.swift @@ -388,6 +388,9 @@ extension PVEmulatorViewController { self.perform(#selector(self.showSpeedMenu), with: nil, afterDelay: 0.1) })) if core.supportsSaveStates { + actionSheet.addAction(Action( "Quicksave", style: .default, handler: { action in + self.perform(#selector(self.quicksave), with: nil, afterDelay: 0.1) + })) actionSheet.addAction(Action("Save States", style: .default, handler: { _ in self.perform(#selector(self.showSaveStateMenu), with: nil, afterDelay: 0.1) })) @@ -443,6 +446,8 @@ extension PVEmulatorViewController { @objc func quicksave(_ sender: Any?) { + self.core.setPauseEmulation(true) + let image = captureScreenshot() if let latestManualSaveState = game.saveStates.sorted(byKeyPath: "date", ascending: true).last { @@ -458,6 +463,9 @@ extension PVEmulatorViewController { case .error(let error): self.presentError("Error writing quicksave: \(error)") } + + self.core.setPauseEmulation(false) + self.isShowingMenu = false } } } From d38f3349de6921b4755be8e47e371203676776c9 Mon Sep 17 00:00:00 2001 From: "Michael J. Miller" Date: Sat, 16 Feb 2019 11:37:11 -0700 Subject: [PATCH 05/12] Making quicksaves distinct from manual and auto saves --- PVLibrary/PVLibrary/Domain/SaveState.swift | 4 +- .../RealmPlatform/Entities/PVGame.swift | 10 +- .../RealmPlatform/Entities/PVSaveState.swift | 44 +- .../PVLibrary/RealmPlatform/RomDatabase.swift | 20 +- .../PVEmulatorViewController+Saves.swift | 20 +- .../PVEmulatorViewController~iOS.swift | 2 +- .../RealmCollectinViewCell.swift | 18 +- .../PVSaveStateInfoViewController.swift | 5 +- .../PVSaveStatesViewController.swift | 91 +- .../User Interface/SaveStates.storyboard | 20 +- .../User Interface/SaveStates~.storyboard | 829 ------------------ 11 files changed, 150 insertions(+), 913 deletions(-) delete mode 100644 Provenance/User Interface/SaveStates~.storyboard diff --git a/PVLibrary/PVLibrary/Domain/SaveState.swift b/PVLibrary/PVLibrary/Domain/SaveState.swift index d26d3c00a9..15d56aa750 100644 --- a/PVLibrary/PVLibrary/Domain/SaveState.swift +++ b/PVLibrary/PVLibrary/Domain/SaveState.swift @@ -16,7 +16,7 @@ public protocol SaveStateInfoProvider { var date: Date { get } var lastOpened: Date? { get } var image: LocalFile? { get } - var isAutosave: Bool { get } + var saveType: SaveType { get } } public struct SaveState: SaveStateInfoProvider, Codable { @@ -27,5 +27,5 @@ public struct SaveState: SaveStateInfoProvider, Codable { public let date: Date public let lastOpened: Date? public let image: LocalFile? - public let isAutosave: Bool + public let saveType: SaveType } diff --git a/PVLibrary/PVLibrary/RealmPlatform/Entities/PVGame.swift b/PVLibrary/PVLibrary/RealmPlatform/Entities/PVGame.swift index 4dd8d232df..71c3a715a1 100644 --- a/PVLibrary/PVLibrary/RealmPlatform/Entities/PVGame.swift +++ b/PVLibrary/PVLibrary/RealmPlatform/Entities/PVGame.swift @@ -127,12 +127,20 @@ extension PVGame: Filed, LocalFileProvider {} public extension PVGame { var autoSaves: Results { - return saveStates.filter("isAutosave == true").sorted(byKeyPath: "date", ascending: false) + return saveStates.filter("saveTypeValue == \(SaveType.auto.rawValue)").sorted(byKeyPath: "date", ascending: false) + } + + public var quickSaves : Results { + return saveStates.filter("saveTypeValue == \(SaveType.quick.rawValue)").sorted(byKeyPath: "date", ascending: false) } var newestAutoSave: PVSaveState? { return autoSaves.first } + + public var newestQuickSave : PVSaveState? { + return quickSaves.first + } var lastAutosaveAge: TimeInterval? { guard let first = autoSaves.first else { diff --git a/PVLibrary/PVLibrary/RealmPlatform/Entities/PVSaveState.swift b/PVLibrary/PVLibrary/RealmPlatform/Entities/PVSaveState.swift index 0b561314a3..6b6a58b154 100644 --- a/PVLibrary/PVLibrary/RealmPlatform/Entities/PVSaveState.swift +++ b/PVLibrary/PVLibrary/RealmPlatform/Entities/PVSaveState.swift @@ -10,6 +10,21 @@ import Foundation import PVSupport import RealmSwift +@objc public enum SaveType: Int, Codable, CustomStringConvertible { + // These enums are explicitly given raw values. These values are encoded in the Realm database and are expected to remain consistent. + case manual = 0 + case auto = 1 + case quick = 2 + + public var description : String { + switch self { + case .manual: return "Manual" + case .auto: return "Auto" + case .quick: return "Quick" + } + } +} + public protocol Filed { associatedtype LocalFileProviderType: LocalFileProvider var file: LocalFileProviderType! { get } @@ -29,16 +44,22 @@ public final class PVSaveState: Object, Filed, LocalFileProvider { public dynamic var date: Date = Date() public dynamic var lastOpened: Date? public dynamic var image: PVImageFile? - public dynamic var isAutosave: Bool = false + + // Realm won't store enums, so we store the raw value but allow consumers to interact with the enum + @objc dynamic private var saveTypeValue: Int = SaveType.manual.rawValue + public dynamic var saveType: SaveType { + get { return SaveType(rawValue: saveTypeValue)! } + set { saveTypeValue = newValue.rawValue } + } public dynamic var createdWithCoreVersion: String! - public convenience init(withGame game: PVGame, core: PVCore, file: PVFile, image: PVImageFile? = nil, isAutosave: Bool = false) { + public convenience init(withGame game: PVGame, core: PVCore, file: PVFile, type: SaveType = .manual, image: PVImageFile? = nil) { self.init() self.game = game self.file = file self.image = image - self.isAutosave = isAutosave + self.saveType = type self.core = core createdWithCoreVersion = core.projectVersion } @@ -63,13 +84,23 @@ public final class PVSaveState: Object, Filed, LocalFileProvider { } public dynamic var isNewestAutosave: Bool { - guard isAutosave, let game = game, let newestSave = game.autoSaves.first else { + guard saveType == .auto, let game = game, let newestSave = game.autoSaves.first else { return false } let isNewest = newestSave == self return isNewest } + + @objc dynamic public var isNewestQuicksave: Bool { + guard saveType == .quick, let game = game, let newestSave = game.quickSaves.first else { + return false + } + + let isNewest = newestSave == self + return isNewest + } + public static func == (lhs: PVSaveState, rhs: PVSaveState) -> Bool { return lhs.file.url == rhs.file.url @@ -96,7 +127,8 @@ private extension SaveState { } else { image = nil } - isAutosave = saveState.isAutosave + + saveType = saveState.saveType } } @@ -135,7 +167,7 @@ extension SaveState: RealmRepresentable { DLOG("path: \(imagePath)") object.image = PVImageFile(withURL: imagePath) } - object.isAutosave = isAutosave + object.saveType = saveType } } } diff --git a/PVLibrary/PVLibrary/RealmPlatform/RomDatabase.swift b/PVLibrary/PVLibrary/RealmPlatform/RomDatabase.swift index 396afbe69a..2dad8fa17e 100644 --- a/PVLibrary/PVLibrary/RealmPlatform/RomDatabase.swift +++ b/PVLibrary/PVLibrary/RealmPlatform/RomDatabase.swift @@ -11,7 +11,7 @@ import PVSupport import RealmSwift import UIKit -let schemaVersion: UInt64 = 8 +let schemaVersion: UInt64 = 9 public extension Notification.Name { static let DatabaseMigrationStarted = Notification.Name("DatabaseMigrarionStarted") @@ -73,9 +73,11 @@ public final class RealmConfiguration { } let migrationBlock: MigrationBlock = { migration, oldSchemaVersion in + if oldSchemaVersion < schemaVersion { + NotificationCenter.default.post(name: NSNotification.Name.DatabaseMigrationStarted, object: nil) + } if oldSchemaVersion < 2 { ILOG("Migrating to version 2. Adding MD5s") - NotificationCenter.default.post(name: NSNotification.Name.DatabaseMigrationStarted, object: nil) var counter = 0 var deletions = 0 @@ -117,6 +119,20 @@ public final class RealmConfiguration { NotificationCenter.default.post(name: NSNotification.Name.DatabaseMigrationFinished, object: nil) ILOG("Migration complete of \(counter) roms. Removed \(deletions) bad entries.") } + + if oldSchemaVersion < 9 { + ILOG("Migrating to version 9. Adding support for quicksaves.") + + var counter = 0 + migration.enumerateObjects(ofType: PVSaveState.className()) { oldObject, newObject in + counter += 1 + let isAutosave = oldObject!["isAutosave"] as! Bool + newObject!["saveTypeValue"] = isAutosave ? SaveType.auto.rawValue : SaveType.manual.rawValue + } + + NotificationCenter.default.post(name: NSNotification.Name.DatabaseMigrationFinished, object: nil) + ILOG("Migration complete of \(counter) save states.") + } } #if DEBUG diff --git a/Provenance/Emulator/PVEmulatorViewController+Saves.swift b/Provenance/Emulator/PVEmulatorViewController+Saves.swift index 987abd7351..a236a16c5f 100644 --- a/Provenance/Emulator/PVEmulatorViewController+Saves.swift +++ b/Provenance/Emulator/PVEmulatorViewController+Saves.swift @@ -54,7 +54,7 @@ extension PVEmulatorViewController { autosaveTimer = Timer.scheduledTimer(withTimeInterval: interval, repeats: true, block: { _ in DispatchQueue.main.async { let image = self.captureScreenshot() - self.createNewSaveState(auto: true, screenshot: image) { result in + self.createNewSaveState(type: .auto, screenshot: image) { result in switch result { case .success: break case let .error(error): @@ -91,11 +91,11 @@ extension PVEmulatorViewController { } let image = captureScreenshot() - createNewSaveState(auto: true, screenshot: image, completion: completion) + createNewSaveState(type: .auto, screenshot: image, completion: completion) } // #error ("Use to https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/iCloud/iCloud.html to save files to iCloud from local url, and setup packages for bundles") - func createNewSaveState(auto: Bool, screenshot: UIImage?, completion: @escaping SaveCompletion) { + func createNewSaveState(type: SaveType, screenshot: UIImage?, completion: @escaping SaveCompletion) { guard core.supportsSaveStates else { WLOG("Core \(core.description) doesn't support save states.") completion(.error(.saveStatesUnsupportedByCore)) @@ -131,7 +131,7 @@ extension PVEmulatorViewController { return } - DLOG("Succeeded saving state, auto: \(auto)") + DLOG("Succeeded saving state, type: \(type)") let realm = try! Realm() guard let core = realm.object(ofType: PVCore.self, forPrimaryKey: self.core.coreIdentifier) else { completion(.error(.noCoreFound(self.core.coreIdentifier))) @@ -142,7 +142,7 @@ extension PVEmulatorViewController { var saveState: PVSaveState! try realm.write { - saveState = PVSaveState(withGame: self.game, core: core, file: saveFile, image: imageFile, isAutosave: auto) + saveState = PVSaveState(withGame: self.game, core: core, file: saveFile, type: type, image: imageFile) realm.add(saveState) } @@ -239,11 +239,11 @@ extension PVEmulatorViewController { } func saveStatesViewControllerCreateNewState(_ saveStatesViewController: PVSaveStatesViewController, completion: @escaping SaveCompletion) { - createNewSaveState(auto: false, screenshot: saveStatesViewController.screenshot, completion: completion) + createNewSaveState(type: .manual, screenshot: saveStatesViewController.screenshot, completion: completion) } func saveStatesViewControllerOverwriteState(_ saveStatesViewController: PVSaveStatesViewController, state: PVSaveState, completion: @escaping SaveCompletion) { - createNewSaveState(auto: false, screenshot: saveStatesViewController.screenshot) { result in + createNewSaveState(type: .manual, screenshot: saveStatesViewController.screenshot) { result in switch result { case .success: do { @@ -321,7 +321,7 @@ extension PVEmulatorViewController { let newURL = saveStatePath.appendingPathComponent("\(game.md5Hash).\(Date().timeIntervalSinceReferenceDate)") try fileManager.moveItem(at: autoSaveURL, to: newURL) let saveFile = PVFile(withURL: newURL) - let newState = PVSaveState(withGame: game, core: core, file: saveFile, image: nil, isAutosave: true) + let newState = PVSaveState(withGame: game, core: core, file: saveFile, type: .auto, image: nil) try realm.write { realm.add(newState) } @@ -341,12 +341,12 @@ extension PVEmulatorViewController { let newURL = saveStatePath.appendingPathComponent("\(game.md5Hash).\(Date().timeIntervalSinceReferenceDate)") try fileManager.moveItem(at: url, to: newURL) let saveFile = PVFile(withURL: newURL) - let newState = PVSaveState(withGame: game, core: core, file: saveFile, image: nil, isAutosave: false) + let newState = PVSaveState(withGame: game, core: core, file: saveFile, type: .manual, image: nil) try realm.write { realm.add(newState) } } catch { - presentError("Unable to convert autosave to new format: \(error.localizedDescription)") + presentError("Unable to convert manual save state to new format: \(error.localizedDescription)") } } } diff --git a/Provenance/Emulator/PVEmulatorViewController~iOS.swift b/Provenance/Emulator/PVEmulatorViewController~iOS.swift index 0d9bbe457b..859c21cad5 100644 --- a/Provenance/Emulator/PVEmulatorViewController~iOS.swift +++ b/Provenance/Emulator/PVEmulatorViewController~iOS.swift @@ -452,7 +452,7 @@ extension PVEmulatorViewController { if let latestManualSaveState = game.saveStates.sorted(byKeyPath: "date", ascending: true).last { - createNewSaveState(auto: false, screenshot: image) { result in + createNewSaveState(type: .quick, screenshot: image) { result in switch result { case .success: do { diff --git a/Provenance/Game Library/UI/CollectionViewInACell/RealmCollectinViewCell.swift b/Provenance/Game Library/UI/CollectionViewInACell/RealmCollectinViewCell.swift index 7f7769e31f..6f28226845 100644 --- a/Provenance/Game Library/UI/CollectionViewInACell/RealmCollectinViewCell.swift +++ b/Provenance/Game Library/UI/CollectionViewInACell/RealmCollectinViewCell.swift @@ -492,16 +492,16 @@ class SaveStatesCollectionCell: RealmCollectinViewCell = SelectionObject.all.filter("game != nil").sorted(by: sortDescriptors) @@ -510,7 +510,7 @@ class SaveStatesCollectionCell: RealmCollectinViewCell Bool { - return !object.isAutosave || object.isNewestAutosave + return object.saveType == .manual || object.isNewestAutosave || object.isNewestQuicksave } @objc override var additionalFilter: Bool { diff --git a/Provenance/Game Library/UI/SaveStates/PVSaveStateInfoViewController.swift b/Provenance/Game Library/UI/SaveStates/PVSaveStateInfoViewController.swift index 4017500a5e..21ab4dd07a 100644 --- a/Provenance/Game Library/UI/SaveStates/PVSaveStateInfoViewController.swift +++ b/Provenance/Game Library/UI/SaveStates/PVSaveStateInfoViewController.swift @@ -21,7 +21,7 @@ final class PVSaveStateInfoViewController: UIViewController, GameLaunchingViewCo @IBOutlet var coreVersionLabel: UILabel! @IBOutlet var createdLabel: UILabel! @IBOutlet var lastPlayedLabel: UILabel! - @IBOutlet var autosaveLabel: UILabel! + @IBOutlet var saveTypeLabel: UILabel! @IBOutlet var playBarButtonItem: UIBarButtonItem! @@ -77,6 +77,7 @@ final class PVSaveStateInfoViewController: UIViewController, GameLaunchingViewCo coreVersionLabel.text = "" createdLabel.text = "" lastPlayedLabel.text = "" + saveTypeLabel.text = "" return } @@ -105,7 +106,7 @@ final class PVSaveStateInfoViewController: UIViewController, GameLaunchingViewCo lastPlayedLabel.text = "Never" } - autosaveLabel.text = saveState.isAutosave ? "Yes" : "No" + saveTypeLabel.text = String(describing: saveState.saveType).capitalized } @IBAction func playButtonTapped(_ sender: Any) { diff --git a/Provenance/Game Library/UI/SaveStates/PVSaveStatesViewController.swift b/Provenance/Game Library/UI/SaveStates/PVSaveStatesViewController.swift index 4ad99f914f..4a07b7c30e 100644 --- a/Provenance/Game Library/UI/SaveStates/PVSaveStatesViewController.swift +++ b/Provenance/Game Library/UI/SaveStates/PVSaveStatesViewController.swift @@ -30,6 +30,7 @@ struct SaveSection { final class PVSaveStatesViewController: UICollectionViewController { private var autoSaveStatesObserverToken: NotificationToken! + private var quickSaveStatesObserverToken: NotificationToken! private var manualSaveStatesObserverToken: NotificationToken! weak var delegate: PVSaveStatesViewControllerDelegate? @@ -40,12 +41,14 @@ final class PVSaveStatesViewController: UICollectionViewController { var coreID: String? private var autoSaves: Results! + private var quickSaves: Results! private var manualSaves: Results! deinit { autoSaveStatesObserverToken.invalidate() autoSaveStatesObserverToken = nil - manualSaveStatesObserverToken.invalidate() + quickSaveStatesObserverToken?.invalidate() + quickSaveStatesObserverToken = nil manualSaveStatesObserverToken = nil } @@ -69,8 +72,9 @@ final class PVSaveStatesViewController: UICollectionViewController { allSaves = saveStates.sorted(byKeyPath: "date", ascending: false) } - autoSaves = allSaves.filter("isAutosave == true") - manualSaves = allSaves.filter("isAutosave == false") + manualSaves = allSaves.filter("saveTypeValue == \(SaveType.manual.rawValue)") + autoSaves = allSaves.filter("saveTypeValue == \(SaveType.auto.rawValue)") + quickSaves = allSaves.filter("saveTypeValue == \(SaveType.quick.rawValue)") if screenshot == nil { navigationItem.rightBarButtonItem = nil @@ -107,52 +111,42 @@ final class PVSaveStatesViewController: UICollectionViewController { // autoSaveStatesObserverToken = autoSaves.observe { [weak self] (changes: RealmCollectionChange) in guard let `self` = self else { return } - switch changes { - case .initial: - self.collectionView?.reloadData() - case .update(_, let deletions, _, _): - guard !deletions.isEmpty else { - return - } - - let fromItem = { (item: Int) -> IndexPath in - let section = 0 - return IndexPath(item: item, section: section) - } - self.collectionView?.performBatchUpdates({ - self.collectionView?.deleteItems(at: deletions.map(fromItem)) - }, completion: nil) - case let .error(error): - ELOG("Error updating save states: " + error.localizedDescription) - } + self.handleRealmCollectionChange(changes: changes, collectionView: self.collectionView, section: 0) + } + + quickSaveStatesObserverToken = quickSaves.observe { [weak self] (changes: RealmCollectionChange) in + guard let `self` = self else { return } + self.handleRealmCollectionChange(changes: changes, collectionView: self.collectionView, section: 1) } manualSaveStatesObserverToken = manualSaves.observe { [weak self] (changes: RealmCollectionChange) in guard let `self` = self else { return } - - switch changes { - case .initial: - self.collectionView?.reloadData() - case .update(_, let deletions, let insertions, _): - guard !deletions.isEmpty || !insertions.isEmpty else { - return - } - let fromItem = { (item: Int) -> IndexPath in - let section = 1 - return IndexPath(item: item, section: section) - } - self.collectionView?.performBatchUpdates({ - self.collectionView?.deleteItems(at: deletions.map(fromItem)) - self.collectionView?.insertItems(at: insertions.map(fromItem)) - }, completion: nil) - case let .error(error): - ELOG("Error updating save states: " + error.localizedDescription) - } + self.handleRealmCollectionChange(changes: changes, collectionView: self.collectionView, section: 2) } - - let longPressRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(longPressRecognized(_:))) + + let longPressRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(self.longPressRecognized(_:))) collectionView?.addGestureRecognizer(longPressRecognizer) } + + func handleRealmCollectionChange(changes: RealmCollectionChange>, collectionView: UICollectionView, section: Int) { + switch changes { + case .initial: + self.collectionView?.reloadData() + case .update(_, let deletions, let insertions, _): + guard deletions.count > 0 || insertions.count > 0 else { + return + } + let fromItem = { (item: Int) -> IndexPath in + return IndexPath(item: item, section: section) + } + self.collectionView?.performBatchUpdates({ + self.collectionView?.deleteItems(at: deletions.map(fromItem)) + self.collectionView?.insertItems(at: insertions.map(fromItem)) + }, completion: nil) + case .error(let error): + ELOG("Error updating save states: " + error.localizedDescription) + } + } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) @@ -185,6 +179,8 @@ final class PVSaveStatesViewController: UICollectionViewController { case 0: state = autoSaves[indexPath.item] case 1: + state = quickSaves[indexPath.item] + case 2: state = manualSaves[indexPath.item] default: break @@ -253,7 +249,7 @@ final class PVSaveStatesViewController: UICollectionViewController { } override func numberOfSections(in _: UICollectionView) -> Int { - return 2 + return 3 } override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { @@ -262,6 +258,8 @@ final class PVSaveStatesViewController: UICollectionViewController { case 0: reusableView.label.text = "Auto Save" case 1: + reusableView.label.text = "Quick Save" + case 2: reusableView.label.text = "Save States" default: break @@ -275,6 +273,8 @@ final class PVSaveStatesViewController: UICollectionViewController { case 0: return autoSaves.count case 1: + return quickSaves.count + case 2: return manualSaves.count default: return 0 @@ -288,6 +288,8 @@ final class PVSaveStatesViewController: UICollectionViewController { case 0: saveState = autoSaves[indexPath.item] case 1: + saveState = quickSaves[indexPath.item] + case 2: saveState = manualSaves[indexPath.item] default: break @@ -304,6 +306,9 @@ final class PVSaveStatesViewController: UICollectionViewController { let saveState = autoSaves[indexPath.item] delegate?.saveStatesViewController(self, load: saveState) case 1: + let saveState = quickSaves[indexPath.item] + delegate?.saveStatesViewController(self, load: saveState) + case 2: var saveState: PVSaveState? saveState = manualSaves[indexPath.item] guard let state = saveState else { diff --git a/Provenance/User Interface/SaveStates.storyboard b/Provenance/User Interface/SaveStates.storyboard index fba9882bd6..156b5bb3c2 100644 --- a/Provenance/User Interface/SaveStates.storyboard +++ b/Provenance/User Interface/SaveStates.storyboard @@ -1,7 +1,11 @@ - + + + + - + + @@ -11,7 +15,7 @@ - + @@ -180,7 +184,7 @@ -