Adds support for handling ActionInputs from GameControllers
• Quick Save • Quick Load • Fast Forward
This commit is contained in:
parent
c6875c44b6
commit
61440ef994
@ -1 +1 @@
|
||||
Subproject commit 5d0d2d40b7f7797b3bb270241c7d6a0c16abe4bc
|
||||
Subproject commit ff014bab5c42e5c0a5fcbacb5c3c737ef33564e7
|
||||
@ -52,7 +52,6 @@
|
||||
BF59426B1E09BBD00051894B /* GridCollectionViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF5942691E09BBD00051894B /* GridCollectionViewLayout.swift */; };
|
||||
BF59426F1E09BC5D0051894B /* DatabaseManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF59426D1E09BC5D0051894B /* DatabaseManager.swift */; };
|
||||
BF5942701E09BC5D0051894B /* GamesDatabase.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF59426E1E09BC5D0051894B /* GamesDatabase.swift */; };
|
||||
BF5942731E09BC700051894B /* Delta.xcdatamodel in Sources */ = {isa = PBXBuildFile; fileRef = BF5942721E09BC700051894B /* Delta.xcdatamodel */; };
|
||||
BF59427C1E09BC830051894B /* Cheat.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF5942771E09BC830051894B /* Cheat.swift */; };
|
||||
BF59427D1E09BC830051894B /* ControllerSkin.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF5942781E09BC830051894B /* ControllerSkin.swift */; };
|
||||
BF59427E1E09BC830051894B /* Game.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF5942791E09BC830051894B /* Game.swift */; };
|
||||
@ -104,11 +103,14 @@
|
||||
BFC9B7391CEFCD34008629BB /* CheatsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC9B7381CEFCD34008629BB /* CheatsViewController.swift */; };
|
||||
BFCEA67E1D56FF640061A534 /* UIViewControllerContextTransitioning+Conveniences.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFCEA67D1D56FF640061A534 /* UIViewControllerContextTransitioning+Conveniences.swift */; };
|
||||
BFD097211D3A01B8005A44C2 /* SaveStatesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3540041C5DA70400C1184C /* SaveStatesViewController.swift */; };
|
||||
BFDC7B9F1F7DE0CF0052A7C5 /* Delta.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = BFDC7B9E1F7DE0CF0052A7C5 /* Delta.xcmappingmodel */; };
|
||||
BFDD04F11D5E2C27002D450E /* GameCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDD04F01D5E2C27002D450E /* GameCollectionViewController.swift */; };
|
||||
BFE022A01F5B57FF0052D888 /* PopoverMenuButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFE0229F1F5B577D0052D888 /* PopoverMenuButton.swift */; };
|
||||
BFE4269E1D9C68E600DC913F /* SaveStatesStoryboardSegue.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFE4269D1D9C68E600DC913F /* SaveStatesStoryboardSegue.swift */; };
|
||||
BFEC732D1AAECC4A00650035 /* Roxas.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BFEC732C1AAECC4A00650035 /* Roxas.framework */; };
|
||||
BFEC732E1AAECC4A00650035 /* Roxas.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BFEC732C1AAECC4A00650035 /* Roxas.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
BFEF24EF1F7DD3F100454C62 /* Delta.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = BFEF24EC1F7DD3F100454C62 /* Delta.xcdatamodeld */; };
|
||||
BFEF24F31F7DD4FD00454C62 /* SaveStateMigrationPolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFEF24F21F7DD4FB00454C62 /* SaveStateMigrationPolicy.swift */; };
|
||||
BFF0742C1E9DC17500ACDF4A /* GBCDeltaCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BFF0742B1E9DC17500ACDF4A /* GBCDeltaCore.framework */; };
|
||||
BFF0742D1E9DC17500ACDF4A /* GBCDeltaCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BFF0742B1E9DC17500ACDF4A /* GBCDeltaCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
BFF6452E1F7CC5060056533E /* GameControllerInputMappingTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6B82A41F7CC2A300042BFB /* GameControllerInputMappingTransformer.swift */; };
|
||||
@ -180,7 +182,6 @@
|
||||
BF5942691E09BBD00051894B /* GridCollectionViewLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GridCollectionViewLayout.swift; path = "Components/Collection View/GridCollectionViewLayout.swift"; sourceTree = "<group>"; };
|
||||
BF59426D1E09BC5D0051894B /* DatabaseManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DatabaseManager.swift; path = Database/DatabaseManager.swift; sourceTree = "<group>"; };
|
||||
BF59426E1E09BC5D0051894B /* GamesDatabase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GamesDatabase.swift; path = Database/OpenVGDB/GamesDatabase.swift; sourceTree = "<group>"; };
|
||||
BF5942721E09BC700051894B /* Delta.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; name = Delta.xcdatamodel; path = Database/Model/Delta.xcdatamodel; sourceTree = "<group>"; };
|
||||
BF5942771E09BC830051894B /* Cheat.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Cheat.swift; path = Database/Model/Human/Cheat.swift; sourceTree = "<group>"; };
|
||||
BF5942781E09BC830051894B /* ControllerSkin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ControllerSkin.swift; path = Database/Model/Human/ControllerSkin.swift; sourceTree = "<group>"; };
|
||||
BF5942791E09BC830051894B /* Game.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Game.swift; path = Database/Model/Human/Game.swift; sourceTree = "<group>"; };
|
||||
@ -232,10 +233,14 @@
|
||||
BFC6F7B71F435BC500221B96 /* Input+Display.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Input+Display.swift"; sourceTree = "<group>"; };
|
||||
BFC9B7381CEFCD34008629BB /* CheatsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CheatsViewController.swift; path = "Pause Menu/Cheats/CheatsViewController.swift"; sourceTree = "<group>"; };
|
||||
BFCEA67D1D56FF640061A534 /* UIViewControllerContextTransitioning+Conveniences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewControllerContextTransitioning+Conveniences.swift"; sourceTree = "<group>"; };
|
||||
BFDC7B9E1F7DE0CF0052A7C5 /* Delta.xcmappingmodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcmappingmodel; name = Delta.xcmappingmodel; path = Database/Model/Migrations/Delta.xcmappingmodel; sourceTree = "<group>"; };
|
||||
BFDD04F01D5E2C27002D450E /* GameCollectionViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameCollectionViewController.swift; sourceTree = "<group>"; };
|
||||
BFE0229F1F5B577D0052D888 /* PopoverMenuButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PopoverMenuButton.swift; path = "Components/Popover Menu/PopoverMenuButton.swift"; sourceTree = "<group>"; };
|
||||
BFE4269D1D9C68E600DC913F /* SaveStatesStoryboardSegue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SaveStatesStoryboardSegue.swift; path = Segues/SaveStatesStoryboardSegue.swift; sourceTree = "<group>"; };
|
||||
BFEC732C1AAECC4A00650035 /* Roxas.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Roxas.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
BFEF24ED1F7DD3F100454C62 /* Delta 2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Delta 2.xcdatamodel"; sourceTree = "<group>"; };
|
||||
BFEF24EE1F7DD3F100454C62 /* Delta.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Delta.xcdatamodel; sourceTree = "<group>"; };
|
||||
BFEF24F21F7DD4FB00454C62 /* SaveStateMigrationPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SaveStateMigrationPolicy.swift; path = Database/Model/Migrations/Policies/SaveStateMigrationPolicy.swift; sourceTree = "<group>"; };
|
||||
BFF0742B1E9DC17500ACDF4A /* GBCDeltaCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = GBCDeltaCore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
BFF93A9F1E0FB036005EC865 /* InputStreamOutputWriter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = InputStreamOutputWriter.swift; path = Database/InputStreamOutputWriter.swift; sourceTree = "<group>"; };
|
||||
BFFA4C081E8A24D600D87934 /* GameMetadataTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GameMetadataTableViewCell.swift; path = Database/OpenVGDB/GameMetadataTableViewCell.swift; sourceTree = "<group>"; };
|
||||
@ -381,10 +386,11 @@
|
||||
BF5942711E09BC690051894B /* Model */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BF5942721E09BC700051894B /* Delta.xcdatamodel */,
|
||||
BFEF24EC1F7DD3F100454C62 /* Delta.xcdatamodeld */,
|
||||
BF5942741E09BC740051894B /* Human */,
|
||||
BF5942751E09BC780051894B /* Machine */,
|
||||
BF6B82A31F7CC29A00042BFB /* Transformers */,
|
||||
BFEF24F01F7DD4B600454C62 /* Migrations */,
|
||||
BF5942761E09BC7C0051894B /* Misc */,
|
||||
);
|
||||
name = Model;
|
||||
@ -565,6 +571,23 @@
|
||||
path = Resources;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BFEF24F01F7DD4B600454C62 /* Migrations */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BFDC7B9E1F7DE0CF0052A7C5 /* Delta.xcmappingmodel */,
|
||||
BFEF24F11F7DD4BE00454C62 /* Policies */,
|
||||
);
|
||||
name = Migrations;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BFEF24F11F7DD4BE00454C62 /* Policies */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BFEF24F21F7DD4FB00454C62 /* SaveStateMigrationPolicy.swift */,
|
||||
);
|
||||
name = Policies;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BFFA71CE1AAC406100EE9DD1 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -844,7 +867,6 @@
|
||||
BF6BF3131EB7E47F008E83CD /* ImportOption.swift in Sources */,
|
||||
BF31878B1D489AAA00BD020D /* CheatValidator.swift in Sources */,
|
||||
BFFC46201D59823500AF2CC6 /* InitialGamesStoryboardSegue.swift in Sources */,
|
||||
BF5942731E09BC700051894B /* Delta.xcdatamodel in Sources */,
|
||||
BF15AF841F54B43B009B6AAB /* ActionInput.swift in Sources */,
|
||||
BF5942951E09BD1A0051894B /* NSManagedObjectContext+Conveniences.swift in Sources */,
|
||||
BF353FF91C5D870B00C1184C /* MenuItem.swift in Sources */,
|
||||
@ -854,6 +876,7 @@
|
||||
BFFA71DD1AAC406100EE9DD1 /* AppDelegate.swift in Sources */,
|
||||
BF59426B1E09BBD00051894B /* GridCollectionViewLayout.swift in Sources */,
|
||||
BF6424851F5CBDC900D6AB44 /* UIView+ParentViewController.swift in Sources */,
|
||||
BFDC7B9F1F7DE0CF0052A7C5 /* Delta.xcmappingmodel in Sources */,
|
||||
BF04E6FF1DB8625C000F35D3 /* ControllerSkinsViewController.swift in Sources */,
|
||||
BF5942891E09BC8B0051894B /* _GameCollection.swift in Sources */,
|
||||
BF34FA111CF1899D006624C7 /* CheatTextView.swift in Sources */,
|
||||
@ -878,6 +901,7 @@
|
||||
BF80E1D21F13117000847008 /* ControllerInputsViewController.swift in Sources */,
|
||||
BF5942641E09BBB10051894B /* LoadControllerSkinImageOperation.swift in Sources */,
|
||||
BF6EE5E91F7C5F860051AD6C /* _GameControllerInputMapping.swift in Sources */,
|
||||
BFEF24EF1F7DD3F100454C62 /* Delta.xcdatamodeld in Sources */,
|
||||
BF5942871E09BC8B0051894B /* _ControllerSkin.swift in Sources */,
|
||||
BF95E2791E4982A10030E7AD /* GamesDatabaseBrowserViewController.swift in Sources */,
|
||||
BFD097211D3A01B8005A44C2 /* SaveStatesViewController.swift in Sources */,
|
||||
@ -895,6 +919,7 @@
|
||||
BF5942861E09BC8B0051894B /* _Cheat.swift in Sources */,
|
||||
BF5E7F441B9A650B00AE44F8 /* SettingsViewController.swift in Sources */,
|
||||
BF8CA9361F5F651900499FDD /* PopoverMenuController.swift in Sources */,
|
||||
BFEF24F31F7DD4FD00454C62 /* SaveStateMigrationPolicy.swift in Sources */,
|
||||
BF5942931E09BD1A0051894B /* NSFetchedResultsController+Conveniences.m in Sources */,
|
||||
BF6BF3271EB87EB8008E83CD /* PhotoLibraryImportOption.swift in Sources */,
|
||||
BF5942661E09BBB10051894B /* LoadImageURLOperation.swift in Sources */,
|
||||
@ -1129,6 +1154,21 @@
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCVersionGroup section */
|
||||
BFEF24EC1F7DD3F100454C62 /* Delta.xcdatamodeld */ = {
|
||||
isa = XCVersionGroup;
|
||||
children = (
|
||||
BFEF24ED1F7DD3F100454C62 /* Delta 2.xcdatamodel */,
|
||||
BFEF24EE1F7DD3F100454C62 /* Delta.xcdatamodel */,
|
||||
);
|
||||
currentVersion = BFEF24ED1F7DD3F100454C62 /* Delta 2.xcdatamodel */;
|
||||
name = Delta.xcdatamodeld;
|
||||
path = Database/Model/Delta.xcdatamodeld;
|
||||
sourceTree = "<group>";
|
||||
versionGroupType = wrapper.xcdatamodel;
|
||||
};
|
||||
/* End XCVersionGroup section */
|
||||
};
|
||||
rootObject = BFFA71CF1AAC406100EE9DD1 /* Project object */;
|
||||
}
|
||||
|
||||
@ -65,11 +65,10 @@ final class DatabaseManager: NSPersistentContainer
|
||||
private init()
|
||||
{
|
||||
guard
|
||||
let modelURL = Bundle(for: DatabaseManager.self).url(forResource: "Delta", withExtension: "mom"),
|
||||
let modelURL = Bundle(for: DatabaseManager.self).url(forResource: "Delta", withExtension: "momd"),
|
||||
let managedObjectModel = NSManagedObjectModel(contentsOf: modelURL)
|
||||
else { fatalError("Core Data model cannot be found. Aborting.") }
|
||||
|
||||
|
||||
super.init(name: "Delta", managedObjectModel: managedObjectModel)
|
||||
|
||||
self.viewContext.automaticallyMergesChangesFromParent = true
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>_XCCurrentVersionName</key>
|
||||
<string>Delta 2.xcdatamodel</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@ -0,0 +1,138 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="13240" systemVersion="16G29" minimumToolsVersion="Xcode 7.0" sourceLanguage="Objective-C" userDefinedModelVersionIdentifier="1.0">
|
||||
<entity name="Cheat" representedClassName="Cheat" syncable="YES">
|
||||
<attribute name="code" attributeType="String" syncable="YES"/>
|
||||
<attribute name="creationDate" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="identifier" attributeType="String" syncable="YES"/>
|
||||
<attribute name="isEnabled" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="modifiedDate" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="name" optional="YES" attributeType="String" syncable="YES"/>
|
||||
<attribute name="type" attributeType="Transformable" syncable="YES">
|
||||
<userInfo>
|
||||
<entry key="attributeValueClassName" value="CheatType"/>
|
||||
</userInfo>
|
||||
</attribute>
|
||||
<relationship name="game" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Game" inverseName="cheats" inverseEntity="Game" syncable="YES"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="identifier"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="ControllerSkin" representedClassName="ControllerSkin" syncable="YES">
|
||||
<attribute name="filename" attributeType="String" syncable="YES"/>
|
||||
<attribute name="gameType" attributeType="Transformable" syncable="YES">
|
||||
<userInfo>
|
||||
<entry key="attributeValueClassName" value="GameType"/>
|
||||
</userInfo>
|
||||
</attribute>
|
||||
<attribute name="identifier" attributeType="String" syncable="YES"/>
|
||||
<attribute name="isStandard" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
|
||||
<attribute name="name" attributeType="String" syncable="YES"/>
|
||||
<attribute name="supportedConfigurations" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES" syncable="YES">
|
||||
<userInfo>
|
||||
<entry key="attributeValueScalarType" value="ControllerSkinConfigurations"/>
|
||||
</userInfo>
|
||||
</attribute>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="identifier"/>
|
||||
<constraint value="gameType"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="Game" representedClassName="Game" syncable="YES">
|
||||
<attribute name="artworkURL" optional="YES" attributeType="Transformable" syncable="YES">
|
||||
<userInfo>
|
||||
<entry key="attributeValueClassName" value="URL"/>
|
||||
</userInfo>
|
||||
</attribute>
|
||||
<attribute name="filename" attributeType="String" syncable="YES">
|
||||
<userInfo>
|
||||
<entry key="attributeValueClassName" value="NSURL"/>
|
||||
</userInfo>
|
||||
</attribute>
|
||||
<attribute name="identifier" attributeType="String" syncable="YES"/>
|
||||
<attribute name="name" attributeType="String" syncable="YES"/>
|
||||
<attribute name="type" attributeType="Transformable" syncable="YES">
|
||||
<userInfo>
|
||||
<entry key="attributeValueClassName" value="GameType"/>
|
||||
</userInfo>
|
||||
</attribute>
|
||||
<relationship name="cheats" toMany="YES" deletionRule="Cascade" destinationEntity="Cheat" inverseName="game" inverseEntity="Cheat" syncable="YES"/>
|
||||
<relationship name="gameCollections" toMany="YES" deletionRule="Nullify" destinationEntity="GameCollection" inverseName="games" inverseEntity="GameCollection" syncable="YES"/>
|
||||
<relationship name="previewSaveState" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="SaveState" inverseName="previewGame" inverseEntity="SaveState" syncable="YES"/>
|
||||
<relationship name="saveStates" toMany="YES" deletionRule="Cascade" destinationEntity="SaveState" inverseName="game" inverseEntity="SaveState" syncable="YES"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="identifier"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="GameCollection" representedClassName="GameCollection" syncable="YES">
|
||||
<attribute name="identifier" attributeType="String" syncable="YES"/>
|
||||
<attribute name="index" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
|
||||
<relationship name="games" toMany="YES" deletionRule="Nullify" destinationEntity="Game" inverseName="gameCollections" inverseEntity="Game" syncable="YES"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="identifier"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="GameControllerInputMapping" representedClassName="GameControllerInputMapping" syncable="YES">
|
||||
<attribute name="deltaCoreInputMapping" attributeType="Transformable" valueTransformerName="GameControllerInputMappingTransformer" syncable="YES">
|
||||
<userInfo>
|
||||
<entry key="attributeValueClassName" value="Any"/>
|
||||
</userInfo>
|
||||
</attribute>
|
||||
<attribute name="gameControllerInputType" attributeType="Transformable" valueTransformerName="" syncable="YES">
|
||||
<userInfo>
|
||||
<entry key="attributeValueClassName" value="GameControllerInputType"/>
|
||||
</userInfo>
|
||||
</attribute>
|
||||
<attribute name="gameType" attributeType="Transformable" syncable="YES">
|
||||
<userInfo>
|
||||
<entry key="attributeValueClassName" value="GameType"/>
|
||||
</userInfo>
|
||||
</attribute>
|
||||
<attribute name="playerIndex" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="gameControllerInputType"/>
|
||||
<constraint value="gameType"/>
|
||||
<constraint value="playerIndex"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="SaveState" representedClassName="SaveState" versionHashModifier="quick" syncable="YES">
|
||||
<attribute name="creationDate" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="filename" attributeType="String" syncable="YES">
|
||||
<userInfo>
|
||||
<entry key="attributeValueClassName" value="NSURL"/>
|
||||
</userInfo>
|
||||
</attribute>
|
||||
<attribute name="identifier" attributeType="String" syncable="YES"/>
|
||||
<attribute name="modifiedDate" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="name" optional="YES" attributeType="String" syncable="YES"/>
|
||||
<attribute name="type" attributeType="Integer 16" defaultValueString="1" usesScalarValueType="NO" syncable="YES">
|
||||
<userInfo>
|
||||
<entry key="attributeValueScalarType" value="SaveStateType"/>
|
||||
</userInfo>
|
||||
</attribute>
|
||||
<relationship name="game" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Game" inverseName="saveStates" inverseEntity="Game" syncable="YES"/>
|
||||
<relationship name="previewGame" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Game" inverseName="previewSaveState" inverseEntity="Game" syncable="YES"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="identifier"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<elements>
|
||||
<element name="Cheat" positionX="-198" positionY="-63" width="128" height="165"/>
|
||||
<element name="ControllerSkin" positionX="-387" positionY="90" width="128" height="135"/>
|
||||
<element name="Game" positionX="-378" positionY="-54" width="128" height="180"/>
|
||||
<element name="GameCollection" positionX="-585" positionY="-27" width="128" height="90"/>
|
||||
<element name="GameControllerInputMapping" positionX="-387" positionY="90" width="128" height="105"/>
|
||||
<element name="SaveState" positionX="-198" positionY="113" width="128" height="165"/>
|
||||
</elements>
|
||||
</model>
|
||||
@ -13,6 +13,7 @@ import DeltaCore
|
||||
@objc public enum SaveStateType: Int16
|
||||
{
|
||||
case auto
|
||||
case quick
|
||||
case general
|
||||
case locked
|
||||
}
|
||||
@ -78,4 +79,14 @@ public class SaveState: _SaveState, SaveStateProtocol
|
||||
print(error)
|
||||
}
|
||||
}
|
||||
|
||||
class func fetchRequest(for game: Game, type: SaveStateType) -> NSFetchRequest<SaveState>
|
||||
{
|
||||
let predicate = NSPredicate(format: "%K == %@ AND %K == %d", #keyPath(SaveState.game), game, #keyPath(SaveState.type), type.rawValue)
|
||||
|
||||
let fetchRequest: NSFetchRequest<SaveState> = SaveState.fetchRequest()
|
||||
fetchRequest.predicate = predicate
|
||||
|
||||
return fetchRequest
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -0,0 +1,25 @@
|
||||
//
|
||||
// SaveStateMigrationPolicy.swift
|
||||
// Delta
|
||||
//
|
||||
// Created by Riley Testut on 9/28/17.
|
||||
// Copyright © 2017 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
@objc(SaveStateToSaveStateMigrationPolicy)
|
||||
class SaveStateToSaveStateMigrationPolicy: NSEntityMigrationPolicy
|
||||
{
|
||||
@objc(migrateSaveStateType:)
|
||||
func migrateSaveStateType(_ rawValue: NSNumber) -> NSNumber
|
||||
{
|
||||
switch rawValue.intValue
|
||||
{
|
||||
case 0: return NSNumber(value: SaveStateType.auto.rawValue)
|
||||
case 1: return NSNumber(value: SaveStateType.general.rawValue)
|
||||
case 2: return NSNumber(value: SaveStateType.locked.rawValue)
|
||||
default: return rawValue
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -15,8 +15,8 @@ public extension GameControllerInputType
|
||||
|
||||
enum ActionInput: String
|
||||
{
|
||||
case saveState
|
||||
case loadState
|
||||
case quickSave
|
||||
case quickLoad
|
||||
case fastForward
|
||||
}
|
||||
|
||||
|
||||
@ -139,13 +139,41 @@ class GameViewController: DeltaCore.GameViewController
|
||||
{
|
||||
super.gameController(gameController, didActivate: input)
|
||||
|
||||
guard self.isSelectingSustainedButtons else { return }
|
||||
|
||||
guard let pausingGameController = self.pausingGameController, gameController == pausingGameController else { return }
|
||||
|
||||
if input != StandardGameControllerInput.menu
|
||||
if self.isSelectingSustainedButtons
|
||||
{
|
||||
gameController.sustain(input)
|
||||
guard let pausingGameController = self.pausingGameController, gameController == pausingGameController else { return }
|
||||
|
||||
if input != StandardGameControllerInput.menu
|
||||
{
|
||||
gameController.sustain(input)
|
||||
}
|
||||
}
|
||||
else if self.emulatorCore?.state == .running
|
||||
{
|
||||
guard let actionInput = ActionInput(input: input) else { return }
|
||||
|
||||
switch actionInput
|
||||
{
|
||||
case .quickSave: self.performQuickSaveAction()
|
||||
case .quickLoad: self.performQuickLoadAction()
|
||||
case .fastForward: self.performFastForwardAction(activate: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func gameController(_ gameController: GameController, didDeactivate input: Input)
|
||||
{
|
||||
super.gameController(gameController, didDeactivate: input)
|
||||
|
||||
guard !self.isSelectingSustainedButtons else { return }
|
||||
|
||||
guard let actionInput = ActionInput(input: input) else { return }
|
||||
|
||||
switch actionInput
|
||||
{
|
||||
case .quickSave: break
|
||||
case .quickLoad: break
|
||||
case .fastForward: self.performFastForwardAction(activate: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -251,8 +279,7 @@ extension GameViewController
|
||||
|
||||
pauseViewController.fastForwardItem?.isSelected = (self.emulatorCore?.rate != self.emulatorCore?.deltaCore.supportedRates.lowerBound)
|
||||
pauseViewController.fastForwardItem?.action = { [unowned self] item in
|
||||
guard let emulatorCore = self.emulatorCore else { return }
|
||||
emulatorCore.rate = item.isSelected ? emulatorCore.deltaCore.supportedRates.upperBound : emulatorCore.deltaCore.supportedRates.lowerBound
|
||||
self.performFastForwardAction(activate: item.isSelected)
|
||||
}
|
||||
|
||||
pauseViewController.sustainButtonsItem?.isSelected = gameController.sustainedInputs.count > 0
|
||||
@ -441,42 +468,37 @@ extension GameViewController: SaveStatesViewControllerDelegate
|
||||
|
||||
let game = backgroundContext.object(with: game.objectID) as! Game
|
||||
|
||||
let predicate = NSPredicate(format: "%K == %d AND %K == %@", #keyPath(SaveState.type), SaveStateType.auto.rawValue, #keyPath(SaveState.game), game)
|
||||
|
||||
let fetchRequest: NSFetchRequest<SaveState> = SaveState.fetchRequest()
|
||||
fetchRequest.predicate = predicate
|
||||
let fetchRequest = SaveState.fetchRequest(for: game, type: .auto)
|
||||
fetchRequest.sortDescriptors = [NSSortDescriptor(key: #keyPath(SaveState.creationDate), ascending: true)]
|
||||
|
||||
var saveStates: [SaveState]? = nil
|
||||
|
||||
do
|
||||
{
|
||||
saveStates = try fetchRequest.execute()
|
||||
let saveStates = try fetchRequest.execute()
|
||||
|
||||
if let saveState = saveStates.first, saveStates.count >= 2
|
||||
{
|
||||
// If there are two or more auto save states, update the oldest one
|
||||
self.update(saveState, with: self.pausedSaveState)
|
||||
|
||||
// Tiny hack: SaveStatesViewController sorts save states by creation date, so we update the creation date too
|
||||
// Simpler than deleting old save states ¯\_(ツ)_/¯
|
||||
saveState.creationDate = saveState.modifiedDate
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise, create a new one
|
||||
let saveState = SaveState.insertIntoManagedObjectContext(backgroundContext)
|
||||
saveState.type = .auto
|
||||
saveState.game = game
|
||||
|
||||
self.update(saveState, with: self.pausedSaveState)
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
print(error)
|
||||
}
|
||||
|
||||
if let saveStates = saveStates, let saveState = saveStates.first, saveStates.count >= 2
|
||||
{
|
||||
// If there are two or more auto save states, update the oldest one
|
||||
self.update(saveState, with: self.pausedSaveState)
|
||||
|
||||
// Tiny hack; SaveStatesViewController sorts save states by creation date, so we update the creation date too
|
||||
// Simpler than deleting old save states ¯\_(ツ)_/¯
|
||||
saveState.creationDate = saveState.modifiedDate
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise, create a new one
|
||||
let saveState = SaveState.insertIntoManagedObjectContext(backgroundContext)
|
||||
saveState.type = .auto
|
||||
saveState.game = game
|
||||
|
||||
self.update(saveState, with: self.pausedSaveState)
|
||||
}
|
||||
|
||||
|
||||
backgroundContext.saveWithErrorLogging()
|
||||
}
|
||||
}
|
||||
@ -535,25 +557,14 @@ extension GameViewController: SaveStatesViewControllerDelegate
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - SaveStatesViewControllerDelegate
|
||||
|
||||
func saveStatesViewController(_ saveStatesViewController: SaveStatesViewController, updateSaveState saveState: SaveState)
|
||||
fileprivate func load(_ saveState: SaveStateProtocol)
|
||||
{
|
||||
let updatingExistingSaveState = FileManager.default.fileExists(atPath: saveState.fileURL.path)
|
||||
let isRunning = (self.emulatorCore?.state == .running)
|
||||
|
||||
self.update(saveState)
|
||||
|
||||
// Dismiss if updating an existing save state.
|
||||
// If creating a new one, don't dismiss.
|
||||
if updatingExistingSaveState
|
||||
if isRunning
|
||||
{
|
||||
self.pauseViewController?.dismiss()
|
||||
self.pauseEmulation()
|
||||
}
|
||||
}
|
||||
|
||||
func saveStatesViewController(_ saveStatesViewController: SaveStatesViewController, loadSaveState saveState: SaveStateProtocol)
|
||||
{
|
||||
self._isLoadingSaveState = true
|
||||
|
||||
// If we're loading the auto save state, we need to create a temporary copy of saveState.
|
||||
// Then, we update the auto save state, but load our copy so everything works out.
|
||||
@ -597,6 +608,34 @@ extension GameViewController: SaveStatesViewControllerDelegate
|
||||
print(error)
|
||||
}
|
||||
|
||||
if isRunning
|
||||
{
|
||||
self.resumeEmulation()
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - SaveStatesViewControllerDelegate
|
||||
|
||||
func saveStatesViewController(_ saveStatesViewController: SaveStatesViewController, updateSaveState saveState: SaveState)
|
||||
{
|
||||
let updatingExistingSaveState = FileManager.default.fileExists(atPath: saveState.fileURL.path)
|
||||
|
||||
self.update(saveState)
|
||||
|
||||
// Dismiss if updating an existing save state.
|
||||
// If creating a new one, don't dismiss.
|
||||
if updatingExistingSaveState
|
||||
{
|
||||
self.pauseViewController?.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
func saveStatesViewController(_ saveStatesViewController: SaveStatesViewController, loadSaveState saveState: SaveStateProtocol)
|
||||
{
|
||||
self._isLoadingSaveState = true
|
||||
|
||||
self.load(saveState)
|
||||
|
||||
self.pauseViewController?.dismiss()
|
||||
}
|
||||
}
|
||||
@ -666,6 +705,78 @@ private extension GameViewController
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - Action Inputs -
|
||||
/// Action Inputs
|
||||
extension GameViewController
|
||||
{
|
||||
func performQuickSaveAction()
|
||||
{
|
||||
guard let game = self.game as? Game else { return }
|
||||
|
||||
let backgroundContext = DatabaseManager.shared.newBackgroundContext()
|
||||
backgroundContext.performAndWait {
|
||||
|
||||
let game = backgroundContext.object(with: game.objectID) as! Game
|
||||
let fetchRequest = SaveState.fetchRequest(for: game, type: .quick)
|
||||
|
||||
do
|
||||
{
|
||||
if let quickSaveState = try fetchRequest.execute().first
|
||||
{
|
||||
self.update(quickSaveState)
|
||||
}
|
||||
else
|
||||
{
|
||||
let saveState = SaveState(context: backgroundContext)
|
||||
saveState.type = .quick
|
||||
saveState.game = game
|
||||
|
||||
self.update(saveState)
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
print(error)
|
||||
}
|
||||
|
||||
backgroundContext.saveWithErrorLogging()
|
||||
}
|
||||
}
|
||||
|
||||
func performQuickLoadAction()
|
||||
{
|
||||
guard let game = self.game as? Game else { return }
|
||||
|
||||
let fetchRequest = SaveState.fetchRequest(for: game, type: .quick)
|
||||
|
||||
do
|
||||
{
|
||||
if let quickSaveState = try fetchRequest.execute().first
|
||||
{
|
||||
self.load(quickSaveState)
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
print(error)
|
||||
}
|
||||
}
|
||||
|
||||
func performFastForwardAction(activate: Bool)
|
||||
{
|
||||
guard let emulatorCore = self.emulatorCore else { return }
|
||||
|
||||
if activate
|
||||
{
|
||||
emulatorCore.rate = emulatorCore.deltaCore.supportedRates.upperBound
|
||||
}
|
||||
else
|
||||
{
|
||||
emulatorCore.rate = emulatorCore.deltaCore.supportedRates.lowerBound
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - GameViewControllerDelegate -
|
||||
/// GameViewControllerDelegate
|
||||
extension GameViewController: GameViewControllerDelegate
|
||||
|
||||
@ -29,6 +29,7 @@ extension SaveStatesViewController
|
||||
enum Section: Int
|
||||
{
|
||||
case auto
|
||||
case quick
|
||||
case general
|
||||
case locked
|
||||
}
|
||||
@ -268,6 +269,7 @@ private extension SaveStatesViewController
|
||||
switch section
|
||||
{
|
||||
case .auto: title = NSLocalizedString("Auto Save", comment: "")
|
||||
case .quick: title = NSLocalizedString("Quick Save", comment: "")
|
||||
case .general: title = NSLocalizedString("General", comment: "")
|
||||
case .locked: title = NSLocalizedString("Locked", comment: "")
|
||||
}
|
||||
@ -310,6 +312,7 @@ private extension SaveStatesViewController
|
||||
let game = backgroundContext.object(with: self.game.objectID) as! Game
|
||||
|
||||
saveState = SaveState.insertIntoManagedObjectContext(backgroundContext)
|
||||
saveState.type = .general
|
||||
saveState.game = game
|
||||
}
|
||||
|
||||
@ -480,6 +483,7 @@ private extension SaveStatesViewController
|
||||
switch saveState.type
|
||||
{
|
||||
case .auto: break
|
||||
case .quick: break
|
||||
case .general:
|
||||
let lockAction = Action(title: NSLocalizedString("Lock", comment: ""), style: .default, action: { [unowned self] action in
|
||||
self.lockSaveState(saveState)
|
||||
@ -643,11 +647,11 @@ extension SaveStatesViewController
|
||||
{
|
||||
case .saving:
|
||||
|
||||
let section = self.correctedSectionForSectionIndex((indexPath as NSIndexPath).section)
|
||||
let section = self.correctedSectionForSectionIndex(indexPath.section)
|
||||
switch section
|
||||
{
|
||||
case .auto: break
|
||||
case .general:
|
||||
case .quick, .general:
|
||||
let backgroundContext = DatabaseManager.shared.newBackgroundContext()
|
||||
backgroundContext.performAndWait() {
|
||||
let temporarySaveState = backgroundContext.object(with: saveState.objectID) as! SaveState
|
||||
|
||||
@ -31,7 +31,7 @@ class ControllerInputsViewController: UIViewController
|
||||
fileprivate lazy var managedObjectContext: NSManagedObjectContext = DatabaseManager.shared.newBackgroundContext()
|
||||
fileprivate var inputMappings = [System: GameControllerInputMapping]()
|
||||
|
||||
fileprivate let supportedActionInputs: [ActionInput] = [.saveState, .loadState, .fastForward]
|
||||
fileprivate let supportedActionInputs: [ActionInput] = [.quickSave, .quickLoad, .fastForward]
|
||||
|
||||
fileprivate var gameViewController: DeltaCore.GameViewController!
|
||||
fileprivate var actionsMenuViewController: GridMenuViewController!
|
||||
@ -188,13 +188,13 @@ private extension ControllerInputsViewController
|
||||
|
||||
switch input
|
||||
{
|
||||
case .saveState:
|
||||
case .quickSave:
|
||||
image = #imageLiteral(resourceName: "SaveSaveState")
|
||||
text = NSLocalizedString("Save State", comment: "")
|
||||
text = NSLocalizedString("Quick Save", comment: "")
|
||||
|
||||
case .loadState:
|
||||
case .quickLoad:
|
||||
image = #imageLiteral(resourceName: "LoadSaveState")
|
||||
text = NSLocalizedString("Load State", comment: "")
|
||||
text = NSLocalizedString("Quick Load", comment: "")
|
||||
|
||||
case .fastForward:
|
||||
image = #imageLiteral(resourceName: "FastForward")
|
||||
|
||||
Loading…
Reference in New Issue
Block a user