Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Quicksave #1070

Open
wants to merge 12 commits into
base: develop
Choose a base branch
from
2 changes: 1 addition & 1 deletion Carthage/Checkouts/SteamController
Submodule SteamController updated 72 files
+1 −0 .gitignore
+12 −0 .jazzy.yaml
+2 −2 README.md
+11 −2 SteamController.podspec
+6 −0 SteamController.xcodeproj/project.pbxproj
+169 −5 SteamController/SteamController.h
+166 −86 SteamController/SteamController.m
+5 −0 SteamController/SteamControllerExtendedGamepad.h
+6 −0 SteamController/SteamControllerExtendedGamepad.m
+0 −25 SteamController/SteamControllerInput.h
+29 −3 SteamController/SteamControllerManager.h
+76 −0 SteamController/SteamControllerManager.m
+2 −0 SteamControllerTestApp/AppDelegate.m
+511 −0 SteamControllerTestApp/Base.lproj/Main.storyboard
+1 −0 SteamControllerTestApp/ControllerTableViewCell.h
+30 −0 SteamControllerTestApp/ControllerTableViewCell.m
+26 −0 SteamControllerTestApp/DetailViewController.h
+96 −0 SteamControllerTestApp/DetailViewController.m
+14 −7 SteamControllerTestApp/TableViewController.m
+164 −0 docs/Categories.html
+211 −0 docs/Categories/GCExtendedGamepad(SteamController).html
+230 −0 docs/Classes.html
+562 −0 docs/Classes/SteamController.html
+298 −0 docs/Classes/SteamControllerManager.html
+260 −0 docs/Enums.html
+909 −0 docs/Enums/SteamControllerButton.html
+277 −0 docs/Enums/SteamControllerMapping.html
+226 −0 docs/Enums/SteamControllerMode.html
+166 −0 docs/Functions.html
+167 −0 docs/Type Definitions.html
+28 −0 docs/badge.svg
+200 −0 docs/css/highlight.css
+372 −0 docs/css/jazzy.css
+20 −0 docs/docsets/SteamController.docset/Contents/Info.plist
+164 −0 docs/docsets/SteamController.docset/Contents/Resources/Documents/Categories.html
+211 −0 ...sets/SteamController.docset/Contents/Resources/Documents/Categories/GCExtendedGamepad(SteamController).html
+230 −0 docs/docsets/SteamController.docset/Contents/Resources/Documents/Classes.html
+562 −0 docs/docsets/SteamController.docset/Contents/Resources/Documents/Classes/SteamController.html
+298 −0 docs/docsets/SteamController.docset/Contents/Resources/Documents/Classes/SteamControllerManager.html
+260 −0 docs/docsets/SteamController.docset/Contents/Resources/Documents/Enums.html
+909 −0 docs/docsets/SteamController.docset/Contents/Resources/Documents/Enums/SteamControllerButton.html
+277 −0 docs/docsets/SteamController.docset/Contents/Resources/Documents/Enums/SteamControllerMapping.html
+226 −0 docs/docsets/SteamController.docset/Contents/Resources/Documents/Enums/SteamControllerMode.html
+166 −0 docs/docsets/SteamController.docset/Contents/Resources/Documents/Functions.html
+167 −0 docs/docsets/SteamController.docset/Contents/Resources/Documents/Type Definitions.html
+200 −0 docs/docsets/SteamController.docset/Contents/Resources/Documents/css/highlight.css
+372 −0 docs/docsets/SteamController.docset/Contents/Resources/Documents/css/jazzy.css
+ docs/docsets/SteamController.docset/Contents/Resources/Documents/img/carat.png
+ docs/docsets/SteamController.docset/Contents/Resources/Documents/img/dash.png
+ docs/docsets/SteamController.docset/Contents/Resources/Documents/img/gh.png
+ docs/docsets/SteamController.docset/Contents/Resources/Documents/img/spinner.gif
+219 −0 docs/docsets/SteamController.docset/Contents/Resources/Documents/index.html
+43 −0 docs/docsets/SteamController.docset/Contents/Resources/Documents/js/jazzy.js
+63 −0 docs/docsets/SteamController.docset/Contents/Resources/Documents/js/jazzy.search.js
+4 −0 docs/docsets/SteamController.docset/Contents/Resources/Documents/js/jquery.min.js
+6 −0 docs/docsets/SteamController.docset/Contents/Resources/Documents/js/lunr.min.js
+1,538 −0 docs/docsets/SteamController.docset/Contents/Resources/Documents/js/typeahead.jquery.js
+1 −0 docs/docsets/SteamController.docset/Contents/Resources/Documents/search.json
+ docs/docsets/SteamController.docset/Contents/Resources/docSet.dsidx
+ docs/docsets/SteamController.tgz
+ docs/img/carat.png
+ docs/img/dash.png
+ docs/img/gh.png
+ docs/img/spinner.gif
+219 −0 docs/index.html
+43 −0 docs/js/jazzy.js
+63 −0 docs/js/jazzy.search.js
+4 −0 docs/js/jquery.min.js
+6 −0 docs/js/lunr.min.js
+1,538 −0 docs/js/typeahead.jquery.js
+1 −0 docs/search.json
+6 −0 docs/undocumented.json
4 changes: 2 additions & 2 deletions PVLibrary/PVLibrary/Domain/SaveState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
}
10 changes: 9 additions & 1 deletion PVLibrary/PVLibrary/RealmPlatform/Entities/PVGame.swift
Original file line number Diff line number Diff line change
Expand Up @@ -127,12 +127,20 @@ extension PVGame: Filed, LocalFileProvider {}

public extension PVGame {
var autoSaves: Results<PVSaveState> {
return saveStates.filter("isAutosave == true").sorted(byKeyPath: "date", ascending: false)
return saveStates.filter("saveTypeRawValue == '\(SaveType.auto.rawValue)'").sorted(byKeyPath: "date", ascending: false)
}

public var quickSaves : Results<PVSaveState> {
return saveStates.filter("saveTypeRawValue == '\(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 {
Expand Down
41 changes: 35 additions & 6 deletions PVLibrary/PVLibrary/RealmPlatform/Entities/PVSaveState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ import Foundation
import PVSupport
import RealmSwift

public enum SaveType: String, Codable {
case manual
case auto
case quick
}

public protocol Filed {
associatedtype LocalFileProviderType: LocalFileProvider
var file: LocalFileProviderType! { get }
Expand All @@ -29,16 +35,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 saveTypeRawValue: String = SaveType.manual.rawValue
public dynamic var saveType: SaveType {
get { return SaveType(rawValue: saveTypeRawValue)! }
set { saveTypeRawValue = 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
}
Expand All @@ -47,12 +59,18 @@ public final class PVSaveState: Object, Filed, LocalFileProvider {
do {
// Temp store these URLs
let fileURL = state.file.url
let jsonFileURL = fileURL.appendingPathExtension("json")
let imageURl = state.image?.url

let database = RomDatabase.sharedInstance
try database.delete(state)

try FileManager.default.removeItem(at: fileURL)

if (FileManager.default.fileExists(atPath: jsonFileURL.absoluteString)) {
try FileManager.default.removeItem(at: jsonFileURL)
}

if let imageURl = imageURl {
try FileManager.default.removeItem(at: imageURl)
}
Expand All @@ -63,13 +81,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
Expand All @@ -96,7 +124,8 @@ private extension SaveState {
} else {
image = nil
}
isAutosave = saveState.isAutosave

saveType = saveState.saveType
}
}

Expand Down Expand Up @@ -135,7 +164,7 @@ extension SaveState: RealmRepresentable {
DLOG("path: \(imagePath)")
object.image = PVImageFile(withURL: imagePath)
}
object.isAutosave = isAutosave
object.saveType = saveType
}
}
}
25 changes: 22 additions & 3 deletions PVLibrary/PVLibrary/RealmPlatform/RomDatabase.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -73,9 +73,12 @@ 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
Expand Down Expand Up @@ -114,9 +117,25 @@ public final class RealmConfiguration {
newObject!["importDate"] = Date()
}

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!["saveTypeRawValue"] = isAutosave ? SaveType.auto.rawValue : SaveType.manual.rawValue
}

ILOG("Migration complete of \(counter) save states.")
}

if oldSchemaVersion < schemaVersion {
NotificationCenter.default.post(name: NSNotification.Name.DatabaseMigrationFinished, object: nil)
}
}

#if DEBUG
Expand Down
1 change: 1 addition & 0 deletions PVSupport/Settings/PVSettingsModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
39 changes: 25 additions & 14 deletions Provenance/Emulator/PVEmulatorViewController+Saves.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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)))
Expand All @@ -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)
}

Expand All @@ -160,13 +160,22 @@ extension PVEmulatorViewController {
}

do {
// Delete the oldest auto-saves over 5 count
try realm.write {
if (type == .auto) {
// Delete the oldest auto-saves over 5 count
let autoSaves = self.game.autoSaves
if autoSaves.count > 5 {
autoSaves.suffix(from: 5).forEach {
try autoSaves.suffix(from: 5).reversed().forEach {
DLOG("Deleting old auto save of \($0.game.title) dated: \($0.date.description)")
realm.delete($0)
try PVSaveState.delete($0)
}
}
} else if (type == .quick) {
// Delete the oldest quicksaves over 5 count
let quickSaves = self.game.quickSaves
if quickSaves.count > 5 {
try quickSaves.suffix(from: 5).reversed().forEach {
DLOG("Deleting old quicksave of \($0.game.title) dated: \($0.date.description)")
try PVSaveState.delete($0)
}
}
}
Expand Down Expand Up @@ -198,6 +207,8 @@ extension PVEmulatorViewController {
state.lastOpened = Date()
}

self.core.setPauseEmulation(true)

self.core.loadStateFromFile(atPath: state.file.url.path) { success, error in
let completion = {
self.core.setPauseEmulation(false)
Expand Down Expand Up @@ -239,11 +250,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 {
Expand Down Expand Up @@ -321,7 +332,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)
}
Expand All @@ -341,12 +352,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)")
}
}
}
Expand Down