Adds basic implementation of GameViewController, replacement for EmulationViewController
This commit is contained in:
parent
e827faadad
commit
60bc9dcfbc
@ -1 +1 @@
|
|||||||
Subproject commit 9a587be47fc89154d7917267e8d423e8a5af0951
|
Subproject commit d819614e5fe422aa4975f70e3d4f1d2dc97b9c24
|
||||||
@ -27,7 +27,6 @@
|
|||||||
BF353FFF1C5DA3C500C1184C /* PausePresentationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF353FFD1C5DA3C500C1184C /* PausePresentationController.swift */; };
|
BF353FFF1C5DA3C500C1184C /* PausePresentationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF353FFD1C5DA3C500C1184C /* PausePresentationController.swift */; };
|
||||||
BF3540001C5DA3C500C1184C /* PausePresentationControllerContentView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BF353FFE1C5DA3C500C1184C /* PausePresentationControllerContentView.xib */; };
|
BF3540001C5DA3C500C1184C /* PausePresentationControllerContentView.xib in Resources */ = {isa = PBXBuildFile; fileRef = BF353FFE1C5DA3C500C1184C /* PausePresentationControllerContentView.xib */; };
|
||||||
BF3540021C5DA3D500C1184C /* PauseStoryboardSegue.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3540011C5DA3D500C1184C /* PauseStoryboardSegue.swift */; };
|
BF3540021C5DA3D500C1184C /* PauseStoryboardSegue.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3540011C5DA3D500C1184C /* PauseStoryboardSegue.swift */; };
|
||||||
BF3540051C5DA70400C1184C /* SaveStatesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3540041C5DA70400C1184C /* SaveStatesViewController.swift */; };
|
|
||||||
BF3540081C5DAFAD00C1184C /* PauseTransitionCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3540071C5DAFAD00C1184C /* PauseTransitionCoordinator.swift */; };
|
BF3540081C5DAFAD00C1184C /* PauseTransitionCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3540071C5DAFAD00C1184C /* PauseTransitionCoordinator.swift */; };
|
||||||
BF4566E81BC090B6007BFA1A /* Model.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = BF4566E61BC090B6007BFA1A /* Model.xcdatamodeld */; };
|
BF4566E81BC090B6007BFA1A /* Model.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = BF4566E61BC090B6007BFA1A /* Model.xcdatamodeld */; };
|
||||||
BF5E7F441B9A650B00AE44F8 /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF5E7F431B9A650B00AE44F8 /* SettingsViewController.swift */; };
|
BF5E7F441B9A650B00AE44F8 /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF5E7F431B9A650B00AE44F8 /* SettingsViewController.swift */; };
|
||||||
@ -46,12 +45,14 @@
|
|||||||
BF99C6951D0A9AA600BA92BC /* SNESDeltaCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BFC134E01AAD82460087AD7B /* SNESDeltaCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
BF99C6951D0A9AA600BA92BC /* SNESDeltaCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BFC134E01AAD82460087AD7B /* SNESDeltaCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||||
BF9F4FCF1AAD7B87004C9500 /* DeltaCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF9F4FCE1AAD7B87004C9500 /* DeltaCore.framework */; };
|
BF9F4FCF1AAD7B87004C9500 /* DeltaCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF9F4FCE1AAD7B87004C9500 /* DeltaCore.framework */; };
|
||||||
BF9F4FD01AAD7B87004C9500 /* DeltaCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BF9F4FCE1AAD7B87004C9500 /* DeltaCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
BF9F4FD01AAD7B87004C9500 /* DeltaCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BF9F4FCE1AAD7B87004C9500 /* DeltaCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||||
|
BFA0D1271D3AE1F600565894 /* GameViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF63BDE91D389EEB00FCB040 /* GameViewController.swift */; };
|
||||||
BFA2315C1CED10BE0011E35A /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA2315B1CED10BE0011E35A /* Action.swift */; };
|
BFA2315C1CED10BE0011E35A /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA2315B1CED10BE0011E35A /* Action.swift */; };
|
||||||
BFAA1FED1B8AA4FA00495943 /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFAA1FEC1B8AA4FA00495943 /* Settings.swift */; };
|
BFAA1FED1B8AA4FA00495943 /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFAA1FEC1B8AA4FA00495943 /* Settings.swift */; };
|
||||||
BFAA1FF41B8AD7F900495943 /* ControllersSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFAA1FF31B8AD7F900495943 /* ControllersSettingsViewController.swift */; };
|
BFAA1FF41B8AD7F900495943 /* ControllersSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFAA1FF31B8AD7F900495943 /* ControllersSettingsViewController.swift */; };
|
||||||
BFB141181BE46934004FBF46 /* GameCollectionViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFB141171BE46934004FBF46 /* GameCollectionViewDataSource.swift */; };
|
BFB141181BE46934004FBF46 /* GameCollectionViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFB141171BE46934004FBF46 /* GameCollectionViewDataSource.swift */; };
|
||||||
BFC2731A1BE6152200D22B05 /* GameCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC273171BE6152200D22B05 /* GameCollection.swift */; };
|
BFC2731A1BE6152200D22B05 /* GameCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC273171BE6152200D22B05 /* GameCollection.swift */; };
|
||||||
BFC9B7391CEFCD34008629BB /* CheatsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC9B7381CEFCD34008629BB /* CheatsViewController.swift */; };
|
BFC9B7391CEFCD34008629BB /* CheatsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC9B7381CEFCD34008629BB /* CheatsViewController.swift */; };
|
||||||
|
BFD097211D3A01B8005A44C2 /* SaveStatesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3540041C5DA70400C1184C /* SaveStatesViewController.swift */; };
|
||||||
BFDB28451BC9DA7B001D0C83 /* GamePickerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDB28441BC9DA7B001D0C83 /* GamePickerController.swift */; };
|
BFDB28451BC9DA7B001D0C83 /* GamePickerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDB28441BC9DA7B001D0C83 /* GamePickerController.swift */; };
|
||||||
BFDE393C1BC0CEDF003F72E8 /* Game.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDE39391BC0CEDF003F72E8 /* Game.swift */; };
|
BFDE393C1BC0CEDF003F72E8 /* Game.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDE39391BC0CEDF003F72E8 /* Game.swift */; };
|
||||||
BFE704F51CEA426E0058BAC8 /* Pods.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 22506DA00971C4300AF90A35 /* Pods.framework */; };
|
BFE704F51CEA426E0058BAC8 /* Pods.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 22506DA00971C4300AF90A35 /* Pods.framework */; };
|
||||||
@ -61,7 +62,6 @@
|
|||||||
BFFA71DD1AAC406100EE9DD1 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFFA71DC1AAC406100EE9DD1 /* AppDelegate.swift */; };
|
BFFA71DD1AAC406100EE9DD1 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFFA71DC1AAC406100EE9DD1 /* AppDelegate.swift */; };
|
||||||
BFFA71E21AAC406100EE9DD1 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BFFA71E01AAC406100EE9DD1 /* Main.storyboard */; };
|
BFFA71E21AAC406100EE9DD1 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BFFA71E01AAC406100EE9DD1 /* Main.storyboard */; };
|
||||||
BFFA71E71AAC406100EE9DD1 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = BFFA71E51AAC406100EE9DD1 /* LaunchScreen.xib */; };
|
BFFA71E71AAC406100EE9DD1 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = BFFA71E51AAC406100EE9DD1 /* LaunchScreen.xib */; };
|
||||||
BFFB709F1AF99B1700DE56FE /* EmulationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFFB709E1AF99B1700DE56FE /* EmulationViewController.swift */; };
|
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXCopyFilesBuildPhase section */
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
@ -115,6 +115,7 @@
|
|||||||
BF4566E71BC090B6007BFA1A /* Model.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Model.xcdatamodel; sourceTree = "<group>"; };
|
BF4566E71BC090B6007BFA1A /* Model.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Model.xcdatamodel; sourceTree = "<group>"; };
|
||||||
BF5E7F431B9A650B00AE44F8 /* SettingsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = "<group>"; };
|
BF5E7F431B9A650B00AE44F8 /* SettingsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = "<group>"; };
|
||||||
BF5E7F451B9A652600AE44F8 /* Settings.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Settings.storyboard; sourceTree = "<group>"; };
|
BF5E7F451B9A652600AE44F8 /* Settings.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Settings.storyboard; sourceTree = "<group>"; };
|
||||||
|
BF63BDE91D389EEB00FCB040 /* GameViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameViewController.swift; sourceTree = "<group>"; };
|
||||||
BF65E8621CEE5C6A00CD3247 /* Cheat.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Cheat.swift; sourceTree = "<group>"; };
|
BF65E8621CEE5C6A00CD3247 /* Cheat.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Cheat.swift; sourceTree = "<group>"; };
|
||||||
BF6BB2451BB73FE800CCF94A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
BF6BB2451BB73FE800CCF94A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
BF70798B1B6B464B0019077C /* ZipZap.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = ZipZap.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
BF70798B1B6B464B0019077C /* ZipZap.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = ZipZap.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
@ -142,7 +143,6 @@
|
|||||||
BFFA71DC1AAC406100EE9DD1 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
BFFA71DC1AAC406100EE9DD1 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
BFFA71E11AAC406100EE9DD1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
BFFA71E11AAC406100EE9DD1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||||
BFFA71E61AAC406100EE9DD1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = "<group>"; };
|
BFFA71E61AAC406100EE9DD1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = "<group>"; };
|
||||||
BFFB709E1AF99B1700DE56FE /* EmulationViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmulationViewController.swift; sourceTree = "<group>"; };
|
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@ -399,7 +399,7 @@
|
|||||||
BFFB709D1AF99ACA00DE56FE /* Emulation */ = {
|
BFFB709D1AF99ACA00DE56FE /* Emulation */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
BFFB709E1AF99B1700DE56FE /* EmulationViewController.swift */,
|
BF63BDE91D389EEB00FCB040 /* GameViewController.swift */,
|
||||||
);
|
);
|
||||||
path = Emulation;
|
path = Emulation;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -541,9 +541,9 @@
|
|||||||
BFAA1FED1B8AA4FA00495943 /* Settings.swift in Sources */,
|
BFAA1FED1B8AA4FA00495943 /* Settings.swift in Sources */,
|
||||||
BF7AE8241C2E984300B1B5BC /* GridCollectionViewLayout.swift in Sources */,
|
BF7AE8241C2E984300B1B5BC /* GridCollectionViewLayout.swift in Sources */,
|
||||||
BF1FB1861C5EE643007E2494 /* SaveState.swift in Sources */,
|
BF1FB1861C5EE643007E2494 /* SaveState.swift in Sources */,
|
||||||
|
BFA0D1271D3AE1F600565894 /* GameViewController.swift in Sources */,
|
||||||
BFB141181BE46934004FBF46 /* GameCollectionViewDataSource.swift in Sources */,
|
BFB141181BE46934004FBF46 /* GameCollectionViewDataSource.swift in Sources */,
|
||||||
BFA2315C1CED10BE0011E35A /* Action.swift in Sources */,
|
BFA2315C1CED10BE0011E35A /* Action.swift in Sources */,
|
||||||
BFFB709F1AF99B1700DE56FE /* EmulationViewController.swift in Sources */,
|
|
||||||
BFAA1FF41B8AD7F900495943 /* ControllersSettingsViewController.swift in Sources */,
|
BFAA1FF41B8AD7F900495943 /* ControllersSettingsViewController.swift in Sources */,
|
||||||
BF353FF91C5D870B00C1184C /* PauseItem.swift in Sources */,
|
BF353FF91C5D870B00C1184C /* PauseItem.swift in Sources */,
|
||||||
BF27CC971BCC890700A20D89 /* GamesCollectionViewController.swift in Sources */,
|
BF27CC971BCC890700A20D89 /* GamesCollectionViewController.swift in Sources */,
|
||||||
@ -565,6 +565,7 @@
|
|||||||
BF762E9E1BC19D31002C8866 /* DatabaseManager.swift in Sources */,
|
BF762E9E1BC19D31002C8866 /* DatabaseManager.swift in Sources */,
|
||||||
BF090CF41B490D8300DCAB45 /* UIDevice+Vibration.m in Sources */,
|
BF090CF41B490D8300DCAB45 /* UIDevice+Vibration.m in Sources */,
|
||||||
BF797A2D1C2D339F00F1A000 /* UILabel+FontSize.swift in Sources */,
|
BF797A2D1C2D339F00F1A000 /* UILabel+FontSize.swift in Sources */,
|
||||||
|
BFD097211D3A01B8005A44C2 /* SaveStatesViewController.swift in Sources */,
|
||||||
BF65E8631CEE5C6A00CD3247 /* Cheat.swift in Sources */,
|
BF65E8631CEE5C6A00CD3247 /* Cheat.swift in Sources */,
|
||||||
BF3540021C5DA3D500C1184C /* PauseStoryboardSegue.swift in Sources */,
|
BF3540021C5DA3D500C1184C /* PauseStoryboardSegue.swift in Sources */,
|
||||||
BF0CDDAD1C8155D200640168 /* LoadImageOperation.swift in Sources */,
|
BF0CDDAD1C8155D200640168 /* LoadImageOperation.swift in Sources */,
|
||||||
@ -574,7 +575,6 @@
|
|||||||
BF353FF21C5D7FB000C1184C /* PauseViewController.swift in Sources */,
|
BF353FF21C5D7FB000C1184C /* PauseViewController.swift in Sources */,
|
||||||
BFDB28451BC9DA7B001D0C83 /* GamePickerController.swift in Sources */,
|
BFDB28451BC9DA7B001D0C83 /* GamePickerController.swift in Sources */,
|
||||||
BF7AE8081C2E858400B1B5BC /* PauseMenuViewController.swift in Sources */,
|
BF7AE8081C2E858400B1B5BC /* PauseMenuViewController.swift in Sources */,
|
||||||
BF3540051C5DA70400C1184C /* SaveStatesViewController.swift in Sources */,
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="wKV-3d-NIY">
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11163.2" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="wKV-3d-NIY">
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
|
<deployment identifier="iOS"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11133"/>
|
||||||
<capability name="Segues with Peek and Pop" minToolsVersion="7.1"/>
|
<capability name="Segues with Peek and Pop" minToolsVersion="7.1"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<scenes>
|
<scenes>
|
||||||
<!--Games-->
|
<!--Games-->
|
||||||
@ -14,17 +16,16 @@
|
|||||||
<viewControllerLayoutGuide type="bottom" id="0om-QB-N5a"/>
|
<viewControllerLayoutGuide type="bottom" id="0om-QB-N5a"/>
|
||||||
</layoutGuides>
|
</layoutGuides>
|
||||||
<view key="view" contentMode="scaleToFill" id="3Bk-k3-7J9">
|
<view key="view" contentMode="scaleToFill" id="3Bk-k3-7J9">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<containerView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="tmn-gd-5UN">
|
<containerView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="tmn-gd-5UN">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
|
||||||
<connections>
|
<connections>
|
||||||
<segue destination="tpK-ou-yEA" kind="embed" id="cjU-nW-cHY"/>
|
<segue destination="tpK-ou-yEA" kind="embed" id="cjU-nW-cHY"/>
|
||||||
</connections>
|
</connections>
|
||||||
</containerView>
|
</containerView>
|
||||||
</subviews>
|
</subviews>
|
||||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="trailing" secondItem="tmn-gd-5UN" secondAttribute="trailing" id="9Rq-HM-vqk"/>
|
<constraint firstAttribute="trailing" secondItem="tmn-gd-5UN" secondAttribute="trailing" id="9Rq-HM-vqk"/>
|
||||||
<constraint firstItem="0om-QB-N5a" firstAttribute="top" secondItem="tmn-gd-5UN" secondAttribute="bottom" id="DV5-hh-1VN"/>
|
<constraint firstItem="0om-QB-N5a" firstAttribute="top" secondItem="tmn-gd-5UN" secondAttribute="bottom" id="DV5-hh-1VN"/>
|
||||||
@ -54,9 +55,9 @@
|
|||||||
<objects>
|
<objects>
|
||||||
<collectionViewController storyboardIdentifier="gamesCollectionViewController" clearsSelectionOnViewWillAppear="NO" id="kqu-75-owz" customClass="GamesCollectionViewController" customModule="Delta" customModuleProvider="target" sceneMemberID="viewController">
|
<collectionViewController storyboardIdentifier="gamesCollectionViewController" clearsSelectionOnViewWillAppear="NO" id="kqu-75-owz" customClass="GamesCollectionViewController" customModule="Delta" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<collectionView key="view" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" id="OIq-Z8-kxO">
|
<collectionView key="view" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" id="OIq-Z8-kxO">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="20" minimumInteritemSpacing="10" id="NKN-dd-bTh" customClass="GridCollectionViewLayout" customModule="Delta" customModuleProvider="target">
|
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="20" minimumInteritemSpacing="10" id="NKN-dd-bTh" customClass="GridCollectionViewLayout" customModule="Delta" customModuleProvider="target">
|
||||||
<size key="itemSize" width="100" height="100"/>
|
<size key="itemSize" width="100" height="100"/>
|
||||||
<size key="headerReferenceSize" width="0.0" height="0.0"/>
|
<size key="headerReferenceSize" width="0.0" height="0.0"/>
|
||||||
@ -65,17 +66,16 @@
|
|||||||
</collectionViewFlowLayout>
|
</collectionViewFlowLayout>
|
||||||
<cells>
|
<cells>
|
||||||
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="GameCell" id="ioT-sh-j8y" customClass="GridCollectionViewCell" customModule="Delta" customModuleProvider="target">
|
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="GameCell" id="ioT-sh-j8y" customClass="GridCollectionViewCell" customModule="Delta" customModuleProvider="target">
|
||||||
<rect key="frame" x="0.0" y="20" width="100" height="100"/>
|
<frame key="frameInset" minY="20" width="100" height="100"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
|
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="100" height="100"/>
|
<rect key="frame" x="0.0" y="0.0" width="100" height="100"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
|
||||||
</view>
|
</view>
|
||||||
<connections>
|
<connections>
|
||||||
<segue destination="hx4-Ze-0Jw" kind="presentation" id="Ila-yL-N8w">
|
<segue destination="yhz-fF-D91" kind="presentation" identifier="presentGameViewController" id="4ws-DY-L4D">
|
||||||
<segue key="commit" inheritsFrom="parent" id="0B7-vE-k6Z"/>
|
<segue key="commit" inheritsFrom="parent" id="sub-e4-fpl"/>
|
||||||
<segue key="preview" destination="hx4-Ze-0Jw" identifier="peekEmulationViewController" id="Jd4-q3-dNr"/>
|
<segue key="preview" inheritsFrom="commit" id="WKY-hV-sn0"/>
|
||||||
</segue>
|
</segue>
|
||||||
</connections>
|
</connections>
|
||||||
</collectionViewCell>
|
</collectionViewCell>
|
||||||
@ -88,130 +88,25 @@
|
|||||||
</collectionViewController>
|
</collectionViewController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="bW1-t8-idm" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="bW1-t8-idm" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="1707" y="1002"/>
|
<point key="canvasLocation" x="1727" y="1002"/>
|
||||||
</scene>
|
</scene>
|
||||||
<!--Emulation View Controller-->
|
<!--Game View Controller-->
|
||||||
<scene sceneID="g58-A4-ib1">
|
<scene sceneID="ASV-Uk-0aP">
|
||||||
<objects>
|
<objects>
|
||||||
<viewController storyboardIdentifier="emulationViewController" id="hx4-Ze-0Jw" customClass="EmulationViewController" customModule="Delta" customModuleProvider="target" sceneMemberID="viewController">
|
<viewController id="yhz-fF-D91" customClass="GameViewController" customModule="Delta" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<layoutGuides>
|
<layoutGuides>
|
||||||
<viewControllerLayoutGuide type="top" id="Fo4-YI-5cN"/>
|
<viewControllerLayoutGuide type="top" id="ItC-Bu-WRI"/>
|
||||||
<viewControllerLayoutGuide type="bottom" id="UIe-oC-tUc"/>
|
<viewControllerLayoutGuide type="bottom" id="g58-HO-6L5"/>
|
||||||
</layoutGuides>
|
</layoutGuides>
|
||||||
<view key="view" contentMode="scaleToFill" id="7ei-Ah-JvQ">
|
<view key="view" contentMode="scaleToFill" id="skW-1S-YD4">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
|
||||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="GfI-Mx-CQT" customClass="GameView" customModule="DeltaCore">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="600" height="300"/>
|
|
||||||
<color key="backgroundColor" red="0.0" green="0.47843137250000001" blue="1" alpha="1" colorSpace="calibratedRGB"/>
|
|
||||||
</view>
|
|
||||||
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="noi-yo-HIE">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="600" height="300"/>
|
|
||||||
<subviews>
|
|
||||||
<visualEffectView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="pwD-5i-uQ2">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="600" height="300"/>
|
|
||||||
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="S8Q-YY-Kog">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="600" height="300"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
|
||||||
<subviews>
|
|
||||||
<visualEffectView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="9Eo-TR-4O4">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="600" height="300"/>
|
|
||||||
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="8ur-lW-yiV">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="600" height="300"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
|
||||||
<subviews>
|
|
||||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="JRb-Vg-JHA" customClass="RSTBackgroundView">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="600" height="300"/>
|
|
||||||
</view>
|
|
||||||
</subviews>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstItem="JRb-Vg-JHA" firstAttribute="top" secondItem="8ur-lW-yiV" secondAttribute="top" id="613-jP-t8K"/>
|
|
||||||
<constraint firstAttribute="trailing" secondItem="JRb-Vg-JHA" secondAttribute="trailing" id="Ele-t2-Kcg"/>
|
|
||||||
<constraint firstAttribute="bottom" secondItem="JRb-Vg-JHA" secondAttribute="bottom" id="aTp-7v-QFY"/>
|
|
||||||
<constraint firstItem="JRb-Vg-JHA" firstAttribute="leading" secondItem="8ur-lW-yiV" secondAttribute="leading" id="c05-Bl-4Q8"/>
|
|
||||||
</constraints>
|
|
||||||
</view>
|
|
||||||
<vibrancyEffect>
|
|
||||||
<blurEffect style="dark"/>
|
|
||||||
</vibrancyEffect>
|
|
||||||
</visualEffectView>
|
|
||||||
</subviews>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstItem="9Eo-TR-4O4" firstAttribute="top" secondItem="S8Q-YY-Kog" secondAttribute="top" id="EgV-fR-NeD"/>
|
|
||||||
<constraint firstAttribute="bottom" secondItem="9Eo-TR-4O4" secondAttribute="bottom" id="TYE-at-xaY"/>
|
|
||||||
<constraint firstItem="9Eo-TR-4O4" firstAttribute="leading" secondItem="S8Q-YY-Kog" secondAttribute="leading" id="YRo-nB-Hsb"/>
|
|
||||||
<constraint firstAttribute="trailing" secondItem="9Eo-TR-4O4" secondAttribute="trailing" id="rWV-bD-7li"/>
|
|
||||||
</constraints>
|
|
||||||
</view>
|
|
||||||
<blurEffect style="dark"/>
|
|
||||||
</visualEffectView>
|
|
||||||
</subviews>
|
|
||||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstItem="pwD-5i-uQ2" firstAttribute="leading" secondItem="noi-yo-HIE" secondAttribute="leading" id="ai3-VH-ZSe"/>
|
|
||||||
<constraint firstAttribute="trailing" secondItem="pwD-5i-uQ2" secondAttribute="trailing" id="fTZ-ah-pgi"/>
|
|
||||||
<constraint firstItem="pwD-5i-uQ2" firstAttribute="top" secondItem="noi-yo-HIE" secondAttribute="top" id="gYf-Oa-jve"/>
|
|
||||||
<constraint firstAttribute="bottom" secondItem="pwD-5i-uQ2" secondAttribute="bottom" id="u3P-PF-gpZ"/>
|
|
||||||
</constraints>
|
|
||||||
</view>
|
|
||||||
<view contentMode="scaleToFill" placeholderIntrinsicWidth="600" placeholderIntrinsicHeight="300" translatesAutoresizingMaskIntoConstraints="NO" id="2W1-IT-Y2l" customClass="ControllerView" customModule="DeltaCore">
|
|
||||||
<rect key="frame" x="0.0" y="300" width="600" height="300"/>
|
|
||||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstAttribute="height" constant="300" id="PEV-zv-Buo"/>
|
|
||||||
</constraints>
|
|
||||||
<variation key="heightClass=compact">
|
|
||||||
<mask key="constraints">
|
|
||||||
<exclude reference="PEV-zv-Buo"/>
|
|
||||||
</mask>
|
|
||||||
</variation>
|
|
||||||
</view>
|
|
||||||
</subviews>
|
|
||||||
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
|
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
<constraints>
|
|
||||||
<constraint firstItem="noi-yo-HIE" firstAttribute="top" secondItem="GfI-Mx-CQT" secondAttribute="top" id="1wY-jJ-o0w"/>
|
|
||||||
<constraint firstItem="2W1-IT-Y2l" firstAttribute="top" secondItem="7ei-Ah-JvQ" secondAttribute="top" id="EF1-5h-FJs"/>
|
|
||||||
<constraint firstAttribute="trailing" secondItem="GfI-Mx-CQT" secondAttribute="trailing" id="EKN-e9-x37"/>
|
|
||||||
<constraint firstItem="noi-yo-HIE" firstAttribute="bottom" secondItem="GfI-Mx-CQT" secondAttribute="bottom" id="IEw-2f-nC4"/>
|
|
||||||
<constraint firstItem="noi-yo-HIE" firstAttribute="leading" secondItem="GfI-Mx-CQT" secondAttribute="leading" id="aE5-xg-A1u"/>
|
|
||||||
<constraint firstItem="GfI-Mx-CQT" firstAttribute="top" secondItem="7ei-Ah-JvQ" secondAttribute="top" id="ala-4P-wQR"/>
|
|
||||||
<constraint firstAttribute="bottom" secondItem="GfI-Mx-CQT" secondAttribute="bottom" id="gFo-XU-4PO">
|
|
||||||
<variation key="heightClass=compact" constant="0.0"/>
|
|
||||||
</constraint>
|
|
||||||
<constraint firstItem="GfI-Mx-CQT" firstAttribute="leading" secondItem="7ei-Ah-JvQ" secondAttribute="leading" id="hck-DY-vlt"/>
|
|
||||||
<constraint firstItem="noi-yo-HIE" firstAttribute="trailing" secondItem="GfI-Mx-CQT" secondAttribute="trailing" id="l7o-xb-Zxa"/>
|
|
||||||
<constraint firstItem="2W1-IT-Y2l" firstAttribute="top" secondItem="GfI-Mx-CQT" secondAttribute="bottom" id="uWb-Cv-NM1"/>
|
|
||||||
<constraint firstAttribute="bottom" secondItem="2W1-IT-Y2l" secondAttribute="bottom" id="vZg-yU-Kqm"/>
|
|
||||||
<constraint firstAttribute="trailing" secondItem="2W1-IT-Y2l" secondAttribute="trailing" id="wcY-Qm-RRC"/>
|
|
||||||
<constraint firstItem="2W1-IT-Y2l" firstAttribute="leading" secondItem="7ei-Ah-JvQ" secondAttribute="leading" id="ygW-0T-DPh"/>
|
|
||||||
</constraints>
|
|
||||||
<variation key="default">
|
|
||||||
<mask key="constraints">
|
|
||||||
<exclude reference="gFo-XU-4PO"/>
|
|
||||||
<exclude reference="EF1-5h-FJs"/>
|
|
||||||
</mask>
|
|
||||||
</variation>
|
|
||||||
<variation key="heightClass=compact">
|
|
||||||
<mask key="constraints">
|
|
||||||
<include reference="gFo-XU-4PO"/>
|
|
||||||
<include reference="EF1-5h-FJs"/>
|
|
||||||
<exclude reference="uWb-Cv-NM1"/>
|
|
||||||
</mask>
|
|
||||||
</variation>
|
|
||||||
</view>
|
</view>
|
||||||
<connections>
|
|
||||||
<outlet property="backgroundView" destination="JRb-Vg-JHA" id="85O-6W-WOe"/>
|
|
||||||
<outlet property="controllerView" destination="2W1-IT-Y2l" id="WCa-LM-fXF"/>
|
|
||||||
<outlet property="controllerViewHeightConstraint" destination="PEV-zv-Buo" id="1jo-Kg-HpO"/>
|
|
||||||
<outlet property="gameView" destination="GfI-Mx-CQT" id="HmP-OA-mci"/>
|
|
||||||
<outlet property="sustainButtonContentView" destination="noi-yo-HIE" id="7FG-2i-LC2"/>
|
|
||||||
<segue destination="Yrw-9v-Pcr" kind="presentation" identifier="pauseSegue" customClass="PauseStoryboardSegue" customModule="Delta" customModuleProvider="target" id="9cz-mr-lTk"/>
|
|
||||||
</connections>
|
|
||||||
</viewController>
|
</viewController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="ZYd-It-2t0" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="gxI-00-NlJ" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="2361" y="1002"/>
|
<point key="canvasLocation" x="2472" y="1002"/>
|
||||||
</scene>
|
</scene>
|
||||||
<!--Page View Controller-->
|
<!--Page View Controller-->
|
||||||
<scene sceneID="35q-Io-64T">
|
<scene sceneID="35q-Io-64T">
|
||||||
@ -258,13 +153,13 @@
|
|||||||
<viewControllerPlaceholder storyboardName="PauseMenu" id="Yrw-9v-Pcr" sceneMemberID="viewController"/>
|
<viewControllerPlaceholder storyboardName="PauseMenu" id="Yrw-9v-Pcr" sceneMemberID="viewController"/>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="BDU-Ql-OgK" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="BDU-Ql-OgK" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="2361" y="1440"/>
|
<point key="canvasLocation" x="2470" y="1338"/>
|
||||||
</scene>
|
</scene>
|
||||||
</scenes>
|
</scenes>
|
||||||
<resources>
|
<resources>
|
||||||
<image name="Settings_Button" width="22" height="22"/>
|
<image name="Settings_Button" width="22" height="22"/>
|
||||||
</resources>
|
</resources>
|
||||||
<inferredMetricsTieBreakers>
|
<inferredMetricsTieBreakers>
|
||||||
<segue reference="Ila-yL-N8w"/>
|
<segue reference="4ws-DY-L4D"/>
|
||||||
</inferredMetricsTieBreakers>
|
</inferredMetricsTieBreakers>
|
||||||
</document>
|
</document>
|
||||||
|
|||||||
@ -1,681 +0,0 @@
|
|||||||
//
|
|
||||||
// EmulationViewController.swift
|
|
||||||
// Delta
|
|
||||||
//
|
|
||||||
// Created by Riley Testut on 5/5/15.
|
|
||||||
// Copyright (c) 2015 Riley Testut. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
import DeltaCore
|
|
||||||
import Roxas
|
|
||||||
|
|
||||||
// Temporary wrapper around dispatch_semaphore_t until Swift 3 + modernized libdispatch
|
|
||||||
private struct DispatchSemaphore: Hashable
|
|
||||||
{
|
|
||||||
let semaphore: Dispatch.DispatchSemaphore
|
|
||||||
|
|
||||||
var hashValue: Int {
|
|
||||||
return semaphore.hash
|
|
||||||
}
|
|
||||||
|
|
||||||
init(value: Int)
|
|
||||||
{
|
|
||||||
self.semaphore = Dispatch.DispatchSemaphore(value: value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func ==(lhs: DispatchSemaphore, rhs: DispatchSemaphore) -> Bool
|
|
||||||
{
|
|
||||||
return lhs.semaphore.isEqual(rhs.semaphore)
|
|
||||||
}
|
|
||||||
|
|
||||||
class EmulationViewController: UIViewController
|
|
||||||
{
|
|
||||||
//MARK: - Properties -
|
|
||||||
/** Properties **/
|
|
||||||
|
|
||||||
/// Should only be set when preparing for segue. Otherwise, should be considered immutable
|
|
||||||
var game: Game! {
|
|
||||||
didSet
|
|
||||||
{
|
|
||||||
guard oldValue != game else { return }
|
|
||||||
|
|
||||||
self.emulatorCore = EmulatorCore(game: game)
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private(set) var emulatorCore: EmulatorCore! {
|
|
||||||
didSet
|
|
||||||
{
|
|
||||||
// Cannot set directly, or else we're left with a strong reference cycle
|
|
||||||
//self.emulatorCore.updateHandler = emulatorCoreDidUpdate
|
|
||||||
|
|
||||||
self.emulatorCore.updateHandler = { [weak self] core in
|
|
||||||
self?.emulatorCoreDidUpdate(core)
|
|
||||||
}
|
|
||||||
|
|
||||||
self.preferredContentSize = self.emulatorCore.preferredRenderingSize
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If non-nil, will override the default preview action items returned in previewActionItems()
|
|
||||||
var overridePreviewActionItems: [UIPreviewActionItem]?
|
|
||||||
|
|
||||||
// Annoying iOS gotcha: if the previewingContext(_:viewControllerForLocation:) callback takes too long, the peek/preview starts, but fails to actually present the view controller
|
|
||||||
// To workaround, we have this closure to defer work for Peeking/Popping until the view controller appears
|
|
||||||
// Hacky, but works
|
|
||||||
var deferredPreparationHandler: ((Void) -> Void)?
|
|
||||||
|
|
||||||
//MARK: - Private Properties
|
|
||||||
private var pauseViewController: PauseViewController?
|
|
||||||
private var pausingGameController: GameController?
|
|
||||||
|
|
||||||
private var context = CIContext(options: [kCIContextWorkingColorSpace: NSNull()])
|
|
||||||
|
|
||||||
private var updateSemaphores = Set<DispatchSemaphore>()
|
|
||||||
|
|
||||||
private var sustainedInputs = [ObjectIdentifier: [Input]]()
|
|
||||||
private var reactivateSustainInputsQueue: OperationQueue
|
|
||||||
private var choosingSustainedButtons = false
|
|
||||||
|
|
||||||
@IBOutlet private var controllerView: ControllerView!
|
|
||||||
@IBOutlet private var gameView: GameView!
|
|
||||||
@IBOutlet private var sustainButtonContentView: UIView!
|
|
||||||
@IBOutlet private var backgroundView: RSTBackgroundView!
|
|
||||||
|
|
||||||
@IBOutlet private var controllerViewHeightConstraint: NSLayoutConstraint!
|
|
||||||
|
|
||||||
|
|
||||||
//MARK: - Initializers -
|
|
||||||
/** Initializers **/
|
|
||||||
required init?(coder aDecoder: NSCoder)
|
|
||||||
{
|
|
||||||
self.reactivateSustainInputsQueue = OperationQueue()
|
|
||||||
self.reactivateSustainInputsQueue.maxConcurrentOperationCount = 1
|
|
||||||
|
|
||||||
super.init(coder: aDecoder)
|
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(EmulationViewController.updateControllers), name: .externalControllerDidConnect, object: nil)
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(EmulationViewController.updateControllers), name: .externalControllerDidDisconnect, object: nil)
|
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(EmulationViewController.willResignActive(_:)), name: NSNotification.Name.UIApplicationWillResignActive, object: UIApplication.shared())
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(EmulationViewController.didBecomeActive(_:)), name: NSNotification.Name.UIApplicationDidBecomeActive, object: UIApplication.shared())
|
|
||||||
}
|
|
||||||
|
|
||||||
deinit
|
|
||||||
{
|
|
||||||
// To ensure the emulation stops when cancelling a peek/preview gesture
|
|
||||||
self.emulatorCore.stop()
|
|
||||||
}
|
|
||||||
|
|
||||||
//MARK: - Overrides
|
|
||||||
/** Overrides **/
|
|
||||||
|
|
||||||
//MARK: - UIViewController
|
|
||||||
/// UIViewController
|
|
||||||
override func viewDidLoad()
|
|
||||||
{
|
|
||||||
super.viewDidLoad()
|
|
||||||
|
|
||||||
// Set this to 0 now and update it in viewDidLayoutSubviews to ensure there are never conflicting constraints
|
|
||||||
// (such as when peeking and popping)
|
|
||||||
self.controllerViewHeightConstraint.constant = 0
|
|
||||||
|
|
||||||
self.gameView.backgroundColor = UIColor.clear()
|
|
||||||
self.emulatorCore.add(self.gameView)
|
|
||||||
|
|
||||||
self.backgroundView.textLabel.text = NSLocalizedString("Select Buttons to Sustain", comment: "")
|
|
||||||
self.backgroundView.detailTextLabel.text = NSLocalizedString("Press the Menu button when finished.", comment: "")
|
|
||||||
|
|
||||||
let controllerSkin = ControllerSkin.standardControllerSkin(for: self.game.type)
|
|
||||||
|
|
||||||
self.controllerView.controllerSkin = controllerSkin
|
|
||||||
|
|
||||||
self.updateControllers()
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewDidAppear(_ animated: Bool)
|
|
||||||
{
|
|
||||||
super.viewDidAppear(animated)
|
|
||||||
|
|
||||||
self.deferredPreparationHandler?()
|
|
||||||
self.deferredPreparationHandler = nil
|
|
||||||
|
|
||||||
// Yes, order DOES matter here, in order to prevent audio from being slightly delayed after peeking with 3D Touch (ugh so tired of that issue)
|
|
||||||
switch self.emulatorCore.state
|
|
||||||
{
|
|
||||||
case .stopped:
|
|
||||||
self.emulatorCore.start()
|
|
||||||
self.updateCheats()
|
|
||||||
|
|
||||||
case .running: break
|
|
||||||
case .paused:
|
|
||||||
self.updateCheats()
|
|
||||||
self.resumeEmulation()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Toggle audioManager.enabled to reset the audio buffer and ensure the audio isn't delayed from the beginning
|
|
||||||
// This is especially noticeable when peeking a game
|
|
||||||
self.emulatorCore.audioManager.enabled = false
|
|
||||||
self.emulatorCore.audioManager.enabled = true
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewDidLayoutSubviews()
|
|
||||||
{
|
|
||||||
super.viewDidLayoutSubviews()
|
|
||||||
|
|
||||||
if Settings.localControllerPlayerIndex != nil && self.controllerView.intrinsicContentSize() != CGSize(width: UIViewNoIntrinsicMetric, height: UIViewNoIntrinsicMetric) && !self.isPreviewing
|
|
||||||
{
|
|
||||||
let scale = self.view.bounds.width / self.controllerView.intrinsicContentSize().width
|
|
||||||
self.controllerViewHeightConstraint.constant = self.controllerView.intrinsicContentSize().height * scale
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self.controllerViewHeightConstraint.constant = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
self.controllerView.isHidden = self.isPreviewing
|
|
||||||
}
|
|
||||||
|
|
||||||
override func prefersStatusBarHidden() -> Bool
|
|
||||||
{
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <UIContentContainer>
|
|
||||||
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator)
|
|
||||||
{
|
|
||||||
super.viewWillTransition(to: size, with: coordinator)
|
|
||||||
|
|
||||||
self.controllerView.beginAnimatingUpdateControllerSkin()
|
|
||||||
|
|
||||||
coordinator.animate(alongsideTransition: { _ in
|
|
||||||
|
|
||||||
if self.emulatorCore.state == .paused
|
|
||||||
{
|
|
||||||
// We need to manually "refresh" the game screen, otherwise the system tries to cache the rendered image, but skews it incorrectly when rotating b/c of UIVisualEffectView
|
|
||||||
self.gameView.inputImage = self.gameView.outputImage
|
|
||||||
}
|
|
||||||
|
|
||||||
}, completion: { _ in
|
|
||||||
self.controllerView.finishAnimatingUpdateControllerSkin()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Navigation -
|
|
||||||
|
|
||||||
// In a storyboard-based application, you will often want to do a little preparation before navigation
|
|
||||||
override func prepare(for segue: UIStoryboardSegue, sender: AnyObject?)
|
|
||||||
{
|
|
||||||
self.pauseEmulation()
|
|
||||||
|
|
||||||
if segue.identifier == "pauseSegue"
|
|
||||||
{
|
|
||||||
guard let gameController = sender as? GameController else { fatalError("sender for pauseSegue must be the game controller that pressed the Menu button") }
|
|
||||||
|
|
||||||
self.pausingGameController = gameController
|
|
||||||
|
|
||||||
let pauseViewController = segue.destinationViewController as! PauseViewController
|
|
||||||
pauseViewController.pauseText = self.game.name
|
|
||||||
|
|
||||||
// Swift has a bug where using unowned references can lead to swift_abortRetainUnowned errors.
|
|
||||||
// Specifically, if you pause a game, open the save states menu, go back, return to menu, select a new game, then try to pause it, it will crash
|
|
||||||
// As a dirty workaround, we just use a weak reference, and force unwrap it if needed
|
|
||||||
|
|
||||||
let saveStateItem = PauseItem(image: UIImage(named: "SaveSaveState")!, text: NSLocalizedString("Save State", comment: ""), action: { [unowned self] _ in
|
|
||||||
pauseViewController.presentSaveStateViewControllerWithMode(.saving, delegate: self)
|
|
||||||
})
|
|
||||||
|
|
||||||
let loadStateItem = PauseItem(image: UIImage(named: "LoadSaveState")!, text: NSLocalizedString("Load State", comment: ""), action: { [unowned self] _ in
|
|
||||||
pauseViewController.presentSaveStateViewControllerWithMode(.loading, delegate: self)
|
|
||||||
})
|
|
||||||
|
|
||||||
let cheatCodesItem = PauseItem(image: UIImage(named: "SmallPause")!, text: NSLocalizedString("Cheat Codes", comment: ""), action: { [unowned self] _ in
|
|
||||||
pauseViewController.presentCheatsViewController(delegate: self)
|
|
||||||
})
|
|
||||||
|
|
||||||
var sustainButtonsItem = PauseItem(image: UIImage(named: "SmallPause")!, text: NSLocalizedString("Sustain Buttons", comment: ""), action: { [unowned self] item in
|
|
||||||
|
|
||||||
self.resetSustainedInputs(forGameController: gameController)
|
|
||||||
|
|
||||||
if item.selected
|
|
||||||
{
|
|
||||||
self.showSustainButtonView()
|
|
||||||
pauseViewController.dismiss()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
sustainButtonsItem.selected = self.sustainedInputs[ObjectIdentifier(gameController)]?.count > 0
|
|
||||||
|
|
||||||
var fastForwardItem = PauseItem(image: UIImage(named: "FastForward")!, text: NSLocalizedString("Fast Forward", comment: ""), action: { [unowned self] item in
|
|
||||||
self.emulatorCore.rate = item.selected ? self.emulatorCore.configuration.supportedRates.upperBound : self.emulatorCore.configuration.supportedRates.lowerBound
|
|
||||||
})
|
|
||||||
fastForwardItem.selected = self.emulatorCore.rate == self.emulatorCore.configuration.supportedRates.lowerBound ? false : true
|
|
||||||
|
|
||||||
pauseViewController.items = [saveStateItem, loadStateItem, cheatCodesItem, fastForwardItem, sustainButtonsItem]
|
|
||||||
|
|
||||||
self.pauseViewController = pauseViewController
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@IBAction func unwindFromPauseViewController(_ segue: UIStoryboardSegue)
|
|
||||||
{
|
|
||||||
self.pauseViewController = nil
|
|
||||||
self.pausingGameController = nil
|
|
||||||
|
|
||||||
if self.resumeEmulation()
|
|
||||||
{
|
|
||||||
// Temporarily disable audioManager to prevent delayed audio bug when using 3D Touch Peek & Pop
|
|
||||||
self.emulatorCore.audioManager.enabled = false
|
|
||||||
|
|
||||||
// Re-enable after delay
|
|
||||||
DispatchQueue.main.after(when: DispatchTime.now() + Double(Int64(0.1 * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)) {
|
|
||||||
self.emulatorCore.audioManager.enabled = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//MARK: - 3D Touch -
|
|
||||||
/// 3D Touch
|
|
||||||
override func previewActionItems() -> [UIPreviewActionItem]
|
|
||||||
{
|
|
||||||
if let previewActionItems = self.overridePreviewActionItems
|
|
||||||
{
|
|
||||||
return previewActionItems
|
|
||||||
}
|
|
||||||
|
|
||||||
let presentingViewController = self.presentingViewController
|
|
||||||
|
|
||||||
let launchGameAction = UIPreviewAction(title: NSLocalizedString("Launch \(self.game.name)", comment: ""), style: .default) { (action, viewController) in
|
|
||||||
// Delaying until next run loop prevents self from being dismissed immediately
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
presentingViewController?.present(viewController, animated: true, completion: nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [launchGameAction]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//MARK: - Emulation -
|
|
||||||
/// Emulation
|
|
||||||
private extension EmulationViewController
|
|
||||||
{
|
|
||||||
func pause(sender: AnyObject?)
|
|
||||||
{
|
|
||||||
self.performSegue(withIdentifier: "pauseSegue", sender: sender)
|
|
||||||
}
|
|
||||||
|
|
||||||
@discardableResult func pauseEmulation() -> Bool
|
|
||||||
{
|
|
||||||
return self.emulatorCore.pause()
|
|
||||||
}
|
|
||||||
|
|
||||||
@discardableResult func resumeEmulation() -> Bool
|
|
||||||
{
|
|
||||||
guard !self.choosingSustainedButtons && self.pauseViewController == nil else { return false }
|
|
||||||
|
|
||||||
return self.emulatorCore.resume()
|
|
||||||
}
|
|
||||||
|
|
||||||
func emulatorCoreDidUpdate(_ emulatorCore: EmulatorCore)
|
|
||||||
{
|
|
||||||
for semaphore in self.updateSemaphores
|
|
||||||
{
|
|
||||||
semaphore.semaphore.signal()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//MARK: - Controllers -
|
|
||||||
/// Controllers
|
|
||||||
private extension EmulationViewController
|
|
||||||
{
|
|
||||||
@objc func updateControllers()
|
|
||||||
{
|
|
||||||
self.emulatorCore.removeAllGameControllers()
|
|
||||||
|
|
||||||
if let index = Settings.localControllerPlayerIndex
|
|
||||||
{
|
|
||||||
self.controllerView.playerIndex = index
|
|
||||||
}
|
|
||||||
|
|
||||||
var controllers = [GameController]()
|
|
||||||
controllers.append(self.controllerView)
|
|
||||||
|
|
||||||
// We need to map each item as a GameControllerProtocol due to a Swift bug
|
|
||||||
controllers.append(contentsOf: ExternalControllerManager.shared.connectedControllers.map { $0 as GameController })
|
|
||||||
|
|
||||||
for controller in controllers
|
|
||||||
{
|
|
||||||
if let index = controller.playerIndex
|
|
||||||
{
|
|
||||||
self.emulatorCore.setGameController(controller, at: index)
|
|
||||||
controller.addReceiver(self)
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
controller.removeReceiver(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.view.setNeedsLayout()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//MARK: - Sustain Button -
|
|
||||||
private extension EmulationViewController
|
|
||||||
{
|
|
||||||
func showSustainButtonView()
|
|
||||||
{
|
|
||||||
self.choosingSustainedButtons = true
|
|
||||||
self.sustainButtonContentView.isHidden = false
|
|
||||||
}
|
|
||||||
|
|
||||||
func hideSustainButtonView()
|
|
||||||
{
|
|
||||||
self.choosingSustainedButtons = false
|
|
||||||
|
|
||||||
UIView.animate(withDuration: 0.4, animations: {
|
|
||||||
self.sustainButtonContentView.alpha = 0.0
|
|
||||||
}) { (finished) in
|
|
||||||
self.sustainButtonContentView.isHidden = true
|
|
||||||
self.sustainButtonContentView.alpha = 1.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func resetSustainedInputs(forGameController gameController: GameController)
|
|
||||||
{
|
|
||||||
if let previousInputs = self.sustainedInputs[ObjectIdentifier(gameController)]
|
|
||||||
{
|
|
||||||
let receivers = gameController.receivers
|
|
||||||
receivers.forEach { gameController.removeReceiver($0) }
|
|
||||||
|
|
||||||
// Activate previousInputs without notifying anyone so we can then deactivate them
|
|
||||||
// We do this because deactivating an already deactivated input has no effect
|
|
||||||
previousInputs.forEach { gameController.activate($0) }
|
|
||||||
|
|
||||||
receivers.forEach { gameController.addReceiver($0) }
|
|
||||||
|
|
||||||
// Deactivate previously sustained inputs
|
|
||||||
previousInputs.forEach { gameController.deactivate($0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
self.sustainedInputs[ObjectIdentifier(gameController)] = []
|
|
||||||
}
|
|
||||||
|
|
||||||
func addSustainedInput(_ input: Input, gameController: GameController)
|
|
||||||
{
|
|
||||||
var inputs = self.sustainedInputs[ObjectIdentifier(gameController)] ?? []
|
|
||||||
|
|
||||||
guard !inputs.contains({ $0.isEqual(input) }) else { return }
|
|
||||||
|
|
||||||
inputs.append(input)
|
|
||||||
self.sustainedInputs[ObjectIdentifier(gameController)] = inputs
|
|
||||||
|
|
||||||
let receivers = gameController.receivers
|
|
||||||
receivers.forEach { gameController.removeReceiver($0) }
|
|
||||||
|
|
||||||
// Causes input to be considered deactivated, so gameController won't send a subsequent message to observers when user actually deactivates
|
|
||||||
// However, at this point the core still thinks it is activated, and is temporarily not a receiver, thus sustaining it
|
|
||||||
gameController.deactivate(input)
|
|
||||||
|
|
||||||
receivers.forEach { gameController.addReceiver($0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
func reactivateSustainedInput(_ input: Input, gameController: GameController)
|
|
||||||
{
|
|
||||||
// These MUST be performed serially, or else Bad Things Happen™ if multiple inputs are reactivated at once
|
|
||||||
self.reactivateSustainInputsQueue.addOperation {
|
|
||||||
|
|
||||||
// The manual activations/deactivations here are hidden implementation details, so we won't notify ourselves about them
|
|
||||||
gameController.removeReceiver(self)
|
|
||||||
|
|
||||||
// Must deactivate first so core recognizes a secondary activation
|
|
||||||
gameController.deactivate(input)
|
|
||||||
|
|
||||||
let dispatchQueue = DispatchQueue(label: "com.rileytestut.Delta.sustainButtonsQueue", attributes: DispatchQueueAttributes.serial)
|
|
||||||
dispatchQueue.async {
|
|
||||||
|
|
||||||
let semaphore = DispatchSemaphore(value: 0)
|
|
||||||
self.updateSemaphores.insert(semaphore)
|
|
||||||
|
|
||||||
// To ensure the emulator core recognizes us activating the input again, we need to wait at least two frames
|
|
||||||
// Unfortunately we cannot init DispatchSemaphore with value less than 0
|
|
||||||
// To compensate, we simply wait twice; once the first wait returns, we wait again
|
|
||||||
semaphore.semaphore.wait()
|
|
||||||
semaphore.semaphore.wait()
|
|
||||||
|
|
||||||
// These MUST be performed serially, or else Bad Things Happen™ if multiple inputs are reactivated at once
|
|
||||||
self.reactivateSustainInputsQueue.addOperation {
|
|
||||||
|
|
||||||
self.updateSemaphores.remove(semaphore)
|
|
||||||
|
|
||||||
// Ensure we still are not a receiver (to prevent rare race conditions)
|
|
||||||
gameController.removeReceiver(self)
|
|
||||||
|
|
||||||
gameController.activate(input)
|
|
||||||
|
|
||||||
let receivers = gameController.receivers
|
|
||||||
receivers.forEach { gameController.removeReceiver($0) }
|
|
||||||
|
|
||||||
// Causes input to be considered deactivated, so gameController won't send a subsequent message to observers when user actually deactivates
|
|
||||||
// However, at this point the core still thinks it is activated, and is temporarily not a receiver, thus sustaining it
|
|
||||||
gameController.deactivate(input)
|
|
||||||
|
|
||||||
receivers.forEach { gameController.addReceiver($0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
// More Bad Things Happen™ if we add self as observer before ALL reactivations have occurred (notable, infinite loops)
|
|
||||||
self.reactivateSustainInputsQueue.waitUntilAllOperationsAreFinished()
|
|
||||||
|
|
||||||
gameController.addReceiver(self)
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//MARK: - Save States
|
|
||||||
/// Save States
|
|
||||||
extension EmulationViewController: SaveStatesViewControllerDelegate
|
|
||||||
{
|
|
||||||
func saveStatesViewControllerActiveEmulatorCore(_ saveStatesViewController: SaveStatesViewController) -> EmulatorCore
|
|
||||||
{
|
|
||||||
return self.emulatorCore
|
|
||||||
}
|
|
||||||
|
|
||||||
func saveStatesViewController(_ saveStatesViewController: SaveStatesViewController, updateSaveState saveState: SaveState)
|
|
||||||
{
|
|
||||||
guard let filepath = saveState.fileURL.path else { return }
|
|
||||||
|
|
||||||
var updatingExistingSaveState = true
|
|
||||||
|
|
||||||
self.emulatorCore.save { (temporarySaveState) in
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if FileManager.default.fileExists(atPath: filepath)
|
|
||||||
{
|
|
||||||
try FileManager.default.replaceItem(at: saveState.fileURL, withItemAt: temporarySaveState.fileURL, backupItemName: nil, options: [], resultingItemURL: nil)
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
try FileManager.default.moveItem(at: temporarySaveState.fileURL, to: saveState.fileURL)
|
|
||||||
|
|
||||||
updatingExistingSaveState = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch let error as NSError
|
|
||||||
{
|
|
||||||
print(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let outputImage = self.gameView.outputImage, let quartzImage = self.context.createCGImage(outputImage, from: outputImage.extent)
|
|
||||||
{
|
|
||||||
let image = UIImage(cgImage: quartzImage)
|
|
||||||
try! UIImagePNGRepresentation(image)?.write(to: saveState.imageFileURL, options: [.atomicWrite])
|
|
||||||
}
|
|
||||||
|
|
||||||
saveState.modifiedDate = Date()
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
{
|
|
||||||
do
|
|
||||||
{
|
|
||||||
try self.emulatorCore.load(saveState)
|
|
||||||
}
|
|
||||||
catch EmulatorCore.SaveStateError.doesNotExist
|
|
||||||
{
|
|
||||||
print("Save State does not exist.")
|
|
||||||
}
|
|
||||||
catch let error as NSError
|
|
||||||
{
|
|
||||||
print(error)
|
|
||||||
}
|
|
||||||
|
|
||||||
self.updateCheats()
|
|
||||||
|
|
||||||
self.pauseViewController?.dismiss()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//MARK: - Cheats
|
|
||||||
/// Cheats
|
|
||||||
extension EmulationViewController: CheatsViewControllerDelegate
|
|
||||||
{
|
|
||||||
func cheatsViewControllerActiveEmulatorCore(_ saveStatesViewController: CheatsViewController) -> EmulatorCore
|
|
||||||
{
|
|
||||||
return self.emulatorCore
|
|
||||||
}
|
|
||||||
|
|
||||||
func cheatsViewController(_ cheatsViewController: CheatsViewController, didActivateCheat cheat: Cheat) throws
|
|
||||||
{
|
|
||||||
try self.emulatorCore.activate(cheat)
|
|
||||||
}
|
|
||||||
|
|
||||||
func cheatsViewController(_ cheatsViewController: CheatsViewController, didDeactivateCheat cheat: Cheat)
|
|
||||||
{
|
|
||||||
self.emulatorCore.deactivate(cheat)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func updateCheats()
|
|
||||||
{
|
|
||||||
let backgroundContext = DatabaseManager.sharedManager.backgroundManagedObjectContext()
|
|
||||||
backgroundContext.performAndWait {
|
|
||||||
|
|
||||||
let running = (self.emulatorCore.state == .running)
|
|
||||||
|
|
||||||
if running
|
|
||||||
{
|
|
||||||
// Core MUST be paused when activating cheats, or else race conditions could crash the core
|
|
||||||
self.pauseEmulation()
|
|
||||||
}
|
|
||||||
|
|
||||||
let predicate = Predicate(format: "%K == %@", Cheat.Attributes.game.rawValue, self.emulatorCore.game as! Game)
|
|
||||||
|
|
||||||
let cheats = Cheat.instancesWithPredicate(predicate, inManagedObjectContext: backgroundContext, type: Cheat.self)
|
|
||||||
for cheat in cheats
|
|
||||||
{
|
|
||||||
if cheat.enabled
|
|
||||||
{
|
|
||||||
do
|
|
||||||
{
|
|
||||||
try self.emulatorCore.activate(cheat)
|
|
||||||
}
|
|
||||||
catch EmulatorCore.CheatError.invalid
|
|
||||||
{
|
|
||||||
print("Invalid cheat:", cheat.name, cheat.code)
|
|
||||||
}
|
|
||||||
catch let error as NSError
|
|
||||||
{
|
|
||||||
print("Unknown Cheat Error:", error, cheat.name, cheat.code)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self.emulatorCore.deactivate(cheat)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if running
|
|
||||||
{
|
|
||||||
self.resumeEmulation()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//MARK: - App Lifecycle -
|
|
||||||
private extension EmulationViewController
|
|
||||||
{
|
|
||||||
@objc func willResignActive(_ notification: Notification)
|
|
||||||
{
|
|
||||||
self.pauseEmulation()
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func didBecomeActive(_ notification: Notification)
|
|
||||||
{
|
|
||||||
self.resumeEmulation()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//MARK: - <GameControllerReceiver> -
|
|
||||||
/// <GameControllerReceiver>
|
|
||||||
extension EmulationViewController: GameControllerReceiver
|
|
||||||
{
|
|
||||||
func gameController(_ gameController: GameController, didActivate input: Input)
|
|
||||||
{
|
|
||||||
if gameController is ControllerView && UIDevice.current().isVibrationSupported
|
|
||||||
{
|
|
||||||
UIDevice.current().vibrate()
|
|
||||||
}
|
|
||||||
|
|
||||||
if let input = input as? ControllerInput
|
|
||||||
{
|
|
||||||
switch input
|
|
||||||
{
|
|
||||||
case ControllerInput.menu:
|
|
||||||
if self.choosingSustainedButtons { self.hideSustainButtonView() }
|
|
||||||
self.pause(sender: gameController)
|
|
||||||
|
|
||||||
// Return now, because Menu cannot be sustained
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.choosingSustainedButtons
|
|
||||||
{
|
|
||||||
self.addSustainedInput(input, gameController: gameController)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if let sustainedInputs = self.sustainedInputs[ObjectIdentifier(gameController)] where sustainedInputs.contains({ $0.isEqual(input) })
|
|
||||||
{
|
|
||||||
// Perform on next run loop
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.reactivateSustainedInput(input, gameController: gameController)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func gameController(_ gameController: GameController, didDeactivate input: Input)
|
|
||||||
{
|
|
||||||
guard let input = input as? ControllerInput else { return }
|
|
||||||
|
|
||||||
print("Deactivated \(input)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
153
Delta/Emulation/GameViewController.swift
Normal file
153
Delta/Emulation/GameViewController.swift
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
//
|
||||||
|
// GameViewController.swift
|
||||||
|
// Delta
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 5/5/15.
|
||||||
|
// Copyright © 2016 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
import DeltaCore
|
||||||
|
|
||||||
|
class GameViewController: DeltaCore.GameViewController
|
||||||
|
{
|
||||||
|
override var game: GameProtocol? {
|
||||||
|
didSet {
|
||||||
|
guard let emulatorCore = self.emulatorCore else { return }
|
||||||
|
self.preferredContentSize = emulatorCore.preferredRenderingSize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If non-nil, will override the default preview action items returned in previewActionItems()
|
||||||
|
var overridePreviewActionItems: [UIPreviewActionItem]?
|
||||||
|
|
||||||
|
required init()
|
||||||
|
{
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
self.initialize()
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder aDecoder: NSCoder)
|
||||||
|
{
|
||||||
|
super.init(coder: aDecoder)
|
||||||
|
|
||||||
|
self.initialize()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func initialize()
|
||||||
|
{
|
||||||
|
self.delegate = self
|
||||||
|
|
||||||
|
NotificationCenter.default.addObserver(self, selector: #selector(GameViewController.updateControllers), name: .externalControllerDidConnect, object: nil)
|
||||||
|
NotificationCenter.default.addObserver(self, selector: #selector(GameViewController.updateControllers), name: .externalControllerDidDisconnect, object: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: GameControllerReceiver -
|
||||||
|
override func gameController(_ gameController: GameController, didActivate input: Input)
|
||||||
|
{
|
||||||
|
super.gameController(gameController, didActivate: input)
|
||||||
|
|
||||||
|
if gameController is ControllerView && UIDevice.current().isVibrationSupported
|
||||||
|
{
|
||||||
|
UIDevice.current().vibrate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//MARK: UIViewController -
|
||||||
|
/// UIViewController
|
||||||
|
extension GameViewController
|
||||||
|
{
|
||||||
|
override func viewDidLoad()
|
||||||
|
{
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
self.updateControllers()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewDidLayoutSubviews()
|
||||||
|
{
|
||||||
|
super.viewDidLayoutSubviews()
|
||||||
|
|
||||||
|
self.controllerView.isHidden = self.isPreviewing
|
||||||
|
}
|
||||||
|
|
||||||
|
override func previewActionItems() -> [UIPreviewActionItem]
|
||||||
|
{
|
||||||
|
if let previewActionItems = self.overridePreviewActionItems
|
||||||
|
{
|
||||||
|
return previewActionItems
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let game = self.game as? Game else { return [] }
|
||||||
|
|
||||||
|
let presentingViewController = self.presentingViewController
|
||||||
|
|
||||||
|
let launchGameAction = UIPreviewAction(title: NSLocalizedString("Launch \(game.name)", comment: ""), style: .default) { (action, viewController) in
|
||||||
|
// Delaying until next run loop prevents self from being dismissed immediately
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
presentingViewController?.present(viewController, animated: true, completion: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [launchGameAction]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//MARK: Controllers -
|
||||||
|
private extension GameViewController
|
||||||
|
{
|
||||||
|
@objc func updateControllers()
|
||||||
|
{
|
||||||
|
self.emulatorCore?.removeAllGameControllers()
|
||||||
|
|
||||||
|
if let index = Settings.localControllerPlayerIndex
|
||||||
|
{
|
||||||
|
self.controllerView.playerIndex = index
|
||||||
|
}
|
||||||
|
|
||||||
|
var controllers = [GameController]()
|
||||||
|
controllers.append(self.controllerView)
|
||||||
|
|
||||||
|
// We need to map each item as a GameControllerProtocol due to a Swift bug
|
||||||
|
controllers.append(contentsOf: ExternalControllerManager.shared.connectedControllers.map { $0 as GameController })
|
||||||
|
|
||||||
|
for controller in controllers
|
||||||
|
{
|
||||||
|
if let index = controller.playerIndex
|
||||||
|
{
|
||||||
|
// We need to place the underscore here to silence erroneous unused result warning despite annotating function with @discardableResult
|
||||||
|
// Hopefully this bug won't be around for too long...
|
||||||
|
_ = self.emulatorCore?.setGameController(controller, at: index)
|
||||||
|
controller.addReceiver(self)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
controller.removeReceiver(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.view.setNeedsLayout()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//MARK: GameViewControllerDelegate -
|
||||||
|
/// GameViewControllerDelegate
|
||||||
|
extension GameViewController: GameViewControllerDelegate
|
||||||
|
{
|
||||||
|
func gameViewController(gameViewController: DeltaCore.GameViewController, handleMenuInputFrom gameController: GameController)
|
||||||
|
{
|
||||||
|
self.pauseEmulation()
|
||||||
|
|
||||||
|
let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
|
||||||
|
alertController.addAction(UIAlertAction(title: NSLocalizedString("Cancel", comment: ""), style: .cancel, handler: { (action) in
|
||||||
|
self.resumeEmulation()
|
||||||
|
}))
|
||||||
|
alertController.addAction(UIAlertAction(title: NSLocalizedString("Stop Emulation", comment: ""), style: .destructive, handler: { (action) in
|
||||||
|
self.dismiss(animated: true)
|
||||||
|
}))
|
||||||
|
self.present(alertController, animated: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -118,50 +118,19 @@ class GamesViewController: UIViewController
|
|||||||
override func prepare(for segue: UIStoryboardSegue, sender: AnyObject?)
|
override func prepare(for segue: UIStoryboardSegue, sender: AnyObject?)
|
||||||
{
|
{
|
||||||
guard let sourceViewController = segue.sourceViewController as? GamesCollectionViewController else { return }
|
guard let sourceViewController = segue.sourceViewController as? GamesCollectionViewController else { return }
|
||||||
guard let destinationViewController = segue.destinationViewController as? EmulationViewController else { return }
|
guard let destinationViewController = segue.destinationViewController as? GameViewController else { return }
|
||||||
guard let cell = sender as? UICollectionViewCell else { return }
|
guard let cell = sender as? UICollectionViewCell else { return }
|
||||||
|
|
||||||
let indexPath = sourceViewController.collectionView?.indexPath(for: cell)
|
let indexPath = sourceViewController.collectionView?.indexPath(for: cell)
|
||||||
let game = sourceViewController.dataSource.fetchedResultsController.object(at: indexPath!) as! Game
|
let game = sourceViewController.dataSource.fetchedResultsController.object(at: indexPath!) as! Game
|
||||||
|
|
||||||
destinationViewController.game = game
|
destinationViewController.game = game
|
||||||
|
|
||||||
if segue.identifier == "peekEmulationViewController"
|
|
||||||
{
|
|
||||||
destinationViewController.deferredPreparationHandler = { [unowned destinationViewController] in
|
|
||||||
|
|
||||||
if let saveState = game.previewSaveState
|
|
||||||
{
|
|
||||||
destinationViewController.emulatorCore.start()
|
|
||||||
destinationViewController.emulatorCore.pause()
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
try destinationViewController.emulatorCore.load(saveState)
|
|
||||||
}
|
|
||||||
catch EmulatorCore.SaveStateError.doesNotExist
|
|
||||||
{
|
|
||||||
print("Save State \(saveState.name) does not exist.")
|
|
||||||
}
|
|
||||||
catch let error as NSError
|
|
||||||
{
|
|
||||||
print(error)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func unwindFromSettingsViewController(_ segue: UIStoryboardSegue)
|
@IBAction func unwindFromSettingsViewController(_ segue: UIStoryboardSegue)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func unwindFromEmulationViewController(_ segue: UIStoryboardSegue)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension GamesViewController
|
private extension GamesViewController
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user