Merge branch 'release/beta5'
This commit is contained in:
commit
93ffb83e94
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -10,3 +10,6 @@
|
|||||||
[submodule "Cores/GBADeltaCore"]
|
[submodule "Cores/GBADeltaCore"]
|
||||||
path = Cores/GBADeltaCore
|
path = Cores/GBADeltaCore
|
||||||
url = git@github.com:rileytestut/GBADeltaCore.git
|
url = git@github.com:rileytestut/GBADeltaCore.git
|
||||||
|
[submodule "Cores/GBCDeltaCore"]
|
||||||
|
path = Cores/GBCDeltaCore
|
||||||
|
url = git@github.com:rileytestut/GBCDeltaCore.git
|
||||||
|
|||||||
5168
Artwork/Icon.ai
Normal file
5168
Artwork/Icon.ai
Normal file
File diff suppressed because one or more lines are too long
Binary file not shown.
@ -1 +1 @@
|
|||||||
Subproject commit 99da200c440a8dad93d9b92314f91b933b659bf8
|
Subproject commit d7213260852683ea5a3f6c2274733bbd65d2ec92
|
||||||
@ -1 +1 @@
|
|||||||
Subproject commit ae5ab38dbe6a5fc995f4f3a8cb7075dad3f8f6d5
|
Subproject commit 7babfcf93a32916ad06989c9a63681acf7f07d50
|
||||||
1
Cores/GBCDeltaCore
Submodule
1
Cores/GBCDeltaCore
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 9d714254ace7bd5d63805d434f4ccd1f2a947951
|
||||||
@ -1 +1 @@
|
|||||||
Subproject commit e49d318e2e18034b2bccea34047800956298de4a
|
Subproject commit a354c3700fc4576bbefb6a8324bcd7a121d9b934
|
||||||
@ -27,12 +27,12 @@
|
|||||||
BF0418151D01E93400E85BCF /* GBADeltaCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BF0418131D01E93400E85BCF /* GBADeltaCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
BF0418151D01E93400E85BCF /* GBADeltaCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BF0418131D01E93400E85BCF /* GBADeltaCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||||
BF04E6FF1DB8625C000F35D3 /* ControllerSkinsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF04E6FE1DB8625C000F35D3 /* ControllerSkinsViewController.swift */; };
|
BF04E6FF1DB8625C000F35D3 /* ControllerSkinsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF04E6FE1DB8625C000F35D3 /* ControllerSkinsViewController.swift */; };
|
||||||
BF107EC41BF413F000E0C32C /* GamesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF107EC31BF413F000E0C32C /* GamesViewController.swift */; };
|
BF107EC41BF413F000E0C32C /* GamesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF107EC31BF413F000E0C32C /* GamesViewController.swift */; };
|
||||||
BF11734D1DA32A5200047DF8 /* GameType+Localization.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF11734C1DA32A5200047DF8 /* GameType+Localization.swift */; };
|
|
||||||
BF1173501DA32CF600047DF8 /* ControllersSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF11734F1DA32CF600047DF8 /* ControllersSettingsViewController.swift */; };
|
BF1173501DA32CF600047DF8 /* ControllersSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF11734F1DA32CF600047DF8 /* ControllersSettingsViewController.swift */; };
|
||||||
BF13A7561D5D29B0000BB055 /* PreviewGameViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF13A7551D5D29B0000BB055 /* PreviewGameViewController.swift */; };
|
BF13A7561D5D29B0000BB055 /* PreviewGameViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF13A7551D5D29B0000BB055 /* PreviewGameViewController.swift */; };
|
||||||
BF13A7581D5D2FD9000BB055 /* EmulatorCore+Cheats.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF13A7571D5D2FD9000BB055 /* EmulatorCore+Cheats.swift */; };
|
BF13A7581D5D2FD9000BB055 /* EmulatorCore+Cheats.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF13A7571D5D2FD9000BB055 /* EmulatorCore+Cheats.swift */; };
|
||||||
|
BF15AF841F54B43B009B6AAB /* ActionInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF15AF831F54B43B009B6AAB /* ActionInput.swift */; };
|
||||||
BF18B61F1E2985F900F70067 /* UIAlertController+Importing.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF18B61E1E2985F900F70067 /* UIAlertController+Importing.swift */; };
|
BF18B61F1E2985F900F70067 /* UIAlertController+Importing.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF18B61E1E2985F900F70067 /* UIAlertController+Importing.swift */; };
|
||||||
BF1DAD5D1D9F576000E752A7 /* GameTypeControllerSkinsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1DAD5C1D9F576000E752A7 /* GameTypeControllerSkinsViewController.swift */; };
|
BF1DAD5D1D9F576000E752A7 /* SystemControllerSkinsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1DAD5C1D9F576000E752A7 /* SystemControllerSkinsViewController.swift */; };
|
||||||
BF27CC8E1BC9FEA200A20D89 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BF6BB2451BB73FE800CCF94A /* Assets.xcassets */; };
|
BF27CC8E1BC9FEA200A20D89 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BF6BB2451BB73FE800CCF94A /* Assets.xcassets */; };
|
||||||
BF2B98E61C97E32F00F6D57D /* SaveStatesCollectionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF2B98E51C97E32F00F6D57D /* SaveStatesCollectionHeaderView.swift */; };
|
BF2B98E61C97E32F00F6D57D /* SaveStatesCollectionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF2B98E51C97E32F00F6D57D /* SaveStatesCollectionHeaderView.swift */; };
|
||||||
BF31878B1D489AAA00BD020D /* CheatValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF31878A1D489AAA00BD020D /* CheatValidator.swift */; };
|
BF31878B1D489AAA00BD020D /* CheatValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF31878A1D489AAA00BD020D /* CheatValidator.swift */; };
|
||||||
@ -40,19 +40,20 @@
|
|||||||
BF34FA111CF1899D006624C7 /* CheatTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF34FA101CF1899D006624C7 /* CheatTextView.swift */; };
|
BF34FA111CF1899D006624C7 /* CheatTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF34FA101CF1899D006624C7 /* CheatTextView.swift */; };
|
||||||
BF353FF21C5D7FB000C1184C /* PauseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF353FF11C5D7FB000C1184C /* PauseViewController.swift */; };
|
BF353FF21C5D7FB000C1184C /* PauseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF353FF11C5D7FB000C1184C /* PauseViewController.swift */; };
|
||||||
BF353FF61C5D837600C1184C /* PauseMenu.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BF353FF41C5D837600C1184C /* PauseMenu.storyboard */; };
|
BF353FF61C5D837600C1184C /* PauseMenu.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BF353FF41C5D837600C1184C /* PauseMenu.storyboard */; };
|
||||||
BF353FF91C5D870B00C1184C /* PauseItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF353FF81C5D870B00C1184C /* PauseItem.swift */; };
|
BF353FF91C5D870B00C1184C /* MenuItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF353FF81C5D870B00C1184C /* MenuItem.swift */; };
|
||||||
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 */; };
|
||||||
BF3540081C5DAFAD00C1184C /* PauseTransitionCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3540071C5DAFAD00C1184C /* PauseTransitionCoordinator.swift */; };
|
BF3540081C5DAFAD00C1184C /* PauseTransitionCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3540071C5DAFAD00C1184C /* PauseTransitionCoordinator.swift */; };
|
||||||
BF59425C1E09BB810051894B /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF5942581E09BB810051894B /* Action.swift */; };
|
BF4828841F9027B600028B97 /* Delta.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = BF4828811F9027B600028B97 /* Delta.xcdatamodeld */; };
|
||||||
|
BF4828861F9028F500028B97 /* System.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF4828851F9028F500028B97 /* System.swift */; };
|
||||||
|
BF4828881F90290F00028B97 /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF4828871F90290F00028B97 /* Action.swift */; };
|
||||||
BF5942641E09BBB10051894B /* LoadControllerSkinImageOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF5942611E09BBB10051894B /* LoadControllerSkinImageOperation.swift */; };
|
BF5942641E09BBB10051894B /* LoadControllerSkinImageOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF5942611E09BBB10051894B /* LoadControllerSkinImageOperation.swift */; };
|
||||||
BF5942661E09BBB10051894B /* LoadImageURLOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF5942631E09BBB10051894B /* LoadImageURLOperation.swift */; };
|
BF5942661E09BBB10051894B /* LoadImageURLOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF5942631E09BBB10051894B /* LoadImageURLOperation.swift */; };
|
||||||
BF59426A1E09BBD00051894B /* GridCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF5942681E09BBD00051894B /* GridCollectionViewCell.swift */; };
|
BF59426A1E09BBD00051894B /* GridCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF5942681E09BBD00051894B /* GridCollectionViewCell.swift */; };
|
||||||
BF59426B1E09BBD00051894B /* GridCollectionViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF5942691E09BBD00051894B /* GridCollectionViewLayout.swift */; };
|
BF59426B1E09BBD00051894B /* GridCollectionViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF5942691E09BBD00051894B /* GridCollectionViewLayout.swift */; };
|
||||||
BF59426F1E09BC5D0051894B /* DatabaseManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF59426D1E09BC5D0051894B /* DatabaseManager.swift */; };
|
BF59426F1E09BC5D0051894B /* DatabaseManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF59426D1E09BC5D0051894B /* DatabaseManager.swift */; };
|
||||||
BF5942701E09BC5D0051894B /* GamesDatabase.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF59426E1E09BC5D0051894B /* GamesDatabase.swift */; };
|
BF5942701E09BC5D0051894B /* GamesDatabase.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF59426E1E09BC5D0051894B /* GamesDatabase.swift */; };
|
||||||
BF5942731E09BC700051894B /* Model.xcdatamodel in Sources */ = {isa = PBXBuildFile; fileRef = BF5942721E09BC700051894B /* Model.xcdatamodel */; };
|
|
||||||
BF59427C1E09BC830051894B /* Cheat.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF5942771E09BC830051894B /* Cheat.swift */; };
|
BF59427C1E09BC830051894B /* Cheat.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF5942771E09BC830051894B /* Cheat.swift */; };
|
||||||
BF59427D1E09BC830051894B /* ControllerSkin.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF5942781E09BC830051894B /* ControllerSkin.swift */; };
|
BF59427D1E09BC830051894B /* ControllerSkin.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF5942781E09BC830051894B /* ControllerSkin.swift */; };
|
||||||
BF59427E1E09BC830051894B /* Game.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF5942791E09BC830051894B /* Game.swift */; };
|
BF59427E1E09BC830051894B /* Game.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF5942791E09BC830051894B /* Game.swift */; };
|
||||||
@ -69,13 +70,26 @@
|
|||||||
BF5942951E09BD1A0051894B /* NSManagedObjectContext+Conveniences.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF5942921E09BD1A0051894B /* NSManagedObjectContext+Conveniences.swift */; };
|
BF5942951E09BD1A0051894B /* NSManagedObjectContext+Conveniences.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF5942921E09BD1A0051894B /* NSManagedObjectContext+Conveniences.swift */; };
|
||||||
BF5E7F441B9A650B00AE44F8 /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF5E7F431B9A650B00AE44F8 /* SettingsViewController.swift */; };
|
BF5E7F441B9A650B00AE44F8 /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF5E7F431B9A650B00AE44F8 /* SettingsViewController.swift */; };
|
||||||
BF5E7F461B9A652600AE44F8 /* Settings.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BF5E7F451B9A652600AE44F8 /* Settings.storyboard */; };
|
BF5E7F461B9A652600AE44F8 /* Settings.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BF5E7F451B9A652600AE44F8 /* Settings.storyboard */; };
|
||||||
|
BF6424831F5B8F3F00D6AB44 /* ListMenuViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6424821F5B8F3F00D6AB44 /* ListMenuViewController.swift */; };
|
||||||
|
BF6424851F5CBDC900D6AB44 /* UIView+ParentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6424841F5CBDC900D6AB44 /* UIView+ParentViewController.swift */; };
|
||||||
BF6866171DCAC8B900BF2D06 /* ControllerSkin+Configuring.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6866161DCAC8B900BF2D06 /* ControllerSkin+Configuring.swift */; };
|
BF6866171DCAC8B900BF2D06 /* ControllerSkin+Configuring.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6866161DCAC8B900BF2D06 /* ControllerSkin+Configuring.swift */; };
|
||||||
BF696B801D9B2B02009639E0 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF696B7F1D9B2B02009639E0 /* Theme.swift */; };
|
BF696B801D9B2B02009639E0 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF696B7F1D9B2B02009639E0 /* Theme.swift */; };
|
||||||
|
BF6BF3131EB7E47F008E83CD /* ImportOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6BF3121EB7E47F008E83CD /* ImportOption.swift */; };
|
||||||
|
BF6BF3181EB82111008E83CD /* iTunesImportOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6BF3171EB82111008E83CD /* iTunesImportOption.swift */; };
|
||||||
|
BF6BF31A1EB82146008E83CD /* ClipboardImportOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6BF3191EB82146008E83CD /* ClipboardImportOption.swift */; };
|
||||||
|
BF6BF31C1EB821A0008E83CD /* GamesDatabaseImportOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6BF31B1EB821A0008E83CD /* GamesDatabaseImportOption.swift */; };
|
||||||
|
BF6BF3211EB82362008E83CD /* GamesDatabase.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BF6BF31F1EB82362008E83CD /* GamesDatabase.storyboard */; };
|
||||||
|
BF6BF3271EB87EB8008E83CD /* PhotoLibraryImportOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6BF3261EB87EB8008E83CD /* PhotoLibraryImportOption.swift */; };
|
||||||
|
BF6EE5E91F7C5F860051AD6C /* _GameControllerInputMapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6EE5E81F7C5F860051AD6C /* _GameControllerInputMapping.swift */; };
|
||||||
|
BF6EE5EB1F7C5F8F0051AD6C /* GameControllerInputMapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF6EE5EA1F7C5F8F0051AD6C /* GameControllerInputMapping.swift */; };
|
||||||
BF70798C1B6B464B0019077C /* ZipZap.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF70798B1B6B464B0019077C /* ZipZap.framework */; };
|
BF70798C1B6B464B0019077C /* ZipZap.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF70798B1B6B464B0019077C /* ZipZap.framework */; };
|
||||||
BF70798D1B6B464B0019077C /* ZipZap.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BF70798B1B6B464B0019077C /* ZipZap.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
BF70798D1B6B464B0019077C /* ZipZap.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BF70798B1B6B464B0019077C /* ZipZap.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||||
BF797A2D1C2D339F00F1A000 /* UILabel+FontSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF797A2C1C2D339F00F1A000 /* UILabel+FontSize.swift */; };
|
BF797A2D1C2D339F00F1A000 /* UILabel+FontSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF797A2C1C2D339F00F1A000 /* UILabel+FontSize.swift */; };
|
||||||
BF7AE8081C2E858400B1B5BC /* PauseMenuViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF7AE8041C2E858400B1B5BC /* PauseMenuViewController.swift */; };
|
BF7AE8081C2E858400B1B5BC /* GridMenuViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF7AE8041C2E858400B1B5BC /* GridMenuViewController.swift */; };
|
||||||
BF7AE80A1C2E8C7600B1B5BC /* UIColor+Delta.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF7AE8091C2E8C7600B1B5BC /* UIColor+Delta.swift */; };
|
BF7AE80A1C2E8C7600B1B5BC /* UIColor+Delta.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF7AE8091C2E8C7600B1B5BC /* UIColor+Delta.swift */; };
|
||||||
|
BF80E1D21F13117000847008 /* ControllerInputsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF616A121F08184A0077F8B2 /* ControllerInputsViewController.swift */; };
|
||||||
|
BF8CA9361F5F651900499FDD /* PopoverMenuController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8CA9351F5F651900499FDD /* PopoverMenuController.swift */; };
|
||||||
|
BF8DDD241F4F6C880088A21B /* InputCalloutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8DDD231F4F6C880088A21B /* InputCalloutView.swift */; };
|
||||||
BF95E2771E4977BF0030E7AD /* GameMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF95E2761E4977BF0030E7AD /* GameMetadata.swift */; };
|
BF95E2771E4977BF0030E7AD /* GameMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF95E2761E4977BF0030E7AD /* GameMetadata.swift */; };
|
||||||
BF95E2791E4982A10030E7AD /* GamesDatabaseBrowserViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF95E2781E4982A10030E7AD /* GamesDatabaseBrowserViewController.swift */; };
|
BF95E2791E4982A10030E7AD /* GamesDatabaseBrowserViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF95E2781E4982A10030E7AD /* GamesDatabaseBrowserViewController.swift */; };
|
||||||
BF99A5971DC2F9C400468E9E /* ControllerSkinTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF99A5961DC2F9C400468E9E /* ControllerSkinTableViewCell.swift */; };
|
BF99A5971DC2F9C400468E9E /* ControllerSkinTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF99A5961DC2F9C400468E9E /* ControllerSkinTableViewCell.swift */; };
|
||||||
@ -85,14 +99,21 @@
|
|||||||
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 */; };
|
BFA0D1271D3AE1F600565894 /* GameViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF63BDE91D389EEB00FCB040 /* GameViewController.swift */; };
|
||||||
BFAA1FED1B8AA4FA00495943 /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFAA1FEC1B8AA4FA00495943 /* Settings.swift */; };
|
BFAA1FED1B8AA4FA00495943 /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFAA1FEC1B8AA4FA00495943 /* Settings.swift */; };
|
||||||
BFC314771E0C8CFC0056E3A8 /* GameType+Delta.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC314761E0C8CFC0056E3A8 /* GameType+Delta.swift */; };
|
BFBAB2E31EB685A2004E0B0E /* DeltaCoreProtocol+Delta.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFBAB2E21EB685A2004E0B0E /* DeltaCoreProtocol+Delta.swift */; };
|
||||||
|
BFC6F7B81F435BC500221B96 /* Input+Display.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC6F7B71F435BC500221B96 /* Input+Display.swift */; };
|
||||||
BFC9B7391CEFCD34008629BB /* CheatsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC9B7381CEFCD34008629BB /* CheatsViewController.swift */; };
|
BFC9B7391CEFCD34008629BB /* CheatsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC9B7381CEFCD34008629BB /* CheatsViewController.swift */; };
|
||||||
BFCEA67E1D56FF640061A534 /* UIViewControllerContextTransitioning+Conveniences.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFCEA67D1D56FF640061A534 /* UIViewControllerContextTransitioning+Conveniences.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 */; };
|
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 */; };
|
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 */; };
|
BFE4269E1D9C68E600DC913F /* SaveStatesStoryboardSegue.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFE4269D1D9C68E600DC913F /* SaveStatesStoryboardSegue.swift */; };
|
||||||
BFEC732D1AAECC4A00650035 /* Roxas.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BFEC732C1AAECC4A00650035 /* Roxas.framework */; };
|
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, ); }; };
|
BFEC732E1AAECC4A00650035 /* Roxas.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BFEC732C1AAECC4A00650035 /* Roxas.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||||
|
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 */; };
|
||||||
BFF93AA01E0FB036005EC865 /* InputStreamOutputWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF93A9F1E0FB036005EC865 /* InputStreamOutputWriter.swift */; };
|
BFF93AA01E0FB036005EC865 /* InputStreamOutputWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF93A9F1E0FB036005EC865 /* InputStreamOutputWriter.swift */; };
|
||||||
BFFA4C091E8A24D600D87934 /* GameMetadataTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFFA4C081E8A24D600D87934 /* GameMetadataTableViewCell.swift */; };
|
BFFA4C091E8A24D600D87934 /* GameMetadataTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFFA4C081E8A24D600D87934 /* GameMetadataTableViewCell.swift */; };
|
||||||
BFFA71DD1AAC406100EE9DD1 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFFA71DC1AAC406100EE9DD1 /* AppDelegate.swift */; };
|
BFFA71DD1AAC406100EE9DD1 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFFA71DC1AAC406100EE9DD1 /* AppDelegate.swift */; };
|
||||||
@ -113,6 +134,7 @@
|
|||||||
dstSubfolderSpec = 10;
|
dstSubfolderSpec = 10;
|
||||||
files = (
|
files = (
|
||||||
BF9F4FD01AAD7B87004C9500 /* DeltaCore.framework in Embed Frameworks */,
|
BF9F4FD01AAD7B87004C9500 /* DeltaCore.framework in Embed Frameworks */,
|
||||||
|
BFF0742D1E9DC17500ACDF4A /* GBCDeltaCore.framework in Embed Frameworks */,
|
||||||
BFEC732E1AAECC4A00650035 /* Roxas.framework in Embed Frameworks */,
|
BFEC732E1AAECC4A00650035 /* Roxas.framework in Embed Frameworks */,
|
||||||
BF70798D1B6B464B0019077C /* ZipZap.framework in Embed Frameworks */,
|
BF70798D1B6B464B0019077C /* ZipZap.framework in Embed Frameworks */,
|
||||||
BF0418151D01E93400E85BCF /* GBADeltaCore.framework in Embed Frameworks */,
|
BF0418151D01E93400E85BCF /* GBADeltaCore.framework in Embed Frameworks */,
|
||||||
@ -129,88 +151,109 @@
|
|||||||
A19FF50F55441BC2B2248241 /* Pods-Delta.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Delta.release.xcconfig"; path = "Pods/Target Support Files/Pods-Delta/Pods-Delta.release.xcconfig"; sourceTree = "<group>"; };
|
A19FF50F55441BC2B2248241 /* Pods-Delta.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Delta.release.xcconfig"; path = "Pods/Target Support Files/Pods-Delta/Pods-Delta.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
BF02D5D91DDEBB3000A5E131 /* openvgdb.sqlite */ = {isa = PBXFileReference; lastKnownFileType = file; path = openvgdb.sqlite; sourceTree = "<group>"; };
|
BF02D5D91DDEBB3000A5E131 /* openvgdb.sqlite */ = {isa = PBXFileReference; lastKnownFileType = file; path = openvgdb.sqlite; sourceTree = "<group>"; };
|
||||||
BF0418131D01E93400E85BCF /* GBADeltaCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = GBADeltaCore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
BF0418131D01E93400E85BCF /* GBADeltaCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = GBADeltaCore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
BF04E6FE1DB8625C000F35D3 /* ControllerSkinsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ControllerSkinsViewController.swift; path = "Controller Skins/ControllerSkinsViewController.swift"; sourceTree = "<group>"; };
|
BF04E6FE1DB8625C000F35D3 /* ControllerSkinsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControllerSkinsViewController.swift; sourceTree = "<group>"; };
|
||||||
BF090CF11B490D8300DCAB45 /* Delta-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Delta-Bridging-Header.h"; sourceTree = "<group>"; };
|
BF090CF11B490D8300DCAB45 /* Delta-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Delta-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||||
BF107EC31BF413F000E0C32C /* GamesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GamesViewController.swift; sourceTree = "<group>"; };
|
BF107EC31BF413F000E0C32C /* GamesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GamesViewController.swift; sourceTree = "<group>"; };
|
||||||
BF11734C1DA32A5200047DF8 /* GameType+Localization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "GameType+Localization.swift"; sourceTree = "<group>"; };
|
BF11734F1DA32CF600047DF8 /* ControllersSettingsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControllersSettingsViewController.swift; sourceTree = "<group>"; };
|
||||||
BF11734F1DA32CF600047DF8 /* ControllersSettingsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ControllersSettingsViewController.swift; path = Controllers/ControllersSettingsViewController.swift; sourceTree = "<group>"; };
|
|
||||||
BF13A7551D5D29B0000BB055 /* PreviewGameViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreviewGameViewController.swift; sourceTree = "<group>"; };
|
BF13A7551D5D29B0000BB055 /* PreviewGameViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreviewGameViewController.swift; sourceTree = "<group>"; };
|
||||||
BF13A7571D5D2FD9000BB055 /* EmulatorCore+Cheats.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "EmulatorCore+Cheats.swift"; sourceTree = "<group>"; };
|
BF13A7571D5D2FD9000BB055 /* EmulatorCore+Cheats.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "EmulatorCore+Cheats.swift"; sourceTree = "<group>"; };
|
||||||
|
BF15AF831F54B43B009B6AAB /* ActionInput.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionInput.swift; sourceTree = "<group>"; };
|
||||||
BF18B61E1E2985F900F70067 /* UIAlertController+Importing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIAlertController+Importing.swift"; sourceTree = "<group>"; };
|
BF18B61E1E2985F900F70067 /* UIAlertController+Importing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIAlertController+Importing.swift"; sourceTree = "<group>"; };
|
||||||
BF1DAD5C1D9F576000E752A7 /* GameTypeControllerSkinsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GameTypeControllerSkinsViewController.swift; path = "Controller Skins/GameTypeControllerSkinsViewController.swift"; sourceTree = "<group>"; };
|
BF1DAD5C1D9F576000E752A7 /* SystemControllerSkinsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SystemControllerSkinsViewController.swift; sourceTree = "<group>"; };
|
||||||
BF27CC861BC9E3C600A20D89 /* Delta.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = Delta.entitlements; sourceTree = "<group>"; };
|
BF27CC861BC9E3C600A20D89 /* Delta.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = Delta.entitlements; sourceTree = "<group>"; };
|
||||||
BF27CC8A1BC9FE4D00A20D89 /* Pods.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Pods.framework; path = "Pods/../build/Debug-appletvos/Pods.framework"; sourceTree = "<group>"; };
|
BF27CC8A1BC9FE4D00A20D89 /* Pods.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Pods.framework; path = "Pods/../build/Debug-appletvos/Pods.framework"; sourceTree = "<group>"; };
|
||||||
BF27CC941BCB7B7A00A20D89 /* GameController.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameController.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS9.0.sdk/System/Library/Frameworks/GameController.framework; sourceTree = DEVELOPER_DIR; };
|
BF27CC941BCB7B7A00A20D89 /* GameController.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameController.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS9.0.sdk/System/Library/Frameworks/GameController.framework; sourceTree = DEVELOPER_DIR; };
|
||||||
BF2B98E51C97E32F00F6D57D /* SaveStatesCollectionHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SaveStatesCollectionHeaderView.swift; path = "Pause Menu/Save States/SaveStatesCollectionHeaderView.swift"; sourceTree = "<group>"; };
|
BF2B98E51C97E32F00F6D57D /* SaveStatesCollectionHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SaveStatesCollectionHeaderView.swift; sourceTree = "<group>"; };
|
||||||
BF31878A1D489AAA00BD020D /* CheatValidator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CheatValidator.swift; path = "Pause Menu/Cheats/CheatValidator.swift"; sourceTree = "<group>"; };
|
BF31878A1D489AAA00BD020D /* CheatValidator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CheatValidator.swift; sourceTree = "<group>"; };
|
||||||
BF34FA061CF0F510006624C7 /* EditCheatViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = EditCheatViewController.swift; path = "Pause Menu/Cheats/EditCheatViewController.swift"; sourceTree = "<group>"; };
|
BF34FA061CF0F510006624C7 /* EditCheatViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EditCheatViewController.swift; sourceTree = "<group>"; };
|
||||||
BF34FA101CF1899D006624C7 /* CheatTextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CheatTextView.swift; path = "Pause Menu/Cheats/CheatTextView.swift"; sourceTree = "<group>"; };
|
BF34FA101CF1899D006624C7 /* CheatTextView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CheatTextView.swift; sourceTree = "<group>"; };
|
||||||
BF353FF11C5D7FB000C1184C /* PauseViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PauseViewController.swift; path = "Pause Menu/PauseViewController.swift"; sourceTree = "<group>"; };
|
BF353FF11C5D7FB000C1184C /* PauseViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PauseViewController.swift; sourceTree = "<group>"; };
|
||||||
BF353FF51C5D837600C1184C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/PauseMenu.storyboard; sourceTree = "<group>"; };
|
BF353FF51C5D837600C1184C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/PauseMenu.storyboard; sourceTree = "<group>"; };
|
||||||
BF353FF81C5D870B00C1184C /* PauseItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PauseItem.swift; path = "Pause Menu/PauseItem.swift"; sourceTree = "<group>"; };
|
BF353FF81C5D870B00C1184C /* MenuItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MenuItem.swift; sourceTree = "<group>"; };
|
||||||
BF353FFD1C5DA3C500C1184C /* PausePresentationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PausePresentationController.swift; path = "Pause Menu/Presentation Controller/PausePresentationController.swift"; sourceTree = "<group>"; };
|
BF353FFD1C5DA3C500C1184C /* PausePresentationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PausePresentationController.swift; sourceTree = "<group>"; };
|
||||||
BF353FFE1C5DA3C500C1184C /* PausePresentationControllerContentView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = PausePresentationControllerContentView.xib; path = "Pause Menu/Presentation Controller/PausePresentationControllerContentView.xib"; sourceTree = "<group>"; };
|
BF353FFE1C5DA3C500C1184C /* PausePresentationControllerContentView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = PausePresentationControllerContentView.xib; path = Delta/Base.lproj/PausePresentationControllerContentView.xib; sourceTree = SOURCE_ROOT; };
|
||||||
BF3540011C5DA3D500C1184C /* PauseStoryboardSegue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PauseStoryboardSegue.swift; path = "Pause Menu/Segues/PauseStoryboardSegue.swift"; sourceTree = "<group>"; };
|
BF3540011C5DA3D500C1184C /* PauseStoryboardSegue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PauseStoryboardSegue.swift; sourceTree = "<group>"; };
|
||||||
BF3540041C5DA70400C1184C /* SaveStatesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SaveStatesViewController.swift; path = "Pause Menu/Save States/SaveStatesViewController.swift"; sourceTree = "<group>"; };
|
BF3540041C5DA70400C1184C /* SaveStatesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SaveStatesViewController.swift; sourceTree = "<group>"; };
|
||||||
BF3540071C5DAFAD00C1184C /* PauseTransitionCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PauseTransitionCoordinator.swift; path = "Pause Menu/Segues/PauseTransitionCoordinator.swift"; sourceTree = "<group>"; };
|
BF3540071C5DAFAD00C1184C /* PauseTransitionCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PauseTransitionCoordinator.swift; sourceTree = "<group>"; };
|
||||||
BF5942581E09BB810051894B /* Action.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Action.swift; path = Components/Action.swift; sourceTree = "<group>"; };
|
BF4828821F9027B600028B97 /* Delta 2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Delta 2.xcdatamodel"; sourceTree = "<group>"; };
|
||||||
BF5942611E09BBB10051894B /* LoadControllerSkinImageOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = LoadControllerSkinImageOperation.swift; path = Components/Loading/LoadControllerSkinImageOperation.swift; sourceTree = "<group>"; };
|
BF4828831F9027B600028B97 /* Delta.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Delta.xcdatamodel; sourceTree = "<group>"; };
|
||||||
BF5942631E09BBB10051894B /* LoadImageURLOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = LoadImageURLOperation.swift; path = Components/Loading/LoadImageURLOperation.swift; sourceTree = "<group>"; };
|
BF4828851F9028F500028B97 /* System.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = System.swift; sourceTree = "<group>"; };
|
||||||
BF5942681E09BBD00051894B /* GridCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GridCollectionViewCell.swift; path = "Components/Collection View/GridCollectionViewCell.swift"; sourceTree = "<group>"; };
|
BF4828871F90290F00028B97 /* Action.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Action.swift; sourceTree = "<group>"; };
|
||||||
BF5942691E09BBD00051894B /* GridCollectionViewLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GridCollectionViewLayout.swift; path = "Components/Collection View/GridCollectionViewLayout.swift"; sourceTree = "<group>"; };
|
BF5942611E09BBB10051894B /* LoadControllerSkinImageOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoadControllerSkinImageOperation.swift; sourceTree = "<group>"; };
|
||||||
BF59426D1E09BC5D0051894B /* DatabaseManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DatabaseManager.swift; path = Database/DatabaseManager.swift; sourceTree = "<group>"; };
|
BF5942631E09BBB10051894B /* LoadImageURLOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoadImageURLOperation.swift; sourceTree = "<group>"; };
|
||||||
BF59426E1E09BC5D0051894B /* GamesDatabase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GamesDatabase.swift; path = Database/OpenVGDB/GamesDatabase.swift; sourceTree = "<group>"; };
|
BF5942681E09BBD00051894B /* GridCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GridCollectionViewCell.swift; sourceTree = "<group>"; };
|
||||||
BF5942721E09BC700051894B /* Model.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; name = Model.xcdatamodel; path = Database/Model/Model.xcdatamodel; sourceTree = "<group>"; };
|
BF5942691E09BBD00051894B /* GridCollectionViewLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GridCollectionViewLayout.swift; sourceTree = "<group>"; };
|
||||||
BF5942771E09BC830051894B /* Cheat.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Cheat.swift; path = Database/Model/Human/Cheat.swift; sourceTree = "<group>"; };
|
BF59426D1E09BC5D0051894B /* DatabaseManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseManager.swift; sourceTree = "<group>"; };
|
||||||
BF5942781E09BC830051894B /* ControllerSkin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ControllerSkin.swift; path = Database/Model/Human/ControllerSkin.swift; sourceTree = "<group>"; };
|
BF59426E1E09BC5D0051894B /* GamesDatabase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GamesDatabase.swift; sourceTree = "<group>"; };
|
||||||
BF5942791E09BC830051894B /* Game.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Game.swift; path = Database/Model/Human/Game.swift; sourceTree = "<group>"; };
|
BF5942771E09BC830051894B /* Cheat.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Cheat.swift; sourceTree = "<group>"; };
|
||||||
BF59427A1E09BC830051894B /* GameCollection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GameCollection.swift; path = Database/Model/Human/GameCollection.swift; sourceTree = "<group>"; };
|
BF5942781E09BC830051894B /* ControllerSkin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControllerSkin.swift; sourceTree = "<group>"; };
|
||||||
BF59427B1E09BC830051894B /* SaveState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SaveState.swift; path = Database/Model/Human/SaveState.swift; sourceTree = "<group>"; };
|
BF5942791E09BC830051894B /* Game.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Game.swift; sourceTree = "<group>"; };
|
||||||
BF5942811E09BC8B0051894B /* _Cheat.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = _Cheat.swift; path = Database/Model/Machine/_Cheat.swift; sourceTree = "<group>"; };
|
BF59427A1E09BC830051894B /* GameCollection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameCollection.swift; sourceTree = "<group>"; };
|
||||||
BF5942821E09BC8B0051894B /* _ControllerSkin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = _ControllerSkin.swift; path = Database/Model/Machine/_ControllerSkin.swift; sourceTree = "<group>"; };
|
BF59427B1E09BC830051894B /* SaveState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SaveState.swift; sourceTree = "<group>"; };
|
||||||
BF5942831E09BC8B0051894B /* _Game.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = _Game.swift; path = Database/Model/Machine/_Game.swift; sourceTree = "<group>"; };
|
BF5942811E09BC8B0051894B /* _Cheat.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _Cheat.swift; sourceTree = "<group>"; };
|
||||||
BF5942841E09BC8B0051894B /* _GameCollection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = _GameCollection.swift; path = Database/Model/Machine/_GameCollection.swift; sourceTree = "<group>"; };
|
BF5942821E09BC8B0051894B /* _ControllerSkin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _ControllerSkin.swift; sourceTree = "<group>"; };
|
||||||
BF5942851E09BC8B0051894B /* _SaveState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = _SaveState.swift; path = Database/Model/Machine/_SaveState.swift; sourceTree = "<group>"; };
|
BF5942831E09BC8B0051894B /* _Game.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _Game.swift; sourceTree = "<group>"; };
|
||||||
BF59428B1E09BC930051894B /* ControllerSkinConfigurations.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ControllerSkinConfigurations.h; path = Database/Model/Misc/ControllerSkinConfigurations.h; sourceTree = "<group>"; };
|
BF5942841E09BC8B0051894B /* _GameCollection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _GameCollection.swift; sourceTree = "<group>"; };
|
||||||
BF59428D1E09BCFB0051894B /* ImportController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImportController.swift; path = Components/Importing/ImportController.swift; sourceTree = "<group>"; };
|
BF5942851E09BC8B0051894B /* _SaveState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = _SaveState.swift; sourceTree = "<group>"; };
|
||||||
|
BF59428B1E09BC930051894B /* ControllerSkinConfigurations.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ControllerSkinConfigurations.h; sourceTree = "<group>"; };
|
||||||
|
BF59428D1E09BCFB0051894B /* ImportController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImportController.swift; sourceTree = "<group>"; };
|
||||||
BF59428F1E09BD1A0051894B /* NSFetchedResultsController+Conveniences.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSFetchedResultsController+Conveniences.h"; sourceTree = "<group>"; };
|
BF59428F1E09BD1A0051894B /* NSFetchedResultsController+Conveniences.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSFetchedResultsController+Conveniences.h"; sourceTree = "<group>"; };
|
||||||
BF5942901E09BD1A0051894B /* NSFetchedResultsController+Conveniences.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSFetchedResultsController+Conveniences.m"; sourceTree = "<group>"; };
|
BF5942901E09BD1A0051894B /* NSFetchedResultsController+Conveniences.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSFetchedResultsController+Conveniences.m"; sourceTree = "<group>"; };
|
||||||
BF5942911E09BD1A0051894B /* NSManagedObject+Conveniences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSManagedObject+Conveniences.swift"; sourceTree = "<group>"; };
|
BF5942911E09BD1A0051894B /* NSManagedObject+Conveniences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSManagedObject+Conveniences.swift"; sourceTree = "<group>"; };
|
||||||
BF5942921E09BD1A0051894B /* NSManagedObjectContext+Conveniences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSManagedObjectContext+Conveniences.swift"; sourceTree = "<group>"; };
|
BF5942921E09BD1A0051894B /* NSManagedObjectContext+Conveniences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSManagedObjectContext+Conveniences.swift"; 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; name = Settings.storyboard; path = Delta/Base.lproj/Settings.storyboard; sourceTree = SOURCE_ROOT; };
|
||||||
|
BF616A121F08184A0077F8B2 /* ControllerInputsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControllerInputsViewController.swift; sourceTree = "<group>"; };
|
||||||
BF63BDE91D389EEB00FCB040 /* GameViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameViewController.swift; sourceTree = "<group>"; };
|
BF63BDE91D389EEB00FCB040 /* GameViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameViewController.swift; sourceTree = "<group>"; };
|
||||||
|
BF6424821F5B8F3F00D6AB44 /* ListMenuViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListMenuViewController.swift; sourceTree = "<group>"; };
|
||||||
|
BF6424841F5CBDC900D6AB44 /* UIView+ParentViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+ParentViewController.swift"; sourceTree = "<group>"; };
|
||||||
BF6866161DCAC8B900BF2D06 /* ControllerSkin+Configuring.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ControllerSkin+Configuring.swift"; sourceTree = "<group>"; };
|
BF6866161DCAC8B900BF2D06 /* ControllerSkin+Configuring.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ControllerSkin+Configuring.swift"; sourceTree = "<group>"; };
|
||||||
BF696B7F1D9B2B02009639E0 /* Theme.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Theme.swift; path = Theming/Theme.swift; sourceTree = "<group>"; };
|
BF696B7F1D9B2B02009639E0 /* Theme.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = "<group>"; };
|
||||||
|
BF6B82A41F7CC2A300042BFB /* GameControllerInputMappingTransformer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameControllerInputMappingTransformer.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>"; };
|
||||||
|
BF6BF3121EB7E47F008E83CD /* ImportOption.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImportOption.swift; sourceTree = "<group>"; };
|
||||||
|
BF6BF3171EB82111008E83CD /* iTunesImportOption.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = iTunesImportOption.swift; sourceTree = "<group>"; };
|
||||||
|
BF6BF3191EB82146008E83CD /* ClipboardImportOption.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClipboardImportOption.swift; sourceTree = "<group>"; };
|
||||||
|
BF6BF31B1EB821A0008E83CD /* GamesDatabaseImportOption.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GamesDatabaseImportOption.swift; sourceTree = "<group>"; };
|
||||||
|
BF6BF3201EB82362008E83CD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/GamesDatabase.storyboard; sourceTree = "<group>"; };
|
||||||
|
BF6BF3261EB87EB8008E83CD /* PhotoLibraryImportOption.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotoLibraryImportOption.swift; sourceTree = "<group>"; };
|
||||||
|
BF6EE5E81F7C5F860051AD6C /* _GameControllerInputMapping.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = _GameControllerInputMapping.swift; sourceTree = "<group>"; };
|
||||||
|
BF6EE5EA1F7C5F8F0051AD6C /* GameControllerInputMapping.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameControllerInputMapping.swift; 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; };
|
||||||
BF797A2C1C2D339F00F1A000 /* UILabel+FontSize.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UILabel+FontSize.swift"; sourceTree = "<group>"; };
|
BF797A2C1C2D339F00F1A000 /* UILabel+FontSize.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UILabel+FontSize.swift"; sourceTree = "<group>"; };
|
||||||
BF7AE8041C2E858400B1B5BC /* PauseMenuViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PauseMenuViewController.swift; path = "Pause Menu/PauseMenuViewController.swift"; sourceTree = "<group>"; };
|
BF7AE8041C2E858400B1B5BC /* GridMenuViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GridMenuViewController.swift; sourceTree = "<group>"; };
|
||||||
BF7AE8091C2E8C7600B1B5BC /* UIColor+Delta.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+Delta.swift"; sourceTree = "<group>"; };
|
BF7AE8091C2E8C7600B1B5BC /* UIColor+Delta.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+Delta.swift"; sourceTree = "<group>"; };
|
||||||
BF95E2761E4977BF0030E7AD /* GameMetadata.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GameMetadata.swift; path = Database/OpenVGDB/GameMetadata.swift; sourceTree = "<group>"; };
|
BF8CA9351F5F651900499FDD /* PopoverMenuController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopoverMenuController.swift; sourceTree = "<group>"; };
|
||||||
BF95E2781E4982A10030E7AD /* GamesDatabaseBrowserViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GamesDatabaseBrowserViewController.swift; path = Database/OpenVGDB/GamesDatabaseBrowserViewController.swift; sourceTree = "<group>"; };
|
BF8DDD231F4F6C880088A21B /* InputCalloutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputCalloutView.swift; sourceTree = "<group>"; };
|
||||||
BF99A5961DC2F9C400468E9E /* ControllerSkinTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ControllerSkinTableViewCell.swift; path = "Controller Skins/ControllerSkinTableViewCell.swift"; sourceTree = "<group>"; };
|
BF95E2761E4977BF0030E7AD /* GameMetadata.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameMetadata.swift; sourceTree = "<group>"; };
|
||||||
|
BF95E2781E4982A10030E7AD /* GamesDatabaseBrowserViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GamesDatabaseBrowserViewController.swift; sourceTree = "<group>"; };
|
||||||
|
BF99A5961DC2F9C400468E9E /* ControllerSkinTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControllerSkinTableViewCell.swift; sourceTree = "<group>"; };
|
||||||
BF9F4FCE1AAD7B87004C9500 /* DeltaCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DeltaCore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
BF9F4FCE1AAD7B87004C9500 /* DeltaCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DeltaCore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
BFAA1FEC1B8AA4FA00495943 /* Settings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = "<group>"; };
|
BFAA1FEC1B8AA4FA00495943 /* Settings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = "<group>"; };
|
||||||
|
BFBAB2E21EB685A2004E0B0E /* DeltaCoreProtocol+Delta.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DeltaCoreProtocol+Delta.swift"; sourceTree = "<group>"; };
|
||||||
BFC134E01AAD82460087AD7B /* SNESDeltaCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SNESDeltaCore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
BFC134E01AAD82460087AD7B /* SNESDeltaCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SNESDeltaCore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
BFC314761E0C8CFC0056E3A8 /* GameType+Delta.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "GameType+Delta.swift"; sourceTree = "<group>"; };
|
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>"; };
|
BFC9B7381CEFCD34008629BB /* CheatsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CheatsViewController.swift; sourceTree = "<group>"; };
|
||||||
BFCEA67D1D56FF640061A534 /* UIViewControllerContextTransitioning+Conveniences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewControllerContextTransitioning+Conveniences.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; path = Delta.xcmappingmodel; sourceTree = "<group>"; };
|
||||||
BFDD04F01D5E2C27002D450E /* GameCollectionViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameCollectionViewController.swift; sourceTree = "<group>"; };
|
BFDD04F01D5E2C27002D450E /* GameCollectionViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameCollectionViewController.swift; sourceTree = "<group>"; };
|
||||||
BFE4269D1D9C68E600DC913F /* SaveStatesStoryboardSegue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SaveStatesStoryboardSegue.swift; path = Segues/SaveStatesStoryboardSegue.swift; sourceTree = "<group>"; };
|
BFE0229F1F5B577D0052D888 /* PopoverMenuButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopoverMenuButton.swift; sourceTree = "<group>"; };
|
||||||
|
BFE4269D1D9C68E600DC913F /* SaveStatesStoryboardSegue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SaveStatesStoryboardSegue.swift; sourceTree = "<group>"; };
|
||||||
BFEC732C1AAECC4A00650035 /* Roxas.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Roxas.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
BFEC732C1AAECC4A00650035 /* Roxas.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Roxas.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
BFF93A9F1E0FB036005EC865 /* InputStreamOutputWriter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = InputStreamOutputWriter.swift; path = Components/Importing/InputStreamOutputWriter.swift; sourceTree = "<group>"; };
|
BFEF24F21F7DD4FB00454C62 /* SaveStateMigrationPolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SaveStateMigrationPolicy.swift; sourceTree = "<group>"; };
|
||||||
BFFA4C081E8A24D600D87934 /* GameMetadataTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GameMetadataTableViewCell.swift; path = Database/OpenVGDB/GameMetadataTableViewCell.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; path = InputStreamOutputWriter.swift; sourceTree = "<group>"; };
|
||||||
|
BFFA4C081E8A24D600D87934 /* GameMetadataTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameMetadataTableViewCell.swift; sourceTree = "<group>"; };
|
||||||
BFFA71D71AAC406100EE9DD1 /* Delta.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Delta.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
BFFA71D71AAC406100EE9DD1 /* Delta.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Delta.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
BFFA71DB1AAC406100EE9DD1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
BFFA71DB1AAC406100EE9DD1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
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>"; };
|
||||||
BFFC461B1D59823500AF2CC6 /* GamesPresentationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GamesPresentationController.swift; path = Segues/GamesPresentationController.swift; sourceTree = "<group>"; };
|
BFFC461B1D59823500AF2CC6 /* GamesPresentationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GamesPresentationController.swift; sourceTree = "<group>"; };
|
||||||
BFFC461C1D59823500AF2CC6 /* GamesStoryboardSegue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GamesStoryboardSegue.swift; path = Segues/GamesStoryboardSegue.swift; sourceTree = "<group>"; };
|
BFFC461C1D59823500AF2CC6 /* GamesStoryboardSegue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GamesStoryboardSegue.swift; sourceTree = "<group>"; };
|
||||||
BFFC461D1D59823500AF2CC6 /* InitialGamesStoryboardSegue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = InitialGamesStoryboardSegue.swift; path = Segues/InitialGamesStoryboardSegue.swift; sourceTree = "<group>"; };
|
BFFC461D1D59823500AF2CC6 /* InitialGamesStoryboardSegue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InitialGamesStoryboardSegue.swift; sourceTree = "<group>"; };
|
||||||
BFFC46221D5984A000AF2CC6 /* LaunchViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = LaunchViewController.swift; path = Launch/LaunchViewController.swift; sourceTree = "<group>"; };
|
BFFC46221D5984A000AF2CC6 /* LaunchViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LaunchViewController.swift; sourceTree = "<group>"; };
|
||||||
BFFC46451D59861000AF2CC6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
BFFC46451D59861000AF2CC6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||||
BFFC464B1D5998D600AF2CC6 /* CheatTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CheatTableViewCell.swift; path = "Pause Menu/Cheats/CheatTableViewCell.swift"; sourceTree = "<group>"; };
|
BFFC464B1D5998D600AF2CC6 /* CheatTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CheatTableViewCell.swift; sourceTree = "<group>"; };
|
||||||
C786AF1D2DDB6223BE2063CC /* Pods-Delta.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Delta.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Delta/Pods-Delta.debug.xcconfig"; sourceTree = "<group>"; };
|
C786AF1D2DDB6223BE2063CC /* Pods-Delta.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Delta.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Delta/Pods-Delta.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
@ -224,6 +267,7 @@
|
|||||||
BF99C6941D0A9AA600BA92BC /* SNESDeltaCore.framework in Frameworks */,
|
BF99C6941D0A9AA600BA92BC /* SNESDeltaCore.framework in Frameworks */,
|
||||||
BF70798C1B6B464B0019077C /* ZipZap.framework in Frameworks */,
|
BF70798C1B6B464B0019077C /* ZipZap.framework in Frameworks */,
|
||||||
BF0418141D01E93400E85BCF /* GBADeltaCore.framework in Frameworks */,
|
BF0418141D01E93400E85BCF /* GBADeltaCore.framework in Frameworks */,
|
||||||
|
BFF0742C1E9DC17500ACDF4A /* GBCDeltaCore.framework in Frameworks */,
|
||||||
4FE8465FD28810191C3E5212 /* Pods_Delta.framework in Frameworks */,
|
4FE8465FD28810191C3E5212 /* Pods_Delta.framework in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@ -238,14 +282,15 @@
|
|||||||
BF7AE8091C2E8C7600B1B5BC /* UIColor+Delta.swift */,
|
BF7AE8091C2E8C7600B1B5BC /* UIColor+Delta.swift */,
|
||||||
BFCEA67D1D56FF640061A534 /* UIViewControllerContextTransitioning+Conveniences.swift */,
|
BFCEA67D1D56FF640061A534 /* UIViewControllerContextTransitioning+Conveniences.swift */,
|
||||||
BF13A7571D5D2FD9000BB055 /* EmulatorCore+Cheats.swift */,
|
BF13A7571D5D2FD9000BB055 /* EmulatorCore+Cheats.swift */,
|
||||||
BF11734C1DA32A5200047DF8 /* GameType+Localization.swift */,
|
|
||||||
BF6866161DCAC8B900BF2D06 /* ControllerSkin+Configuring.swift */,
|
BF6866161DCAC8B900BF2D06 /* ControllerSkin+Configuring.swift */,
|
||||||
BF59428F1E09BD1A0051894B /* NSFetchedResultsController+Conveniences.h */,
|
BF59428F1E09BD1A0051894B /* NSFetchedResultsController+Conveniences.h */,
|
||||||
BF5942901E09BD1A0051894B /* NSFetchedResultsController+Conveniences.m */,
|
BF5942901E09BD1A0051894B /* NSFetchedResultsController+Conveniences.m */,
|
||||||
BF5942911E09BD1A0051894B /* NSManagedObject+Conveniences.swift */,
|
BF5942911E09BD1A0051894B /* NSManagedObject+Conveniences.swift */,
|
||||||
BF5942921E09BD1A0051894B /* NSManagedObjectContext+Conveniences.swift */,
|
BF5942921E09BD1A0051894B /* NSManagedObjectContext+Conveniences.swift */,
|
||||||
BFC314761E0C8CFC0056E3A8 /* GameType+Delta.swift */,
|
BFBAB2E21EB685A2004E0B0E /* DeltaCoreProtocol+Delta.swift */,
|
||||||
BF18B61E1E2985F900F70067 /* UIAlertController+Importing.swift */,
|
BF18B61E1E2985F900F70067 /* UIAlertController+Importing.swift */,
|
||||||
|
BFC6F7B71F435BC500221B96 /* Input+Display.swift */,
|
||||||
|
BF6424841F5CBDC900D6AB44 /* UIView+ParentViewController.swift */,
|
||||||
);
|
);
|
||||||
path = Extensions;
|
path = Extensions;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -254,18 +299,20 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
BF11734F1DA32CF600047DF8 /* ControllersSettingsViewController.swift */,
|
BF11734F1DA32CF600047DF8 /* ControllersSettingsViewController.swift */,
|
||||||
|
BF616A121F08184A0077F8B2 /* ControllerInputsViewController.swift */,
|
||||||
|
BF8DDD231F4F6C880088A21B /* InputCalloutView.swift */,
|
||||||
);
|
);
|
||||||
name = Controllers;
|
path = Controllers;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
BF1DAD5B1D9F574900E752A7 /* Controller Skins */ = {
|
BF1DAD5B1D9F574900E752A7 /* Controller Skins */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
BF1DAD5C1D9F576000E752A7 /* GameTypeControllerSkinsViewController.swift */,
|
BF1DAD5C1D9F576000E752A7 /* SystemControllerSkinsViewController.swift */,
|
||||||
BF04E6FE1DB8625C000F35D3 /* ControllerSkinsViewController.swift */,
|
BF04E6FE1DB8625C000F35D3 /* ControllerSkinsViewController.swift */,
|
||||||
BF99A5961DC2F9C400468E9E /* ControllerSkinTableViewCell.swift */,
|
BF99A5961DC2F9C400468E9E /* ControllerSkinTableViewCell.swift */,
|
||||||
);
|
);
|
||||||
name = "Controller Skins";
|
path = "Controller Skins";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
BF353FFB1C5DA2F600C1184C /* Presentation Controller */ = {
|
BF353FFB1C5DA2F600C1184C /* Presentation Controller */ = {
|
||||||
@ -274,7 +321,7 @@
|
|||||||
BF353FFD1C5DA3C500C1184C /* PausePresentationController.swift */,
|
BF353FFD1C5DA3C500C1184C /* PausePresentationController.swift */,
|
||||||
BF353FFE1C5DA3C500C1184C /* PausePresentationControllerContentView.xib */,
|
BF353FFE1C5DA3C500C1184C /* PausePresentationControllerContentView.xib */,
|
||||||
);
|
);
|
||||||
name = "Presentation Controller";
|
path = "Presentation Controller";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
BF3540031C5DA6D800C1184C /* Save States */ = {
|
BF3540031C5DA6D800C1184C /* Save States */ = {
|
||||||
@ -283,7 +330,7 @@
|
|||||||
BF3540041C5DA70400C1184C /* SaveStatesViewController.swift */,
|
BF3540041C5DA70400C1184C /* SaveStatesViewController.swift */,
|
||||||
BF2B98E51C97E32F00F6D57D /* SaveStatesCollectionHeaderView.swift */,
|
BF2B98E51C97E32F00F6D57D /* SaveStatesCollectionHeaderView.swift */,
|
||||||
);
|
);
|
||||||
name = "Save States";
|
path = "Save States";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
BF46894D1AAC469800A2586D /* Game Selection */ = {
|
BF46894D1AAC469800A2586D /* Game Selection */ = {
|
||||||
@ -299,12 +346,12 @@
|
|||||||
BF5942571E09BB5D0051894B /* Components */ = {
|
BF5942571E09BB5D0051894B /* Components */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
BF5942581E09BB810051894B /* Action.swift */,
|
BF4828871F90290F00028B97 /* Action.swift */,
|
||||||
BF59428C1E09BCE50051894B /* Importing */,
|
BFE0229C1F5B56840052D888 /* Popover Menu */,
|
||||||
BF5942671E09BBB70051894B /* Collection View */,
|
BF5942671E09BBB70051894B /* Collection View */,
|
||||||
BF5942601E09BBA80051894B /* Loading */,
|
BF5942601E09BBA80051894B /* Loading */,
|
||||||
);
|
);
|
||||||
name = Components;
|
path = Components;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
BF5942601E09BBA80051894B /* Loading */ = {
|
BF5942601E09BBA80051894B /* Loading */ = {
|
||||||
@ -313,7 +360,7 @@
|
|||||||
BF5942631E09BBB10051894B /* LoadImageURLOperation.swift */,
|
BF5942631E09BBB10051894B /* LoadImageURLOperation.swift */,
|
||||||
BF5942611E09BBB10051894B /* LoadControllerSkinImageOperation.swift */,
|
BF5942611E09BBB10051894B /* LoadControllerSkinImageOperation.swift */,
|
||||||
);
|
);
|
||||||
name = Loading;
|
path = Loading;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
BF5942671E09BBB70051894B /* Collection View */ = {
|
BF5942671E09BBB70051894B /* Collection View */ = {
|
||||||
@ -322,28 +369,31 @@
|
|||||||
BF5942681E09BBD00051894B /* GridCollectionViewCell.swift */,
|
BF5942681E09BBD00051894B /* GridCollectionViewCell.swift */,
|
||||||
BF5942691E09BBD00051894B /* GridCollectionViewLayout.swift */,
|
BF5942691E09BBD00051894B /* GridCollectionViewLayout.swift */,
|
||||||
);
|
);
|
||||||
name = "Collection View";
|
path = "Collection View";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
BF59426C1E09BC450051894B /* Database */ = {
|
BF59426C1E09BC450051894B /* Database */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
BF59426D1E09BC5D0051894B /* DatabaseManager.swift */,
|
BF59426D1E09BC5D0051894B /* DatabaseManager.swift */,
|
||||||
|
BFF93A9F1E0FB036005EC865 /* InputStreamOutputWriter.swift */,
|
||||||
BF5942711E09BC690051894B /* Model */,
|
BF5942711E09BC690051894B /* Model */,
|
||||||
BF95E2751E49763D0030E7AD /* OpenVGDB */,
|
BF95E2751E49763D0030E7AD /* OpenVGDB */,
|
||||||
);
|
);
|
||||||
name = Database;
|
path = Database;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
BF5942711E09BC690051894B /* Model */ = {
|
BF5942711E09BC690051894B /* Model */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
BF5942721E09BC700051894B /* Model.xcdatamodel */,
|
BF4828811F9027B600028B97 /* Delta.xcdatamodeld */,
|
||||||
BF5942741E09BC740051894B /* Human */,
|
BF5942741E09BC740051894B /* Human */,
|
||||||
BF5942751E09BC780051894B /* Machine */,
|
BF5942751E09BC780051894B /* Machine */,
|
||||||
|
BF6B82A31F7CC29A00042BFB /* Transformers */,
|
||||||
|
BFEF24F01F7DD4B600454C62 /* Migrations */,
|
||||||
BF5942761E09BC7C0051894B /* Misc */,
|
BF5942761E09BC7C0051894B /* Misc */,
|
||||||
);
|
);
|
||||||
name = Model;
|
path = Model;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
BF5942741E09BC740051894B /* Human */ = {
|
BF5942741E09BC740051894B /* Human */ = {
|
||||||
@ -353,9 +403,10 @@
|
|||||||
BF5942781E09BC830051894B /* ControllerSkin.swift */,
|
BF5942781E09BC830051894B /* ControllerSkin.swift */,
|
||||||
BF5942791E09BC830051894B /* Game.swift */,
|
BF5942791E09BC830051894B /* Game.swift */,
|
||||||
BF59427A1E09BC830051894B /* GameCollection.swift */,
|
BF59427A1E09BC830051894B /* GameCollection.swift */,
|
||||||
|
BF6EE5EA1F7C5F8F0051AD6C /* GameControllerInputMapping.swift */,
|
||||||
BF59427B1E09BC830051894B /* SaveState.swift */,
|
BF59427B1E09BC830051894B /* SaveState.swift */,
|
||||||
);
|
);
|
||||||
name = Human;
|
path = Human;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
BF5942751E09BC780051894B /* Machine */ = {
|
BF5942751E09BC780051894B /* Machine */ = {
|
||||||
@ -365,9 +416,10 @@
|
|||||||
BF5942821E09BC8B0051894B /* _ControllerSkin.swift */,
|
BF5942821E09BC8B0051894B /* _ControllerSkin.swift */,
|
||||||
BF5942831E09BC8B0051894B /* _Game.swift */,
|
BF5942831E09BC8B0051894B /* _Game.swift */,
|
||||||
BF5942841E09BC8B0051894B /* _GameCollection.swift */,
|
BF5942841E09BC8B0051894B /* _GameCollection.swift */,
|
||||||
|
BF6EE5E81F7C5F860051AD6C /* _GameControllerInputMapping.swift */,
|
||||||
BF5942851E09BC8B0051894B /* _SaveState.swift */,
|
BF5942851E09BC8B0051894B /* _SaveState.swift */,
|
||||||
);
|
);
|
||||||
name = Machine;
|
path = Machine;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
BF5942761E09BC7C0051894B /* Misc */ = {
|
BF5942761E09BC7C0051894B /* Misc */ = {
|
||||||
@ -375,16 +427,17 @@
|
|||||||
children = (
|
children = (
|
||||||
BF59428B1E09BC930051894B /* ControllerSkinConfigurations.h */,
|
BF59428B1E09BC930051894B /* ControllerSkinConfigurations.h */,
|
||||||
);
|
);
|
||||||
name = Misc;
|
path = Misc;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
BF59428C1E09BCE50051894B /* Importing */ = {
|
BF59428C1E09BCE50051894B /* Importing */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
BF59428D1E09BCFB0051894B /* ImportController.swift */,
|
BF59428D1E09BCFB0051894B /* ImportController.swift */,
|
||||||
BFF93A9F1E0FB036005EC865 /* InputStreamOutputWriter.swift */,
|
BF6BF3121EB7E47F008E83CD /* ImportOption.swift */,
|
||||||
|
BF6BF3161EB820F4008E83CD /* Import Options */,
|
||||||
);
|
);
|
||||||
name = Importing;
|
path = Importing;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
BF696B7E1D9B2AE6009639E0 /* Theming */ = {
|
BF696B7E1D9B2AE6009639E0 /* Theming */ = {
|
||||||
@ -392,7 +445,26 @@
|
|||||||
children = (
|
children = (
|
||||||
BF696B7F1D9B2B02009639E0 /* Theme.swift */,
|
BF696B7F1D9B2B02009639E0 /* Theme.swift */,
|
||||||
);
|
);
|
||||||
name = Theming;
|
path = Theming;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
BF6B82A31F7CC29A00042BFB /* Transformers */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
BF6B82A41F7CC2A300042BFB /* GameControllerInputMappingTransformer.swift */,
|
||||||
|
);
|
||||||
|
path = Transformers;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
BF6BF3161EB820F4008E83CD /* Import Options */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
BF6BF3171EB82111008E83CD /* iTunesImportOption.swift */,
|
||||||
|
BF6BF3191EB82146008E83CD /* ClipboardImportOption.swift */,
|
||||||
|
BF6BF3261EB87EB8008E83CD /* PhotoLibraryImportOption.swift */,
|
||||||
|
BF6BF31B1EB821A0008E83CD /* GamesDatabaseImportOption.swift */,
|
||||||
|
);
|
||||||
|
path = "Import Options";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
BF7AE7FA1C2E851F00B1B5BC /* Pause Menu */ = {
|
BF7AE7FA1C2E851F00B1B5BC /* Pause Menu */ = {
|
||||||
@ -400,14 +472,14 @@
|
|||||||
children = (
|
children = (
|
||||||
BF353FF41C5D837600C1184C /* PauseMenu.storyboard */,
|
BF353FF41C5D837600C1184C /* PauseMenu.storyboard */,
|
||||||
BF353FF11C5D7FB000C1184C /* PauseViewController.swift */,
|
BF353FF11C5D7FB000C1184C /* PauseViewController.swift */,
|
||||||
BF7AE8041C2E858400B1B5BC /* PauseMenuViewController.swift */,
|
BF7AE8041C2E858400B1B5BC /* GridMenuViewController.swift */,
|
||||||
BF353FF81C5D870B00C1184C /* PauseItem.swift */,
|
BF353FF81C5D870B00C1184C /* MenuItem.swift */,
|
||||||
BF3540031C5DA6D800C1184C /* Save States */,
|
BF3540031C5DA6D800C1184C /* Save States */,
|
||||||
BFC9B7371CEFCD08008629BB /* Cheats */,
|
BFC9B7371CEFCD08008629BB /* Cheats */,
|
||||||
BF353FFB1C5DA2F600C1184C /* Presentation Controller */,
|
BF353FFB1C5DA2F600C1184C /* Presentation Controller */,
|
||||||
BF912E481C5CB5D50041527C /* Segues */,
|
BF912E481C5CB5D50041527C /* Segues */,
|
||||||
);
|
);
|
||||||
name = "Pause Menu";
|
path = "Pause Menu";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
BF912E481C5CB5D50041527C /* Segues */ = {
|
BF912E481C5CB5D50041527C /* Segues */ = {
|
||||||
@ -416,23 +488,33 @@
|
|||||||
BF3540011C5DA3D500C1184C /* PauseStoryboardSegue.swift */,
|
BF3540011C5DA3D500C1184C /* PauseStoryboardSegue.swift */,
|
||||||
BF3540071C5DAFAD00C1184C /* PauseTransitionCoordinator.swift */,
|
BF3540071C5DAFAD00C1184C /* PauseTransitionCoordinator.swift */,
|
||||||
);
|
);
|
||||||
name = Segues;
|
path = Segues;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
BF930FFB1EB6D6EC00E8DBA0 /* Systems */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
BF4828851F9028F500028B97 /* System.swift */,
|
||||||
|
);
|
||||||
|
path = Systems;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
BF95E2751E49763D0030E7AD /* OpenVGDB */ = {
|
BF95E2751E49763D0030E7AD /* OpenVGDB */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
BF6BF31F1EB82362008E83CD /* GamesDatabase.storyboard */,
|
||||||
BF59426E1E09BC5D0051894B /* GamesDatabase.swift */,
|
BF59426E1E09BC5D0051894B /* GamesDatabase.swift */,
|
||||||
BF95E2761E4977BF0030E7AD /* GameMetadata.swift */,
|
BF95E2761E4977BF0030E7AD /* GameMetadata.swift */,
|
||||||
BF95E2781E4982A10030E7AD /* GamesDatabaseBrowserViewController.swift */,
|
BF95E2781E4982A10030E7AD /* GamesDatabaseBrowserViewController.swift */,
|
||||||
BFFA4C081E8A24D600D87934 /* GameMetadataTableViewCell.swift */,
|
BFFA4C081E8A24D600D87934 /* GameMetadataTableViewCell.swift */,
|
||||||
);
|
);
|
||||||
name = OpenVGDB;
|
path = OpenVGDB;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
BF9F4FCD1AAD7B25004C9500 /* Frameworks */ = {
|
BF9F4FCD1AAD7B25004C9500 /* Frameworks */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
BFF0742B1E9DC17500ACDF4A /* GBCDeltaCore.framework */,
|
||||||
BF0418131D01E93400E85BCF /* GBADeltaCore.framework */,
|
BF0418131D01E93400E85BCF /* GBADeltaCore.framework */,
|
||||||
BF27CC941BCB7B7A00A20D89 /* GameController.framework */,
|
BF27CC941BCB7B7A00A20D89 /* GameController.framework */,
|
||||||
BF27CC8A1BC9FE4D00A20D89 /* Pods.framework */,
|
BF27CC8A1BC9FE4D00A20D89 /* Pods.framework */,
|
||||||
@ -467,7 +549,17 @@
|
|||||||
BF34FA101CF1899D006624C7 /* CheatTextView.swift */,
|
BF34FA101CF1899D006624C7 /* CheatTextView.swift */,
|
||||||
BF31878A1D489AAA00BD020D /* CheatValidator.swift */,
|
BF31878A1D489AAA00BD020D /* CheatValidator.swift */,
|
||||||
);
|
);
|
||||||
name = Cheats;
|
path = Cheats;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
BFE0229C1F5B56840052D888 /* Popover Menu */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
BF8CA9351F5F651900499FDD /* PopoverMenuController.swift */,
|
||||||
|
BFE0229F1F5B577D0052D888 /* PopoverMenuButton.swift */,
|
||||||
|
BF6424821F5B8F3F00D6AB44 /* ListMenuViewController.swift */,
|
||||||
|
);
|
||||||
|
path = "Popover Menu";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
BFEC732F1AAECCBD00650035 /* Resources */ = {
|
BFEC732F1AAECCBD00650035 /* Resources */ = {
|
||||||
@ -479,12 +571,29 @@
|
|||||||
path = Resources;
|
path = Resources;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
BFEF24F01F7DD4B600454C62 /* Migrations */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
BFDC7B9E1F7DE0CF0052A7C5 /* Delta.xcmappingmodel */,
|
||||||
|
BFEF24F11F7DD4BE00454C62 /* Policies */,
|
||||||
|
);
|
||||||
|
path = Migrations;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
BFEF24F11F7DD4BE00454C62 /* Policies */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
BFEF24F21F7DD4FB00454C62 /* SaveStateMigrationPolicy.swift */,
|
||||||
|
);
|
||||||
|
path = Policies;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
BFFA71CE1AAC406100EE9DD1 = {
|
BFFA71CE1AAC406100EE9DD1 = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
BFFA71D91AAC406100EE9DD1 /* Delta */,
|
BFFA71D91AAC406100EE9DD1 /* Delta */,
|
||||||
BFEC732F1AAECCBD00650035 /* Resources */,
|
|
||||||
BF9F4FCD1AAD7B25004C9500 /* Frameworks */,
|
BF9F4FCD1AAD7B25004C9500 /* Frameworks */,
|
||||||
|
BFEC732F1AAECCBD00650035 /* Resources */,
|
||||||
BFFA71D81AAC406100EE9DD1 /* Products */,
|
BFFA71D81AAC406100EE9DD1 /* Products */,
|
||||||
FD1E8AE87FA2DB8793F7B937 /* Pods */,
|
FD1E8AE87FA2DB8793F7B937 /* Pods */,
|
||||||
);
|
);
|
||||||
@ -509,6 +618,8 @@
|
|||||||
BF7AE7FA1C2E851F00B1B5BC /* Pause Menu */,
|
BF7AE7FA1C2E851F00B1B5BC /* Pause Menu */,
|
||||||
BFAA1FEB1B8AA4E800495943 /* Settings */,
|
BFAA1FEB1B8AA4E800495943 /* Settings */,
|
||||||
BF59426C1E09BC450051894B /* Database */,
|
BF59426C1E09BC450051894B /* Database */,
|
||||||
|
BF59428C1E09BCE50051894B /* Importing */,
|
||||||
|
BF930FFB1EB6D6EC00E8DBA0 /* Systems */,
|
||||||
BF5942571E09BB5D0051894B /* Components */,
|
BF5942571E09BB5D0051894B /* Components */,
|
||||||
BF696B7E1D9B2AE6009639E0 /* Theming */,
|
BF696B7E1D9B2AE6009639E0 /* Theming */,
|
||||||
BF090CEE1B490C1A00DCAB45 /* Extensions */,
|
BF090CEE1B490C1A00DCAB45 /* Extensions */,
|
||||||
@ -532,6 +643,7 @@
|
|||||||
children = (
|
children = (
|
||||||
BF63BDE91D389EEB00FCB040 /* GameViewController.swift */,
|
BF63BDE91D389EEB00FCB040 /* GameViewController.swift */,
|
||||||
BF13A7551D5D29B0000BB055 /* PreviewGameViewController.swift */,
|
BF13A7551D5D29B0000BB055 /* PreviewGameViewController.swift */,
|
||||||
|
BF15AF831F54B43B009B6AAB /* ActionInput.swift */,
|
||||||
);
|
);
|
||||||
path = Emulation;
|
path = Emulation;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -544,7 +656,7 @@
|
|||||||
BFFC461B1D59823500AF2CC6 /* GamesPresentationController.swift */,
|
BFFC461B1D59823500AF2CC6 /* GamesPresentationController.swift */,
|
||||||
BFE4269D1D9C68E600DC913F /* SaveStatesStoryboardSegue.swift */,
|
BFE4269D1D9C68E600DC913F /* SaveStatesStoryboardSegue.swift */,
|
||||||
);
|
);
|
||||||
name = Segues;
|
path = Segues;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
BFFC46211D59848000AF2CC6 /* Launch */ = {
|
BFFC46211D59848000AF2CC6 /* Launch */ = {
|
||||||
@ -553,7 +665,7 @@
|
|||||||
BFFC46441D59861000AF2CC6 /* LaunchScreen.storyboard */,
|
BFFC46441D59861000AF2CC6 /* LaunchScreen.storyboard */,
|
||||||
BFFC46221D5984A000AF2CC6 /* LaunchViewController.swift */,
|
BFFC46221D5984A000AF2CC6 /* LaunchViewController.swift */,
|
||||||
);
|
);
|
||||||
name = Launch;
|
path = Launch;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
FD1E8AE87FA2DB8793F7B937 /* Pods */ = {
|
FD1E8AE87FA2DB8793F7B937 /* Pods */ = {
|
||||||
@ -579,6 +691,7 @@
|
|||||||
BF9F4FCC1AAD7AEE004C9500 /* Embed Frameworks */,
|
BF9F4FCC1AAD7AEE004C9500 /* Embed Frameworks */,
|
||||||
B444B2BB31CBCEE7D86E943D /* [CP] Embed Pods Frameworks */,
|
B444B2BB31CBCEE7D86E943D /* [CP] Embed Pods Frameworks */,
|
||||||
3521C8FA1BB6004A6E9FE324 /* [CP] Copy Pods Resources */,
|
3521C8FA1BB6004A6E9FE324 /* [CP] Copy Pods Resources */,
|
||||||
|
BF6BF3281EB897F6008E83CD /* Fabric */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
@ -596,7 +709,7 @@
|
|||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
LastSwiftUpdateCheck = 0700;
|
LastSwiftUpdateCheck = 0700;
|
||||||
LastUpgradeCheck = 0820;
|
LastUpgradeCheck = 0910;
|
||||||
ORGANIZATIONNAME = "Riley Testut";
|
ORGANIZATIONNAME = "Riley Testut";
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
BF14D8941DE7A512002CA1BE = {
|
BF14D8941DE7A512002CA1BE = {
|
||||||
@ -607,7 +720,8 @@
|
|||||||
BFFA71D61AAC406100EE9DD1 = {
|
BFFA71D61AAC406100EE9DD1 = {
|
||||||
CreatedOnToolsVersion = 6.3;
|
CreatedOnToolsVersion = 6.3;
|
||||||
DevelopmentTeam = 6XVY5G3U44;
|
DevelopmentTeam = 6XVY5G3U44;
|
||||||
LastSwiftMigration = 0800;
|
LastSwiftMigration = 0900;
|
||||||
|
ProvisioningStyle = Automatic;
|
||||||
SystemCapabilities = {
|
SystemCapabilities = {
|
||||||
com.apple.iCloud = {
|
com.apple.iCloud = {
|
||||||
enabled = 1;
|
enabled = 1;
|
||||||
@ -641,6 +755,7 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
BFFA71E21AAC406100EE9DD1 /* Main.storyboard in Resources */,
|
BFFA71E21AAC406100EE9DD1 /* Main.storyboard in Resources */,
|
||||||
|
BF6BF3211EB82362008E83CD /* GamesDatabase.storyboard in Resources */,
|
||||||
BF3540001C5DA3C500C1184C /* PausePresentationControllerContentView.xib in Resources */,
|
BF3540001C5DA3C500C1184C /* PausePresentationControllerContentView.xib in Resources */,
|
||||||
BF5E7F461B9A652600AE44F8 /* Settings.storyboard in Resources */,
|
BF5E7F461B9A652600AE44F8 /* Settings.storyboard in Resources */,
|
||||||
BF02D5DA1DDEBB3000A5E131 /* openvgdb.sqlite in Resources */,
|
BF02D5DA1DDEBB3000A5E131 /* openvgdb.sqlite in Resources */,
|
||||||
@ -697,6 +812,20 @@
|
|||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "/usr/local/bin/mogenerator -m \"Delta/Database/Model/Model.xcdatamodel\" --human-dir \"Delta/Database/Model/Human\" --machine-dir \"Delta/Database/Model/Machine\" --swift --template-var scalarsWhenNonOptional=true --template-path \"Delta/Database/Model/mogenerator/templates\"";
|
shellScript = "/usr/local/bin/mogenerator -m \"Delta/Database/Model/Model.xcdatamodel\" --human-dir \"Delta/Database/Model/Human\" --machine-dir \"Delta/Database/Model/Machine\" --swift --template-var scalarsWhenNonOptional=true --template-path \"Delta/Database/Model/mogenerator/templates\"";
|
||||||
};
|
};
|
||||||
|
BF6BF3281EB897F6008E83CD /* Fabric */ = {
|
||||||
|
isa = PBXShellScriptBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
inputPaths = (
|
||||||
|
);
|
||||||
|
name = Fabric;
|
||||||
|
outputPaths = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
shellPath = /bin/sh;
|
||||||
|
shellScript = "\"${PODS_ROOT}/Fabric/run\" d542629b4f6625cfd5564d27318550321272076d 333118df9345dcec21e4ba0bb7fa8f6c67c4eb41734374e24f6c71a8dcd5c870";
|
||||||
|
};
|
||||||
DBD91E7D7EC2729786B4C5B1 /* [CP] Check Pods Manifest.lock */ = {
|
DBD91E7D7EC2729786B4C5B1 /* [CP] Check Pods Manifest.lock */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
@ -709,7 +838,7 @@
|
|||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n";
|
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n";
|
||||||
showEnvVarsInLog = 0;
|
showEnvVarsInLog = 0;
|
||||||
};
|
};
|
||||||
/* End PBXShellScriptBuildPhase section */
|
/* End PBXShellScriptBuildPhase section */
|
||||||
@ -719,8 +848,12 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
BFC6F7B81F435BC500221B96 /* Input+Display.swift in Sources */,
|
||||||
BF59426A1E09BBD00051894B /* GridCollectionViewCell.swift in Sources */,
|
BF59426A1E09BBD00051894B /* GridCollectionViewCell.swift in Sources */,
|
||||||
|
BF6BF3181EB82111008E83CD /* iTunesImportOption.swift in Sources */,
|
||||||
|
BFF6452E1F7CC5060056533E /* GameControllerInputMappingTransformer.swift in Sources */,
|
||||||
BF59427C1E09BC830051894B /* Cheat.swift in Sources */,
|
BF59427C1E09BC830051894B /* Cheat.swift in Sources */,
|
||||||
|
BFBAB2E31EB685A2004E0B0E /* DeltaCoreProtocol+Delta.swift in Sources */,
|
||||||
BF3540081C5DAFAD00C1184C /* PauseTransitionCoordinator.swift in Sources */,
|
BF3540081C5DAFAD00C1184C /* PauseTransitionCoordinator.swift in Sources */,
|
||||||
BF59427E1E09BC830051894B /* Game.swift in Sources */,
|
BF59427E1E09BC830051894B /* Game.swift in Sources */,
|
||||||
BFAA1FED1B8AA4FA00495943 /* Settings.swift in Sources */,
|
BFAA1FED1B8AA4FA00495943 /* Settings.swift in Sources */,
|
||||||
@ -729,27 +862,34 @@
|
|||||||
BF5942801E09BC830051894B /* SaveState.swift in Sources */,
|
BF5942801E09BC830051894B /* SaveState.swift in Sources */,
|
||||||
BF59428E1E09BCFB0051894B /* ImportController.swift in Sources */,
|
BF59428E1E09BCFB0051894B /* ImportController.swift in Sources */,
|
||||||
BF13A7581D5D2FD9000BB055 /* EmulatorCore+Cheats.swift in Sources */,
|
BF13A7581D5D2FD9000BB055 /* EmulatorCore+Cheats.swift in Sources */,
|
||||||
|
BF6424831F5B8F3F00D6AB44 /* ListMenuViewController.swift in Sources */,
|
||||||
|
BF6BF3131EB7E47F008E83CD /* ImportOption.swift in Sources */,
|
||||||
BF31878B1D489AAA00BD020D /* CheatValidator.swift in Sources */,
|
BF31878B1D489AAA00BD020D /* CheatValidator.swift in Sources */,
|
||||||
BFFC46201D59823500AF2CC6 /* InitialGamesStoryboardSegue.swift in Sources */,
|
BFFC46201D59823500AF2CC6 /* InitialGamesStoryboardSegue.swift in Sources */,
|
||||||
BF5942731E09BC700051894B /* Model.xcdatamodel in Sources */,
|
BF15AF841F54B43B009B6AAB /* ActionInput.swift in Sources */,
|
||||||
|
BF4828841F9027B600028B97 /* Delta.xcdatamodeld in Sources */,
|
||||||
BF5942951E09BD1A0051894B /* NSManagedObjectContext+Conveniences.swift in Sources */,
|
BF5942951E09BD1A0051894B /* NSManagedObjectContext+Conveniences.swift in Sources */,
|
||||||
BF353FF91C5D870B00C1184C /* PauseItem.swift in Sources */,
|
BF353FF91C5D870B00C1184C /* MenuItem.swift in Sources */,
|
||||||
BF18B61F1E2985F900F70067 /* UIAlertController+Importing.swift in Sources */,
|
BF18B61F1E2985F900F70067 /* UIAlertController+Importing.swift in Sources */,
|
||||||
BFDD04F11D5E2C27002D450E /* GameCollectionViewController.swift in Sources */,
|
BFDD04F11D5E2C27002D450E /* GameCollectionViewController.swift in Sources */,
|
||||||
BFE4269E1D9C68E600DC913F /* SaveStatesStoryboardSegue.swift in Sources */,
|
BFE4269E1D9C68E600DC913F /* SaveStatesStoryboardSegue.swift in Sources */,
|
||||||
BFFA71DD1AAC406100EE9DD1 /* AppDelegate.swift in Sources */,
|
BFFA71DD1AAC406100EE9DD1 /* AppDelegate.swift in Sources */,
|
||||||
BF59426B1E09BBD00051894B /* GridCollectionViewLayout.swift in Sources */,
|
BF59426B1E09BBD00051894B /* GridCollectionViewLayout.swift in Sources */,
|
||||||
|
BF6424851F5CBDC900D6AB44 /* UIView+ParentViewController.swift in Sources */,
|
||||||
|
BFDC7B9F1F7DE0CF0052A7C5 /* Delta.xcmappingmodel in Sources */,
|
||||||
BF04E6FF1DB8625C000F35D3 /* ControllerSkinsViewController.swift in Sources */,
|
BF04E6FF1DB8625C000F35D3 /* ControllerSkinsViewController.swift in Sources */,
|
||||||
BF5942891E09BC8B0051894B /* _GameCollection.swift in Sources */,
|
BF5942891E09BC8B0051894B /* _GameCollection.swift in Sources */,
|
||||||
BF34FA111CF1899D006624C7 /* CheatTextView.swift in Sources */,
|
BF34FA111CF1899D006624C7 /* CheatTextView.swift in Sources */,
|
||||||
BF1DAD5D1D9F576000E752A7 /* GameTypeControllerSkinsViewController.swift in Sources */,
|
BF1DAD5D1D9F576000E752A7 /* SystemControllerSkinsViewController.swift in Sources */,
|
||||||
|
BFE022A01F5B57FF0052D888 /* PopoverMenuButton.swift in Sources */,
|
||||||
|
BF6BF31C1EB821A0008E83CD /* GamesDatabaseImportOption.swift in Sources */,
|
||||||
BFFA4C091E8A24D600D87934 /* GameMetadataTableViewCell.swift in Sources */,
|
BFFA4C091E8A24D600D87934 /* GameMetadataTableViewCell.swift in Sources */,
|
||||||
BFFC46231D5984A000AF2CC6 /* LaunchViewController.swift in Sources */,
|
BFFC46231D5984A000AF2CC6 /* LaunchViewController.swift in Sources */,
|
||||||
|
BF8DDD241F4F6C880088A21B /* InputCalloutView.swift in Sources */,
|
||||||
BF5942701E09BC5D0051894B /* GamesDatabase.swift in Sources */,
|
BF5942701E09BC5D0051894B /* GamesDatabase.swift in Sources */,
|
||||||
BF34FA071CF0F510006624C7 /* EditCheatViewController.swift in Sources */,
|
BF34FA071CF0F510006624C7 /* EditCheatViewController.swift in Sources */,
|
||||||
BF5942881E09BC8B0051894B /* _Game.swift in Sources */,
|
BF5942881E09BC8B0051894B /* _Game.swift in Sources */,
|
||||||
BF95E2771E4977BF0030E7AD /* GameMetadata.swift in Sources */,
|
BF95E2771E4977BF0030E7AD /* GameMetadata.swift in Sources */,
|
||||||
BF11734D1DA32A5200047DF8 /* GameType+Localization.swift in Sources */,
|
|
||||||
BFFC461F1D59823500AF2CC6 /* GamesStoryboardSegue.swift in Sources */,
|
BFFC461F1D59823500AF2CC6 /* GamesStoryboardSegue.swift in Sources */,
|
||||||
BF2B98E61C97E32F00F6D57D /* SaveStatesCollectionHeaderView.swift in Sources */,
|
BF2B98E61C97E32F00F6D57D /* SaveStatesCollectionHeaderView.swift in Sources */,
|
||||||
BFC9B7391CEFCD34008629BB /* CheatsViewController.swift in Sources */,
|
BFC9B7391CEFCD34008629BB /* CheatsViewController.swift in Sources */,
|
||||||
@ -758,14 +898,17 @@
|
|||||||
BF5942941E09BD1A0051894B /* NSManagedObject+Conveniences.swift in Sources */,
|
BF5942941E09BD1A0051894B /* NSManagedObject+Conveniences.swift in Sources */,
|
||||||
BF7AE80A1C2E8C7600B1B5BC /* UIColor+Delta.swift in Sources */,
|
BF7AE80A1C2E8C7600B1B5BC /* UIColor+Delta.swift in Sources */,
|
||||||
BF797A2D1C2D339F00F1A000 /* UILabel+FontSize.swift in Sources */,
|
BF797A2D1C2D339F00F1A000 /* UILabel+FontSize.swift in Sources */,
|
||||||
|
BF80E1D21F13117000847008 /* ControllerInputsViewController.swift in Sources */,
|
||||||
BF5942641E09BBB10051894B /* LoadControllerSkinImageOperation.swift in Sources */,
|
BF5942641E09BBB10051894B /* LoadControllerSkinImageOperation.swift in Sources */,
|
||||||
|
BF6EE5E91F7C5F860051AD6C /* _GameControllerInputMapping.swift in Sources */,
|
||||||
BF5942871E09BC8B0051894B /* _ControllerSkin.swift in Sources */,
|
BF5942871E09BC8B0051894B /* _ControllerSkin.swift in Sources */,
|
||||||
BF95E2791E4982A10030E7AD /* GamesDatabaseBrowserViewController.swift in Sources */,
|
BF95E2791E4982A10030E7AD /* GamesDatabaseBrowserViewController.swift in Sources */,
|
||||||
BFD097211D3A01B8005A44C2 /* SaveStatesViewController.swift in Sources */,
|
BFD097211D3A01B8005A44C2 /* SaveStatesViewController.swift in Sources */,
|
||||||
|
BF6EE5EB1F7C5F8F0051AD6C /* GameControllerInputMapping.swift in Sources */,
|
||||||
BF3540021C5DA3D500C1184C /* PauseStoryboardSegue.swift in Sources */,
|
BF3540021C5DA3D500C1184C /* PauseStoryboardSegue.swift in Sources */,
|
||||||
BFC314771E0C8CFC0056E3A8 /* GameType+Delta.swift in Sources */,
|
|
||||||
BF107EC41BF413F000E0C32C /* GamesViewController.swift in Sources */,
|
BF107EC41BF413F000E0C32C /* GamesViewController.swift in Sources */,
|
||||||
BF59426F1E09BC5D0051894B /* DatabaseManager.swift in Sources */,
|
BF59426F1E09BC5D0051894B /* DatabaseManager.swift in Sources */,
|
||||||
|
BF4828861F9028F500028B97 /* System.swift in Sources */,
|
||||||
BF13A7561D5D29B0000BB055 /* PreviewGameViewController.swift in Sources */,
|
BF13A7561D5D29B0000BB055 /* PreviewGameViewController.swift in Sources */,
|
||||||
BF6866171DCAC8B900BF2D06 /* ControllerSkin+Configuring.swift in Sources */,
|
BF6866171DCAC8B900BF2D06 /* ControllerSkin+Configuring.swift in Sources */,
|
||||||
BF59427D1E09BC830051894B /* ControllerSkin.swift in Sources */,
|
BF59427D1E09BC830051894B /* ControllerSkin.swift in Sources */,
|
||||||
@ -775,12 +918,16 @@
|
|||||||
BF99A5971DC2F9C400468E9E /* ControllerSkinTableViewCell.swift in Sources */,
|
BF99A5971DC2F9C400468E9E /* ControllerSkinTableViewCell.swift in Sources */,
|
||||||
BF5942861E09BC8B0051894B /* _Cheat.swift in Sources */,
|
BF5942861E09BC8B0051894B /* _Cheat.swift in Sources */,
|
||||||
BF5E7F441B9A650B00AE44F8 /* SettingsViewController.swift in Sources */,
|
BF5E7F441B9A650B00AE44F8 /* SettingsViewController.swift in Sources */,
|
||||||
|
BF8CA9361F5F651900499FDD /* PopoverMenuController.swift in Sources */,
|
||||||
|
BFEF24F31F7DD4FD00454C62 /* SaveStateMigrationPolicy.swift in Sources */,
|
||||||
BF5942931E09BD1A0051894B /* NSFetchedResultsController+Conveniences.m in Sources */,
|
BF5942931E09BD1A0051894B /* NSFetchedResultsController+Conveniences.m in Sources */,
|
||||||
|
BF4828881F90290F00028B97 /* Action.swift in Sources */,
|
||||||
|
BF6BF3271EB87EB8008E83CD /* PhotoLibraryImportOption.swift in Sources */,
|
||||||
BF5942661E09BBB10051894B /* LoadImageURLOperation.swift in Sources */,
|
BF5942661E09BBB10051894B /* LoadImageURLOperation.swift in Sources */,
|
||||||
BF353FF21C5D7FB000C1184C /* PauseViewController.swift in Sources */,
|
BF353FF21C5D7FB000C1184C /* PauseViewController.swift in Sources */,
|
||||||
BF59425C1E09BB810051894B /* Action.swift in Sources */,
|
|
||||||
BF696B801D9B2B02009639E0 /* Theme.swift in Sources */,
|
BF696B801D9B2B02009639E0 /* Theme.swift in Sources */,
|
||||||
BF7AE8081C2E858400B1B5BC /* PauseMenuViewController.swift in Sources */,
|
BF7AE8081C2E858400B1B5BC /* GridMenuViewController.swift in Sources */,
|
||||||
|
BF6BF31A1EB82146008E83CD /* ClipboardImportOption.swift in Sources */,
|
||||||
BFF93AA01E0FB036005EC865 /* InputStreamOutputWriter.swift in Sources */,
|
BFF93AA01E0FB036005EC865 /* InputStreamOutputWriter.swift in Sources */,
|
||||||
BF59427F1E09BC830051894B /* GameCollection.swift in Sources */,
|
BF59427F1E09BC830051894B /* GameCollection.swift in Sources */,
|
||||||
);
|
);
|
||||||
@ -795,7 +942,17 @@
|
|||||||
BF353FF51C5D837600C1184C /* Base */,
|
BF353FF51C5D837600C1184C /* Base */,
|
||||||
);
|
);
|
||||||
name = PauseMenu.storyboard;
|
name = PauseMenu.storyboard;
|
||||||
sourceTree = "<group>";
|
path = Delta;
|
||||||
|
sourceTree = SOURCE_ROOT;
|
||||||
|
};
|
||||||
|
BF6BF31F1EB82362008E83CD /* GamesDatabase.storyboard */ = {
|
||||||
|
isa = PBXVariantGroup;
|
||||||
|
children = (
|
||||||
|
BF6BF3201EB82362008E83CD /* Base */,
|
||||||
|
);
|
||||||
|
name = GamesDatabase.storyboard;
|
||||||
|
path = Delta;
|
||||||
|
sourceTree = SOURCE_ROOT;
|
||||||
};
|
};
|
||||||
BFFA71E01AAC406100EE9DD1 /* Main.storyboard */ = {
|
BFFA71E01AAC406100EE9DD1 /* Main.storyboard */ = {
|
||||||
isa = PBXVariantGroup;
|
isa = PBXVariantGroup;
|
||||||
@ -803,8 +960,8 @@
|
|||||||
BFFA71E11AAC406100EE9DD1 /* Base */,
|
BFFA71E11AAC406100EE9DD1 /* Base */,
|
||||||
);
|
);
|
||||||
name = Main.storyboard;
|
name = Main.storyboard;
|
||||||
path = .;
|
path = Delta;
|
||||||
sourceTree = "<group>";
|
sourceTree = SOURCE_ROOT;
|
||||||
};
|
};
|
||||||
BFFC46441D59861000AF2CC6 /* LaunchScreen.storyboard */ = {
|
BFFC46441D59861000AF2CC6 /* LaunchScreen.storyboard */ = {
|
||||||
isa = PBXVariantGroup;
|
isa = PBXVariantGroup;
|
||||||
@ -812,7 +969,8 @@
|
|||||||
BFFC46451D59861000AF2CC6 /* Base */,
|
BFFC46451D59861000AF2CC6 /* Base */,
|
||||||
);
|
);
|
||||||
name = LaunchScreen.storyboard;
|
name = LaunchScreen.storyboard;
|
||||||
sourceTree = "<group>";
|
path = Delta;
|
||||||
|
sourceTree = SOURCE_ROOT;
|
||||||
};
|
};
|
||||||
/* End PBXVariantGroup section */
|
/* End PBXVariantGroup section */
|
||||||
|
|
||||||
@ -841,20 +999,26 @@
|
|||||||
CLANG_CXX_LIBRARY = "libc++";
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CLANG_ENABLE_OBJC_ARC = YES;
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
CLANG_WARN_EMPTY_BODY = YES;
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
CLANG_WARN_INT_CONVERSION = YES;
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
ENABLE_TESTABILITY = YES;
|
ENABLE_TESTABILITY = YES;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||||
@ -891,14 +1055,20 @@
|
|||||||
CLANG_CXX_LIBRARY = "libc++";
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CLANG_ENABLE_OBJC_ARC = YES;
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
CLANG_WARN_EMPTY_BODY = YES;
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
CLANG_WARN_INT_CONVERSION = YES;
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
@ -934,12 +1104,17 @@
|
|||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = "Delta/Supporting Files/Delta.entitlements";
|
CODE_SIGN_ENTITLEMENTS = "Delta/Supporting Files/Delta.entitlements";
|
||||||
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
|
DEVELOPMENT_TEAM = 6XVY5G3U44;
|
||||||
INFOPLIST_FILE = "Delta/Supporting Files/Info.plist";
|
INFOPLIST_FILE = "Delta/Supporting Files/Info.plist";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||||
|
PROVISIONING_PROFILE = "";
|
||||||
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Delta/Supporting Files/Delta-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Delta/Supporting Files/Delta-Bridging-Header.h";
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
SWIFT_VERSION = 3.0;
|
SWIFT_SWIFT3_OBJC_INFERENCE = Off;
|
||||||
|
SWIFT_VERSION = 4.0;
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
};
|
};
|
||||||
@ -950,12 +1125,18 @@
|
|||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = "Delta/Supporting Files/Delta.entitlements";
|
CODE_SIGN_ENTITLEMENTS = "Delta/Supporting Files/Delta.entitlements";
|
||||||
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
|
DEVELOPMENT_TEAM = 6XVY5G3U44;
|
||||||
INFOPLIST_FILE = "Delta/Supporting Files/Info.plist";
|
INFOPLIST_FILE = "Delta/Supporting Files/Info.plist";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||||
|
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" -DIMPACTOR";
|
||||||
|
PROVISIONING_PROFILE = "";
|
||||||
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Delta/Supporting Files/Delta-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Delta/Supporting Files/Delta-Bridging-Header.h";
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||||
SWIFT_VERSION = 3.0;
|
SWIFT_SWIFT3_OBJC_INFERENCE = Off;
|
||||||
|
SWIFT_VERSION = 4.0;
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
};
|
};
|
||||||
@ -990,6 +1171,20 @@
|
|||||||
defaultConfigurationName = Release;
|
defaultConfigurationName = Release;
|
||||||
};
|
};
|
||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
|
|
||||||
|
/* Begin XCVersionGroup section */
|
||||||
|
BF4828811F9027B600028B97 /* Delta.xcdatamodeld */ = {
|
||||||
|
isa = XCVersionGroup;
|
||||||
|
children = (
|
||||||
|
BF4828821F9027B600028B97 /* Delta 2.xcdatamodel */,
|
||||||
|
BF4828831F9027B600028B97 /* Delta.xcdatamodel */,
|
||||||
|
);
|
||||||
|
currentVersion = BF4828821F9027B600028B97 /* Delta 2.xcdatamodel */;
|
||||||
|
path = Delta.xcdatamodeld;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
versionGroupType = wrapper.xcdatamodel;
|
||||||
|
};
|
||||||
|
/* End XCVersionGroup section */
|
||||||
};
|
};
|
||||||
rootObject = BFFA71CF1AAC406100EE9DD1 /* Project object */;
|
rootObject = BFFA71CF1AAC406100EE9DD1 /* Project object */;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "0820"
|
LastUpgradeVersion = "0910"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "NO"
|
parallelizeBuildables = "NO"
|
||||||
@ -62,6 +62,20 @@
|
|||||||
ReferencedContainer = "container:Cores/GBADeltaCore/GBADeltaCore.xcodeproj">
|
ReferencedContainer = "container:Cores/GBADeltaCore/GBADeltaCore.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</BuildActionEntry>
|
</BuildActionEntry>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "BF8F2AAD1E9C879300F89F15"
|
||||||
|
BuildableName = "GBCDeltaCore.framework"
|
||||||
|
BlueprintName = "GBCDeltaCore"
|
||||||
|
ReferencedContainer = "container:Cores/GBCDeltaCore/GBCDeltaCore.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
<BuildActionEntry
|
<BuildActionEntry
|
||||||
buildForTesting = "YES"
|
buildForTesting = "YES"
|
||||||
buildForRunning = "YES"
|
buildForRunning = "YES"
|
||||||
@ -82,6 +96,7 @@
|
|||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
language = ""
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
<Testables>
|
<Testables>
|
||||||
</Testables>
|
</Testables>
|
||||||
@ -101,6 +116,7 @@
|
|||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
language = ""
|
||||||
launchStyle = "0"
|
launchStyle = "0"
|
||||||
useCustomWorkingDirectory = "NO"
|
useCustomWorkingDirectory = "NO"
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
|||||||
3
Delta.xcworkspace/contents.xcworkspacedata
generated
3
Delta.xcworkspace/contents.xcworkspacedata
generated
@ -13,6 +13,9 @@
|
|||||||
<FileRef
|
<FileRef
|
||||||
location = "group:Cores/GBADeltaCore/GBADeltaCore.xcodeproj">
|
location = "group:Cores/GBADeltaCore/GBADeltaCore.xcodeproj">
|
||||||
</FileRef>
|
</FileRef>
|
||||||
|
<FileRef
|
||||||
|
location = "group:Cores/GBCDeltaCore/GBCDeltaCore.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
<FileRef
|
<FileRef
|
||||||
location = "group:External/Roxas/Roxas.xcodeproj">
|
location = "group:External/Roxas/Roxas.xcodeproj">
|
||||||
</FileRef>
|
</FileRef>
|
||||||
|
|||||||
@ -9,8 +9,9 @@
|
|||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
import DeltaCore
|
import DeltaCore
|
||||||
import SNESDeltaCore
|
|
||||||
import GBADeltaCore
|
import Fabric
|
||||||
|
import Crashlytics
|
||||||
|
|
||||||
@UIApplicationMain
|
@UIApplicationMain
|
||||||
class AppDelegate: UIResponder, UIApplicationDelegate
|
class AppDelegate: UIResponder, UIApplicationDelegate
|
||||||
@ -19,10 +20,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate
|
|||||||
|
|
||||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool
|
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool
|
||||||
{
|
{
|
||||||
|
Fabric.with([Crashlytics.self])
|
||||||
|
|
||||||
Settings.registerDefaults()
|
Settings.registerDefaults()
|
||||||
|
|
||||||
Delta.register(SNES.core)
|
System.supportedSystems.forEach { Delta.register($0.deltaCore) }
|
||||||
Delta.register(GBA.core)
|
|
||||||
|
|
||||||
self.configureAppearance()
|
self.configureAppearance()
|
||||||
|
|
||||||
@ -38,7 +40,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Controllers
|
// Controllers
|
||||||
ExternalControllerManager.shared.startMonitoringExternalControllers()
|
ExternalGameControllerManager.shared.startMonitoring()
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -77,7 +79,7 @@ extension AppDelegate
|
|||||||
{
|
{
|
||||||
self.window?.tintColor = UIColor.deltaPurple
|
self.window?.tintColor = UIColor.deltaPurple
|
||||||
|
|
||||||
UITextField.appearance(whenContainedInInstancesOf: [UISearchBar.self]).defaultTextAttributes[NSForegroundColorAttributeName] = UIColor.white
|
UITextField.appearance(whenContainedInInstancesOf: [UISearchBar.self]).defaultTextAttributes[NSAttributedStringKey.foregroundColor.rawValue] = UIColor.white
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,12 +90,11 @@ extension AppDelegate
|
|||||||
return self.openURL(url)
|
return self.openURL(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
@discardableResult fileprivate func openURL(_ url: URL) -> Bool
|
@discardableResult private func openURL(_ url: URL) -> Bool
|
||||||
{
|
{
|
||||||
guard url.isFileURL else { return false }
|
guard url.isFileURL else { return false }
|
||||||
|
|
||||||
let gameType = GameType.gameType(forFileExtension: url.pathExtension)
|
if GameType(fileExtension: url.pathExtension) != nil || url.pathExtension.lowercased() == "zip"
|
||||||
if gameType != .unknown || url.pathExtension.lowercased() == "zip"
|
|
||||||
{
|
{
|
||||||
return self.importGame(at: url)
|
return self.importGame(at: url)
|
||||||
}
|
}
|
||||||
|
|||||||
107
Delta/Base.lproj/GamesDatabase.storyboard
Normal file
107
Delta/Base.lproj/GamesDatabase.storyboard
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13528" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="6bq-zy-UZU">
|
||||||
|
<device id="retina4_7" orientation="portrait">
|
||||||
|
<adaptation id="fullscreen"/>
|
||||||
|
</device>
|
||||||
|
<dependencies>
|
||||||
|
<deployment identifier="iOS"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13526"/>
|
||||||
|
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
</dependencies>
|
||||||
|
<scenes>
|
||||||
|
<!--Games Database-->
|
||||||
|
<scene sceneID="S7I-gw-igt">
|
||||||
|
<objects>
|
||||||
|
<tableViewController id="SB6-jW-dhZ" customClass="GamesDatabaseBrowserViewController" customModule="Delta" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
|
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="97" sectionHeaderHeight="28" sectionFooterHeight="28" id="bJf-Sa-ZOX">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
|
<prototypes>
|
||||||
|
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="Cell" id="4cJ-4B-Kgt" customClass="GameMetadataTableViewCell" customModule="Delta" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="0.0" y="28" width="375" height="97"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="4cJ-4B-Kgt" id="7ze-s0-mpI">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="375" height="96.5"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<subviews>
|
||||||
|
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="DSH-Hk-snb" userLabel="Selected Background View">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="375" height="97.5"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<color key="backgroundColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
|
</view>
|
||||||
|
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="BoxArt" translatesAutoresizingMaskIntoConstraints="NO" id="tNY-2F-llo">
|
||||||
|
<rect key="frame" x="15" y="8" width="80" height="80"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" secondItem="tNY-2F-llo" secondAttribute="height" multiplier="1:1" id="f4E-bV-L96"/>
|
||||||
|
</constraints>
|
||||||
|
</imageView>
|
||||||
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Super Mario World" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="DND-Fv-FyB">
|
||||||
|
<rect key="frame" x="110" y="38.5" width="250" height="20.5"/>
|
||||||
|
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
||||||
|
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
</subviews>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="DND-Fv-FyB" firstAttribute="leading" secondItem="tNY-2F-llo" secondAttribute="trailing" constant="15" id="71e-t3-7Av"/>
|
||||||
|
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="DND-Fv-FyB" secondAttribute="bottom" constant="1" id="9No-RE-0xx"/>
|
||||||
|
<constraint firstItem="DND-Fv-FyB" firstAttribute="top" relation="greaterThanOrEqual" secondItem="7ze-s0-mpI" secondAttribute="top" constant="1" id="F9q-6H-sqC"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="DND-Fv-FyB" secondAttribute="trailing" constant="15" id="KFv-7n-LrD"/>
|
||||||
|
<constraint firstItem="DND-Fv-FyB" firstAttribute="centerY" secondItem="7ze-s0-mpI" secondAttribute="centerY" id="YBX-t4-jkR"/>
|
||||||
|
<constraint firstItem="tNY-2F-llo" firstAttribute="top" secondItem="7ze-s0-mpI" secondAttribute="top" constant="8" id="bYX-gA-QvB"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="tNY-2F-llo" secondAttribute="bottom" constant="8" id="fxr-wr-I6X"/>
|
||||||
|
<constraint firstItem="tNY-2F-llo" firstAttribute="leading" secondItem="7ze-s0-mpI" secondAttribute="leading" constant="15" id="hX2-Gr-Bnz"/>
|
||||||
|
</constraints>
|
||||||
|
</tableViewCellContentView>
|
||||||
|
<connections>
|
||||||
|
<outlet property="artworkImageView" destination="tNY-2F-llo" id="GqY-jv-rso"/>
|
||||||
|
<outlet property="artworkImageViewLeadingConstraint" destination="hX2-Gr-Bnz" id="be8-dr-c8K"/>
|
||||||
|
<outlet property="artworkImageViewTrailingConstraint" destination="71e-t3-7Av" id="y62-KO-y1r"/>
|
||||||
|
<outlet property="nameLabel" destination="DND-Fv-FyB" id="LhN-cA-8Hy"/>
|
||||||
|
<outlet property="selectedBackgroundView" destination="DSH-Hk-snb" id="hLY-4k-VxU"/>
|
||||||
|
</connections>
|
||||||
|
</tableViewCell>
|
||||||
|
</prototypes>
|
||||||
|
<connections>
|
||||||
|
<outlet property="dataSource" destination="SB6-jW-dhZ" id="2aq-ZA-84E"/>
|
||||||
|
<outlet property="delegate" destination="SB6-jW-dhZ" id="WgY-cp-m7K"/>
|
||||||
|
</connections>
|
||||||
|
</tableView>
|
||||||
|
<navigationItem key="navigationItem" title="Games Database" id="rwF-kd-avR">
|
||||||
|
<barButtonItem key="leftBarButtonItem" systemItem="cancel" id="BnB-5n-Rff">
|
||||||
|
<connections>
|
||||||
|
<segue destination="mUU-ug-yNs" kind="unwind" unwindAction="unwindFromGamesDatabaseBrowserWith:" id="zdg-Az-WwQ"/>
|
||||||
|
</connections>
|
||||||
|
</barButtonItem>
|
||||||
|
</navigationItem>
|
||||||
|
</tableViewController>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="f3a-hX-Qnu" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
|
<exit id="mUU-ug-yNs" userLabel="Exit" sceneMemberID="exit"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="2652" y="1001.649175412294"/>
|
||||||
|
</scene>
|
||||||
|
<!--Navigation Controller-->
|
||||||
|
<scene sceneID="REv-V5-eEz">
|
||||||
|
<objects>
|
||||||
|
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="6bq-zy-UZU" sceneMemberID="viewController">
|
||||||
|
<toolbarItems/>
|
||||||
|
<navigationBar key="navigationBar" contentMode="scaleToFill" barStyle="black" id="uzY-vR-coL">
|
||||||
|
<rect key="frame" x="0.0" y="20" width="375" height="44"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</navigationBar>
|
||||||
|
<nil name="viewControllers"/>
|
||||||
|
<connections>
|
||||||
|
<segue destination="SB6-jW-dhZ" kind="relationship" relationship="rootViewController" id="b0w-Fq-hrk"/>
|
||||||
|
</connections>
|
||||||
|
</navigationController>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="Hr9-N6-XXA" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="1854" y="1002"/>
|
||||||
|
</scene>
|
||||||
|
</scenes>
|
||||||
|
<resources>
|
||||||
|
<image name="BoxArt" width="100" height="100"/>
|
||||||
|
</resources>
|
||||||
|
</document>
|
||||||
@ -1,11 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11760" systemVersion="16B2657" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="dkK-ii-Bx4">
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13528" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="dkK-ii-Bx4">
|
||||||
<device id="retina4_7" orientation="portrait">
|
<device id="retina4_7" orientation="portrait">
|
||||||
<adaptation id="fullscreen"/>
|
<adaptation id="fullscreen"/>
|
||||||
</device>
|
</device>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="iOS"/>
|
<deployment identifier="iOS"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11755"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13526"/>
|
||||||
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
|
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
|
||||||
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
|
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
@ -47,6 +47,6 @@
|
|||||||
</scene>
|
</scene>
|
||||||
</scenes>
|
</scenes>
|
||||||
<resources>
|
<resources>
|
||||||
<image name="Delta" width="1280" height="1140"/>
|
<image name="Delta" width="1342" height="1196"/>
|
||||||
</resources>
|
</resources>
|
||||||
</document>
|
</document>
|
||||||
|
|||||||
@ -1,12 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11762" systemVersion="16C68" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="SPq-Bk-fQl">
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13528" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="SPq-Bk-fQl">
|
||||||
<device id="retina4_7" orientation="portrait">
|
<device id="retina4_7" orientation="portrait">
|
||||||
<adaptation id="fullscreen"/>
|
<adaptation id="fullscreen"/>
|
||||||
</device>
|
</device>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="iOS"/>
|
<deployment identifier="iOS"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13526"/>
|
||||||
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
|
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<scenes>
|
<scenes>
|
||||||
@ -32,13 +31,13 @@
|
|||||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
<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="tmn-gd-5UN" firstAttribute="leading" secondItem="3Bk-k3-7J9" secondAttribute="leading" id="f1f-sa-dBA"/>
|
<constraint firstItem="tmn-gd-5UN" firstAttribute="leading" secondItem="3Bk-k3-7J9" secondAttribute="leading" id="f1f-sa-dBA"/>
|
||||||
|
<constraint firstAttribute="bottom" secondItem="tmn-gd-5UN" secondAttribute="bottom" id="ifM-Wa-u9y"/>
|
||||||
<constraint firstItem="tmn-gd-5UN" firstAttribute="top" secondItem="3Bk-k3-7J9" secondAttribute="top" id="nhS-aC-rUR"/>
|
<constraint firstItem="tmn-gd-5UN" firstAttribute="top" secondItem="3Bk-k3-7J9" secondAttribute="top" id="nhS-aC-rUR"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
</view>
|
</view>
|
||||||
<navigationItem key="navigationItem" title="Games" id="pFk-as-3k4">
|
<navigationItem key="navigationItem" title="Games" id="pFk-as-3k4">
|
||||||
<barButtonItem key="leftBarButtonItem" image="Settings_Button" id="2gg-lC-FhX">
|
<barButtonItem key="leftBarButtonItem" image="SettingsButton" id="2gg-lC-FhX">
|
||||||
<connections>
|
<connections>
|
||||||
<segue destination="xMK-Cs-fAS" kind="presentation" id="uN5-PN-7FK"/>
|
<segue destination="xMK-Cs-fAS" kind="presentation" id="uN5-PN-7FK"/>
|
||||||
</connections>
|
</connections>
|
||||||
@ -49,91 +48,16 @@
|
|||||||
</connections>
|
</connections>
|
||||||
</barButtonItem>
|
</barButtonItem>
|
||||||
</navigationItem>
|
</navigationItem>
|
||||||
<connections>
|
|
||||||
<segue destination="6bq-zy-UZU" kind="presentation" identifier="gamesDatabaseBrowser" id="7TT-mP-bjt"/>
|
|
||||||
</connections>
|
|
||||||
</viewController>
|
</viewController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="JYx-xE-nis" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="JYx-xE-nis" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="1036" y="1002"/>
|
<point key="canvasLocation" x="1036" y="1002"/>
|
||||||
</scene>
|
</scene>
|
||||||
<!--Games Database-->
|
|
||||||
<scene sceneID="S7I-gw-igt">
|
|
||||||
<objects>
|
|
||||||
<tableViewController id="SB6-jW-dhZ" customClass="GamesDatabaseBrowserViewController" customModule="Delta" customModuleProvider="target" sceneMemberID="viewController">
|
|
||||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="97" sectionHeaderHeight="28" sectionFooterHeight="28" id="bJf-Sa-ZOX">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
|
||||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
|
||||||
<prototypes>
|
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="Cell" id="4cJ-4B-Kgt" customClass="GameMetadataTableViewCell" customModule="Delta" customModuleProvider="target">
|
|
||||||
<rect key="frame" x="0.0" y="28" width="375" height="97"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="4cJ-4B-Kgt" id="7ze-s0-mpI">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="96"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
<subviews>
|
|
||||||
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="DSH-Hk-snb" userLabel="Selected Background View">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="97"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
|
||||||
<color key="backgroundColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
|
|
||||||
</view>
|
|
||||||
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="BoxArt" translatesAutoresizingMaskIntoConstraints="NO" id="tNY-2F-llo">
|
|
||||||
<rect key="frame" x="15" y="8" width="80" height="80"/>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstAttribute="width" secondItem="tNY-2F-llo" secondAttribute="height" multiplier="1:1" id="f4E-bV-L96"/>
|
|
||||||
</constraints>
|
|
||||||
</imageView>
|
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Super Mario World" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="DND-Fv-FyB">
|
|
||||||
<rect key="frame" x="110" y="38.5" width="250" height="20.5"/>
|
|
||||||
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
|
|
||||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
</subviews>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstItem="DND-Fv-FyB" firstAttribute="leading" secondItem="tNY-2F-llo" secondAttribute="trailing" constant="15" id="71e-t3-7Av"/>
|
|
||||||
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="DND-Fv-FyB" secondAttribute="bottom" constant="1" id="9No-RE-0xx"/>
|
|
||||||
<constraint firstItem="DND-Fv-FyB" firstAttribute="top" relation="greaterThanOrEqual" secondItem="7ze-s0-mpI" secondAttribute="top" constant="1" id="F9q-6H-sqC"/>
|
|
||||||
<constraint firstAttribute="trailing" secondItem="DND-Fv-FyB" secondAttribute="trailing" constant="15" id="KFv-7n-LrD"/>
|
|
||||||
<constraint firstItem="DND-Fv-FyB" firstAttribute="centerY" secondItem="7ze-s0-mpI" secondAttribute="centerY" id="YBX-t4-jkR"/>
|
|
||||||
<constraint firstItem="tNY-2F-llo" firstAttribute="top" secondItem="7ze-s0-mpI" secondAttribute="top" constant="8" id="bYX-gA-QvB"/>
|
|
||||||
<constraint firstAttribute="bottom" secondItem="tNY-2F-llo" secondAttribute="bottom" constant="8" id="fxr-wr-I6X"/>
|
|
||||||
<constraint firstItem="tNY-2F-llo" firstAttribute="leading" secondItem="7ze-s0-mpI" secondAttribute="leading" constant="15" id="hX2-Gr-Bnz"/>
|
|
||||||
</constraints>
|
|
||||||
</tableViewCellContentView>
|
|
||||||
<connections>
|
|
||||||
<outlet property="artworkImageView" destination="tNY-2F-llo" id="GqY-jv-rso"/>
|
|
||||||
<outlet property="artworkImageViewLeadingConstraint" destination="hX2-Gr-Bnz" id="be8-dr-c8K"/>
|
|
||||||
<outlet property="artworkImageViewTrailingConstraint" destination="71e-t3-7Av" id="y62-KO-y1r"/>
|
|
||||||
<outlet property="nameLabel" destination="DND-Fv-FyB" id="LhN-cA-8Hy"/>
|
|
||||||
<outlet property="selectedBackgroundView" destination="DSH-Hk-snb" id="hLY-4k-VxU"/>
|
|
||||||
</connections>
|
|
||||||
</tableViewCell>
|
|
||||||
</prototypes>
|
|
||||||
<connections>
|
|
||||||
<outlet property="dataSource" destination="SB6-jW-dhZ" id="2aq-ZA-84E"/>
|
|
||||||
<outlet property="delegate" destination="SB6-jW-dhZ" id="WgY-cp-m7K"/>
|
|
||||||
</connections>
|
|
||||||
</tableView>
|
|
||||||
<navigationItem key="navigationItem" title="Games Database" id="rwF-kd-avR">
|
|
||||||
<barButtonItem key="leftBarButtonItem" systemItem="cancel" id="BnB-5n-Rff">
|
|
||||||
<connections>
|
|
||||||
<segue destination="mUU-ug-yNs" kind="unwind" unwindAction="unwindFromGamesDatabaseBrowserWith:" id="zdg-Az-WwQ"/>
|
|
||||||
</connections>
|
|
||||||
</barButtonItem>
|
|
||||||
</navigationItem>
|
|
||||||
</tableViewController>
|
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="f3a-hX-Qnu" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
|
||||||
<exit id="mUU-ug-yNs" userLabel="Exit" sceneMemberID="exit"/>
|
|
||||||
</objects>
|
|
||||||
<point key="canvasLocation" x="2652" y="1001.649175412294"/>
|
|
||||||
</scene>
|
|
||||||
<!--Game Collection View Controller-->
|
<!--Game Collection View Controller-->
|
||||||
<scene sceneID="qNA-NP-TiF">
|
<scene sceneID="qNA-NP-TiF">
|
||||||
<objects>
|
<objects>
|
||||||
<collectionViewController storyboardIdentifier="gameCollectionViewController" clearsSelectionOnViewWillAppear="NO" id="kqu-75-owz" customClass="GameCollectionViewController" customModule="Delta" customModuleProvider="target" sceneMemberID="viewController">
|
<collectionViewController storyboardIdentifier="gameCollectionViewController" clearsSelectionOnViewWillAppear="NO" id="kqu-75-owz" customClass="GameCollectionViewController" 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" indicatorStyle="white" dataMode="prototypes" id="OIq-Z8-kxO">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
<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" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
@ -161,7 +85,6 @@
|
|||||||
<connections>
|
<connections>
|
||||||
<segue destination="X2o-q6-XD5" kind="unwind" identifier="unwindFromGames" unwindAction="unwindFromGamesViewControllerWith:" id="k8C-Xn-maU"/>
|
<segue destination="X2o-q6-XD5" kind="unwind" identifier="unwindFromGames" unwindAction="unwindFromGamesViewControllerWith:" id="k8C-Xn-maU"/>
|
||||||
<segue destination="MPk-bF-nkj" kind="presentation" identifier="saveStates" customClass="SaveStatesStoryboardSegue" customModule="Delta" customModuleProvider="target" id="1Xp-2J-0cq"/>
|
<segue destination="MPk-bF-nkj" kind="presentation" identifier="saveStates" customClass="SaveStatesStoryboardSegue" customModule="Delta" customModuleProvider="target" id="1Xp-2J-0cq"/>
|
||||||
<segue destination="6bq-zy-UZU" kind="presentation" identifier="gamesDatabaseBrowser" id="mzX-Bb-MaX"/>
|
|
||||||
</connections>
|
</connections>
|
||||||
</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"/>
|
||||||
@ -260,12 +183,11 @@
|
|||||||
<toolbarItems/>
|
<toolbarItems/>
|
||||||
<nil key="simulatedBottomBarMetrics"/>
|
<nil key="simulatedBottomBarMetrics"/>
|
||||||
<navigationBar key="navigationBar" contentMode="scaleToFill" id="wj9-1e-eev">
|
<navigationBar key="navigationBar" contentMode="scaleToFill" id="wj9-1e-eev">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
|
<rect key="frame" x="0.0" y="20" width="375" height="44"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
</navigationBar>
|
</navigationBar>
|
||||||
<nil name="viewControllers"/>
|
<nil name="viewControllers"/>
|
||||||
<toolbar key="toolbar" opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="M4r-sO-G4H">
|
<toolbar key="toolbar" opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="M4r-sO-G4H">
|
||||||
<rect key="frame" x="0.0" y="556" width="600" height="44"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
</toolbar>
|
</toolbar>
|
||||||
<connections>
|
<connections>
|
||||||
@ -290,7 +212,7 @@
|
|||||||
<navigationController storyboardIdentifier="saveStatesNavigationController" automaticallyAdjustsScrollViewInsets="NO" id="MPk-bF-nkj" sceneMemberID="viewController">
|
<navigationController storyboardIdentifier="saveStatesNavigationController" automaticallyAdjustsScrollViewInsets="NO" id="MPk-bF-nkj" sceneMemberID="viewController">
|
||||||
<toolbarItems/>
|
<toolbarItems/>
|
||||||
<navigationBar key="navigationBar" contentMode="scaleToFill" id="twH-3X-6DV">
|
<navigationBar key="navigationBar" contentMode="scaleToFill" id="twH-3X-6DV">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
|
<rect key="frame" x="0.0" y="20" width="375" height="44"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
</navigationBar>
|
</navigationBar>
|
||||||
<nil name="viewControllers"/>
|
<nil name="viewControllers"/>
|
||||||
@ -314,31 +236,11 @@
|
|||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="3409" y="1716"/>
|
<point key="canvasLocation" x="3409" y="1716"/>
|
||||||
</scene>
|
</scene>
|
||||||
<!--Navigation Controller-->
|
|
||||||
<scene sceneID="REv-V5-eEz">
|
|
||||||
<objects>
|
|
||||||
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="6bq-zy-UZU" sceneMemberID="viewController">
|
|
||||||
<toolbarItems/>
|
|
||||||
<navigationBar key="navigationBar" contentMode="scaleToFill" misplaced="YES" barStyle="black" id="uzY-vR-coL">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
</navigationBar>
|
|
||||||
<nil name="viewControllers"/>
|
|
||||||
<connections>
|
|
||||||
<segue destination="SB6-jW-dhZ" kind="relationship" relationship="rootViewController" id="b0w-Fq-hrk"/>
|
|
||||||
</connections>
|
|
||||||
</navigationController>
|
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="Hr9-N6-XXA" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
|
||||||
</objects>
|
|
||||||
<point key="canvasLocation" x="1854" y="1002"/>
|
|
||||||
</scene>
|
|
||||||
</scenes>
|
</scenes>
|
||||||
<resources>
|
<resources>
|
||||||
<image name="BoxArt" width="100" height="100"/>
|
<image name="SettingsButton" width="16" height="16"/>
|
||||||
<image name="Settings_Button" width="22" height="22"/>
|
|
||||||
</resources>
|
</resources>
|
||||||
<inferredMetricsTieBreakers>
|
<inferredMetricsTieBreakers>
|
||||||
<segue reference="mzX-Bb-MaX"/>
|
|
||||||
<segue reference="Tey-6Z-UHp"/>
|
<segue reference="Tey-6Z-UHp"/>
|
||||||
</inferredMetricsTieBreakers>
|
</inferredMetricsTieBreakers>
|
||||||
</document>
|
</document>
|
||||||
|
|||||||
@ -1,11 +1,15 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11185.3" systemVersion="15G31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="Dt0-nV-isV">
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.19" systemVersion="16F73" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="Dt0-nV-isV">
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="iOS"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.16"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11151.4"/>
|
|
||||||
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
|
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
<customFonts key="customFonts">
|
||||||
|
<array key="Menlo.ttc">
|
||||||
|
<string>Menlo-Regular</string>
|
||||||
|
</array>
|
||||||
|
</customFonts>
|
||||||
<scenes>
|
<scenes>
|
||||||
<!--Pause View Controller-->
|
<!--Pause View Controller-->
|
||||||
<scene sceneID="Wst-Dv-TjM">
|
<scene sceneID="Wst-Dv-TjM">
|
||||||
@ -16,15 +20,17 @@
|
|||||||
<viewControllerLayoutGuide type="bottom" id="gF0-0U-kR7"/>
|
<viewControllerLayoutGuide type="bottom" id="gF0-0U-kR7"/>
|
||||||
</layoutGuides>
|
</layoutGuides>
|
||||||
<view key="view" contentMode="scaleToFill" id="oOH-ea-jcb">
|
<view key="view" contentMode="scaleToFill" id="oOH-ea-jcb">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<visualEffectView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="p2M-dE-BJs" userLabel="Blur View">
|
<visualEffectView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="p2M-dE-BJs" userLabel="Blur View">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
||||||
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="eyD-0d-RHe" userLabel="Blur Content View">
|
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="eyD-0d-RHe" userLabel="Blur Content View">
|
||||||
<frame key="frameInset"/>
|
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<containerView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="rqN-NB-jbb">
|
<containerView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="rqN-NB-jbb">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
||||||
<connections>
|
<connections>
|
||||||
<segue destination="sWv-Ky-VGs" kind="embed" identifier="embedNavigationController" id="1Ja-XW-uoT"/>
|
<segue destination="sWv-Ky-VGs" kind="embed" identifier="embedNavigationController" id="1Ja-XW-uoT"/>
|
||||||
</connections>
|
</connections>
|
||||||
@ -63,7 +69,7 @@
|
|||||||
<objects>
|
<objects>
|
||||||
<navigationController id="sWv-Ky-VGs" sceneMemberID="viewController">
|
<navigationController id="sWv-Ky-VGs" sceneMemberID="viewController">
|
||||||
<simulatedNavigationBarMetrics key="simulatedTopBarMetrics" barStyle="black" prompted="NO"/>
|
<simulatedNavigationBarMetrics key="simulatedTopBarMetrics" barStyle="black" prompted="NO"/>
|
||||||
<navigationBar key="navigationBar" contentMode="scaleToFill" barStyle="black" id="Snh-Z0-9kC">
|
<navigationBar key="navigationBar" contentMode="scaleToFill" misplaced="YES" barStyle="black" id="Snh-Z0-9kC">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
|
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
</navigationBar>
|
</navigationBar>
|
||||||
@ -78,9 +84,9 @@
|
|||||||
<!--Paused-->
|
<!--Paused-->
|
||||||
<scene sceneID="1md-hu-g0J">
|
<scene sceneID="1md-hu-g0J">
|
||||||
<objects>
|
<objects>
|
||||||
<collectionViewController id="0jA-NY-mvB" customClass="PauseMenuViewController" customModule="Delta" customModuleProvider="target" sceneMemberID="viewController">
|
<collectionViewController id="0jA-NY-mvB" customClass="GridMenuViewController" customModule="Delta" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<collectionView key="view" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" delaysContentTouches="NO" dataMode="prototypes" id="scc-uc-vaJ">
|
<collectionView key="view" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" misplaced="YES" delaysContentTouches="NO" dataMode="prototypes" id="scc-uc-vaJ">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
<rect key="frame" x="0.0" y="0.0" width="1000" height="1000"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
<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="yXv-zl-idO" customClass="GridCollectionViewLayout" customModule="Delta" customModuleProvider="target">
|
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="20" minimumInteritemSpacing="10" id="yXv-zl-idO" customClass="GridCollectionViewLayout" customModule="Delta" customModuleProvider="target">
|
||||||
@ -91,7 +97,7 @@
|
|||||||
</collectionViewFlowLayout>
|
</collectionViewFlowLayout>
|
||||||
<cells>
|
<cells>
|
||||||
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="Cell" id="6XS-Ne-nGZ" customClass="GridCollectionViewCell" customModule="Delta" customModuleProvider="target">
|
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="Cell" id="6XS-Ne-nGZ" customClass="GridCollectionViewCell" customModule="Delta" customModuleProvider="target">
|
||||||
<frame key="frameInset" minY="84" width="60" height="80"/>
|
<rect key="frame" x="0.0" y="20" width="60" height="80"/>
|
||||||
<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="60" height="80"/>
|
<rect key="frame" x="0.0" y="0.0" width="60" height="80"/>
|
||||||
@ -128,7 +134,7 @@
|
|||||||
<objects>
|
<objects>
|
||||||
<collectionViewController storyboardIdentifier="saveStatesViewController" id="OOk-k7-INg" customClass="SaveStatesViewController" customModule="Delta" customModuleProvider="target" sceneMemberID="viewController">
|
<collectionViewController storyboardIdentifier="saveStatesViewController" id="OOk-k7-INg" customClass="SaveStatesViewController" customModule="Delta" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<collectionView key="view" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" id="XgF-OF-CVf">
|
<collectionView key="view" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" id="XgF-OF-CVf">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
<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="20" id="tvW-q1-PD8" customClass="GridCollectionViewLayout" customModule="Delta" customModuleProvider="target">
|
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="20" minimumInteritemSpacing="20" id="tvW-q1-PD8" customClass="GridCollectionViewLayout" customModule="Delta" customModuleProvider="target">
|
||||||
@ -139,7 +145,7 @@
|
|||||||
</collectionViewFlowLayout>
|
</collectionViewFlowLayout>
|
||||||
<cells>
|
<cells>
|
||||||
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="Cell" id="c3N-1A-ryV" customClass="GridCollectionViewCell" customModule="Delta" customModuleProvider="target">
|
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="Cell" id="c3N-1A-ryV" customClass="GridCollectionViewCell" customModule="Delta" customModuleProvider="target">
|
||||||
<frame key="frameInset" minX="20" minY="124" width="50" height="50"/>
|
<rect key="frame" x="20" y="60" width="50" height="50"/>
|
||||||
<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="50" height="50"/>
|
<rect key="frame" x="0.0" y="0.0" width="50" height="50"/>
|
||||||
@ -148,7 +154,7 @@
|
|||||||
</collectionViewCell>
|
</collectionViewCell>
|
||||||
</cells>
|
</cells>
|
||||||
<collectionReusableView key="sectionHeaderView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="Header" id="YeY-W9-CC6" customClass="SaveStatesCollectionHeaderView" customModule="Delta" customModuleProvider="target">
|
<collectionReusableView key="sectionHeaderView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="Header" id="YeY-W9-CC6" customClass="SaveStatesCollectionHeaderView" customModule="Delta" customModuleProvider="target">
|
||||||
<frame key="frameInset" minY="64" width="375" height="50"/>
|
<rect key="frame" x="0.0" y="0.0" width="600" height="50"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
</collectionReusableView>
|
</collectionReusableView>
|
||||||
<connections>
|
<connections>
|
||||||
@ -174,22 +180,22 @@
|
|||||||
<objects>
|
<objects>
|
||||||
<tableViewController id="wb8-5o-1jE" customClass="CheatsViewController" customModule="Delta" customModuleProvider="target" sceneMemberID="viewController">
|
<tableViewController id="wb8-5o-1jE" customClass="CheatsViewController" customModule="Delta" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="28" sectionFooterHeight="28" id="f5S-hV-1yV">
|
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="28" sectionFooterHeight="28" id="f5S-hV-1yV">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
<prototypes>
|
<prototypes>
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="Cell" id="e8g-ZW-5lQ" customClass="CheatTableViewCell" customModule="Delta" customModuleProvider="target">
|
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="Cell" id="e8g-ZW-5lQ" customClass="CheatTableViewCell" customModule="Delta" customModuleProvider="target">
|
||||||
<frame key="frameInset" minY="92" width="375" height="44"/>
|
<rect key="frame" x="0.0" y="28" width="600" height="44"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="e8g-ZW-5lQ" id="AHE-Jk-ULE">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="e8g-ZW-5lQ" id="AHE-Jk-ULE">
|
||||||
<frame key="frameInset" width="375" height="43.5"/>
|
<rect key="frame" x="0.0" y="0.0" width="600" height="43.5"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<visualEffectView opaque="NO" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="emc-gw-KkJ" userLabel="Selected Background View">
|
<visualEffectView opaque="NO" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="emc-gw-KkJ" userLabel="Selected Background View">
|
||||||
<frame key="frameInset" maxY="-0.5"/>
|
<rect key="frame" x="0.0" y="0.0" width="600" height="44"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" ambiguous="YES" id="9bA-Tg-Bko">
|
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" ambiguous="YES" id="9bA-Tg-Bko">
|
||||||
<frame key="frameInset"/>
|
<rect key="frame" x="0.0" y="0.0" width="600" height="44"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
</view>
|
</view>
|
||||||
@ -198,8 +204,9 @@
|
|||||||
</vibrancyEffect>
|
</vibrancyEffect>
|
||||||
</visualEffectView>
|
</visualEffectView>
|
||||||
<visualEffectView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="R4A-9d-DGb">
|
<visualEffectView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="R4A-9d-DGb">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="600" height="43.5"/>
|
||||||
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="EdX-fU-x54">
|
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="EdX-fU-x54">
|
||||||
<frame key="frameInset"/>
|
<rect key="frame" x="0.0" y="0.0" width="600" height="43.5"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
</view>
|
</view>
|
||||||
<vibrancyEffect>
|
<vibrancyEffect>
|
||||||
@ -245,20 +252,21 @@
|
|||||||
<objects>
|
<objects>
|
||||||
<tableViewController storyboardIdentifier="editCheatViewController" id="jTR-Oe-YUJ" customClass="EditCheatViewController" customModule="Delta" customModuleProvider="target" sceneMemberID="viewController">
|
<tableViewController storyboardIdentifier="editCheatViewController" id="jTR-Oe-YUJ" customClass="EditCheatViewController" customModule="Delta" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" keyboardDismissMode="interactive" dataMode="static" style="grouped" separatorStyle="default" rowHeight="44" sectionHeaderHeight="18" sectionFooterHeight="18" id="BV9-ff-x83">
|
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" keyboardDismissMode="interactive" dataMode="static" style="grouped" separatorStyle="default" rowHeight="44" sectionHeaderHeight="18" sectionFooterHeight="18" id="BV9-ff-x83">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<color key="backgroundColor" red="0.93725490199999995" green="0.93725490199999995" blue="0.95686274510000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
<color key="backgroundColor" red="0.93725490199999995" green="0.93725490199999995" blue="0.95686274510000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
<sections>
|
<sections>
|
||||||
<tableViewSection headerTitle="Name" id="QT6-DZ-g70">
|
<tableViewSection headerTitle="Name" id="QT6-DZ-g70">
|
||||||
<cells>
|
<cells>
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="ZeC-rg-QFa">
|
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="ZeC-rg-QFa">
|
||||||
<rect key="frame" x="0.0" y="119.5" width="375" height="44"/>
|
<rect key="frame" x="0.0" y="55.5" width="600" height="44"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" tableViewCell="ZeC-rg-QFa" id="UIF-fK-ApW">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" tableViewCell="ZeC-rg-QFa" id="UIF-fK-ApW">
|
||||||
<frame key="frameInset" width="375" height="43.5"/>
|
<rect key="frame" x="0.0" y="0.0" width="600" height="43.5"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Cheat Name" minimumFontSize="17" clearButtonMode="whileEditing" translatesAutoresizingMaskIntoConstraints="NO" id="DD1-X0-hg7">
|
<textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" placeholder="Cheat Name" minimumFontSize="17" clearButtonMode="whileEditing" translatesAutoresizingMaskIntoConstraints="NO" id="DD1-X0-hg7">
|
||||||
|
<rect key="frame" x="20" y="0.0" width="560" height="43.5"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
<textInputTraits key="textInputTraits" autocapitalizationType="words" autocorrectionType="no" returnKeyType="done"/>
|
<textInputTraits key="textInputTraits" autocapitalizationType="words" autocorrectionType="no" returnKeyType="done"/>
|
||||||
<connections>
|
<connections>
|
||||||
@ -280,13 +288,14 @@
|
|||||||
<tableViewSection headerTitle="Type" footerTitle="Description" id="rvn-VK-2uH">
|
<tableViewSection headerTitle="Type" footerTitle="Description" id="rvn-VK-2uH">
|
||||||
<cells>
|
<cells>
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="Tst-zn-e04">
|
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" id="Tst-zn-e04">
|
||||||
<rect key="frame" x="0.0" y="227" width="375" height="44"/>
|
<rect key="frame" x="0.0" y="163" width="600" height="44"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" tableViewCell="Tst-zn-e04" id="gwV-zS-RWQ">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" tableViewCell="Tst-zn-e04" id="gwV-zS-RWQ">
|
||||||
<frame key="frameInset" width="375" height="43.5"/>
|
<rect key="frame" x="0.0" y="0.0" width="600" height="43.5"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="xrD-ue-96Q">
|
<segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="xrD-ue-96Q">
|
||||||
|
<rect key="frame" x="20" y="8" width="560" height="29"/>
|
||||||
<segments>
|
<segments>
|
||||||
<segment title="First"/>
|
<segment title="First"/>
|
||||||
<segment title="Second"/>
|
<segment title="Second"/>
|
||||||
@ -308,13 +317,14 @@
|
|||||||
<tableViewSection headerTitle="Code" footerTitle="Description" id="rHC-nA-ga0">
|
<tableViewSection headerTitle="Code" footerTitle="Description" id="rHC-nA-ga0">
|
||||||
<cells>
|
<cells>
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" rowHeight="210" id="xxc-cz-sb7">
|
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" rowHeight="210" id="xxc-cz-sb7">
|
||||||
<rect key="frame" x="0.0" y="346.5" width="375" height="210"/>
|
<rect key="frame" x="0.0" y="282.5" width="600" height="210"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" tableViewCell="xxc-cz-sb7" id="agU-SE-fNa">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" tableViewCell="xxc-cz-sb7" id="agU-SE-fNa">
|
||||||
<frame key="frameInset" width="375" height="209.5"/>
|
<rect key="frame" x="0.0" y="0.0" width="600" height="209.5"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" text="XXXXXXXX YYYYYYYY" translatesAutoresizingMaskIntoConstraints="NO" id="a17-LB-QXD" customClass="CheatTextView" customModule="Delta" customModuleProvider="target">
|
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" text="XXXXXXXX YYYYYYYY" translatesAutoresizingMaskIntoConstraints="NO" id="a17-LB-QXD" customClass="CheatTextView" customModule="Delta" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="600" height="209.5"/>
|
||||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
<fontDescription key="fontDescription" name="Menlo-Regular" family="Menlo" pointSize="26"/>
|
<fontDescription key="fontDescription" name="Menlo-Regular" family="Menlo" pointSize="26"/>
|
||||||
<textInputTraits key="textInputTraits" autocapitalizationType="allCharacters" autocorrectionType="no" spellCheckingType="no" returnKeyType="done"/>
|
<textInputTraits key="textInputTraits" autocapitalizationType="allCharacters" autocorrectionType="no" spellCheckingType="no" returnKeyType="done"/>
|
||||||
|
|||||||
@ -1,7 +1,12 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="9531" systemVersion="15D21" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13528" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||||
|
<device id="retina4_7" orientation="portrait">
|
||||||
|
<adaptation id="fullscreen"/>
|
||||||
|
</device>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9529"/>
|
<deployment identifier="iOS"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13526"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<objects>
|
<objects>
|
||||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="PausePresentationController" customModule="Delta" customModuleProvider="target">
|
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="PausePresentationController" customModule="Delta" customModuleProvider="target">
|
||||||
@ -13,14 +18,14 @@
|
|||||||
</placeholder>
|
</placeholder>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||||
<view contentMode="scaleToFill" id="iN0-l3-epB">
|
<view contentMode="scaleToFill" id="iN0-l3-epB">
|
||||||
<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>
|
||||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="15" translatesAutoresizingMaskIntoConstraints="NO" id="qFz-hB-77X">
|
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="15" translatesAutoresizingMaskIntoConstraints="NO" id="qFz-hB-77X">
|
||||||
<rect key="frame" x="15" y="236" width="570" height="127.5"/>
|
<rect key="frame" x="15" y="270.5" width="375" height="126.5"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="500" image="Pause" translatesAutoresizingMaskIntoConstraints="NO" id="cWa-Ht-i5m">
|
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="500" image="Pause" translatesAutoresizingMaskIntoConstraints="NO" id="cWa-Ht-i5m">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="570" height="80"/>
|
<rect key="frame" x="0.0" y="0.0" width="375" height="80"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" priority="900" constant="44" id="xki-9P-sHi">
|
<constraint firstAttribute="height" relation="greaterThanOrEqual" priority="900" constant="44" id="xki-9P-sHi">
|
||||||
<variation key="heightClass=compact" constant="25"/>
|
<variation key="heightClass=compact" constant="25"/>
|
||||||
@ -28,9 +33,9 @@
|
|||||||
</constraints>
|
</constraints>
|
||||||
</imageView>
|
</imageView>
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="800" text="Super Mario World" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="ZcT-qE-aES">
|
<label opaque="NO" userInteractionEnabled="NO" contentMode="center" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="800" text="Super Mario World" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="ZcT-qE-aES">
|
||||||
<rect key="frame" x="0.0" y="95" width="570" height="32.5"/>
|
<rect key="frame" x="0.0" y="95" width="375" height="31.5"/>
|
||||||
<fontDescription key="fontDescription" style="UICTFontTextStyleTitle1"/>
|
<fontDescription key="fontDescription" style="UICTFontTextStyleTitle1"/>
|
||||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
</subviews>
|
</subviews>
|
||||||
@ -1,11 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11760" systemVersion="16B2657" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="ssH-mM-uG6">
|
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13528" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="ssH-mM-uG6">
|
||||||
<device id="retina4_7" orientation="portrait">
|
<device id="retina4_7" orientation="portrait">
|
||||||
<adaptation id="fullscreen"/>
|
<adaptation id="fullscreen"/>
|
||||||
</device>
|
</device>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="iOS"/>
|
<deployment identifier="iOS"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11755"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13526"/>
|
||||||
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
|
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
@ -21,15 +21,15 @@
|
|||||||
<sections>
|
<sections>
|
||||||
<tableViewSection headerTitle="Inputs" id="c6K-sJ-0vW">
|
<tableViewSection headerTitle="Inputs" id="c6K-sJ-0vW">
|
||||||
<cells>
|
<cells>
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="tls-Hv-Rx2" detailTextLabel="vJP-Ie-a9H" style="IBUITableViewCellStyleValue1" id="jvV-ZB-Rq1">
|
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="DetailCell" textLabel="tls-Hv-Rx2" detailTextLabel="vJP-Ie-a9H" style="IBUITableViewCellStyleValue1" id="jvV-ZB-Rq1">
|
||||||
<rect key="frame" x="0.0" y="56" width="375" height="44"/>
|
<rect key="frame" x="0.0" y="55.5" width="375" height="44"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="jvV-ZB-Rq1" id="AVi-6C-eIS">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="jvV-ZB-Rq1" id="AVi-6C-eIS">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="342" height="43.5"/>
|
<rect key="frame" x="0.0" y="0.0" width="341" height="43.5"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Player 1" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="tls-Hv-Rx2">
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Player 1" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="tls-Hv-Rx2">
|
||||||
<rect key="frame" x="15" y="12" width="56" height="19.5"/>
|
<rect key="frame" x="16" y="12" width="56" height="19.5"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
||||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
@ -45,15 +45,15 @@
|
|||||||
</subviews>
|
</subviews>
|
||||||
</tableViewCellContentView>
|
</tableViewCellContentView>
|
||||||
</tableViewCell>
|
</tableViewCell>
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="e3u-x9-IEC" detailTextLabel="2OP-A1-VYo" style="IBUITableViewCellStyleValue1" id="1Fv-H5-0oH">
|
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="DetailCell" textLabel="e3u-x9-IEC" detailTextLabel="2OP-A1-VYo" style="IBUITableViewCellStyleValue1" id="1Fv-H5-0oH">
|
||||||
<rect key="frame" x="0.0" y="100" width="375" height="44"/>
|
<rect key="frame" x="0.0" y="99.5" width="375" height="44"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="1Fv-H5-0oH" id="kFJ-zK-MLZ">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="1Fv-H5-0oH" id="kFJ-zK-MLZ">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="342" height="43.5"/>
|
<rect key="frame" x="0.0" y="0.0" width="341" height="43.5"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Player 2" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="e3u-x9-IEC">
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Player 2" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="e3u-x9-IEC">
|
||||||
<rect key="frame" x="15" y="12" width="58" height="19.5"/>
|
<rect key="frame" x="16" y="12" width="58" height="19.5"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
||||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
@ -69,15 +69,15 @@
|
|||||||
</subviews>
|
</subviews>
|
||||||
</tableViewCellContentView>
|
</tableViewCellContentView>
|
||||||
</tableViewCell>
|
</tableViewCell>
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="Cdn-11-xZe" detailTextLabel="wWc-NY-Bsd" style="IBUITableViewCellStyleValue1" id="EcC-Be-jV5">
|
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="DetailCell" textLabel="Cdn-11-xZe" detailTextLabel="wWc-NY-Bsd" style="IBUITableViewCellStyleValue1" id="EcC-Be-jV5">
|
||||||
<rect key="frame" x="0.0" y="144" width="375" height="44"/>
|
<rect key="frame" x="0.0" y="143.5" width="375" height="44"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="EcC-Be-jV5" id="9ZS-um-scR">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="EcC-Be-jV5" id="9ZS-um-scR">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="342" height="43.5"/>
|
<rect key="frame" x="0.0" y="0.0" width="341" height="43.5"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Player 3" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Cdn-11-xZe">
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Player 3" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Cdn-11-xZe">
|
||||||
<rect key="frame" x="15" y="12" width="58.5" height="19.5"/>
|
<rect key="frame" x="16" y="12" width="58.5" height="19.5"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
||||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
@ -93,15 +93,15 @@
|
|||||||
</subviews>
|
</subviews>
|
||||||
</tableViewCellContentView>
|
</tableViewCellContentView>
|
||||||
</tableViewCell>
|
</tableViewCell>
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="Hls-3b-EaS" detailTextLabel="hNf-uc-PLR" style="IBUITableViewCellStyleValue1" id="hO9-Ov-vsA">
|
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="DetailCell" textLabel="Hls-3b-EaS" detailTextLabel="hNf-uc-PLR" style="IBUITableViewCellStyleValue1" id="hO9-Ov-vsA">
|
||||||
<rect key="frame" x="0.0" y="188" width="375" height="44"/>
|
<rect key="frame" x="0.0" y="187.5" width="375" height="44"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="hO9-Ov-vsA" id="MRi-re-XI7">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="hO9-Ov-vsA" id="MRi-re-XI7">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="342" height="43.5"/>
|
<rect key="frame" x="0.0" y="0.0" width="341" height="43.5"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Player 4" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Hls-3b-EaS">
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Player 4" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Hls-3b-EaS">
|
||||||
<rect key="frame" x="15" y="12" width="59" height="19.5"/>
|
<rect key="frame" x="16" y="12" width="59" height="19.5"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
||||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
@ -121,15 +121,15 @@
|
|||||||
</tableViewSection>
|
</tableViewSection>
|
||||||
<tableViewSection headerTitle="Controller Skins" id="Nch-k1-6pR">
|
<tableViewSection headerTitle="Controller Skins" id="Nch-k1-6pR">
|
||||||
<cells>
|
<cells>
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="mBC-YU-BVK" style="IBUITableViewCellStyleDefault" id="ICf-ug-NwS">
|
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="Cell" textLabel="mBC-YU-BVK" style="IBUITableViewCellStyleDefault" id="ICf-ug-NwS">
|
||||||
<rect key="frame" x="0.0" y="289" width="375" height="44"/>
|
<rect key="frame" x="0.0" y="287.5" width="375" height="44"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="ICf-ug-NwS" id="7se-sE-x9e">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="ICf-ug-NwS" id="7se-sE-x9e">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="342" height="43.5"/>
|
<rect key="frame" x="0.0" y="0.0" width="341" height="43.5"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Super Nintendo" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="mBC-YU-BVK">
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="System Name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="mBC-YU-BVK">
|
||||||
<rect key="frame" x="15" y="0.0" width="325" height="43.5"/>
|
<rect key="frame" x="16" y="0.0" width="324" height="43.5"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
@ -138,15 +138,32 @@
|
|||||||
</subviews>
|
</subviews>
|
||||||
</tableViewCellContentView>
|
</tableViewCellContentView>
|
||||||
</tableViewCell>
|
</tableViewCell>
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="MFD-lC-Vfk" style="IBUITableViewCellStyleDefault" id="4Sh-Mb-vqu">
|
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="Cell" textLabel="Dxs-Me-IVU" style="IBUITableViewCellStyleDefault" id="Hqy-yc-Jef">
|
||||||
<rect key="frame" x="0.0" y="333" width="375" height="44"/>
|
<rect key="frame" x="0.0" y="331.5" width="375" height="44"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="4Sh-Mb-vqu" id="cJG-Gr-n6q">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Hqy-yc-Jef" id="wJL-kh-qW0">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="342" height="43.5"/>
|
<rect key="frame" x="0.0" y="0.0" width="341" height="43.5"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Game Boy Advance" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="MFD-lC-Vfk">
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="System Name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Dxs-Me-IVU">
|
||||||
<rect key="frame" x="15" y="0.0" width="325" height="43.5"/>
|
<rect key="frame" x="16" y="0.0" width="324" height="43.5"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
|
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
|
<nil key="highlightedColor"/>
|
||||||
|
</label>
|
||||||
|
</subviews>
|
||||||
|
</tableViewCellContentView>
|
||||||
|
</tableViewCell>
|
||||||
|
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="Cell" textLabel="o9x-Kn-6bC" style="IBUITableViewCellStyleDefault" id="jFa-Qk-1cj">
|
||||||
|
<rect key="frame" x="0.0" y="375.5" width="375" height="44"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="jFa-Qk-1cj" id="rFR-qL-fNQ">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="341" height="43.5"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
<subviews>
|
||||||
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="System Name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="o9x-Kn-6bC">
|
||||||
|
<rect key="frame" x="16" y="0.0" width="324" height="43.5"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
@ -160,14 +177,14 @@
|
|||||||
<tableViewSection headerTitle="Controller Opacity" footerTitle="Determines how translucent the controller appears, if supported by the controller skin." id="SwK-m9-8gt">
|
<tableViewSection headerTitle="Controller Opacity" footerTitle="Determines how translucent the controller appears, if supported by the controller skin." id="SwK-m9-8gt">
|
||||||
<cells>
|
<cells>
|
||||||
<tableViewCell contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" id="Xxk-vo-eu4">
|
<tableViewCell contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" id="Xxk-vo-eu4">
|
||||||
<rect key="frame" x="0.0" y="441" width="375" height="44"/>
|
<rect key="frame" x="0.0" y="483" width="375" height="44"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" tableViewCell="Xxk-vo-eu4" id="vxt-Ex-b4b">
|
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" tableViewCell="Xxk-vo-eu4" id="vxt-Ex-b4b">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="43"/>
|
<rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="0.5" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="whi-If-wFf">
|
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="0.5" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="whi-If-wFf">
|
||||||
<rect key="frame" x="74" y="7" width="288" height="31"/>
|
<rect key="frame" x="75" y="7" width="286" height="31"/>
|
||||||
<connections>
|
<connections>
|
||||||
<action selector="beginChangingControllerOpacityWith:" destination="eHi-aO-uGS" eventType="touchDown" id="NG9-FX-62d"/>
|
<action selector="beginChangingControllerOpacityWith:" destination="eHi-aO-uGS" eventType="touchDown" id="NG9-FX-62d"/>
|
||||||
<action selector="changeControllerOpacityWith:" destination="eHi-aO-uGS" eventType="valueChanged" id="Zci-tN-4uU"/>
|
<action selector="changeControllerOpacityWith:" destination="eHi-aO-uGS" eventType="valueChanged" id="Zci-tN-4uU"/>
|
||||||
@ -177,7 +194,7 @@
|
|||||||
</connections>
|
</connections>
|
||||||
</slider>
|
</slider>
|
||||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="50%" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="zaz-yD-CYG">
|
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="50%" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="zaz-yD-CYG">
|
||||||
<rect key="frame" x="15" y="11" width="46" height="21"/>
|
<rect key="frame" x="16" y="11" width="46" height="21"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="height" constant="21" id="ACD-qY-k0J"/>
|
<constraint firstAttribute="height" constant="21" id="ACD-qY-k0J"/>
|
||||||
<constraint firstAttribute="width" constant="46" id="ZVd-ie-qRm"/>
|
<constraint firstAttribute="width" constant="46" id="ZVd-ie-qRm"/>
|
||||||
@ -223,6 +240,78 @@
|
|||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="1555" y="471"/>
|
<point key="canvasLocation" x="1555" y="471"/>
|
||||||
</scene>
|
</scene>
|
||||||
|
<!--Controls-->
|
||||||
|
<scene sceneID="Gi9-m1-y9x">
|
||||||
|
<objects>
|
||||||
|
<viewController title="Controls" id="x1g-pH-DnF" customClass="ControllerInputsViewController" customModule="Delta" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
|
<layoutGuides>
|
||||||
|
<viewControllerLayoutGuide type="top" id="cH1-gu-g2u"/>
|
||||||
|
<viewControllerLayoutGuide type="bottom" id="Z6c-bc-h6l"/>
|
||||||
|
</layoutGuides>
|
||||||
|
<view key="view" contentMode="scaleToFill" id="cPg-qa-ERT">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<containerView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="6Wl-el-X30" userLabel="GameViewController">
|
||||||
|
<rect key="frame" x="0.0" y="64" width="375" height="603"/>
|
||||||
|
<connections>
|
||||||
|
<segue destination="LIv-AL-s86" kind="embed" identifier="embedGameViewController" id="2Qg-Jw-0mM"/>
|
||||||
|
</connections>
|
||||||
|
</containerView>
|
||||||
|
<containerView opaque="NO" contentMode="scaleToFill" placeholderIntrinsicWidth="375" placeholderIntrinsicHeight="200" translatesAutoresizingMaskIntoConstraints="NO" id="KkE-ji-6Y8" userLabel="GridMenuViewController">
|
||||||
|
<rect key="frame" x="0.0" y="233.5" width="375" height="200"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="height" constant="200" id="MWA-T4-ROi"/>
|
||||||
|
</constraints>
|
||||||
|
<connections>
|
||||||
|
<segue destination="Jpj-e9-6XW" kind="embed" identifier="embedActionsMenuViewController" id="kfu-fO-l6Z"/>
|
||||||
|
</connections>
|
||||||
|
</containerView>
|
||||||
|
</subviews>
|
||||||
|
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
|
<gestureRecognizers/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstItem="KkE-ji-6Y8" firstAttribute="centerY" secondItem="cPg-qa-ERT" secondAttribute="centerY" placeholder="YES" id="4wi-cL-aCQ"/>
|
||||||
|
<constraint firstItem="Z6c-bc-h6l" firstAttribute="top" secondItem="6Wl-el-X30" secondAttribute="bottom" id="Bmp-yB-Yf1"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="KkE-ji-6Y8" secondAttribute="trailing" id="Jeb-8K-VYw"/>
|
||||||
|
<constraint firstItem="6Wl-el-X30" firstAttribute="top" secondItem="cH1-gu-g2u" secondAttribute="bottom" id="TD2-bx-DJC"/>
|
||||||
|
<constraint firstAttribute="trailing" secondItem="6Wl-el-X30" secondAttribute="trailing" id="Xph-DL-tBk"/>
|
||||||
|
<constraint firstItem="6Wl-el-X30" firstAttribute="leading" secondItem="cPg-qa-ERT" secondAttribute="leading" id="gcd-77-5wR"/>
|
||||||
|
<constraint firstItem="KkE-ji-6Y8" firstAttribute="leading" secondItem="cPg-qa-ERT" secondAttribute="leading" id="z7N-Cn-hGs"/>
|
||||||
|
</constraints>
|
||||||
|
<connections>
|
||||||
|
<outletCollection property="gestureRecognizers" destination="4p8-OB-LsR" appends="YES" id="4k4-Oj-XtP"/>
|
||||||
|
</connections>
|
||||||
|
</view>
|
||||||
|
<navigationItem key="navigationItem" id="UeP-Yr-9jA">
|
||||||
|
<barButtonItem key="leftBarButtonItem" systemItem="cancel" id="QfC-sf-WbP">
|
||||||
|
<connections>
|
||||||
|
<segue destination="8l5-7I-Z7e" kind="unwind" identifier="cancelControllerInputs" unwindAction="unwindFromControllerControlsViewController:" id="AkD-Lu-h5b"/>
|
||||||
|
</connections>
|
||||||
|
</barButtonItem>
|
||||||
|
<barButtonItem key="rightBarButtonItem" style="done" systemItem="save" id="WHh-7W-jpl">
|
||||||
|
<connections>
|
||||||
|
<segue destination="8l5-7I-Z7e" kind="unwind" identifier="saveControllerInputs" unwindAction="unwindFromControllerControlsViewController:" id="4xr-OB-4dx"/>
|
||||||
|
</connections>
|
||||||
|
</barButtonItem>
|
||||||
|
</navigationItem>
|
||||||
|
<simulatedNavigationBarMetrics key="simulatedTopBarMetrics" barStyle="black" prompted="NO"/>
|
||||||
|
<connections>
|
||||||
|
<outlet property="actionsMenuViewControllerHeightConstraint" destination="MWA-T4-ROi" id="itx-dZ-m76"/>
|
||||||
|
<outlet property="cancelTapGestureRecognizer" destination="4p8-OB-LsR" id="coO-FL-pbp"/>
|
||||||
|
</connections>
|
||||||
|
</viewController>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="DqP-Jn-rth" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
|
<exit id="8l5-7I-Z7e" userLabel="Exit" sceneMemberID="exit"/>
|
||||||
|
<tapGestureRecognizer delaysTouchesBegan="YES" id="4p8-OB-LsR">
|
||||||
|
<connections>
|
||||||
|
<action selector="handleTapGesture:" destination="x1g-pH-DnF" id="8KO-75-4Iy"/>
|
||||||
|
<outlet property="delegate" destination="x1g-pH-DnF" id="GDY-v6-naf"/>
|
||||||
|
</connections>
|
||||||
|
</tapGestureRecognizer>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="3809" y="471"/>
|
||||||
|
</scene>
|
||||||
<!--Controllers-->
|
<!--Controllers-->
|
||||||
<scene sceneID="swa-DT-VKS">
|
<scene sceneID="swa-DT-VKS">
|
||||||
<objects>
|
<objects>
|
||||||
@ -240,14 +329,14 @@
|
|||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Controller Name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="VBO-V1-Wfu">
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Controller Name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="VBO-V1-Wfu">
|
||||||
<rect key="frame" x="15" y="12" width="118.5" height="19.5"/>
|
<rect key="frame" x="16" y="12" width="118.5" height="19.5"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
||||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
<nil key="highlightedColor"/>
|
<nil key="highlightedColor"/>
|
||||||
</label>
|
</label>
|
||||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Player 1" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="tqn-1q-p53">
|
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Player 1" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="tqn-1q-p53">
|
||||||
<rect key="frame" x="304" y="12" width="56" height="19.5"/>
|
<rect key="frame" x="303" y="12" width="56" height="19.5"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
||||||
<color key="textColor" red="0.55686274509803924" green="0.55686274509803924" blue="0.57647058823529407" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
<color key="textColor" red="0.55686274509803924" green="0.55686274509803924" blue="0.57647058823529407" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||||
@ -255,9 +344,6 @@
|
|||||||
</label>
|
</label>
|
||||||
</subviews>
|
</subviews>
|
||||||
</tableViewCellContentView>
|
</tableViewCellContentView>
|
||||||
<connections>
|
|
||||||
<segue destination="HzL-SB-qry" kind="unwind" identifier="unwindControllersSegue" unwindAction="unwindFromControllersSettingsViewController:" id="WJe-ZI-clo"/>
|
|
||||||
</connections>
|
|
||||||
</tableViewCell>
|
</tableViewCell>
|
||||||
</prototypes>
|
</prototypes>
|
||||||
<connections>
|
<connections>
|
||||||
@ -267,16 +353,18 @@
|
|||||||
</tableView>
|
</tableView>
|
||||||
<navigationItem key="navigationItem" title="Controllers" id="QK7-oi-2jJ"/>
|
<navigationItem key="navigationItem" title="Controllers" id="QK7-oi-2jJ"/>
|
||||||
<simulatedNavigationBarMetrics key="simulatedTopBarMetrics" prompted="NO"/>
|
<simulatedNavigationBarMetrics key="simulatedTopBarMetrics" prompted="NO"/>
|
||||||
|
<connections>
|
||||||
|
<segue destination="0QR-U9-gtx" kind="presentation" identifier="controllerInputsSegue" id="E3Y-yV-zT5"/>
|
||||||
|
</connections>
|
||||||
</tableViewController>
|
</tableViewController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="owG-Kh-rfn" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="owG-Kh-rfn" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
<exit id="HzL-SB-qry" userLabel="Exit" sceneMemberID="exit"/>
|
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="2221" y="471"/>
|
<point key="canvasLocation" x="2221" y="471"/>
|
||||||
</scene>
|
</scene>
|
||||||
<!--Game Boy Advance-->
|
<!--Game Boy Advance-->
|
||||||
<scene sceneID="pkL-Te-puh">
|
<scene sceneID="pkL-Te-puh">
|
||||||
<objects>
|
<objects>
|
||||||
<tableViewController id="56e-ul-z6v" customClass="GameTypeControllerSkinsViewController" customModule="Delta" customModuleProvider="target" sceneMemberID="viewController">
|
<tableViewController id="56e-ul-z6v" customClass="SystemControllerSkinsViewController" customModule="Delta" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="grouped" separatorStyle="default" rowHeight="44" sectionHeaderHeight="18" sectionFooterHeight="18" id="fdQ-n7-kUL">
|
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="grouped" separatorStyle="default" rowHeight="44" sectionHeaderHeight="18" sectionFooterHeight="18" id="fdQ-n7-kUL">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
<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"/>
|
||||||
@ -399,7 +487,7 @@
|
|||||||
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="ssH-mM-uG6" sceneMemberID="viewController">
|
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="ssH-mM-uG6" sceneMemberID="viewController">
|
||||||
<toolbarItems/>
|
<toolbarItems/>
|
||||||
<navigationBar key="navigationBar" contentMode="scaleToFill" id="Ckw-ES-lkE">
|
<navigationBar key="navigationBar" contentMode="scaleToFill" id="Ckw-ES-lkE">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
|
<rect key="frame" x="0.0" y="20" width="375" height="44"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
</navigationBar>
|
</navigationBar>
|
||||||
<nil name="viewControllers"/>
|
<nil name="viewControllers"/>
|
||||||
@ -411,5 +499,76 @@
|
|||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="889" y="471"/>
|
<point key="canvasLocation" x="889" y="471"/>
|
||||||
</scene>
|
</scene>
|
||||||
|
<!--Grid Menu View Controller-->
|
||||||
|
<scene sceneID="Lgi-Ii-M1W">
|
||||||
|
<objects>
|
||||||
|
<collectionViewController id="Jpj-e9-6XW" customClass="GridMenuViewController" customModule="Delta" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
|
<collectionView key="view" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" id="yGk-jU-wZQ">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="10" minimumInteritemSpacing="10" id="tLr-UM-1BH" customClass="GridCollectionViewLayout" customModule="Delta" customModuleProvider="target">
|
||||||
|
<size key="itemSize" width="50" height="50"/>
|
||||||
|
<size key="headerReferenceSize" width="0.0" height="0.0"/>
|
||||||
|
<size key="footerReferenceSize" width="0.0" height="0.0"/>
|
||||||
|
<inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
|
||||||
|
</collectionViewFlowLayout>
|
||||||
|
<cells>
|
||||||
|
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="Cell" id="Hef-IR-nMO" customClass="GridCollectionViewCell" customModule="Delta" customModuleProvider="target">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="50" height="50"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
|
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="50" height="50"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</view>
|
||||||
|
</collectionViewCell>
|
||||||
|
</cells>
|
||||||
|
<connections>
|
||||||
|
<outlet property="dataSource" destination="Jpj-e9-6XW" id="iAK-8A-KXA"/>
|
||||||
|
<outlet property="delegate" destination="Jpj-e9-6XW" id="sbi-az-9kr"/>
|
||||||
|
</connections>
|
||||||
|
</collectionView>
|
||||||
|
<size key="freeformSize" width="375" height="667"/>
|
||||||
|
</collectionViewController>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="pRg-BA-3KK" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="4566" y="-216"/>
|
||||||
|
</scene>
|
||||||
|
<!--Game View Controller-->
|
||||||
|
<scene sceneID="qAz-yz-iOc">
|
||||||
|
<objects>
|
||||||
|
<viewController id="LIv-AL-s86" customClass="GameViewController" customModule="DeltaCore" sceneMemberID="viewController">
|
||||||
|
<layoutGuides>
|
||||||
|
<viewControllerLayoutGuide type="top" id="9u3-RP-Qcj"/>
|
||||||
|
<viewControllerLayoutGuide type="bottom" id="XGZ-ro-kQv"/>
|
||||||
|
</layoutGuides>
|
||||||
|
<view key="view" contentMode="scaleToFill" id="57g-cn-rbZ">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="375" height="603"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
|
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||||
|
</view>
|
||||||
|
</viewController>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="uQK-ch-9AG" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="4566" y="471"/>
|
||||||
|
</scene>
|
||||||
|
<!--Navigation Controller-->
|
||||||
|
<scene sceneID="bwW-s2-fcE">
|
||||||
|
<objects>
|
||||||
|
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="0QR-U9-gtx" sceneMemberID="viewController">
|
||||||
|
<toolbarItems/>
|
||||||
|
<simulatedNavigationBarMetrics key="simulatedTopBarMetrics" barStyle="black" prompted="NO"/>
|
||||||
|
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" barStyle="black" id="Y5H-O6-CQ5">
|
||||||
|
<rect key="frame" x="0.0" y="20" width="375" height="44"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
</navigationBar>
|
||||||
|
<nil name="viewControllers"/>
|
||||||
|
<connections>
|
||||||
|
<segue destination="x1g-pH-DnF" kind="relationship" relationship="rootViewController" id="EOa-ao-vBI"/>
|
||||||
|
</connections>
|
||||||
|
</navigationController>
|
||||||
|
<placeholder placeholderIdentifier="IBFirstResponder" id="D4f-Fb-zfa" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
|
</objects>
|
||||||
|
<point key="canvasLocation" x="2977" y="471"/>
|
||||||
|
</scene>
|
||||||
</scenes>
|
</scenes>
|
||||||
</document>
|
</document>
|
||||||
@ -45,15 +45,15 @@ class GridCollectionViewCell: UICollectionViewCell
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate var vibrancyView = UIVisualEffectView(effect: UIVibrancyEffect(blurEffect: UIBlurEffect(style: .dark)))
|
private var vibrancyView = UIVisualEffectView(effect: UIVibrancyEffect(blurEffect: UIBlurEffect(style: .dark)))
|
||||||
|
|
||||||
fileprivate var imageViewWidthConstraint: NSLayoutConstraint!
|
private var imageViewWidthConstraint: NSLayoutConstraint!
|
||||||
fileprivate var imageViewHeightConstraint: NSLayoutConstraint!
|
private var imageViewHeightConstraint: NSLayoutConstraint!
|
||||||
|
|
||||||
fileprivate var textLabelBottomAnchorConstraint: NSLayoutConstraint!
|
private var textLabelBottomAnchorConstraint: NSLayoutConstraint!
|
||||||
|
|
||||||
fileprivate var textLabelVerticalSpacingConstraint: NSLayoutConstraint!
|
private var textLabelVerticalSpacingConstraint: NSLayoutConstraint!
|
||||||
fileprivate var textLabelFocusedVerticalSpacingConstraint: NSLayoutConstraint?
|
private var textLabelFocusedVerticalSpacingConstraint: NSLayoutConstraint?
|
||||||
|
|
||||||
override init(frame: CGRect)
|
override init(frame: CGRect)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,184 +0,0 @@
|
|||||||
//
|
|
||||||
// ImportController.swift
|
|
||||||
// Delta
|
|
||||||
//
|
|
||||||
// Created by Riley Testut on 10/10/15.
|
|
||||||
// Copyright © 2015 Riley Testut. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
import ObjectiveC
|
|
||||||
|
|
||||||
import DeltaCore
|
|
||||||
|
|
||||||
import MobileCoreServices
|
|
||||||
|
|
||||||
protocol ImportControllerDelegate
|
|
||||||
{
|
|
||||||
func importController(_ importController: ImportController, didImport games: Set<Game>, with errors: Set<DatabaseManager.ImportError>)
|
|
||||||
func importController(_ importController: ImportController, didImport controllerSkins: Set<ControllerSkin>, with errors: Set<DatabaseManager.ImportError>)
|
|
||||||
|
|
||||||
/** Optional **/
|
|
||||||
func importControllerDidCancel(_ importController: ImportController)
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ImportControllerDelegate
|
|
||||||
{
|
|
||||||
func importControllerDidCancel(_ importController: ImportController)
|
|
||||||
{
|
|
||||||
// Empty Implementation
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ImportController: NSObject
|
|
||||||
{
|
|
||||||
var delegate: ImportControllerDelegate?
|
|
||||||
|
|
||||||
fileprivate weak var presentingViewController: UIViewController?
|
|
||||||
|
|
||||||
fileprivate func presentImportController(from presentingViewController: UIViewController, animated: Bool, completion: ((Void) -> Void)?)
|
|
||||||
{
|
|
||||||
self.presentingViewController = presentingViewController
|
|
||||||
|
|
||||||
var documentTypes = GameType.supportedTypes.map { $0.rawValue }
|
|
||||||
documentTypes.append(kUTTypeDeltaControllerSkin as String)
|
|
||||||
documentTypes.append(kUTTypeZipArchive as String)
|
|
||||||
|
|
||||||
// Add GBA4iOS's exported UTIs in case user has GBA4iOS installed (which may override Delta's UTI declarations)
|
|
||||||
documentTypes.append("com.rileytestut.gba")
|
|
||||||
documentTypes.append("com.rileytestut.gbc")
|
|
||||||
documentTypes.append("com.rileytestut.gb")
|
|
||||||
|
|
||||||
#if os(iOS)
|
|
||||||
let documentMenuController = UIDocumentMenuViewController(documentTypes: documentTypes, in: .import)
|
|
||||||
documentMenuController.delegate = self
|
|
||||||
documentMenuController.addOption(withTitle: NSLocalizedString("iTunes", comment: ""), image: nil, order: .first) { self.importFromiTunes(nil) }
|
|
||||||
self.presentingViewController?.present(documentMenuController, animated: true, completion: nil)
|
|
||||||
#else
|
|
||||||
self.importFromiTunes(completion)
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
private func importFromiTunes(_ completion: ((Void) -> Void)?)
|
|
||||||
{
|
|
||||||
let alertController = UIAlertController(title: NSLocalizedString("Import from iTunes?", comment: ""), message: NSLocalizedString("Delta will import the games and controller skins copied over via iTunes.", comment: ""), preferredStyle: .alert)
|
|
||||||
|
|
||||||
let importAction = UIAlertAction(title: NSLocalizedString("Import", comment: ""), style: .default) { action in
|
|
||||||
|
|
||||||
let documentsDirectoryURL = DatabaseManager.defaultDirectoryURL().deletingLastPathComponent()
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
let contents = try FileManager.default.contentsOfDirectory(at: documentsDirectoryURL, includingPropertiesForKeys: nil, options: .skipsHiddenFiles)
|
|
||||||
|
|
||||||
DatabaseManager.shared.performBackgroundTask { (context) in
|
|
||||||
let controllerSkinURLs = contents.filter { $0.pathExtension.lowercased() == "deltaskin" }
|
|
||||||
self.importControllerSkins(at: Set(controllerSkinURLs))
|
|
||||||
|
|
||||||
let gameURLs = contents.filter { GameType.gameType(forFileExtension: $0.pathExtension) != .unknown || $0.pathExtension.lowercased() == "zip" }
|
|
||||||
self.importGames(at: Set(gameURLs))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
catch let error as NSError
|
|
||||||
{
|
|
||||||
print(error)
|
|
||||||
}
|
|
||||||
|
|
||||||
self.presentingViewController?.importController = nil
|
|
||||||
|
|
||||||
}
|
|
||||||
alertController.addAction(importAction)
|
|
||||||
|
|
||||||
let cancelAction = UIAlertAction(title: NSLocalizedString("Cancel", comment: ""), style: .cancel) { action in
|
|
||||||
self.delegate?.importControllerDidCancel(self)
|
|
||||||
self.presentingViewController?.importController = nil
|
|
||||||
}
|
|
||||||
alertController.addAction(cancelAction)
|
|
||||||
|
|
||||||
self.presentingViewController?.present(alertController, animated: true, completion: completion)
|
|
||||||
}
|
|
||||||
|
|
||||||
fileprivate func importGames(at urls: Set<URL>)
|
|
||||||
{
|
|
||||||
DatabaseManager.shared.importGames(at: urls) { (games, errors) in
|
|
||||||
self.delegate?.importController(self, didImport: games, with: errors)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fileprivate func importControllerSkins(at urls: Set<URL>)
|
|
||||||
{
|
|
||||||
DatabaseManager.shared.importControllerSkins(at: urls) { (controllerSkins, errors) in
|
|
||||||
self.delegate?.importController(self, didImport: controllerSkins, with: errors)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if os(iOS)
|
|
||||||
|
|
||||||
extension ImportController: UIDocumentMenuDelegate
|
|
||||||
{
|
|
||||||
func documentMenu(_ documentMenu: UIDocumentMenuViewController, didPickDocumentPicker documentPicker: UIDocumentPickerViewController)
|
|
||||||
{
|
|
||||||
documentPicker.delegate = self
|
|
||||||
self.presentingViewController?.present(documentPicker, animated: true, completion: nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func documentMenuWasCancelled(_ documentMenu: UIDocumentMenuViewController)
|
|
||||||
{
|
|
||||||
self.delegate?.importControllerDidCancel(self)
|
|
||||||
|
|
||||||
self.presentingViewController?.importController = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ImportController: UIDocumentPickerDelegate
|
|
||||||
{
|
|
||||||
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentAt url: URL)
|
|
||||||
{
|
|
||||||
if url.pathExtension.lowercased() == "deltaskin"
|
|
||||||
{
|
|
||||||
self.importControllerSkins(at: [url])
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self.importGames(at: [url])
|
|
||||||
}
|
|
||||||
|
|
||||||
self.presentingViewController?.importController = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController)
|
|
||||||
{
|
|
||||||
self.delegate?.importControllerDidCancel(self)
|
|
||||||
|
|
||||||
self.presentingViewController?.importController = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
private var ImportControllerKey: UInt8 = 0
|
|
||||||
|
|
||||||
extension UIViewController
|
|
||||||
{
|
|
||||||
fileprivate(set) var importController: ImportController?
|
|
||||||
{
|
|
||||||
set
|
|
||||||
{
|
|
||||||
objc_setAssociatedObject(self, &ImportControllerKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
|
||||||
}
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return objc_getAssociatedObject(self, &ImportControllerKey) as? ImportController
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func present(_ importController: ImportController, animated: Bool, completion: ((Void) -> Void)?)
|
|
||||||
{
|
|
||||||
self.importController = importController
|
|
||||||
|
|
||||||
importController.presentImportController(from: self, animated: animated, completion: completion)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -45,6 +45,11 @@ class LoadImageURLOperation: RSTLoadOperation<UIImage, NSURL>
|
|||||||
super.cancel()
|
super.cancel()
|
||||||
|
|
||||||
self.downloadOperation?.cancel()
|
self.downloadOperation?.cancel()
|
||||||
|
|
||||||
|
if self.isAsynchronous
|
||||||
|
{
|
||||||
|
self.finish()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func loadResult(completion: @escaping (UIImage?, Swift.Error?) -> Void)
|
override func loadResult(completion: @escaping (UIImage?, Swift.Error?) -> Void)
|
||||||
@ -74,14 +79,12 @@ class LoadImageURLOperation: RSTLoadOperation<UIImage, NSURL>
|
|||||||
|
|
||||||
private func loadLocalImage(completion: @escaping (UIImage?, Error?) -> Void)
|
private func loadLocalImage(completion: @escaping (UIImage?, Error?) -> Void)
|
||||||
{
|
{
|
||||||
let options: NSDictionary = [kCGImageSourceShouldCache as NSString: true]
|
guard let imageSource = CGImageSourceCreateWithURL(self.url as CFURL, nil) else {
|
||||||
|
|
||||||
guard let imageSource = CGImageSourceCreateWithURL(self.url as CFURL, options) else {
|
|
||||||
completion(nil, .doesNotExist)
|
completion(nil, .doesNotExist)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let quartzImage = CGImageSourceCreateImageAtIndex(imageSource, 0, options) else {
|
guard let quartzImage = CGImageSourceCreateImageAtIndex(imageSource, 0, nil) else {
|
||||||
completion(nil, .invalid)
|
completion(nil, .invalid)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
64
Delta/Components/Popover Menu/ListMenuViewController.swift
Normal file
64
Delta/Components/Popover Menu/ListMenuViewController.swift
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
//
|
||||||
|
// PopoverMenuViewController.swift
|
||||||
|
// Delta
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 9/2/17.
|
||||||
|
// Copyright © 2017 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
import Roxas
|
||||||
|
|
||||||
|
class ListMenuViewController: UITableViewController
|
||||||
|
{
|
||||||
|
var items: [MenuItem] {
|
||||||
|
get { return self.dataSource.items }
|
||||||
|
set { self.dataSource.items = newValue }
|
||||||
|
}
|
||||||
|
|
||||||
|
private let dataSource = RSTArrayTableViewDataSource<MenuItem>(items: [])
|
||||||
|
|
||||||
|
override var preferredContentSize: CGSize {
|
||||||
|
get {
|
||||||
|
let navigationBarHeight = self.navigationController?.navigationBar.bounds.height ?? 0.0
|
||||||
|
return CGSize(width: 0, height: (self.tableView.rowHeight * CGFloat(self.items.count)) + navigationBarHeight)
|
||||||
|
}
|
||||||
|
set {}
|
||||||
|
}
|
||||||
|
|
||||||
|
init()
|
||||||
|
{
|
||||||
|
super.init(style: .plain)
|
||||||
|
|
||||||
|
self.dataSource.cellConfigurationHandler = { (cell, item, indexPath) in
|
||||||
|
cell.textLabel?.text = item.text
|
||||||
|
cell.accessoryType = item.isSelected ? .checkmark : .none
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder aDecoder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewDidLoad()
|
||||||
|
{
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
self.tableView.dataSource = self.dataSource
|
||||||
|
self.tableView.rowHeight = 44
|
||||||
|
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: RSTCellContentGenericCellIdentifier)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ListMenuViewController
|
||||||
|
{
|
||||||
|
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
|
||||||
|
{
|
||||||
|
let item = self.dataSource.item(at: indexPath)
|
||||||
|
item.isSelected = !item.isSelected
|
||||||
|
item.action(item)
|
||||||
|
|
||||||
|
self.tableView.reloadData()
|
||||||
|
}
|
||||||
|
}
|
||||||
121
Delta/Components/Popover Menu/PopoverMenuButton.swift
Normal file
121
Delta/Components/Popover Menu/PopoverMenuButton.swift
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
//
|
||||||
|
// PopoverMenuButton.swift
|
||||||
|
// Delta
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 9/2/17.
|
||||||
|
// Copyright © 2017 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
extension UINavigationBar
|
||||||
|
{
|
||||||
|
fileprivate var defaultTitleTextAttributes: [NSAttributedStringKey: Any]? {
|
||||||
|
if let textAttributes = self._defaultTitleTextAttributes
|
||||||
|
{
|
||||||
|
return textAttributes
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make "copy" of self.
|
||||||
|
let navigationBar = UINavigationBar(frame: .zero)
|
||||||
|
navigationBar.barStyle = self.barStyle
|
||||||
|
|
||||||
|
// Set item with title so we can retrieve default text attributes.
|
||||||
|
let navigationItem = UINavigationItem(title: "Testut")
|
||||||
|
navigationBar.items = [navigationItem]
|
||||||
|
navigationBar.isHidden = true
|
||||||
|
|
||||||
|
// Must be added to window hierarchy for it to create title UILabel.
|
||||||
|
self.addSubview(navigationBar)
|
||||||
|
defer { navigationBar.removeFromSuperview() }
|
||||||
|
|
||||||
|
navigationBar.layoutIfNeeded()
|
||||||
|
|
||||||
|
let textAttributes = navigationBar._defaultTitleTextAttributes
|
||||||
|
return textAttributes
|
||||||
|
}
|
||||||
|
|
||||||
|
private var _defaultTitleTextAttributes: [NSAttributedStringKey: Any]? {
|
||||||
|
guard self.titleTextAttributes == nil else { return self.titleTextAttributes }
|
||||||
|
|
||||||
|
guard
|
||||||
|
let contentView = self.subviews.first(where: { NSStringFromClass(type(of: $0)).contains("ContentView") || NSStringFromClass(type(of: $0)).contains("ItemView") }),
|
||||||
|
let titleLabel = contentView.subviews.first(where: { $0 is UILabel }) as? UILabel
|
||||||
|
else { return nil }
|
||||||
|
|
||||||
|
let textAttributes = titleLabel.attributedText?.attributes(at: 0, effectiveRange: nil)
|
||||||
|
return textAttributes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PopoverMenuButton: UIControl
|
||||||
|
{
|
||||||
|
var title: String {
|
||||||
|
get { return self.textLabel.text ?? "" }
|
||||||
|
set {
|
||||||
|
self.textLabel.text = newValue
|
||||||
|
self.updateTextAttributes()
|
||||||
|
self.invalidateIntrinsicContentSize()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private let textLabel: UILabel
|
||||||
|
private let arrowLabel: UILabel
|
||||||
|
private let stackView: UIStackView
|
||||||
|
|
||||||
|
private var parentNavigationBar: UINavigationBar? {
|
||||||
|
guard let navigationController = self.parentViewController as? UINavigationController ?? self.parentViewController?.navigationController else { return nil }
|
||||||
|
guard self.isDescendant(of: navigationController.navigationBar) else { return nil }
|
||||||
|
|
||||||
|
return navigationController.navigationBar
|
||||||
|
}
|
||||||
|
|
||||||
|
override var intrinsicContentSize: CGSize {
|
||||||
|
return self.stackView.systemLayoutSizeFitting(UILayoutFittingCompressedSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
init()
|
||||||
|
{
|
||||||
|
self.textLabel = UILabel()
|
||||||
|
self.textLabel.textColor = .black
|
||||||
|
|
||||||
|
self.arrowLabel = UILabel()
|
||||||
|
self.arrowLabel.text = "▾"
|
||||||
|
self.arrowLabel.textColor = .black
|
||||||
|
|
||||||
|
self.stackView = UIStackView(arrangedSubviews: [self.textLabel, self.arrowLabel])
|
||||||
|
self.stackView.axis = .horizontal
|
||||||
|
self.stackView.distribution = .fillProportionally
|
||||||
|
self.stackView.alignment = .center
|
||||||
|
self.stackView.spacing = 4.0
|
||||||
|
self.stackView.isUserInteractionEnabled = false
|
||||||
|
|
||||||
|
let intrinsicContentSize = self.stackView.systemLayoutSizeFitting(UILayoutFittingCompressedSize)
|
||||||
|
super.init(frame: CGRect(origin: .zero, size: intrinsicContentSize))
|
||||||
|
|
||||||
|
self.addSubview(self.stackView, pinningEdgesWith: .zero)
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder aDecoder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override func didMoveToSuperview()
|
||||||
|
{
|
||||||
|
self.updateTextAttributes()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension PopoverMenuButton
|
||||||
|
{
|
||||||
|
func updateTextAttributes()
|
||||||
|
{
|
||||||
|
guard let parentNavigationBar = self.parentNavigationBar else { return }
|
||||||
|
guard let textAttributes = parentNavigationBar.defaultTitleTextAttributes else { return }
|
||||||
|
|
||||||
|
for label in [self.textLabel, self.arrowLabel]
|
||||||
|
{
|
||||||
|
label.attributedText = NSAttributedString(string: label.text ?? "", attributes: textAttributes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
102
Delta/Components/Popover Menu/PopoverMenuController.swift
Normal file
102
Delta/Components/Popover Menu/PopoverMenuController.swift
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
//
|
||||||
|
// PopoverMenuController.swift
|
||||||
|
// Delta
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 9/5/17.
|
||||||
|
// Copyright © 2017 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
private var popoverMenuControllerKey: UInt8 = 0
|
||||||
|
|
||||||
|
extension UINavigationItem
|
||||||
|
{
|
||||||
|
var popoverMenuController: PopoverMenuController? {
|
||||||
|
get { return objc_getAssociatedObject(self, &popoverMenuControllerKey) as? PopoverMenuController }
|
||||||
|
set {
|
||||||
|
self.titleView = newValue?.popoverMenuButton
|
||||||
|
objc_setAssociatedObject(self, &popoverMenuControllerKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PopoverMenuController: NSObject
|
||||||
|
{
|
||||||
|
let popoverViewController: UIViewController
|
||||||
|
|
||||||
|
let popoverMenuButton: PopoverMenuButton
|
||||||
|
|
||||||
|
var isActive: Bool = false {
|
||||||
|
willSet {
|
||||||
|
guard newValue != self.isActive else { return }
|
||||||
|
|
||||||
|
if newValue
|
||||||
|
{
|
||||||
|
self.presentPopoverViewController()
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
self.dismissPopoverViewController()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init(popoverViewController: UIViewController)
|
||||||
|
{
|
||||||
|
self.popoverViewController = popoverViewController
|
||||||
|
|
||||||
|
self.popoverMenuButton = PopoverMenuButton()
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
self.popoverMenuButton.addTarget(self, action: #selector(PopoverMenuController.pressedPopoverMenuButton(_:)), for: .touchUpInside)
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder aDecoder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension PopoverMenuController
|
||||||
|
{
|
||||||
|
@objc func pressedPopoverMenuButton(_ button: PopoverMenuButton)
|
||||||
|
{
|
||||||
|
self.isActive = !self.isActive
|
||||||
|
}
|
||||||
|
|
||||||
|
func presentPopoverViewController()
|
||||||
|
{
|
||||||
|
guard !self.isActive else { return }
|
||||||
|
|
||||||
|
guard let presentingViewController = self.popoverMenuButton.parentViewController else { return }
|
||||||
|
|
||||||
|
self.popoverViewController.modalPresentationStyle = .popover
|
||||||
|
self.popoverViewController.popoverPresentationController?.delegate = self
|
||||||
|
self.popoverViewController.popoverPresentationController?.sourceView = self.popoverMenuButton.superview
|
||||||
|
self.popoverViewController.popoverPresentationController?.sourceRect = self.popoverMenuButton.frame
|
||||||
|
|
||||||
|
presentingViewController.present(self.popoverViewController, animated: true, completion: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func dismissPopoverViewController()
|
||||||
|
{
|
||||||
|
guard self.isActive else { return }
|
||||||
|
|
||||||
|
self.popoverViewController.dismiss(animated: true, completion: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension PopoverMenuController: UIPopoverPresentationControllerDelegate
|
||||||
|
{
|
||||||
|
func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle
|
||||||
|
{
|
||||||
|
// Force popover presentation, regardless of trait collection.
|
||||||
|
return .none
|
||||||
|
}
|
||||||
|
|
||||||
|
func popoverPresentationControllerDidDismissPopover(_ popoverPresentationController: UIPopoverPresentationController)
|
||||||
|
{
|
||||||
|
self.isActive = false
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,68 @@
|
|||||||
|
//
|
||||||
|
// PopoverMenuViewController.swift
|
||||||
|
// Delta
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 9/2/17.
|
||||||
|
// Copyright © 2017 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
import Roxas
|
||||||
|
|
||||||
|
class ListMenuViewController: UITableViewController
|
||||||
|
{
|
||||||
|
var items: [MenuItem] {
|
||||||
|
get { return self.dataSource.items }
|
||||||
|
set { self.dataSource.items = newValue; }
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate let dataSource = RSTArrayTableViewDataSource<MenuItem>(items: [])
|
||||||
|
|
||||||
|
override var preferredContentSize: CGSize {
|
||||||
|
get {
|
||||||
|
let navigationBarHeight = self.navigationController?.navigationBar.bounds.height ?? 0.0
|
||||||
|
return CGSize(width: 0, height: (self.tableView.rowHeight * CGFloat(self.items.count)) + navigationBarHeight)
|
||||||
|
}
|
||||||
|
set {}
|
||||||
|
}
|
||||||
|
|
||||||
|
init()
|
||||||
|
{
|
||||||
|
super.init(style: .plain)
|
||||||
|
|
||||||
|
self.dataSource.cellConfigurationHandler = { (cell, item, indexPath) in
|
||||||
|
cell.textLabel?.text = item.text
|
||||||
|
cell.accessoryType = item.isSelected ? .checkmark : .none
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder aDecoder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewDidLoad()
|
||||||
|
{
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
self.tableView.dataSource = self.dataSource
|
||||||
|
self.tableView.rowHeight = 44
|
||||||
|
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: RSTCellContentGenericCellIdentifier)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ListMenuViewController
|
||||||
|
{
|
||||||
|
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
|
||||||
|
{
|
||||||
|
self.items.forEach { $0.isSelected = false }
|
||||||
|
|
||||||
|
let item = self.dataSource.item(at: indexPath)
|
||||||
|
item.isSelected = true
|
||||||
|
item.action(item)
|
||||||
|
|
||||||
|
self.tableView.reloadData()
|
||||||
|
|
||||||
|
self.dismiss(animated: true, completion: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -22,6 +22,7 @@ extension DatabaseManager
|
|||||||
{
|
{
|
||||||
case doesNotExist(URL)
|
case doesNotExist(URL)
|
||||||
case invalid(URL)
|
case invalid(URL)
|
||||||
|
case unsupported(URL)
|
||||||
case unknown(URL, NSError)
|
case unknown(URL, NSError)
|
||||||
case saveFailed(Set<URL>, NSError)
|
case saveFailed(Set<URL>, NSError)
|
||||||
|
|
||||||
@ -30,8 +31,9 @@ extension DatabaseManager
|
|||||||
{
|
{
|
||||||
case .doesNotExist: return 0
|
case .doesNotExist: return 0
|
||||||
case .invalid: return 1
|
case .invalid: return 1
|
||||||
case .unknown: return 2
|
case .unsupported: return 2
|
||||||
case .saveFailed: return 3
|
case .unknown: return 3
|
||||||
|
case .saveFailed: return 4
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,15 +43,16 @@ extension DatabaseManager
|
|||||||
{
|
{
|
||||||
case (let .doesNotExist(url1), let .doesNotExist(url2)) where url1 == url2: return true
|
case (let .doesNotExist(url1), let .doesNotExist(url2)) where url1 == url2: return true
|
||||||
case (let .invalid(url1), let .invalid(url2)) where url1 == url2: return true
|
case (let .invalid(url1), let .invalid(url2)) where url1 == url2: return true
|
||||||
|
case (let .unsupported(url1), let .unsupported(url2)) where url1 == url2: return true
|
||||||
case (let .unknown(url1, error1), let .unknown(url2, error2)) where url1 == url2 && error1 == error2: return true
|
case (let .unknown(url1, error1), let .unknown(url2, error2)) where url1 == url2 && error1 == error2: return true
|
||||||
case (let .saveFailed(urls1, error1), let .saveFailed(urls2, error2)) where urls1 == urls2 && error1 == error2: return true
|
case (let .saveFailed(urls1, error1), let .saveFailed(urls2, error2)) where urls1 == urls2 && error1 == error2: return true
|
||||||
case (.doesNotExist, _): return false
|
case (.doesNotExist, _): return false
|
||||||
case (.invalid, _): return false
|
case (.invalid, _): return false
|
||||||
|
case (.unsupported, _): return false
|
||||||
case (.unknown, _): return false
|
case (.unknown, _): return false
|
||||||
case (.saveFailed, _): return false
|
case (.saveFailed, _): return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,16 +60,15 @@ final class DatabaseManager: NSPersistentContainer
|
|||||||
{
|
{
|
||||||
static let shared = DatabaseManager()
|
static let shared = DatabaseManager()
|
||||||
|
|
||||||
fileprivate var gamesDatabase: GamesDatabase? = nil
|
private var gamesDatabase: GamesDatabase? = nil
|
||||||
|
|
||||||
private init()
|
private init()
|
||||||
{
|
{
|
||||||
guard
|
guard
|
||||||
let modelURL = Bundle(for: DatabaseManager.self).url(forResource: "Model", withExtension: "mom"),
|
let modelURL = Bundle(for: DatabaseManager.self).url(forResource: "Delta", withExtension: "momd"),
|
||||||
let managedObjectModel = NSManagedObjectModel(contentsOf: modelURL)
|
let managedObjectModel = NSManagedObjectModel(contentsOf: modelURL)
|
||||||
else { fatalError("Core Data model cannot be found. Aborting.") }
|
else { fatalError("Core Data model cannot be found. Aborting.") }
|
||||||
|
|
||||||
|
|
||||||
super.init(name: "Delta", managedObjectModel: managedObjectModel)
|
super.init(name: "Delta", managedObjectModel: managedObjectModel)
|
||||||
|
|
||||||
self.viewContext.automaticallyMergesChangesFromParent = true
|
self.viewContext.automaticallyMergesChangesFromParent = true
|
||||||
@ -95,13 +97,13 @@ extension DatabaseManager
|
|||||||
//MARK: - Preparation -
|
//MARK: - Preparation -
|
||||||
private extension DatabaseManager
|
private extension DatabaseManager
|
||||||
{
|
{
|
||||||
func prepareDatabase(completion: @escaping (Void) -> Void)
|
func prepareDatabase(completion: @escaping () -> Void)
|
||||||
{
|
{
|
||||||
self.performBackgroundTask { (context) in
|
self.performBackgroundTask { (context) in
|
||||||
|
|
||||||
for gameType in GameType.supportedTypes
|
for system in System.supportedSystems
|
||||||
{
|
{
|
||||||
guard let deltaControllerSkin = DeltaCore.ControllerSkin.standardControllerSkin(for: gameType) else { continue }
|
guard let deltaControllerSkin = DeltaCore.ControllerSkin.standardControllerSkin(for: system.gameType) else { continue }
|
||||||
|
|
||||||
let controllerSkin = ControllerSkin(context: context)
|
let controllerSkin = ControllerSkin(context: context)
|
||||||
controllerSkin.isStandard = true
|
controllerSkin.isStandard = true
|
||||||
@ -173,21 +175,27 @@ extension DatabaseManager
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
let identifier = FileHash.sha1HashOfFile(atPath: url.path) as String
|
guard let gameType = GameType(fileExtension: url.pathExtension), let system = System(gameType: gameType) else {
|
||||||
|
errors.insert(.unsupported(url))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
let identifier = FileHash.sha1HashOfFile(atPath: url.path) as String
|
||||||
let filename = identifier + "." + url.pathExtension
|
let filename = identifier + "." + url.pathExtension
|
||||||
|
|
||||||
let game = Game.insertIntoManagedObjectContext(context)
|
let game = Game(context: context)
|
||||||
game.identifier = identifier
|
game.identifier = identifier
|
||||||
|
game.type = gameType
|
||||||
game.filename = filename
|
game.filename = filename
|
||||||
|
|
||||||
let databaseMetadata = self.gamesDatabase?.metadata(for: game)
|
let databaseMetadata = self.gamesDatabase?.metadata(for: game)
|
||||||
game.name = databaseMetadata?.name ?? url.deletingPathExtension().lastPathComponent
|
game.name = databaseMetadata?.name ?? url.deletingPathExtension().lastPathComponent
|
||||||
game.artworkURL = databaseMetadata?.artworkURL
|
game.artworkURL = databaseMetadata?.artworkURL
|
||||||
|
|
||||||
let gameCollection = GameCollection.gameSystemCollectionForPathExtension(url.pathExtension, inManagedObjectContext: context)
|
let gameCollection = GameCollection(context: context)
|
||||||
game.type = GameType(rawValue: gameCollection.identifier)
|
gameCollection.identifier = gameType.rawValue
|
||||||
game.gameCollections.insert(gameCollection)
|
gameCollection.index = Int16(system.year)
|
||||||
|
gameCollection.games.insert(game)
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
@ -212,7 +220,6 @@ extension DatabaseManager
|
|||||||
|
|
||||||
errors.insert(.unknown(url, error))
|
errors.insert(.unknown(url, error))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
do
|
do
|
||||||
@ -328,9 +335,8 @@ extension DatabaseManager
|
|||||||
guard !entry.fileName.contains("/") else { continue }
|
guard !entry.fileName.contains("/") else { continue }
|
||||||
|
|
||||||
let fileExtension = (entry.fileName as NSString).pathExtension
|
let fileExtension = (entry.fileName as NSString).pathExtension
|
||||||
let gameType = GameType.gameType(forFileExtension: fileExtension)
|
|
||||||
|
|
||||||
guard gameType != .unknown else { continue }
|
guard GameType(fileExtension: fileExtension) != nil else { continue }
|
||||||
|
|
||||||
// At least one entry is a valid game file, so we set archiveContainsValidGameFile to true
|
// At least one entry is a valid game file, so we set archiveContainsValidGameFile to true
|
||||||
// This will result in this archive being considered valid, and thus we will not return an ImportError.invalid error for the archive
|
// This will result in this archive being considered valid, and thus we will not return an ImportError.invalid error for the archive
|
||||||
@ -486,6 +492,14 @@ extension DatabaseManager
|
|||||||
|
|
||||||
return gameTypeDirectoryURL
|
return gameTypeDirectoryURL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class func artworkURL(for game: Game) -> URL
|
||||||
|
{
|
||||||
|
let gameURL = game.fileURL
|
||||||
|
|
||||||
|
let artworkURL = gameURL.deletingPathExtension().appendingPathExtension("jpg")
|
||||||
|
return artworkURL
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//MARK: - Private -
|
//MARK: - Private -
|
||||||
|
|||||||
@ -15,9 +15,9 @@ class InputStreamOutputWriter: NSObject
|
|||||||
let inputStream: InputStream
|
let inputStream: InputStream
|
||||||
let outputStream: OutputStream
|
let outputStream: OutputStream
|
||||||
|
|
||||||
fileprivate var completion: ((Error?) -> Void)?
|
private var completion: ((Error?) -> Void)?
|
||||||
|
|
||||||
fileprivate var dataBuffer = Data(capacity: MaximumBufferLength * 2)
|
private var dataBuffer = Data(capacity: MaximumBufferLength * 2)
|
||||||
|
|
||||||
init(inputStream: InputStream, outputStream: OutputStream)
|
init(inputStream: InputStream, outputStream: OutputStream)
|
||||||
{
|
{
|
||||||
@ -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="13532" 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>
|
||||||
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="11759" systemVersion="16C68" minimumToolsVersion="Xcode 7.0" sourceLanguage="Objective-C" userDefinedModelVersionIdentifier="1.0">
|
<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">
|
<entity name="Cheat" representedClassName="Cheat" syncable="YES">
|
||||||
<attribute name="code" attributeType="String" syncable="YES"/>
|
<attribute name="code" attributeType="String" syncable="YES"/>
|
||||||
<attribute name="creationDate" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
|
<attribute name="creationDate" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
|
||||||
@ -79,6 +79,31 @@
|
|||||||
</uniquenessConstraint>
|
</uniquenessConstraint>
|
||||||
</uniquenessConstraints>
|
</uniquenessConstraints>
|
||||||
</entity>
|
</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" syncable="YES">
|
<entity name="SaveState" representedClassName="SaveState" syncable="YES">
|
||||||
<attribute name="creationDate" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
|
<attribute name="creationDate" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
|
||||||
<attribute name="filename" attributeType="String" syncable="YES">
|
<attribute name="filename" attributeType="String" syncable="YES">
|
||||||
@ -107,6 +132,7 @@
|
|||||||
<element name="ControllerSkin" positionX="-387" positionY="90" width="128" height="135"/>
|
<element name="ControllerSkin" positionX="-387" positionY="90" width="128" height="135"/>
|
||||||
<element name="Game" positionX="-378" positionY="-54" width="128" height="180"/>
|
<element name="Game" positionX="-378" positionY="-54" width="128" height="180"/>
|
||||||
<element name="GameCollection" positionX="-585" positionY="-27" width="128" height="90"/>
|
<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"/>
|
<element name="SaveState" positionX="-198" positionY="113" width="128" height="165"/>
|
||||||
</elements>
|
</elements>
|
||||||
</model>
|
</model>
|
||||||
@ -61,7 +61,7 @@ public class ControllerSkin: _ControllerSkin
|
|||||||
return self.controllerSkin?.isDebugModeEnabled ?? false
|
return self.controllerSkin?.isDebugModeEnabled ?? false
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate lazy var controllerSkin: DeltaCore.ControllerSkin? = {
|
private lazy var controllerSkin: DeltaCore.ControllerSkin? = {
|
||||||
let controllerSkin = self.isStandard ? DeltaCore.ControllerSkin.standardControllerSkin(for: self.gameType) : DeltaCore.ControllerSkin(fileURL: self.fileURL)
|
let controllerSkin = self.isStandard ? DeltaCore.ControllerSkin.standardControllerSkin(for: self.gameType) : DeltaCore.ControllerSkin(fileURL: self.fileURL)
|
||||||
return controllerSkin
|
return controllerSkin
|
||||||
}()
|
}()
|
||||||
@ -88,9 +88,9 @@ extension ControllerSkin: ControllerSkinProtocol
|
|||||||
return self.controllerSkin?.image(for: traits, preferredSize: preferredSize)
|
return self.controllerSkin?.image(for: traits, preferredSize: preferredSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func inputs(for traits: DeltaCore.ControllerSkin.Traits, point: CGPoint) -> [Input]?
|
public func inputs(for traits: DeltaCore.ControllerSkin.Traits, at point: CGPoint) -> [Input]?
|
||||||
{
|
{
|
||||||
return self.controllerSkin?.inputs(for: traits, point: point)
|
return self.controllerSkin?.inputs(for: traits, at: point)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func items(for traits: DeltaCore.ControllerSkin.Traits) -> [DeltaCore.ControllerSkin.Item]?
|
public func items(for traits: DeltaCore.ControllerSkin.Traits) -> [DeltaCore.ControllerSkin.Item]?
|
||||||
|
|||||||
@ -22,6 +22,37 @@ public class Game: _Game, GameProtocol
|
|||||||
|
|
||||||
return fileURL
|
return fileURL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override var artworkURL: URL? {
|
||||||
|
get {
|
||||||
|
self.willAccessValue(forKey: #keyPath(Game.artworkURL))
|
||||||
|
var artworkURL = self.primitiveValue(forKey: #keyPath(Game.artworkURL)) as? URL
|
||||||
|
self.didAccessValue(forKey: #keyPath(Game.artworkURL))
|
||||||
|
|
||||||
|
if let unwrappedArtworkURL = artworkURL, unwrappedArtworkURL.isFileURL
|
||||||
|
{
|
||||||
|
// Recreate the stored URL relative to current sandbox location.
|
||||||
|
artworkURL = URL(fileURLWithPath: unwrappedArtworkURL.relativePath, relativeTo: DatabaseManager.gamesDirectoryURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
return artworkURL
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
self.willChangeValue(forKey: #keyPath(Game.artworkURL))
|
||||||
|
|
||||||
|
var artworkURL = newValue
|
||||||
|
|
||||||
|
if let newValue = newValue, newValue.isFileURL
|
||||||
|
{
|
||||||
|
// Store a relative URL, since the sandbox location changes.
|
||||||
|
artworkURL = URL(fileURLWithPath: newValue.lastPathComponent, relativeTo: DatabaseManager.gamesDirectoryURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.setPrimitiveValue(artworkURL, forKey: #keyPath(Game.artworkURL))
|
||||||
|
|
||||||
|
self.didChangeValue(forKey: #keyPath(Game.artworkURL))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Game
|
extension Game
|
||||||
|
|||||||
@ -9,48 +9,22 @@
|
|||||||
import CoreData
|
import CoreData
|
||||||
|
|
||||||
import DeltaCore
|
import DeltaCore
|
||||||
import SNESDeltaCore
|
|
||||||
import GBADeltaCore
|
|
||||||
|
|
||||||
@objc(GameCollection)
|
@objc(GameCollection)
|
||||||
public class GameCollection: _GameCollection
|
public class GameCollection: _GameCollection
|
||||||
{
|
{
|
||||||
var name: String
|
var name: String {
|
||||||
{
|
return self.system?.localizedName ?? NSLocalizedString("Unknown", comment: "")
|
||||||
|
}
|
||||||
|
|
||||||
|
var shortName: String {
|
||||||
|
return self.system?.localizedShortName ?? NSLocalizedString("Unknown", comment: "")
|
||||||
|
}
|
||||||
|
|
||||||
|
var system: System? {
|
||||||
let gameType = GameType(rawValue: self.identifier)
|
let gameType = GameType(rawValue: self.identifier)
|
||||||
return gameType.localizedName
|
|
||||||
}
|
|
||||||
|
|
||||||
var shortName: String
|
let system = System(gameType: gameType)
|
||||||
{
|
return system
|
||||||
let gameType = GameType(rawValue: self.identifier)
|
|
||||||
return gameType.localizedShortName
|
|
||||||
}
|
|
||||||
|
|
||||||
class func gameSystemCollectionForPathExtension(_ pathExtension: String?, inManagedObjectContext managedObjectContext: NSManagedObjectContext) -> GameCollection
|
|
||||||
{
|
|
||||||
let gameType = GameType.gameType(forFileExtension: pathExtension ?? "")
|
|
||||||
let identifier = gameType.rawValue
|
|
||||||
|
|
||||||
let index: Int16
|
|
||||||
|
|
||||||
switch gameType
|
|
||||||
{
|
|
||||||
case GameType.snes: index = 1990
|
|
||||||
case GameType.gba: index = 2001
|
|
||||||
default: index = Int16(INT16_MAX)
|
|
||||||
}
|
|
||||||
|
|
||||||
let predicate = NSPredicate(format: "%K == %@", #keyPath(GameCollection.identifier), identifier)
|
|
||||||
|
|
||||||
var gameCollection = GameCollection.instancesWithPredicate(predicate, inManagedObjectContext: managedObjectContext, type: GameCollection.self).first
|
|
||||||
if gameCollection == nil
|
|
||||||
{
|
|
||||||
gameCollection = GameCollection.insertIntoManagedObjectContext(managedObjectContext)
|
|
||||||
gameCollection?.identifier = identifier
|
|
||||||
gameCollection?.index = index
|
|
||||||
}
|
|
||||||
|
|
||||||
return gameCollection!
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
77
Delta/Database/Model/Human/GameControllerInputMapping.swift
Normal file
77
Delta/Database/Model/Human/GameControllerInputMapping.swift
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
//
|
||||||
|
// GameControllerInputMapping.swift
|
||||||
|
// Delta
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 8/30/16.
|
||||||
|
// Copyright (c) 2016 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
import DeltaCore
|
||||||
|
|
||||||
|
@objc(GameControllerInputMapping)
|
||||||
|
public class GameControllerInputMapping: _GameControllerInputMapping
|
||||||
|
{
|
||||||
|
private var inputMapping: DeltaCore.GameControllerInputMapping {
|
||||||
|
get { return self.deltaCoreInputMapping as! DeltaCore.GameControllerInputMapping }
|
||||||
|
set { self.deltaCoreInputMapping = newValue }
|
||||||
|
}
|
||||||
|
|
||||||
|
public convenience init(inputMapping: DeltaCore.GameControllerInputMapping, context: NSManagedObjectContext)
|
||||||
|
{
|
||||||
|
self.init(entity: GameControllerInputMapping.entity(), insertInto: context)
|
||||||
|
|
||||||
|
self.inputMapping = inputMapping
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension GameControllerInputMapping
|
||||||
|
{
|
||||||
|
class func inputMapping(for gameController: GameController, gameType: GameType, in managedObjectContext: NSManagedObjectContext) -> GameControllerInputMapping?
|
||||||
|
{
|
||||||
|
guard let playerIndex = gameController.playerIndex else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let fetchRequest: NSFetchRequest<GameControllerInputMapping> = GameControllerInputMapping.fetchRequest()
|
||||||
|
fetchRequest.returnsObjectsAsFaults = false
|
||||||
|
fetchRequest.predicate = NSPredicate(format: "%K == %@ AND %K == %@ AND %K == %d", #keyPath(GameControllerInputMapping.gameControllerInputType), gameController.inputType.rawValue, #keyPath(GameControllerInputMapping.gameType), gameType.rawValue, #keyPath(GameControllerInputMapping.playerIndex), playerIndex)
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
let inputMappings = try managedObjectContext.fetch(fetchRequest)
|
||||||
|
|
||||||
|
let inputMapping = inputMappings.first
|
||||||
|
return inputMapping
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
print(error)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension GameControllerInputMapping: GameControllerInputMappingProtocol
|
||||||
|
{
|
||||||
|
var name: String? {
|
||||||
|
get { return self.inputMapping.name }
|
||||||
|
set { self.inputMapping.name = newValue }
|
||||||
|
}
|
||||||
|
|
||||||
|
var supportedControllerInputs: [Input] {
|
||||||
|
return self.inputMapping.supportedControllerInputs
|
||||||
|
}
|
||||||
|
|
||||||
|
public func input(forControllerInput controllerInput: Input) -> Input?
|
||||||
|
{
|
||||||
|
return self.inputMapping.input(forControllerInput: controllerInput)
|
||||||
|
}
|
||||||
|
|
||||||
|
func set(_ input: Input?, forControllerInput controllerInput: Input)
|
||||||
|
{
|
||||||
|
self.inputMapping.set(input, forControllerInput: controllerInput)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -13,6 +13,7 @@ import DeltaCore
|
|||||||
@objc public enum SaveStateType: Int16
|
@objc public enum SaveStateType: Int16
|
||||||
{
|
{
|
||||||
case auto
|
case auto
|
||||||
|
case quick
|
||||||
case general
|
case general
|
||||||
case locked
|
case locked
|
||||||
}
|
}
|
||||||
@ -78,4 +79,14 @@ public class SaveState: _SaveState, SaveStateProtocol
|
|||||||
print(error)
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,28 @@
|
|||||||
|
// DO NOT EDIT. This file is machine-generated and constantly overwritten.
|
||||||
|
// Make changes to GameControllerInputMapping.swift instead.
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import CoreData
|
||||||
|
|
||||||
|
import DeltaCore
|
||||||
|
|
||||||
|
public class _GameControllerInputMapping: NSManagedObject
|
||||||
|
{
|
||||||
|
@nonobjc public class func fetchRequest() -> NSFetchRequest<GameControllerInputMapping> {
|
||||||
|
return NSFetchRequest<GameControllerInputMapping>(entityName: "GameControllerInputMapping")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Properties
|
||||||
|
|
||||||
|
@NSManaged public var deltaCoreInputMapping: Any
|
||||||
|
|
||||||
|
@NSManaged public var gameControllerInputType: GameControllerInputType
|
||||||
|
|
||||||
|
@NSManaged public var gameType: GameType
|
||||||
|
|
||||||
|
@NSManaged public var playerIndex: Int16
|
||||||
|
|
||||||
|
// MARK: - Relationships
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,61 @@
|
|||||||
|
//
|
||||||
|
// GameControllerInputMappingTransformer.swift
|
||||||
|
// Delta
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 9/27/17.
|
||||||
|
// Copyright © 2017 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
import DeltaCore
|
||||||
|
|
||||||
|
@objc(GameControllerInputMappingTransformer)
|
||||||
|
class GameControllerInputMappingTransformer: ValueTransformer
|
||||||
|
{
|
||||||
|
override class func transformedValueClass() -> AnyClass {
|
||||||
|
return NSData.self
|
||||||
|
}
|
||||||
|
|
||||||
|
override class func allowsReverseTransformation() -> Bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override func transformedValue(_ value: Any?) -> Any?
|
||||||
|
{
|
||||||
|
guard let inputMapping = value as? DeltaCore.GameControllerInputMapping else { return nil }
|
||||||
|
|
||||||
|
let plistEncoder = PropertyListEncoder()
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
let data = try plistEncoder.encode(inputMapping)
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
print(error)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func reverseTransformedValue(_ value: Any?) -> Any?
|
||||||
|
{
|
||||||
|
guard let inputMappingData = value as? Data else { return nil }
|
||||||
|
|
||||||
|
let plistDecoder = PropertyListDecoder()
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
let inputMapping = try plistDecoder.decode(DeltaCore.GameControllerInputMapping.self, from: inputMappingData)
|
||||||
|
return inputMapping
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
print(error)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -8,9 +8,32 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
// Must be a class (not struct) so it can be used with Objective-C generics
|
// Must be an NSObject subclass so it can be used with RSTCellContentDataSource.
|
||||||
class GameMetadata
|
class GameMetadata: NSObject
|
||||||
{
|
{
|
||||||
var name: String?
|
let identifier: Int
|
||||||
var artworkURL: URL?
|
|
||||||
|
let name: String?
|
||||||
|
let artworkURL: URL?
|
||||||
|
|
||||||
|
init(identifier: Int, name: String?, artworkURL: URL?)
|
||||||
|
{
|
||||||
|
self.name = name
|
||||||
|
self.identifier = identifier
|
||||||
|
self.artworkURL = artworkURL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension GameMetadata
|
||||||
|
{
|
||||||
|
override var hash: Int {
|
||||||
|
return self.identifier.hashValue
|
||||||
|
}
|
||||||
|
|
||||||
|
override func isEqual(_ object: Any?) -> Bool
|
||||||
|
{
|
||||||
|
guard let metadata = object as? GameMetadata else { return false }
|
||||||
|
|
||||||
|
return self.identifier == metadata.identifier
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,6 +9,11 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import SQLite
|
import SQLite
|
||||||
|
|
||||||
|
private extension UserDefaults
|
||||||
|
{
|
||||||
|
@NSManaged var previousGamesDatabaseVersion: Int
|
||||||
|
}
|
||||||
|
|
||||||
extension ExpressionType
|
extension ExpressionType
|
||||||
{
|
{
|
||||||
static var name: SQLite.Expression<String?> {
|
static var name: SQLite.Expression<String?> {
|
||||||
@ -19,13 +24,17 @@ extension ExpressionType
|
|||||||
return SQLite.Expression<String?>("releaseCoverFront")
|
return SQLite.Expression<String?>("releaseCoverFront")
|
||||||
}
|
}
|
||||||
|
|
||||||
static var hash: SQLite.Expression<String> {
|
static var sha1Hash: SQLite.Expression<String> {
|
||||||
return SQLite.Expression<String>("romHashSHA1")
|
return SQLite.Expression<String>("romHashSHA1")
|
||||||
}
|
}
|
||||||
|
|
||||||
static var romID: SQLite.Expression<Int> {
|
static var romID: SQLite.Expression<Int> {
|
||||||
return SQLite.Expression<Int>("romID")
|
return SQLite.Expression<Int>("romID")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static var releaseID: SQLite.Expression<Int> {
|
||||||
|
return SQLite.Expression<Int>("releaseID")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Table
|
extension Table
|
||||||
@ -57,7 +66,9 @@ extension GamesDatabase
|
|||||||
|
|
||||||
class GamesDatabase
|
class GamesDatabase
|
||||||
{
|
{
|
||||||
fileprivate let connection: Connection
|
static let version = -1
|
||||||
|
|
||||||
|
private let connection: Connection
|
||||||
|
|
||||||
init() throws
|
init() throws
|
||||||
{
|
{
|
||||||
@ -71,28 +82,36 @@ class GamesDatabase
|
|||||||
{
|
{
|
||||||
throw Error.connection(error)
|
throw Error.connection(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.invalidateVirtualTableIfNeeded()
|
||||||
}
|
}
|
||||||
|
|
||||||
func metadataResults(forGameName gameName: String) -> [GameMetadata]
|
func metadataResults(forGameName gameName: String) -> [GameMetadata]
|
||||||
{
|
{
|
||||||
|
let releaseID = Expression<Any>.releaseID
|
||||||
let name = Expression<Any>.name
|
let name = Expression<Any>.name
|
||||||
let artworkAddress = Expression<Any>.artworkAddress
|
let artworkAddress = Expression<Any>.artworkAddress
|
||||||
|
|
||||||
let query = VirtualTable.search.select(name, artworkAddress).filter(name.match(gameName + "*"))
|
let query = VirtualTable.search.select(releaseID, name, artworkAddress).filter(name.match(gameName + "*"))
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
let rows = try self.connection.prepare(query)
|
let rows = try self.connection.prepare(query)
|
||||||
|
|
||||||
let results = rows.map { row -> GameMetadata in
|
let results = rows.map { (row) -> GameMetadata in
|
||||||
let metadata = GameMetadata()
|
|
||||||
metadata.name = row[name]
|
|
||||||
|
|
||||||
|
let artworkURL: URL?
|
||||||
if let address = row[artworkAddress]
|
if let address = row[artworkAddress]
|
||||||
{
|
{
|
||||||
metadata.artworkURL = URL(string: address)
|
artworkURL = URL(string: address)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
artworkURL = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let metadata = GameMetadata(identifier: row[releaseID], name: row[name], artworkURL: artworkURL)
|
||||||
return metadata
|
return metadata
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,26 +137,31 @@ class GamesDatabase
|
|||||||
|
|
||||||
func metadata(for game: Game) -> GameMetadata?
|
func metadata(for game: Game) -> GameMetadata?
|
||||||
{
|
{
|
||||||
|
let releaseID = Expression<Any>.releaseID
|
||||||
let name = Expression<Any>.name
|
let name = Expression<Any>.name
|
||||||
let artworkAddress = Expression<Any>.artworkAddress
|
let artworkAddress = Expression<Any>.artworkAddress
|
||||||
let hash = Expression<Any>.hash
|
|
||||||
|
let sha1Hash = Expression<Any>.sha1Hash
|
||||||
let romID = Expression<Any>.romID
|
let romID = Expression<Any>.romID
|
||||||
|
|
||||||
let gameHash = game.identifier.uppercased()
|
let gameHash = game.identifier.uppercased()
|
||||||
let query = Table.roms.select(name, artworkAddress).filter(hash == gameHash).join(Table.releases, on: Table.roms[romID] == Table.releases[romID])
|
let query = Table.roms.select(releaseID, name, artworkAddress).filter(sha1Hash == gameHash).join(Table.releases, on: Table.roms[romID] == Table.releases[romID])
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
if let row = try self.connection.pluck(query)
|
if let row = try self.connection.pluck(query)
|
||||||
{
|
{
|
||||||
let metadata = GameMetadata()
|
let artworkURL: URL?
|
||||||
metadata.name = row[name]
|
|
||||||
|
|
||||||
if let address = row[artworkAddress]
|
if let address = row[artworkAddress]
|
||||||
{
|
{
|
||||||
metadata.artworkURL = URL(string: address)
|
artworkURL = URL(string: address)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
artworkURL = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let metadata = GameMetadata(identifier: row[releaseID], name: row[name], artworkURL: artworkURL)
|
||||||
return metadata
|
return metadata
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -152,16 +176,33 @@ class GamesDatabase
|
|||||||
|
|
||||||
private extension GamesDatabase
|
private extension GamesDatabase
|
||||||
{
|
{
|
||||||
|
func invalidateVirtualTableIfNeeded()
|
||||||
|
{
|
||||||
|
guard UserDefaults.standard.previousGamesDatabaseVersion != GamesDatabase.version else { return }
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
try self.connection.run(VirtualTable.search.drop(ifExists: true))
|
||||||
|
|
||||||
|
UserDefaults.standard.previousGamesDatabaseVersion = GamesDatabase.version
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
print(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func prepareFTS() -> Bool
|
func prepareFTS() -> Bool
|
||||||
{
|
{
|
||||||
let name = Expression<Any>.name
|
let name = Expression<Any>.name
|
||||||
let artworkAddress = Expression<Any>.artworkAddress
|
let artworkAddress = Expression<Any>.artworkAddress
|
||||||
|
let releaseID = Expression<Any>.releaseID
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
try self.connection.run(VirtualTable.search.create(.FTS4([name, artworkAddress], tokenize: .Unicode61())))
|
try self.connection.run(VirtualTable.search.create(.FTS4([releaseID, name, artworkAddress], tokenize: .Unicode61())))
|
||||||
|
|
||||||
let update = VirtualTable.search.insert(Table.releases.select(name, artworkAddress))
|
let update = VirtualTable.search.insert(Table.releases.select(releaseID, name, artworkAddress))
|
||||||
_ = try self.connection.run(update)
|
_ = try self.connection.run(update)
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
|
|||||||
@ -15,11 +15,9 @@ class GamesDatabaseBrowserViewController: UITableViewController
|
|||||||
{
|
{
|
||||||
var selectionHandler: ((GameMetadata) -> Void)?
|
var selectionHandler: ((GameMetadata) -> Void)?
|
||||||
|
|
||||||
fileprivate let database: GamesDatabase?
|
private let database: GamesDatabase?
|
||||||
fileprivate let dataSource: RSTArrayTableViewDataSource<GameMetadata>
|
|
||||||
|
|
||||||
fileprivate let operationQueue = RSTOperationQueue()
|
private let dataSource: RSTArrayTableViewPrefetchingDataSource<GameMetadata, UIImage>
|
||||||
fileprivate let imageCache = NSCache<NSURL, UIImage>()
|
|
||||||
|
|
||||||
override init(style: UITableViewStyle) {
|
override init(style: UITableViewStyle) {
|
||||||
fatalError()
|
fatalError()
|
||||||
@ -37,39 +35,13 @@ class GamesDatabaseBrowserViewController: UITableViewController
|
|||||||
print(error)
|
print(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.dataSource = RSTArrayTableViewDataSource<GameMetadata>(items: [])
|
self.dataSource = RSTArrayTableViewPrefetchingDataSource<GameMetadata, UIImage>(items: [])
|
||||||
|
|
||||||
let placeholderView = RSTPlaceholderView()
|
|
||||||
placeholderView.textLabel.textColor = UIColor.lightText
|
|
||||||
placeholderView.detailTextLabel.textColor = UIColor.lightText
|
|
||||||
|
|
||||||
self.dataSource.placeholderView = placeholderView
|
|
||||||
|
|
||||||
super.init(coder: aDecoder)
|
super.init(coder: aDecoder)
|
||||||
|
|
||||||
self.dataSource.cellConfigurationHandler = { (cell, metadata, indexPath) in
|
|
||||||
self.configure(cell: cell as! GameMetadataTableViewCell, with: metadata, for: indexPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
if let database = self.database
|
|
||||||
{
|
|
||||||
self.dataSource.searchController.searchHandler = { [unowned database, unowned dataSource] (searchValue, previousSearchValue) in
|
|
||||||
|
|
||||||
return RSTBlockOperation(executionBlock: { [unowned database, unowned dataSource] (operation) in
|
|
||||||
let results = database.metadataResults(forGameName: searchValue.text)
|
|
||||||
|
|
||||||
guard !operation.isCancelled else { return }
|
|
||||||
|
|
||||||
dataSource.items = results
|
|
||||||
|
|
||||||
rst_dispatch_sync_on_main_thread {
|
|
||||||
self.updatePlaceholderView()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.definesPresentationContext = true
|
self.definesPresentationContext = true
|
||||||
|
|
||||||
|
self.prepareDataSource()
|
||||||
}
|
}
|
||||||
|
|
||||||
override var preferredStatusBarStyle: UIStatusBarStyle {
|
override var preferredStatusBarStyle: UIStatusBarStyle {
|
||||||
@ -83,6 +55,8 @@ class GamesDatabaseBrowserViewController: UITableViewController
|
|||||||
self.view.backgroundColor = UIColor.deltaDarkGray
|
self.view.backgroundColor = UIColor.deltaDarkGray
|
||||||
|
|
||||||
self.tableView.dataSource = self.dataSource
|
self.tableView.dataSource = self.dataSource
|
||||||
|
self.tableView.prefetchDataSource = self.dataSource
|
||||||
|
|
||||||
self.tableView.indicatorStyle = .white
|
self.tableView.indicatorStyle = .white
|
||||||
self.tableView.separatorColor = UIColor.gray
|
self.tableView.separatorColor = UIColor.gray
|
||||||
|
|
||||||
@ -99,7 +73,73 @@ class GamesDatabaseBrowserViewController: UITableViewController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension GamesDatabaseBrowserViewController
|
private extension GamesDatabaseBrowserViewController
|
||||||
|
{
|
||||||
|
func prepareDataSource()
|
||||||
|
{
|
||||||
|
/* Placeholder View */
|
||||||
|
let placeholderView = RSTPlaceholderView()
|
||||||
|
placeholderView.textLabel.textColor = UIColor.lightText
|
||||||
|
placeholderView.detailTextLabel.textColor = UIColor.lightText
|
||||||
|
self.dataSource.placeholderView = placeholderView
|
||||||
|
|
||||||
|
|
||||||
|
/* Cell Configuration */
|
||||||
|
self.dataSource.cellConfigurationHandler = { [unowned self] (cell, metadata, indexPath) in
|
||||||
|
self.configure(cell: cell as! GameMetadataTableViewCell, with: metadata, for: indexPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Prefetching */
|
||||||
|
self.dataSource.prefetchHandler = { (metadata, indexPath, completionHandler) in
|
||||||
|
guard let artworkURL = metadata.artworkURL else { return nil }
|
||||||
|
|
||||||
|
let operation = LoadImageURLOperation(url: artworkURL)
|
||||||
|
operation.resultHandler = { (image, error) in
|
||||||
|
completionHandler(image, error)
|
||||||
|
}
|
||||||
|
return operation
|
||||||
|
}
|
||||||
|
|
||||||
|
self.dataSource.prefetchCompletionHandler = { (cell, image, indexPath, error) in
|
||||||
|
guard let image = image else { return }
|
||||||
|
|
||||||
|
let cell = cell as! GameMetadataTableViewCell
|
||||||
|
|
||||||
|
let artworkDisplaySize = AVMakeRect(aspectRatio: image.size, insideRect: cell.artworkImageView.bounds)
|
||||||
|
let offset = (cell.artworkImageView.bounds.width - artworkDisplaySize.width) / 2
|
||||||
|
|
||||||
|
// Offset artworkImageViewLeadingConstraint and artworkImageViewTrailingConstraint to right-align artworkImageView
|
||||||
|
cell.artworkImageViewLeadingConstraint.constant += offset
|
||||||
|
cell.artworkImageViewTrailingConstraint.constant -= offset
|
||||||
|
|
||||||
|
cell.artworkImageView.image = image
|
||||||
|
cell.artworkImageView.superview?.layoutIfNeeded()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Searching */
|
||||||
|
if let database = self.database
|
||||||
|
{
|
||||||
|
self.dataSource.searchController.searchHandler = { [unowned self, unowned database] (searchValue, previousSearchValue) in
|
||||||
|
return RSTBlockOperation() { [unowned self, unowned database] (operation) in
|
||||||
|
let results = database.metadataResults(forGameName: searchValue.text)
|
||||||
|
|
||||||
|
guard !operation.isCancelled else { return }
|
||||||
|
|
||||||
|
self.dataSource.items = results
|
||||||
|
|
||||||
|
rst_dispatch_sync_on_main_thread {
|
||||||
|
self.resetTableViewContentOffset()
|
||||||
|
self.updatePlaceholderView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension GamesDatabaseBrowserViewController
|
||||||
{
|
{
|
||||||
func configure(cell: GameMetadataTableViewCell, with metadata: GameMetadata, for indexPath: IndexPath)
|
func configure(cell: GameMetadataTableViewCell, with metadata: GameMetadata, for indexPath: IndexPath)
|
||||||
{
|
{
|
||||||
@ -112,30 +152,6 @@ extension GamesDatabaseBrowserViewController
|
|||||||
cell.artworkImageViewTrailingConstraint.constant = 15
|
cell.artworkImageViewTrailingConstraint.constant = 15
|
||||||
|
|
||||||
cell.separatorInset.left = cell.nameLabel.frame.minX
|
cell.separatorInset.left = cell.nameLabel.frame.minX
|
||||||
|
|
||||||
if let artworkURL = metadata.artworkURL
|
|
||||||
{
|
|
||||||
let operation = LoadImageURLOperation(url: artworkURL)
|
|
||||||
operation.resultsCache = self.imageCache
|
|
||||||
operation.resultHandler = { (image, error) in
|
|
||||||
if let image = image
|
|
||||||
{
|
|
||||||
let artworkDisplaySize = AVMakeRect(aspectRatio: image.size, insideRect: cell.artworkImageView.bounds)
|
|
||||||
let offset = (cell.artworkImageView.bounds.width - artworkDisplaySize.width) / 2
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
// Offset artworkImageViewLeadingConstraint and artworkImageViewTrailingConstraint to right-align artworkImageView
|
|
||||||
cell.artworkImageViewLeadingConstraint.constant += offset
|
|
||||||
cell.artworkImageViewTrailingConstraint.constant -= offset
|
|
||||||
|
|
||||||
cell.artworkImageView.image = image
|
|
||||||
cell.artworkImageView.superview?.layoutIfNeeded()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.operationQueue.addOperation(operation, forKey: indexPath as NSIndexPath)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func updatePlaceholderView()
|
func updatePlaceholderView()
|
||||||
@ -153,6 +169,12 @@ extension GamesDatabaseBrowserViewController
|
|||||||
placeholderView.detailTextLabel.text = NSLocalizedString("Please make sure the name is correct, or try searching for another game.", comment: "")
|
placeholderView.detailTextLabel.text = NSLocalizedString("Please make sure the name is correct, or try searching for another game.", comment: "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func resetTableViewContentOffset()
|
||||||
|
{
|
||||||
|
self.tableView.setContentOffset(CGPoint.zero, animated: false)
|
||||||
|
self.tableView.setContentOffset(CGPoint(x: 0, y: -self.topLayoutGuide.length), animated: false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension GamesDatabaseBrowserViewController
|
extension GamesDatabaseBrowserViewController
|
||||||
@ -167,12 +189,6 @@ extension GamesDatabaseBrowserViewController
|
|||||||
let metadata = self.dataSource.item(at: indexPath)
|
let metadata = self.dataSource.item(at: indexPath)
|
||||||
self.selectionHandler?(metadata)
|
self.selectionHandler?(metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath)
|
|
||||||
{
|
|
||||||
let operation = self.operationQueue[indexPath as NSIndexPath]
|
|
||||||
operation?.cancel()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension GamesDatabaseBrowserViewController: UISearchControllerDelegate
|
extension GamesDatabaseBrowserViewController: UISearchControllerDelegate
|
||||||
@ -193,7 +209,6 @@ extension GamesDatabaseBrowserViewController: UISearchControllerDelegate
|
|||||||
func didDismissSearchController(_ searchController: UISearchController)
|
func didDismissSearchController(_ searchController: UISearchController)
|
||||||
{
|
{
|
||||||
// Fix potentially incorrect offset if user dismisses searchController while scrolling
|
// Fix potentially incorrect offset if user dismisses searchController while scrolling
|
||||||
self.tableView.setContentOffset(CGPoint.zero, animated: false)
|
self.resetTableViewContentOffset()
|
||||||
self.tableView.setContentOffset(CGPoint(x: 0, y: -self.topLayoutGuide.length), animated: false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
28
Delta/Emulation/ActionInput.swift
Normal file
28
Delta/Emulation/ActionInput.swift
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
//
|
||||||
|
// ActionInput.swift
|
||||||
|
// Delta
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 8/28/17.
|
||||||
|
// Copyright © 2017 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import DeltaCore
|
||||||
|
|
||||||
|
public extension GameControllerInputType
|
||||||
|
{
|
||||||
|
static let action = GameControllerInputType("com.rileytestut.Delta.input.action")
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ActionInput: String
|
||||||
|
{
|
||||||
|
case quickSave
|
||||||
|
case quickLoad
|
||||||
|
case fastForward
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ActionInput: Input
|
||||||
|
{
|
||||||
|
var type: InputType {
|
||||||
|
return .controller(.action)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -29,6 +29,25 @@ private extension GameViewController
|
|||||||
self.gameType = gameType
|
self.gameType = gameType
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct SustainInputsMapping: GameControllerInputMappingProtocol
|
||||||
|
{
|
||||||
|
let gameController: GameController
|
||||||
|
|
||||||
|
var gameControllerInputType: GameControllerInputType {
|
||||||
|
return self.gameController.inputType
|
||||||
|
}
|
||||||
|
|
||||||
|
func input(forControllerInput controllerInput: Input) -> Input?
|
||||||
|
{
|
||||||
|
if let mappedInput = self.gameController.defaultInputMapping?.input(forControllerInput: controllerInput), mappedInput == StandardGameControllerInput.menu
|
||||||
|
{
|
||||||
|
return mappedInput
|
||||||
|
}
|
||||||
|
|
||||||
|
return controllerInput
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GameViewController: DeltaCore.GameViewController
|
class GameViewController: DeltaCore.GameViewController
|
||||||
@ -53,11 +72,11 @@ class GameViewController: DeltaCore.GameViewController
|
|||||||
}
|
}
|
||||||
|
|
||||||
//MARK: - Private Properties -
|
//MARK: - Private Properties -
|
||||||
fileprivate var pauseViewController: PauseViewController?
|
private var pauseViewController: PauseViewController?
|
||||||
fileprivate var pausingGameController: GameController?
|
private var pausingGameController: GameController?
|
||||||
|
|
||||||
// Prevents the same save state from being saved multiple times
|
// Prevents the same save state from being saved multiple times
|
||||||
fileprivate var pausedSaveState: PausedSaveState? {
|
private var pausedSaveState: PausedSaveState? {
|
||||||
didSet
|
didSet
|
||||||
{
|
{
|
||||||
if let saveState = oldValue, self.pausedSaveState == nil
|
if let saveState = oldValue, self.pausedSaveState == nil
|
||||||
@ -74,25 +93,20 @@ class GameViewController: DeltaCore.GameViewController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate var _isLoadingSaveState = false
|
private var _isLoadingSaveState = false
|
||||||
|
|
||||||
fileprivate var context = CIContext(options: [kCIContextWorkingColorSpace: NSNull()])
|
private var context = CIContext(options: [kCIContextWorkingColorSpace: NSNull()])
|
||||||
|
|
||||||
// Sustain Buttons
|
// Sustain Buttons
|
||||||
fileprivate var updateSemaphores = Set<DispatchSemaphore>()
|
private var isSelectingSustainedButtons = false
|
||||||
fileprivate var sustainedInputs = [ObjectIdentifier: [Input]]()
|
private var sustainInputsMapping: SustainInputsMapping?
|
||||||
fileprivate var reactivateSustainedInputsQueue: OperationQueue
|
|
||||||
fileprivate var selectingSustainedButtons = false
|
|
||||||
|
|
||||||
fileprivate var sustainButtonsContentView: UIView!
|
private var sustainButtonsContentView: UIView!
|
||||||
fileprivate var sustainButtonsBlurView: UIVisualEffectView!
|
private var sustainButtonsBlurView: UIVisualEffectView!
|
||||||
fileprivate var sustainButtonsBackgroundView: RSTPlaceholderView!
|
private var sustainButtonsBackgroundView: RSTPlaceholderView!
|
||||||
|
|
||||||
required init()
|
required init()
|
||||||
{
|
{
|
||||||
self.reactivateSustainedInputsQueue = OperationQueue()
|
|
||||||
self.reactivateSustainedInputsQueue.maxConcurrentOperationCount = 1
|
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
self.initialize()
|
self.initialize()
|
||||||
@ -100,9 +114,6 @@ class GameViewController: DeltaCore.GameViewController
|
|||||||
|
|
||||||
required init?(coder aDecoder: NSCoder)
|
required init?(coder aDecoder: NSCoder)
|
||||||
{
|
{
|
||||||
self.reactivateSustainedInputsQueue = OperationQueue()
|
|
||||||
self.reactivateSustainedInputsQueue.maxConcurrentOperationCount = 1
|
|
||||||
|
|
||||||
super.init(coder: aDecoder)
|
super.init(coder: aDecoder)
|
||||||
|
|
||||||
self.initialize()
|
self.initialize()
|
||||||
@ -112,8 +123,8 @@ class GameViewController: DeltaCore.GameViewController
|
|||||||
{
|
{
|
||||||
self.delegate = self
|
self.delegate = self
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(GameViewController.updateControllers), name: .externalControllerDidConnect, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(GameViewController.updateControllers), name: .externalGameControllerDidConnect, object: nil)
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(GameViewController.updateControllers), name: .externalControllerDidDisconnect, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(GameViewController.updateControllers), name: .externalGameControllerDidDisconnect, object: nil)
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(GameViewController.didEnterBackground(with:)), name: .UIApplicationDidEnterBackground, object: UIApplication.shared)
|
NotificationCenter.default.addObserver(self, selector: #selector(GameViewController.didEnterBackground(with:)), name: .UIApplicationDidEnterBackground, object: UIApplication.shared)
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(GameViewController.settingsDidChange(with:)), name: .settingsDidChange, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(GameViewController.settingsDidChange(with:)), name: .settingsDidChange, object: nil)
|
||||||
}
|
}
|
||||||
@ -128,20 +139,43 @@ class GameViewController: DeltaCore.GameViewController
|
|||||||
{
|
{
|
||||||
super.gameController(gameController, didActivate: input)
|
super.gameController(gameController, didActivate: input)
|
||||||
|
|
||||||
guard (input as? ControllerInput) != .menu else { return }
|
if self.isSelectingSustainedButtons
|
||||||
|
{
|
||||||
|
guard let pausingGameController = self.pausingGameController, gameController == pausingGameController else { return }
|
||||||
|
|
||||||
if self.selectingSustainedButtons
|
if input != StandardGameControllerInput.menu
|
||||||
{
|
{
|
||||||
self.addSustainedInput(input, for: gameController)
|
gameController.sustain(input)
|
||||||
}
|
}
|
||||||
else if let sustainedInputs = self.sustainedInputs[ObjectIdentifier(gameController)], sustainedInputs.contains(where: { $0.isEqual(input) })
|
}
|
||||||
|
else if self.emulatorCore?.state == .running
|
||||||
{
|
{
|
||||||
// Perform on next run loop
|
guard let actionInput = ActionInput(input: input) else { return }
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.reactivateSustainedInput(input, for: gameController)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -153,10 +187,15 @@ extension GameViewController
|
|||||||
{
|
{
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
// Lays out self.gameView, so we can pin self.sustainButtonsContentView to it without resulting in a temporary "cannot satisfy constraints".
|
||||||
|
self.view.layoutIfNeeded()
|
||||||
|
|
||||||
|
let gameViewContainerView = self.gameView.superview!
|
||||||
|
|
||||||
self.sustainButtonsContentView = UIView(frame: CGRect(x: 0, y: 0, width: self.gameView.bounds.width, height: self.gameView.bounds.height))
|
self.sustainButtonsContentView = UIView(frame: CGRect(x: 0, y: 0, width: self.gameView.bounds.width, height: self.gameView.bounds.height))
|
||||||
self.sustainButtonsContentView.translatesAutoresizingMaskIntoConstraints = false
|
self.sustainButtonsContentView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
self.sustainButtonsContentView.isHidden = true
|
self.sustainButtonsContentView.isHidden = true
|
||||||
self.view.insertSubview(self.sustainButtonsContentView, aboveSubview: self.gameView)
|
self.view.insertSubview(self.sustainButtonsContentView, aboveSubview: gameViewContainerView)
|
||||||
|
|
||||||
let blurEffect = UIBlurEffect(style: .dark)
|
let blurEffect = UIBlurEffect(style: .dark)
|
||||||
let vibrancyEffect = UIVibrancyEffect(blurEffect: blurEffect)
|
let vibrancyEffect = UIVibrancyEffect(blurEffect: blurEffect)
|
||||||
@ -182,10 +221,10 @@ extension GameViewController
|
|||||||
vibrancyView.contentView.addSubview(self.sustainButtonsBackgroundView)
|
vibrancyView.contentView.addSubview(self.sustainButtonsBackgroundView)
|
||||||
|
|
||||||
// Auto Layout
|
// Auto Layout
|
||||||
self.sustainButtonsContentView.leadingAnchor.constraint(equalTo: self.gameView.leadingAnchor).isActive = true
|
self.sustainButtonsContentView.leadingAnchor.constraint(equalTo: gameViewContainerView.leadingAnchor).isActive = true
|
||||||
self.sustainButtonsContentView.trailingAnchor.constraint(equalTo: self.gameView.trailingAnchor).isActive = true
|
self.sustainButtonsContentView.trailingAnchor.constraint(equalTo: gameViewContainerView.trailingAnchor).isActive = true
|
||||||
self.sustainButtonsContentView.topAnchor.constraint(equalTo: self.gameView.topAnchor).isActive = true
|
self.sustainButtonsContentView.topAnchor.constraint(equalTo: gameViewContainerView.topAnchor).isActive = true
|
||||||
self.sustainButtonsContentView.bottomAnchor.constraint(equalTo: self.gameView.bottomAnchor).isActive = true
|
self.sustainButtonsContentView.bottomAnchor.constraint(equalTo: gameViewContainerView.bottomAnchor).isActive = true
|
||||||
|
|
||||||
self.updateControllerSkin()
|
self.updateControllerSkin()
|
||||||
self.updateControllers()
|
self.updateControllers()
|
||||||
@ -220,7 +259,7 @@ extension GameViewController
|
|||||||
|
|
||||||
if let game = self.game
|
if let game = self.game
|
||||||
{
|
{
|
||||||
let fileURL = FileManager.uniqueTemporaryURL()
|
let fileURL = FileManager.default.uniqueTemporaryURL()
|
||||||
self.pausedSaveState = PausedSaveState(fileURL: fileURL, gameType: game.type)
|
self.pausedSaveState = PausedSaveState(fileURL: fileURL, gameType: game.type)
|
||||||
|
|
||||||
self.emulatorCore?.saveSaveState(to: fileURL)
|
self.emulatorCore?.saveSaveState(to: fileURL)
|
||||||
@ -238,22 +277,27 @@ extension GameViewController
|
|||||||
pauseViewController.saveStatesViewControllerDelegate = self
|
pauseViewController.saveStatesViewControllerDelegate = self
|
||||||
pauseViewController.cheatsViewControllerDelegate = self
|
pauseViewController.cheatsViewControllerDelegate = self
|
||||||
|
|
||||||
pauseViewController.fastForwardItem?.selected = (self.emulatorCore?.rate != self.emulatorCore?.configuration.supportedRates.lowerBound)
|
pauseViewController.fastForwardItem?.isSelected = (self.emulatorCore?.rate != self.emulatorCore?.deltaCore.supportedRates.lowerBound)
|
||||||
pauseViewController.fastForwardItem?.action = { [unowned self] item in
|
pauseViewController.fastForwardItem?.action = { [unowned self] item in
|
||||||
guard let emulatorCore = self.emulatorCore else { return }
|
self.performFastForwardAction(activate: item.isSelected)
|
||||||
emulatorCore.rate = item.selected ? emulatorCore.configuration.supportedRates.upperBound : emulatorCore.configuration.supportedRates.lowerBound
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pauseViewController.sustainButtonsItem?.selected = (self.sustainedInputs[ObjectIdentifier(gameController)]?.count ?? 0) > 0
|
pauseViewController.sustainButtonsItem?.isSelected = gameController.sustainedInputs.count > 0
|
||||||
pauseViewController.sustainButtonsItem?.action = { [unowned self, unowned pauseViewController] item in
|
pauseViewController.sustainButtonsItem?.action = { [unowned self, unowned pauseViewController] item in
|
||||||
|
|
||||||
self.resetSustainedInputs(for: gameController)
|
for input in gameController.sustainedInputs
|
||||||
|
{
|
||||||
|
gameController.unsustain(input)
|
||||||
|
}
|
||||||
|
|
||||||
if item.selected
|
if item.isSelected
|
||||||
{
|
{
|
||||||
self.showSustainButtonView()
|
self.showSustainButtonView()
|
||||||
pauseViewController.dismiss()
|
pauseViewController.dismiss()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Re-set gameController as pausingGameController.
|
||||||
|
self.pausingGameController = gameController
|
||||||
}
|
}
|
||||||
|
|
||||||
self.pauseViewController = pauseViewController
|
self.pauseViewController = pauseViewController
|
||||||
@ -341,13 +385,14 @@ private extension GameViewController
|
|||||||
{
|
{
|
||||||
@objc func updateControllers()
|
@objc func updateControllers()
|
||||||
{
|
{
|
||||||
var controllers = [GameController]()
|
let isExternalGameControllerConnected = ExternalGameControllerManager.shared.connectedControllers.contains(where: { $0.playerIndex != nil })
|
||||||
controllers.append(self.controllerView)
|
if !isExternalGameControllerConnected && Settings.localControllerPlayerIndex == nil
|
||||||
|
{
|
||||||
|
Settings.localControllerPlayerIndex = 0
|
||||||
|
}
|
||||||
|
|
||||||
// We need to map each item as a GameControllerProtocol due to a Swift bug
|
// If Settings.localControllerPlayerIndex is non-nil, and there isn't a connected controller with same playerIndex, show controller view.
|
||||||
controllers.append(contentsOf: ExternalControllerManager.shared.connectedControllers.map { $0 as GameController })
|
if let index = Settings.localControllerPlayerIndex, !ExternalGameControllerManager.shared.connectedControllers.contains { $0.playerIndex == index }
|
||||||
|
|
||||||
if let index = Settings.localControllerPlayerIndex
|
|
||||||
{
|
{
|
||||||
self.controllerView.playerIndex = index
|
self.controllerView.playerIndex = index
|
||||||
self.controllerView.isHidden = false
|
self.controllerView.isHidden = false
|
||||||
@ -356,44 +401,48 @@ private extension GameViewController
|
|||||||
{
|
{
|
||||||
self.controllerView.playerIndex = nil
|
self.controllerView.playerIndex = nil
|
||||||
self.controllerView.isHidden = true
|
self.controllerView.isHidden = true
|
||||||
}
|
|
||||||
|
|
||||||
// Removing all game controllers from EmulatorCore will reset each controller's playerIndex to nil
|
Settings.localControllerPlayerIndex = nil
|
||||||
// We temporarily cache their playerIndexes, and then we reset them after removing all controllers
|
|
||||||
var controllerIndexes = [ObjectIdentifier: Int?]()
|
|
||||||
controllers.forEach { controllerIndexes[ObjectIdentifier($0)] = $0.playerIndex }
|
|
||||||
|
|
||||||
self.emulatorCore?.removeAllGameControllers()
|
|
||||||
|
|
||||||
// Reset each controller's playerIndex to what it was before removing all controllers from EmulatorCore
|
|
||||||
controllers.forEach { $0.playerIndex = controllerIndexes[ObjectIdentifier($0)] ?? nil }
|
|
||||||
|
|
||||||
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()
|
self.view.setNeedsLayout()
|
||||||
self.view.layoutIfNeeded()
|
self.view.layoutIfNeeded()
|
||||||
|
|
||||||
|
if let emulatorCore = self.emulatorCore, let game = self.game
|
||||||
|
{
|
||||||
|
let controllers = [self.controllerView as GameController] + ExternalGameControllerManager.shared.connectedControllers
|
||||||
|
|
||||||
|
for gameController in controllers
|
||||||
|
{
|
||||||
|
if gameController.playerIndex != nil
|
||||||
|
{
|
||||||
|
if let inputMapping = GameControllerInputMapping.inputMapping(for: gameController, gameType: game.type, in: DatabaseManager.shared.viewContext)
|
||||||
|
{
|
||||||
|
gameController.addReceiver(self, inputMapping: inputMapping)
|
||||||
|
gameController.addReceiver(emulatorCore, inputMapping: inputMapping)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gameController.addReceiver(self)
|
||||||
|
gameController.addReceiver(emulatorCore)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gameController.removeReceiver(self)
|
||||||
|
gameController.removeReceiver(emulatorCore)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateControllerSkin()
|
func updateControllerSkin()
|
||||||
{
|
{
|
||||||
guard let game = self.game else { return }
|
guard let game = self.game, let system = System(gameType: game.type) else { return }
|
||||||
|
|
||||||
let traits = DeltaCore.ControllerSkin.Traits.defaults(for: self.view)
|
let traits = DeltaCore.ControllerSkin.Traits.defaults(for: self.view)
|
||||||
|
|
||||||
let controllerSkin = Settings.preferredControllerSkin(for: game.type, traits: traits)
|
let controllerSkin = Settings.preferredControllerSkin(for: system, traits: traits)
|
||||||
self.controllerView.controllerSkin = controllerSkin
|
self.controllerView.controllerSkin = controllerSkin
|
||||||
|
|
||||||
if controllerSkin?.isTranslucent(for: traits) ?? false
|
if controllerSkin?.isTranslucent(for: traits) ?? false
|
||||||
@ -411,7 +460,7 @@ private extension GameViewController
|
|||||||
/// Save States
|
/// Save States
|
||||||
extension GameViewController: SaveStatesViewControllerDelegate
|
extension GameViewController: SaveStatesViewControllerDelegate
|
||||||
{
|
{
|
||||||
fileprivate func updateAutoSaveState()
|
private func updateAutoSaveState()
|
||||||
{
|
{
|
||||||
// Ensures game is non-nil and also a Game subclass
|
// Ensures game is non-nil and also a Game subclass
|
||||||
guard let game = self.game as? Game else { return }
|
guard let game = self.game as? Game else { return }
|
||||||
@ -429,29 +478,19 @@ extension GameViewController: SaveStatesViewControllerDelegate
|
|||||||
|
|
||||||
let game = backgroundContext.object(with: game.objectID) as! Game
|
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 = SaveState.fetchRequest(for: game, type: .auto)
|
||||||
|
|
||||||
let fetchRequest: NSFetchRequest<SaveState> = SaveState.fetchRequest()
|
|
||||||
fetchRequest.predicate = predicate
|
|
||||||
fetchRequest.sortDescriptors = [NSSortDescriptor(key: #keyPath(SaveState.creationDate), ascending: true)]
|
fetchRequest.sortDescriptors = [NSSortDescriptor(key: #keyPath(SaveState.creationDate), ascending: true)]
|
||||||
|
|
||||||
var saveStates: [SaveState]? = nil
|
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
saveStates = try fetchRequest.execute()
|
let saveStates = try fetchRequest.execute()
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
print(error)
|
|
||||||
}
|
|
||||||
|
|
||||||
if let saveStates = saveStates, let saveState = saveStates.first, saveStates.count >= 2
|
if let saveState = saveStates.first, saveStates.count >= 2
|
||||||
{
|
{
|
||||||
// If there are two or more auto save states, update the oldest one
|
// If there are two or more auto save states, update the oldest one
|
||||||
self.update(saveState, with: self.pausedSaveState)
|
self.update(saveState, with: self.pausedSaveState)
|
||||||
|
|
||||||
// Tiny hack; SaveStatesViewController sorts save states by creation date, so we update the creation date too
|
// Tiny hack: SaveStatesViewController sorts save states by creation date, so we update the creation date too
|
||||||
// Simpler than deleting old save states ¯\_(ツ)_/¯
|
// Simpler than deleting old save states ¯\_(ツ)_/¯
|
||||||
saveState.creationDate = saveState.modifiedDate
|
saveState.creationDate = saveState.modifiedDate
|
||||||
}
|
}
|
||||||
@ -464,12 +503,17 @@ extension GameViewController: SaveStatesViewControllerDelegate
|
|||||||
|
|
||||||
self.update(saveState, with: self.pausedSaveState)
|
self.update(saveState, with: self.pausedSaveState)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
print(error)
|
||||||
|
}
|
||||||
|
|
||||||
backgroundContext.saveWithErrorLogging()
|
backgroundContext.saveWithErrorLogging()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func update(_ saveState: SaveState, with replacementSaveState: SaveStateProtocol? = nil)
|
private func update(_ saveState: SaveState, with replacementSaveState: SaveStateProtocol? = nil)
|
||||||
{
|
{
|
||||||
let isRunning = (self.emulatorCore?.state == .running)
|
let isRunning = (self.emulatorCore?.state == .running)
|
||||||
|
|
||||||
@ -523,25 +567,14 @@ extension GameViewController: SaveStatesViewControllerDelegate
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//MARK: - SaveStatesViewControllerDelegate
|
private func load(_ saveState: SaveStateProtocol)
|
||||||
|
|
||||||
func saveStatesViewController(_ saveStatesViewController: SaveStatesViewController, updateSaveState saveState: SaveState)
|
|
||||||
{
|
{
|
||||||
let updatingExistingSaveState = FileManager.default.fileExists(atPath: saveState.fileURL.path)
|
let isRunning = (self.emulatorCore?.state == .running)
|
||||||
|
|
||||||
self.update(saveState)
|
if isRunning
|
||||||
|
|
||||||
// Dismiss if updating an existing save state.
|
|
||||||
// If creating a new one, don't dismiss.
|
|
||||||
if updatingExistingSaveState
|
|
||||||
{
|
{
|
||||||
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.
|
// 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.
|
// Then, we update the auto save state, but load our copy so everything works out.
|
||||||
@ -549,7 +582,7 @@ extension GameViewController: SaveStatesViewControllerDelegate
|
|||||||
|
|
||||||
if let autoSaveState = saveState as? SaveState, autoSaveState.type == .auto
|
if let autoSaveState = saveState as? SaveState, autoSaveState.type == .auto
|
||||||
{
|
{
|
||||||
let temporaryURL = FileManager.uniqueTemporaryURL()
|
let temporaryURL = FileManager.default.uniqueTemporaryURL()
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
@ -585,17 +618,34 @@ extension GameViewController: SaveStatesViewControllerDelegate
|
|||||||
print(error)
|
print(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reactivate sustained inputs
|
if isRunning
|
||||||
for gameController in self.emulatorCore?.gameControllers ?? []
|
|
||||||
{
|
{
|
||||||
guard let sustainedInputs = self.sustainedInputs[ObjectIdentifier(gameController)] else { continue }
|
self.resumeEmulation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for input in sustainedInputs
|
//MARK: - SaveStatesViewControllerDelegate
|
||||||
|
|
||||||
|
func saveStatesViewController(_ saveStatesViewController: SaveStatesViewController, updateSaveState saveState: SaveState)
|
||||||
{
|
{
|
||||||
self.reactivateSustainedInput(input, for: gameController)
|
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()
|
self.pauseViewController?.dismiss()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -620,7 +670,12 @@ private extension GameViewController
|
|||||||
{
|
{
|
||||||
func showSustainButtonView()
|
func showSustainButtonView()
|
||||||
{
|
{
|
||||||
self.selectingSustainedButtons = true
|
guard let gameController = self.pausingGameController else { return }
|
||||||
|
|
||||||
|
self.isSelectingSustainedButtons = true
|
||||||
|
|
||||||
|
let sustainInputsMapping = SustainInputsMapping(gameController: gameController)
|
||||||
|
gameController.addReceiver(self, inputMapping: sustainInputsMapping)
|
||||||
|
|
||||||
let blurEffect = self.sustainButtonsBlurView.effect
|
let blurEffect = self.sustainButtonsBlurView.effect
|
||||||
self.sustainButtonsBlurView.effect = nil
|
self.sustainButtonsBlurView.effect = nil
|
||||||
@ -635,7 +690,18 @@ private extension GameViewController
|
|||||||
|
|
||||||
func hideSustainButtonView()
|
func hideSustainButtonView()
|
||||||
{
|
{
|
||||||
self.selectingSustainedButtons = false
|
guard let gameController = self.pausingGameController else { return }
|
||||||
|
|
||||||
|
self.isSelectingSustainedButtons = false
|
||||||
|
|
||||||
|
self.updateControllers()
|
||||||
|
self.sustainInputsMapping = nil
|
||||||
|
|
||||||
|
// Reactivate all sustained inputs, since they will now be mapped to game inputs.
|
||||||
|
for input in gameController.sustainedInputs
|
||||||
|
{
|
||||||
|
gameController.activate(input)
|
||||||
|
}
|
||||||
|
|
||||||
let blurEffect = self.sustainButtonsBlurView.effect
|
let blurEffect = self.sustainButtonsBlurView.effect
|
||||||
|
|
||||||
@ -647,95 +713,77 @@ private extension GameViewController
|
|||||||
self.sustainButtonsBlurView.effect = blurEffect
|
self.sustainButtonsBlurView.effect = blurEffect
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func resetSustainedInputs(for gameController: GameController)
|
//MARK: - Action Inputs -
|
||||||
|
/// Action Inputs
|
||||||
|
extension GameViewController
|
||||||
|
{
|
||||||
|
func performQuickSaveAction()
|
||||||
{
|
{
|
||||||
if let previousInputs = self.sustainedInputs[ObjectIdentifier(gameController)]
|
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
|
||||||
{
|
{
|
||||||
let receivers = gameController.receivers
|
if let quickSaveState = try fetchRequest.execute().first
|
||||||
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, for gameController: GameController)
|
|
||||||
{
|
{
|
||||||
var inputs = self.sustainedInputs[ObjectIdentifier(gameController)] ?? []
|
self.update(quickSaveState)
|
||||||
|
|
||||||
guard !inputs.contains(where: { $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) }
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
func reactivateSustainedInput(_ input: Input, for gameController: GameController)
|
|
||||||
{
|
{
|
||||||
// These MUST be performed serially, or else Bad Things Happen™ if multiple inputs are reactivated at once
|
let saveState = SaveState(context: backgroundContext)
|
||||||
self.reactivateSustainedInputsQueue.addOperation {
|
saveState.type = .quick
|
||||||
|
saveState.game = game
|
||||||
|
|
||||||
// The manual activations/deactivations here are hidden implementation details, so we won't notify ourselves about them
|
self.update(saveState)
|
||||||
gameController.removeReceiver(self)
|
}
|
||||||
|
}
|
||||||
// Must deactivate first so core recognizes a secondary activation
|
catch
|
||||||
gameController.deactivate(input)
|
{
|
||||||
|
print(error)
|
||||||
let dispatchQueue = DispatchQueue(label: "com.rileytestut.Delta.sustainButtonsQueue")
|
|
||||||
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.wait()
|
|
||||||
semaphore.wait()
|
|
||||||
|
|
||||||
// These MUST be performed serially, or else Bad Things Happen™ if multiple inputs are reactivated at once
|
|
||||||
self.reactivateSustainedInputsQueue.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)
|
backgroundContext.saveWithErrorLogging()
|
||||||
self.reactivateSustainedInputsQueue.waitUntilAllOperationsAreFinished()
|
|
||||||
|
|
||||||
gameController.addReceiver(self)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func performQuickLoadAction()
|
||||||
|
{
|
||||||
|
guard let game = self.game as? Game else { return }
|
||||||
|
|
||||||
|
let fetchRequest = SaveState.fetchRequest(for: game, type: .quick)
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if let quickSaveState = try DatabaseManager.shared.viewContext.fetch(fetchRequest).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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -745,12 +793,17 @@ extension GameViewController: GameViewControllerDelegate
|
|||||||
{
|
{
|
||||||
func gameViewController(_ gameViewController: DeltaCore.GameViewController, handleMenuInputFrom gameController: GameController)
|
func gameViewController(_ gameViewController: DeltaCore.GameViewController, handleMenuInputFrom gameController: GameController)
|
||||||
{
|
{
|
||||||
if self.selectingSustainedButtons
|
if let pausingGameController = self.pausingGameController
|
||||||
|
{
|
||||||
|
guard pausingGameController == gameController else { return }
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.isSelectingSustainedButtons
|
||||||
{
|
{
|
||||||
self.hideSustainButtonView()
|
self.hideSustainButtonView()
|
||||||
}
|
}
|
||||||
|
|
||||||
if let pauseViewController = self.pauseViewController, !self.selectingSustainedButtons
|
if let pauseViewController = self.pauseViewController, !self.isSelectingSustainedButtons
|
||||||
{
|
{
|
||||||
pauseViewController.dismiss()
|
pauseViewController.dismiss()
|
||||||
}
|
}
|
||||||
@ -763,15 +816,7 @@ extension GameViewController: GameViewControllerDelegate
|
|||||||
|
|
||||||
func gameViewControllerShouldResumeEmulation(_ gameViewController: DeltaCore.GameViewController) -> Bool
|
func gameViewControllerShouldResumeEmulation(_ gameViewController: DeltaCore.GameViewController) -> Bool
|
||||||
{
|
{
|
||||||
return (self.presentedViewController == nil || self.presentedViewController?.isDisappearing == true) && !self.selectingSustainedButtons && self.view.window != nil
|
return (self.presentedViewController == nil || self.presentedViewController?.isDisappearing == true) && !self.isSelectingSustainedButtons && self.view.window != nil
|
||||||
}
|
|
||||||
|
|
||||||
func gameViewControllerDidUpdate(_ gameViewController: DeltaCore.GameViewController)
|
|
||||||
{
|
|
||||||
for semaphore in self.updateSemaphores
|
|
||||||
{
|
|
||||||
semaphore.signal()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -805,12 +850,12 @@ private extension GameViewController
|
|||||||
|
|
||||||
case .preferredControllerSkin:
|
case .preferredControllerSkin:
|
||||||
guard
|
guard
|
||||||
let gameType = notification.userInfo?[Settings.NotificationUserInfoKey.gameType] as? GameType,
|
let system = notification.userInfo?[Settings.NotificationUserInfoKey.system] as? System,
|
||||||
let traits = notification.userInfo?[Settings.NotificationUserInfoKey.traits] as? DeltaCore.ControllerSkin.Traits
|
let traits = notification.userInfo?[Settings.NotificationUserInfoKey.traits] as? DeltaCore.ControllerSkin.Traits
|
||||||
else { return }
|
else { return }
|
||||||
|
|
||||||
let currentTraits = DeltaCore.ControllerSkin.Traits.defaults(for: self.view)
|
let currentTraits = DeltaCore.ControllerSkin.Traits.defaults(for: self.view)
|
||||||
if gameType == self.game?.type && traits == currentTraits
|
if system.gameType == self.game?.type && traits == currentTraits
|
||||||
{
|
{
|
||||||
self.updateControllerSkin()
|
self.updateControllerSkin()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,7 +27,7 @@ class PreviewGameViewController: DeltaCore.GameViewController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate var emulatorCoreQueue = DispatchQueue(label: "com.rileytestut.Delta.PreviewGameViewController.emulatorCoreQueue", qos: .userInitiated)
|
private var emulatorCoreQueue = DispatchQueue(label: "com.rileytestut.Delta.PreviewGameViewController.emulatorCoreQueue", qos: .userInitiated)
|
||||||
|
|
||||||
override var game: GameProtocol? {
|
override var game: GameProtocol? {
|
||||||
willSet {
|
willSet {
|
||||||
|
|||||||
23
Delta/Extensions/DeltaCoreProtocol+Delta.swift
Normal file
23
Delta/Extensions/DeltaCoreProtocol+Delta.swift
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
//
|
||||||
|
// DeltaCoreProtocol+Delta.swift
|
||||||
|
// Delta
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 4/30/17.
|
||||||
|
// Copyright © 2017 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import DeltaCore
|
||||||
|
|
||||||
|
extension DeltaCoreProtocol
|
||||||
|
{
|
||||||
|
var supportedRates: ClosedRange<Double> {
|
||||||
|
guard let system = System(gameType: self.gameType) else { return 1...1 }
|
||||||
|
|
||||||
|
switch system
|
||||||
|
{
|
||||||
|
case .snes: return 1...4
|
||||||
|
case .gba: return 1...3
|
||||||
|
case .gbc: return 1...4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,31 +0,0 @@
|
|||||||
//
|
|
||||||
// GameType+Delta.swift
|
|
||||||
// Delta
|
|
||||||
//
|
|
||||||
// Created by Riley Testut on 12/22/16.
|
|
||||||
// Copyright © 2016 Riley Testut. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import DeltaCore
|
|
||||||
|
|
||||||
extension GameType
|
|
||||||
{
|
|
||||||
static var supportedTypes: Set<GameType>
|
|
||||||
{
|
|
||||||
return [GameType.snes, GameType.gba]
|
|
||||||
}
|
|
||||||
|
|
||||||
static func gameType(forFileExtension fileExtension: String) -> GameType
|
|
||||||
{
|
|
||||||
let gameType: GameType
|
|
||||||
|
|
||||||
switch fileExtension.lowercased()
|
|
||||||
{
|
|
||||||
case "smc", "sfc", "fig": gameType = GameType.snes
|
|
||||||
case "gba": gameType = GameType.gba
|
|
||||||
default: gameType = GameType.unknown
|
|
||||||
}
|
|
||||||
|
|
||||||
return gameType
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,34 +0,0 @@
|
|||||||
//
|
|
||||||
// GameType+Localization.swift
|
|
||||||
// Delta
|
|
||||||
//
|
|
||||||
// Created by Riley Testut on 10/3/16.
|
|
||||||
// Copyright © 2016 Riley Testut. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import DeltaCore
|
|
||||||
|
|
||||||
extension GameType
|
|
||||||
{
|
|
||||||
var localizedName: String
|
|
||||||
{
|
|
||||||
switch self
|
|
||||||
{
|
|
||||||
case GameType.snes: return NSLocalizedString("Super Nintendo Entertainment System", comment: "")
|
|
||||||
case GameType.gba: return NSLocalizedString("Game Boy Advance", comment: "")
|
|
||||||
case GameType.unknown: return NSLocalizedString("Unsupported System", comment: "")
|
|
||||||
default: return NSLocalizedString("Unknown", comment: "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var localizedShortName: String
|
|
||||||
{
|
|
||||||
switch self
|
|
||||||
{
|
|
||||||
case GameType.snes: return NSLocalizedString("SNES", comment: "")
|
|
||||||
case GameType.gba: return NSLocalizedString("GBA", comment: "")
|
|
||||||
case GameType.unknown: return NSLocalizedString("Unsupported", comment: "")
|
|
||||||
default: return NSLocalizedString("Unknown", comment: "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
112
Delta/Extensions/Input+Display.swift
Normal file
112
Delta/Extensions/Input+Display.swift
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
//
|
||||||
|
// Input+Display.swift
|
||||||
|
// Delta
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 8/15/17.
|
||||||
|
// Copyright © 2017 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import DeltaCore
|
||||||
|
|
||||||
|
extension Input
|
||||||
|
{
|
||||||
|
// With the default GameControllerInputMapping files, multiple controller inputs may map to the same game input.
|
||||||
|
// This is because each controller input maps to a unique standard input, but then multiple standard inputs may map to same game input.
|
||||||
|
// To ensure we only show the most "important" controller input for a game input, we define general "display priorities" for each input.
|
||||||
|
//
|
||||||
|
// For example, MFiGameController.down and MFiGameController.leftThumbstickDown both map to a "down" game input.
|
||||||
|
// However, .down has a higher priority than .leftThumbstickDown, so we show .down instead of .leftThumbstickDown.
|
||||||
|
var displayPriority: Int {
|
||||||
|
switch self.type
|
||||||
|
{
|
||||||
|
case .game: break
|
||||||
|
case .controller(.standard): break
|
||||||
|
case .controller(.mfi):
|
||||||
|
let input = MFiGameController.Input(input: self)!
|
||||||
|
switch input
|
||||||
|
{
|
||||||
|
case .leftThumbstickUp: return 750
|
||||||
|
case .leftThumbstickDown: return 750
|
||||||
|
case .leftThumbstickLeft: return 750
|
||||||
|
case .leftThumbstickRight: return 750
|
||||||
|
case .leftShoulder: return 750
|
||||||
|
case .leftTrigger: return 500
|
||||||
|
case .rightShoulder: return 750
|
||||||
|
case .rightTrigger: return 500
|
||||||
|
default: break
|
||||||
|
}
|
||||||
|
|
||||||
|
default: break
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1000
|
||||||
|
}
|
||||||
|
|
||||||
|
var localizedName: String {
|
||||||
|
switch self.type
|
||||||
|
{
|
||||||
|
case .game: break
|
||||||
|
case .controller(.standard):
|
||||||
|
let input = StandardGameControllerInput(input: self)!
|
||||||
|
switch input
|
||||||
|
{
|
||||||
|
case .menu: return NSLocalizedString("Menu", comment: "")
|
||||||
|
case .up: return NSLocalizedString("Up", comment: "")
|
||||||
|
case .down: return NSLocalizedString("Down", comment: "")
|
||||||
|
case .left: return NSLocalizedString("Left", comment: "")
|
||||||
|
case .right: return NSLocalizedString("Right", comment: "")
|
||||||
|
case .leftThumbstickUp: return NSLocalizedString("L🕹↑", comment: "")
|
||||||
|
case .leftThumbstickDown: return NSLocalizedString("L🕹↓", comment: "")
|
||||||
|
case .leftThumbstickLeft: return NSLocalizedString("L🕹←", comment: "")
|
||||||
|
case .leftThumbstickRight: return NSLocalizedString("L🕹→", comment: "")
|
||||||
|
case .rightThumbstickUp: return NSLocalizedString("R🕹↑", comment: "")
|
||||||
|
case .rightThumbstickDown: return NSLocalizedString("R🕹↓", comment: "")
|
||||||
|
case .rightThumbstickLeft: return NSLocalizedString("R🕹←", comment: "")
|
||||||
|
case .rightThumbstickRight: return NSLocalizedString("R🕹→", comment: "")
|
||||||
|
case .a: return NSLocalizedString("A", comment: "")
|
||||||
|
case .b: return NSLocalizedString("B", comment: "")
|
||||||
|
case .x: return NSLocalizedString("X", comment: "")
|
||||||
|
case .y: return NSLocalizedString("Y", comment: "")
|
||||||
|
case .start: return NSLocalizedString("Start", comment: "Start button")
|
||||||
|
case .select: return NSLocalizedString("Select", comment: "Select button")
|
||||||
|
case .l1: return NSLocalizedString("L1", comment: "")
|
||||||
|
case .l2: return NSLocalizedString("L2", comment: "")
|
||||||
|
case .l3: return NSLocalizedString("L3", comment: "")
|
||||||
|
case .r1: return NSLocalizedString("R1", comment: "")
|
||||||
|
case .r2: return NSLocalizedString("R2", comment: "")
|
||||||
|
case .r3: return NSLocalizedString("R3", comment: "")
|
||||||
|
}
|
||||||
|
|
||||||
|
case .controller(.mfi):
|
||||||
|
let input = MFiGameController.Input(input: self)!
|
||||||
|
switch input
|
||||||
|
{
|
||||||
|
case .menu: return NSLocalizedString("Menu", comment: "")
|
||||||
|
case .up: return NSLocalizedString("Up", comment: "")
|
||||||
|
case .down: return NSLocalizedString("Down", comment: "")
|
||||||
|
case .left: return NSLocalizedString("Left", comment: "")
|
||||||
|
case .right: return NSLocalizedString("Right", comment: "")
|
||||||
|
case .leftThumbstickUp: return NSLocalizedString("L🕹↑", comment: "")
|
||||||
|
case .leftThumbstickDown: return NSLocalizedString("L🕹↓", comment: "")
|
||||||
|
case .leftThumbstickLeft: return NSLocalizedString("L🕹←", comment: "")
|
||||||
|
case .leftThumbstickRight: return NSLocalizedString("L🕹→", comment: "")
|
||||||
|
case .rightThumbstickUp: return NSLocalizedString("R🕹↑", comment: "")
|
||||||
|
case .rightThumbstickDown: return NSLocalizedString("R🕹↓", comment: "")
|
||||||
|
case .rightThumbstickLeft: return NSLocalizedString("R🕹←", comment: "")
|
||||||
|
case .rightThumbstickRight: return NSLocalizedString("R🕹→", comment: "")
|
||||||
|
case .a: return NSLocalizedString("A", comment: "")
|
||||||
|
case .b: return NSLocalizedString("B", comment: "")
|
||||||
|
case .x: return NSLocalizedString("X", comment: "")
|
||||||
|
case .y: return NSLocalizedString("Y", comment: "")
|
||||||
|
case .leftShoulder: return NSLocalizedString("L1", comment: "")
|
||||||
|
case .leftTrigger: return NSLocalizedString("L2", comment: "")
|
||||||
|
case .rightShoulder: return NSLocalizedString("R1", comment: "")
|
||||||
|
case .rightTrigger: return NSLocalizedString("R2", comment: "")
|
||||||
|
}
|
||||||
|
|
||||||
|
default: break
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -38,6 +38,7 @@ extension UIAlertController
|
|||||||
{
|
{
|
||||||
case .doesNotExist(let url): urls.insert(url)
|
case .doesNotExist(let url): urls.insert(url)
|
||||||
case .invalid(let url): urls.insert(url)
|
case .invalid(let url): urls.insert(url)
|
||||||
|
case .unsupported(let url): urls.insert(url)
|
||||||
case .unknown(let url, _): urls.insert(url)
|
case .unknown(let url, _): urls.insert(url)
|
||||||
case .saveFailed(let errorURLs, _): urls.formUnion(errorURLs)
|
case .saveFailed(let errorURLs, _): urls.formUnion(errorURLs)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,7 +12,7 @@ extension UIColor
|
|||||||
{
|
{
|
||||||
class var deltaPurple: UIColor
|
class var deltaPurple: UIColor
|
||||||
{
|
{
|
||||||
return UIColor(red: 140.0/255.0, green: 26.0/255.0, blue: 233.0/255.0, alpha: 1.0)
|
return UIColor(red: 139.0/255.0, green: 40.0/255.0, blue: 247.0/255.0, alpha: 1.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
class var deltaDarkGray: UIColor
|
class var deltaDarkGray: UIColor
|
||||||
|
|||||||
@ -18,7 +18,7 @@ internal extension UILabel
|
|||||||
context.minimumScaleFactor = self.minimumScaleFactor
|
context.minimumScaleFactor = self.minimumScaleFactor
|
||||||
|
|
||||||
// Using self.attributedString returns incorrect calculations, so we create our own attributed string
|
// Using self.attributedString returns incorrect calculations, so we create our own attributed string
|
||||||
let attributedString = NSAttributedString(string: text, attributes: [NSFontAttributeName: self.font])
|
let attributedString = NSAttributedString(string: text, attributes: [.font: self.font])
|
||||||
attributedString.boundingRect(with: self.bounds.size, options: [.usesLineFragmentOrigin, .usesFontLeading], context: context)
|
attributedString.boundingRect(with: self.bounds.size, options: [.usesLineFragmentOrigin, .usesFontLeading], context: context)
|
||||||
|
|
||||||
let scaleFactor = context.actualScaleFactor
|
let scaleFactor = context.actualScaleFactor
|
||||||
|
|||||||
28
Delta/Extensions/UIView+ParentViewController.swift
Normal file
28
Delta/Extensions/UIView+ParentViewController.swift
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
//
|
||||||
|
// UIView+ParentViewController.swift
|
||||||
|
// Delta
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 9/3/17.
|
||||||
|
// Copyright © 2017 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
extension UIView
|
||||||
|
{
|
||||||
|
var parentViewController: UIViewController? {
|
||||||
|
var nextResponder = self.next
|
||||||
|
|
||||||
|
while nextResponder != nil
|
||||||
|
{
|
||||||
|
if let parentViewController = nextResponder as? UIViewController
|
||||||
|
{
|
||||||
|
return parentViewController
|
||||||
|
}
|
||||||
|
|
||||||
|
nextResponder = nextResponder?.next
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -21,11 +21,11 @@ extension UIViewControllerContextTransitioning
|
|||||||
|
|
||||||
/// UIViews
|
/// UIViews
|
||||||
var sourceView: UIView {
|
var sourceView: UIView {
|
||||||
return self.sourceViewController.view
|
return self.view(forKey: .from) ?? self.sourceViewController.view
|
||||||
}
|
}
|
||||||
|
|
||||||
var destinationView: UIView {
|
var destinationView: UIView {
|
||||||
return self.destinationViewController.view
|
return self.view(forKey: .to) ?? self.destinationViewController.view
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import MobileCoreServices
|
||||||
|
|
||||||
import DeltaCore
|
import DeltaCore
|
||||||
|
|
||||||
@ -16,9 +17,9 @@ import SDWebImage
|
|||||||
|
|
||||||
class GameCollectionViewController: UICollectionViewController
|
class GameCollectionViewController: UICollectionViewController
|
||||||
{
|
{
|
||||||
var gameCollection: GameCollection! {
|
var gameCollection: GameCollection? {
|
||||||
didSet {
|
didSet {
|
||||||
self.title = self.gameCollection.shortName
|
self.title = self.gameCollection?.shortName
|
||||||
self.updateDataSource()
|
self.updateDataSource()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -40,17 +41,28 @@ class GameCollectionViewController: UICollectionViewController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal let dataSource: RSTFetchedResultsCollectionViewPrefetchingDataSource<Game, UIImage>
|
||||||
|
|
||||||
weak var activeEmulatorCore: EmulatorCore?
|
weak var activeEmulatorCore: EmulatorCore?
|
||||||
|
|
||||||
fileprivate var activeSaveState: SaveStateProtocol?
|
private var activeSaveState: SaveStateProtocol?
|
||||||
|
|
||||||
fileprivate let dataSource = RSTFetchedResultsCollectionViewDataSource<Game>(fetchedResultsController: NSFetchedResultsController())
|
private let prototypeCell = GridCollectionViewCell()
|
||||||
fileprivate let prototypeCell = GridCollectionViewCell()
|
|
||||||
|
|
||||||
fileprivate var _performing3DTouchTransition = false
|
private var _performing3DTouchTransition = false
|
||||||
fileprivate weak var _destination3DTouchTransitionViewController: UIViewController?
|
private weak var _destination3DTouchTransitionViewController: UIViewController?
|
||||||
|
|
||||||
fileprivate var _renameAction: UIAlertAction?
|
private var _renameAction: UIAlertAction?
|
||||||
|
private var _changingArtworkGame: Game?
|
||||||
|
|
||||||
|
required init?(coder aDecoder: NSCoder)
|
||||||
|
{
|
||||||
|
self.dataSource = RSTFetchedResultsCollectionViewPrefetchingDataSource<Game, UIImage>(fetchedResultsController: NSFetchedResultsController())
|
||||||
|
|
||||||
|
super.init(coder: aDecoder)
|
||||||
|
|
||||||
|
self.prepareDataSource()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//MARK: - UIViewController -
|
//MARK: - UIViewController -
|
||||||
@ -61,11 +73,8 @@ extension GameCollectionViewController
|
|||||||
{
|
{
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
self.dataSource.cellConfigurationHandler = { [unowned self] (cell, item, indexPath) in
|
|
||||||
self.configure(cell as! GridCollectionViewCell, for: indexPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
self.collectionView?.dataSource = self.dataSource
|
self.collectionView?.dataSource = self.dataSource
|
||||||
|
self.collectionView?.prefetchDataSource = self.dataSource
|
||||||
self.collectionView?.delegate = self
|
self.collectionView?.delegate = self
|
||||||
|
|
||||||
let layout = self.collectionViewLayout as! GridCollectionViewLayout
|
let layout = self.collectionViewLayout as! GridCollectionViewLayout
|
||||||
@ -123,24 +132,6 @@ extension GameCollectionViewController
|
|||||||
saveStatesViewController.mode = .loading
|
saveStatesViewController.mode = .loading
|
||||||
saveStatesViewController.theme = self.theme
|
saveStatesViewController.theme = self.theme
|
||||||
|
|
||||||
case "gamesDatabaseBrowser":
|
|
||||||
let game = sender as! Game
|
|
||||||
|
|
||||||
let gamesDatabaseBrowserViewController = (segue.destination as! UINavigationController).topViewController as! GamesDatabaseBrowserViewController
|
|
||||||
gamesDatabaseBrowserViewController.selectionHandler = { (metadata) in
|
|
||||||
|
|
||||||
DatabaseManager.shared.performBackgroundTask({ (context) in
|
|
||||||
let temporaryGame = context.object(with: game.objectID) as! Game
|
|
||||||
temporaryGame.artworkURL = metadata.artworkURL
|
|
||||||
context.saveWithErrorLogging()
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
gamesDatabaseBrowserViewController.dismiss(animated: true, completion: nil)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
case "unwindFromGames":
|
case "unwindFromGames":
|
||||||
let destinationViewController = segue.destination as! GameViewController
|
let destinationViewController = segue.destination as! GameViewController
|
||||||
let cell = sender as! UICollectionViewCell
|
let cell = sender as! UICollectionViewCell
|
||||||
@ -195,11 +186,42 @@ extension GameCollectionViewController
|
|||||||
//MARK: - Private Methods -
|
//MARK: - Private Methods -
|
||||||
private extension GameCollectionViewController
|
private extension GameCollectionViewController
|
||||||
{
|
{
|
||||||
//MARK: - Update
|
//MARK: - Data Source
|
||||||
|
func prepareDataSource()
|
||||||
|
{
|
||||||
|
self.dataSource.cellConfigurationHandler = { [unowned self] (cell, item, indexPath) in
|
||||||
|
self.configure(cell as! GridCollectionViewCell, for: indexPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.dataSource.prefetchHandler = { (game, indexPath, completionHandler) in
|
||||||
|
guard let artworkURL = game.artworkURL else { return nil }
|
||||||
|
|
||||||
|
let imageOperation = LoadImageURLOperation(url: artworkURL)
|
||||||
|
imageOperation.resultHandler = { (image, error) in
|
||||||
|
completionHandler(image, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
return imageOperation
|
||||||
|
}
|
||||||
|
|
||||||
|
self.dataSource.prefetchCompletionHandler = { (cell, image, indexPath, error) in
|
||||||
|
guard let image = image else { return }
|
||||||
|
|
||||||
|
let cell = cell as! GridCollectionViewCell
|
||||||
|
cell.imageView.image = image
|
||||||
|
cell.isImageViewVibrancyEnabled = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func updateDataSource()
|
func updateDataSource()
|
||||||
{
|
{
|
||||||
let fetchRequest: NSFetchRequest<Game> = Game.fetchRequest()
|
let fetchRequest: NSFetchRequest<Game> = Game.fetchRequest()
|
||||||
fetchRequest.predicate = NSPredicate(format: "ANY %K == %@", #keyPath(Game.gameCollections), self.gameCollection)
|
|
||||||
|
if let gameCollection = self.gameCollection
|
||||||
|
{
|
||||||
|
fetchRequest.predicate = NSPredicate(format: "ANY %K == %@", #keyPath(Game.gameCollections), gameCollection)
|
||||||
|
}
|
||||||
|
|
||||||
fetchRequest.sortDescriptors = [NSSortDescriptor(key: #keyPath(Game.name), ascending: true)]
|
fetchRequest.sortDescriptors = [NSSortDescriptor(key: #keyPath(Game.name), ascending: true)]
|
||||||
fetchRequest.returnsObjectsAsFaults = false
|
fetchRequest.returnsObjectsAsFaults = false
|
||||||
|
|
||||||
@ -207,7 +229,7 @@ private extension GameCollectionViewController
|
|||||||
}
|
}
|
||||||
|
|
||||||
//MARK: - Configure Cells
|
//MARK: - Configure Cells
|
||||||
func configure(_ cell: GridCollectionViewCell, for indexPath: IndexPath, ignoreImageOperations: Bool = false)
|
func configure(_ cell: GridCollectionViewCell, for indexPath: IndexPath)
|
||||||
{
|
{
|
||||||
let game = self.dataSource.item(at: indexPath)
|
let game = self.dataSource.item(at: indexPath)
|
||||||
|
|
||||||
@ -222,29 +244,11 @@ private extension GameCollectionViewController
|
|||||||
cell.isImageViewVibrancyEnabled = true
|
cell.isImageViewVibrancyEnabled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cell.imageView.image = #imageLiteral(resourceName: "BoxArt")
|
||||||
|
|
||||||
cell.maximumImageSize = CGSize(width: 90, height: 90)
|
cell.maximumImageSize = CGSize(width: 90, height: 90)
|
||||||
cell.textLabel.text = game.name
|
cell.textLabel.text = game.name
|
||||||
cell.textLabel.textColor = UIColor.gray
|
cell.textLabel.textColor = UIColor.gray
|
||||||
|
|
||||||
if let artworkURL = game.artworkURL, !ignoreImageOperations
|
|
||||||
{
|
|
||||||
cell.imageView.sd_setImage(with: artworkURL, placeholderImage: #imageLiteral(resourceName: "BoxArt"), options: .continueInBackground) { (image, error, type, url) in
|
|
||||||
|
|
||||||
if let error = error
|
|
||||||
{
|
|
||||||
print(error)
|
|
||||||
}
|
|
||||||
|
|
||||||
if image != nil
|
|
||||||
{
|
|
||||||
cell.isImageViewVibrancyEnabled = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cell.imageView.image = #imageLiteral(resourceName: "BoxArt")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//MARK: - Emulation
|
//MARK: - Emulation
|
||||||
@ -342,7 +346,7 @@ private extension GameCollectionViewController
|
|||||||
|
|
||||||
func rename(_ game: Game, with name: String)
|
func rename(_ game: Game, with name: String)
|
||||||
{
|
{
|
||||||
guard name.characters.count > 0 else { return }
|
guard name.count > 0 else { return }
|
||||||
|
|
||||||
DatabaseManager.shared.performBackgroundTask { (context) in
|
DatabaseManager.shared.performBackgroundTask { (context) in
|
||||||
let game = context.object(with: game.objectID) as! Game
|
let game = context.object(with: game.objectID) as! Game
|
||||||
@ -356,7 +360,16 @@ private extension GameCollectionViewController
|
|||||||
|
|
||||||
func changeArtwork(for game: Game)
|
func changeArtwork(for game: Game)
|
||||||
{
|
{
|
||||||
self.performSegue(withIdentifier: "gamesDatabaseBrowser", sender: game)
|
self._changingArtworkGame = game
|
||||||
|
|
||||||
|
let clipboardImportOption = ClipboardImportOption()
|
||||||
|
let photoLibraryImportOption = PhotoLibraryImportOption(presentingViewController: self)
|
||||||
|
let gamesDatabaseImportOption = GamesDatabaseImportOption(presentingViewController: self)
|
||||||
|
|
||||||
|
let importController = ImportController(documentTypes: [kUTTypeImage as String])
|
||||||
|
importController.delegate = self
|
||||||
|
importController.importOptions = [clipboardImportOption, photoLibraryImportOption, gamesDatabaseImportOption]
|
||||||
|
self.present(importController, animated: true, completion: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func share(_ game: Game)
|
func share(_ game: Game)
|
||||||
@ -394,7 +407,7 @@ private extension GameCollectionViewController
|
|||||||
@objc func textFieldTextDidChange(_ textField: UITextField)
|
@objc func textFieldTextDidChange(_ textField: UITextField)
|
||||||
{
|
{
|
||||||
let text = textField.text ?? ""
|
let text = textField.text ?? ""
|
||||||
self._renameAction?.isEnabled = text.characters.count > 0
|
self._renameAction?.isEnabled = text.count > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func handleLongPressGesture(_ gestureRecognizer: UILongPressGestureRecognizer)
|
@objc func handleLongPressGesture(_ gestureRecognizer: UILongPressGestureRecognizer)
|
||||||
@ -417,7 +430,7 @@ extension GameCollectionViewController: UIViewControllerPreviewingDelegate
|
|||||||
{
|
{
|
||||||
func previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController?
|
func previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController?
|
||||||
{
|
{
|
||||||
guard self.gameCollection.identifier != GameType.unknown.rawValue else { return nil }
|
guard self.gameCollection?.identifier != GameType.unknown.rawValue else { return nil }
|
||||||
|
|
||||||
guard
|
guard
|
||||||
let collectionView = self.collectionView,
|
let collectionView = self.collectionView,
|
||||||
@ -452,7 +465,7 @@ extension GameCollectionViewController: UIViewControllerPreviewingDelegate
|
|||||||
let indexPath = self.dataSource.fetchedResultsController.indexPath(forObject: game)!
|
let indexPath = self.dataSource.fetchedResultsController.indexPath(forObject: game)!
|
||||||
let cell = self.collectionView?.cellForItem(at: indexPath)
|
let cell = self.collectionView?.cellForItem(at: indexPath)
|
||||||
|
|
||||||
let fileURL = FileManager.uniqueTemporaryURL()
|
let fileURL = FileManager.default.uniqueTemporaryURL()
|
||||||
self.activeSaveState = gameViewController.emulatorCore?.saveSaveState(to: fileURL)
|
self.activeSaveState = gameViewController.emulatorCore?.saveSaveState(to: fileURL)
|
||||||
|
|
||||||
gameViewController.emulatorCore?.stop()
|
gameViewController.emulatorCore?.stop()
|
||||||
@ -493,13 +506,105 @@ extension GameCollectionViewController: SaveStatesViewControllerDelegate
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//MARK: - ImportControllerDelegate -
|
||||||
|
/// ImportControllerDelegate
|
||||||
|
extension GameCollectionViewController: ImportControllerDelegate
|
||||||
|
{
|
||||||
|
func importController(_ importController: ImportController, didImportItemsAt urls: Set<URL>, errors: [Error])
|
||||||
|
{
|
||||||
|
guard let game = self._changingArtworkGame else { return }
|
||||||
|
|
||||||
|
var errors = errors
|
||||||
|
|
||||||
|
var imageURL: URL?
|
||||||
|
|
||||||
|
if let url = urls.first
|
||||||
|
{
|
||||||
|
if url.isFileURL
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
let imageData = try Data(contentsOf: url)
|
||||||
|
|
||||||
|
if let image = UIImage(data: imageData)
|
||||||
|
{
|
||||||
|
let resizedImage = image.resizing(toFit: CGSize(width: 300, height: 300))
|
||||||
|
|
||||||
|
if let resizedData = UIImageJPEGRepresentation(resizedImage, 0.85)
|
||||||
|
{
|
||||||
|
let destinationURL = DatabaseManager.artworkURL(for: game)
|
||||||
|
try resizedData.write(to: destinationURL, options: .atomic)
|
||||||
|
|
||||||
|
imageURL = destinationURL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
errors.append(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
imageURL = url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for error in errors
|
||||||
|
{
|
||||||
|
print(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let imageURL = imageURL
|
||||||
|
{
|
||||||
|
// Remove previous artwork from cache.
|
||||||
|
self.dataSource.prefetchItemCache.removeObject(forKey: game)
|
||||||
|
|
||||||
|
DatabaseManager.shared.performBackgroundTask { (context) in
|
||||||
|
let temporaryGame = context.object(with: game.objectID) as! Game
|
||||||
|
temporaryGame.artworkURL = imageURL
|
||||||
|
context.saveWithErrorLogging()
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.presentedViewController?.dismiss(animated: true, completion: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
func presentAlertController()
|
||||||
|
{
|
||||||
|
let alertController = UIAlertController(title: NSLocalizedString("Unable to Change Artwork", comment: ""), message: NSLocalizedString("The image might be corrupted or in an unsupported format.", comment: ""), preferredStyle: .alert)
|
||||||
|
alertController.addAction(UIAlertAction(title: RSTSystemLocalizedString("OK"), style: .cancel, handler: nil))
|
||||||
|
self.present(alertController, animated: true, completion: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let presentedViewController = self.presentedViewController
|
||||||
|
{
|
||||||
|
presentedViewController.dismiss(animated: true) {
|
||||||
|
presentAlertController()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
presentAlertController()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func importControllerDidCancel(_ importController: ImportController)
|
||||||
|
{
|
||||||
|
self.presentedViewController?.dismiss(animated: true, completion: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//MARK: - UICollectionViewDelegate -
|
//MARK: - UICollectionViewDelegate -
|
||||||
/// UICollectionViewDelegate
|
/// UICollectionViewDelegate
|
||||||
extension GameCollectionViewController
|
extension GameCollectionViewController
|
||||||
{
|
{
|
||||||
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
|
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
|
||||||
{
|
{
|
||||||
guard self.gameCollection.identifier != GameType.unknown.rawValue else { return }
|
guard self.gameCollection?.identifier != GameType.unknown.rawValue else { return }
|
||||||
|
|
||||||
let cell = collectionView.cellForItem(at: indexPath)
|
let cell = collectionView.cellForItem(at: indexPath)
|
||||||
let game = self.dataSource.item(at: indexPath)
|
let game = self.dataSource.item(at: indexPath)
|
||||||
@ -542,12 +647,6 @@ extension GameCollectionViewController
|
|||||||
self.launchGame(withSender: cell, clearScreen: true)
|
self.launchGame(withSender: cell, clearScreen: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath)
|
|
||||||
{
|
|
||||||
let cell = cell as! GridCollectionViewCell
|
|
||||||
cell.imageView.sd_cancelCurrentImageLoad()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//MARK: - UICollectionViewDelegateFlowLayout -
|
//MARK: - UICollectionViewDelegateFlowLayout -
|
||||||
@ -562,7 +661,7 @@ extension GameCollectionViewController: UICollectionViewDelegateFlowLayout
|
|||||||
widthConstraint.isActive = true
|
widthConstraint.isActive = true
|
||||||
defer { widthConstraint.isActive = false }
|
defer { widthConstraint.isActive = false }
|
||||||
|
|
||||||
self.configure(self.prototypeCell, for: indexPath, ignoreImageOperations: true)
|
self.configure(self.prototypeCell, for: indexPath)
|
||||||
|
|
||||||
let size = self.prototypeCell.contentView.systemLayoutSizeFitting(UILayoutFittingCompressedSize)
|
let size = self.prototypeCell.contentView.systemLayoutSizeFitting(UILayoutFittingCompressedSize)
|
||||||
return size
|
return size
|
||||||
|
|||||||
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
import CoreData
|
import CoreData
|
||||||
|
import MobileCoreServices
|
||||||
|
|
||||||
import DeltaCore
|
import DeltaCore
|
||||||
|
|
||||||
@ -34,11 +35,17 @@ class GamesViewController: UIViewController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate var pageViewController: UIPageViewController!
|
override var preferredStatusBarStyle: UIStatusBarStyle {
|
||||||
fileprivate var placeholderView: RSTPlaceholderView!
|
return .lightContent
|
||||||
fileprivate var pageControl: UIPageControl!
|
}
|
||||||
|
|
||||||
fileprivate let fetchedResultsController: NSFetchedResultsController<NSFetchRequestResult>
|
private var pageViewController: UIPageViewController!
|
||||||
|
private var placeholderView: RSTPlaceholderView!
|
||||||
|
private var pageControl: UIPageControl!
|
||||||
|
|
||||||
|
private let fetchedResultsController: NSFetchedResultsController<NSFetchRequestResult>
|
||||||
|
|
||||||
|
private var searchController: RSTSearchController?
|
||||||
|
|
||||||
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
|
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
|
||||||
fatalError("initWithNibName: not implemented")
|
fatalError("initWithNibName: not implemented")
|
||||||
@ -87,6 +94,11 @@ extension GamesViewController
|
|||||||
self.navigationController?.navigationBar.barStyle = .blackTranslucent
|
self.navigationController?.navigationBar.barStyle = .blackTranslucent
|
||||||
self.navigationController?.toolbar.barStyle = .blackTranslucent
|
self.navigationController?.toolbar.barStyle = .blackTranslucent
|
||||||
|
|
||||||
|
if #available(iOS 11.0, *)
|
||||||
|
{
|
||||||
|
self.prepareSearchController()
|
||||||
|
}
|
||||||
|
|
||||||
self.updateTheme()
|
self.updateTheme()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,6 +120,9 @@ extension GamesViewController
|
|||||||
{
|
{
|
||||||
super.viewDidLayoutSubviews()
|
super.viewDidLayoutSubviews()
|
||||||
|
|
||||||
|
if #available(iOS 11.0, *) {}
|
||||||
|
else
|
||||||
|
{
|
||||||
if let viewControllers = self.pageViewController.viewControllers as? [GameCollectionViewController]
|
if let viewControllers = self.pageViewController.viewControllers as? [GameCollectionViewController]
|
||||||
{
|
{
|
||||||
for viewController in viewControllers
|
for viewController in viewControllers
|
||||||
@ -116,6 +131,7 @@ extension GamesViewController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override func didReceiveMemoryWarning()
|
override func didReceiveMemoryWarning()
|
||||||
{
|
{
|
||||||
@ -148,6 +164,48 @@ extension GamesViewController
|
|||||||
/// UI
|
/// UI
|
||||||
private extension GamesViewController
|
private extension GamesViewController
|
||||||
{
|
{
|
||||||
|
@available(iOS 11.0, *)
|
||||||
|
func prepareSearchController()
|
||||||
|
{
|
||||||
|
let searchResultsController = self.storyboard?.instantiateViewController(withIdentifier: "gameCollectionViewController") as! GameCollectionViewController
|
||||||
|
searchResultsController.gameCollection = nil
|
||||||
|
searchResultsController.theme = self.theme
|
||||||
|
searchResultsController.activeEmulatorCore = self.activeEmulatorCore
|
||||||
|
|
||||||
|
let placeholderView = RSTPlaceholderView()
|
||||||
|
placeholderView.textLabel.text = NSLocalizedString("No Games Found", comment: "")
|
||||||
|
placeholderView.detailTextLabel.text = NSLocalizedString("Please make sure the name is correct, or try searching for another game.", comment: "")
|
||||||
|
|
||||||
|
switch self.theme
|
||||||
|
{
|
||||||
|
case .opaque: searchResultsController.dataSource.placeholderView = placeholderView
|
||||||
|
case .translucent:
|
||||||
|
let vibrancyView = UIVisualEffectView(effect: UIVibrancyEffect(blurEffect: UIBlurEffect(style: .dark)))
|
||||||
|
vibrancyView.contentView.addSubview(placeholderView, pinningEdgesWith: .zero)
|
||||||
|
searchResultsController.dataSource.placeholderView = vibrancyView
|
||||||
|
}
|
||||||
|
|
||||||
|
self.searchController = RSTSearchController(searchResultsController: searchResultsController)
|
||||||
|
self.searchController?.searchableKeyPaths = [#keyPath(Game.name)]
|
||||||
|
self.searchController?.searchHandler = { [weak searchController, weak searchResultsController] (searchValue, _) in
|
||||||
|
if searchController?.searchBar.text?.isEmpty == false
|
||||||
|
{
|
||||||
|
self.pageViewController.view.isHidden = true
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
self.pageViewController.view.isHidden = false
|
||||||
|
}
|
||||||
|
|
||||||
|
searchResultsController?.dataSource.predicate = searchValue.predicate
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
self.navigationItem.searchController = self.searchController
|
||||||
|
self.navigationItem.hidesSearchBarWhenScrolling = false
|
||||||
|
|
||||||
|
self.definesPresentationContext = true
|
||||||
|
}
|
||||||
|
|
||||||
func updateTheme()
|
func updateTheme()
|
||||||
{
|
{
|
||||||
switch self.theme
|
switch self.theme
|
||||||
@ -185,12 +243,16 @@ private extension GamesViewController
|
|||||||
let indexPath = IndexPath(row: safeIndex, section: 0)
|
let indexPath = IndexPath(row: safeIndex, section: 0)
|
||||||
|
|
||||||
let viewController = self.storyboard?.instantiateViewController(withIdentifier: "gameCollectionViewController") as! GameCollectionViewController
|
let viewController = self.storyboard?.instantiateViewController(withIdentifier: "gameCollectionViewController") as! GameCollectionViewController
|
||||||
viewController.gameCollection = self.fetchedResultsController.object(at: indexPath) as! GameCollection
|
viewController.gameCollection = self.fetchedResultsController.object(at: indexPath) as? GameCollection
|
||||||
viewController.theme = self.theme
|
viewController.theme = self.theme
|
||||||
viewController.activeEmulatorCore = self.activeEmulatorCore
|
viewController.activeEmulatorCore = self.activeEmulatorCore
|
||||||
|
|
||||||
|
if #available(iOS 11.0, *) {}
|
||||||
|
else
|
||||||
|
{
|
||||||
// Need to set content inset here AND willTransitionTo callback to ensure its correct for all edge cases
|
// Need to set content inset here AND willTransitionTo callback to ensure its correct for all edge cases
|
||||||
viewController.collectionView?.contentInset.top = self.topLayoutGuide.length
|
viewController.collectionView?.contentInset.top = self.topLayoutGuide.length
|
||||||
|
}
|
||||||
|
|
||||||
return viewController
|
return viewController
|
||||||
}
|
}
|
||||||
@ -264,16 +326,33 @@ private extension GamesViewController
|
|||||||
/// Importing
|
/// Importing
|
||||||
extension GamesViewController: ImportControllerDelegate
|
extension GamesViewController: ImportControllerDelegate
|
||||||
{
|
{
|
||||||
@IBAction fileprivate func importFiles()
|
@IBAction private func importFiles()
|
||||||
{
|
{
|
||||||
let importController = ImportController()
|
var documentTypes = Set(System.supportedSystems.map { $0.gameType.rawValue })
|
||||||
|
documentTypes.insert(kUTTypeZipArchive as String)
|
||||||
|
|
||||||
|
// Add GBA4iOS's exported UTIs in case user has GBA4iOS installed (which may override Delta's UTI declarations)
|
||||||
|
documentTypes.insert("com.rileytestut.gba")
|
||||||
|
documentTypes.insert("com.rileytestut.gbc")
|
||||||
|
documentTypes.insert("com.rileytestut.gb")
|
||||||
|
|
||||||
|
let itunesImportOption = iTunesImportOption(presentingViewController: self)
|
||||||
|
|
||||||
|
let importController = ImportController(documentTypes: documentTypes)
|
||||||
importController.delegate = self
|
importController.delegate = self
|
||||||
|
importController.importOptions = [itunesImportOption]
|
||||||
self.present(importController, animated: true, completion: nil)
|
self.present(importController, animated: true, completion: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
//MARK: - ImportControllerDelegate
|
func importController(_ importController: ImportController, didImportItemsAt urls: Set<URL>, errors: [Error])
|
||||||
@nonobjc func importController(_ importController: ImportController, didImport games: Set<Game>, with errors: Set<DatabaseManager.ImportError>)
|
|
||||||
{
|
{
|
||||||
|
for error in errors
|
||||||
|
{
|
||||||
|
print(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
let gameURLs = urls.filter { $0.pathExtension.lowercased() != "deltaskin" }
|
||||||
|
DatabaseManager.shared.importGames(at: Set(gameURLs)) { (games, errors) in
|
||||||
if errors.count > 0
|
if errors.count > 0
|
||||||
{
|
{
|
||||||
let alertController = UIAlertController.alertController(for: .games, with: errors)
|
let alertController = UIAlertController.alertController(for: .games, with: errors)
|
||||||
@ -286,8 +365,8 @@ extension GamesViewController: ImportControllerDelegate
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@nonobjc func importController(_ importController: ImportController, didImport controllerSkins: Set<ControllerSkin>, with errors: Set<DatabaseManager.ImportError>)
|
let controllerSkinURLs = urls.filter { $0.pathExtension.lowercased() == "deltaskin" }
|
||||||
{
|
DatabaseManager.shared.importControllerSkins(at: Set(controllerSkinURLs)) { (controllerSkins, errors) in
|
||||||
if errors.count > 0
|
if errors.count > 0
|
||||||
{
|
{
|
||||||
let alertController = UIAlertController.alertController(for: .controllerSkins, with: errors)
|
let alertController = UIAlertController.alertController(for: .controllerSkins, with: errors)
|
||||||
@ -299,6 +378,7 @@ extension GamesViewController: ImportControllerDelegate
|
|||||||
print("Imported Controller Skins:", controllerSkins.map { $0.name })
|
print("Imported Controller Skins:", controllerSkins.map { $0.name })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension GamesViewController
|
private extension GamesViewController
|
||||||
@ -347,11 +427,15 @@ extension GamesViewController: UIPageViewControllerDataSource, UIPageViewControl
|
|||||||
{
|
{
|
||||||
guard let viewControllers = pendingViewControllers as? [GameCollectionViewController] else { return }
|
guard let viewControllers = pendingViewControllers as? [GameCollectionViewController] else { return }
|
||||||
|
|
||||||
|
if #available(iOS 11.0, *) {}
|
||||||
|
else
|
||||||
|
{
|
||||||
for viewController in viewControllers
|
for viewController in viewControllers
|
||||||
{
|
{
|
||||||
viewController.collectionView?.contentInset.top = self.topLayoutGuide.length
|
viewController.collectionView?.contentInset.top = self.topLayoutGuide.length
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool)
|
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool)
|
||||||
{
|
{
|
||||||
@ -371,6 +455,21 @@ extension GamesViewController: UIPageViewControllerDataSource, UIPageViewControl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension GamesViewController: UISearchResultsUpdating
|
||||||
|
{
|
||||||
|
func updateSearchResults(for searchController: UISearchController)
|
||||||
|
{
|
||||||
|
if searchController.searchBar.text?.isEmpty == false
|
||||||
|
{
|
||||||
|
self.pageViewController.view.isHidden = true
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
self.pageViewController.view.isHidden = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//MARK: - NSFetchedResultsControllerDelegate -
|
//MARK: - NSFetchedResultsControllerDelegate -
|
||||||
/// NSFetchedResultsControllerDelegate
|
/// NSFetchedResultsControllerDelegate
|
||||||
extension GamesViewController: NSFetchedResultsControllerDelegate
|
extension GamesViewController: NSFetchedResultsControllerDelegate
|
||||||
|
|||||||
@ -12,8 +12,12 @@ class GamesPresentationController: UIPresentationController
|
|||||||
{
|
{
|
||||||
private let blurView: UIVisualEffectView
|
private let blurView: UIVisualEffectView
|
||||||
|
|
||||||
override init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?)
|
private let animator: UIViewPropertyAnimator
|
||||||
|
|
||||||
|
init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?, animator: UIViewPropertyAnimator)
|
||||||
{
|
{
|
||||||
|
self.animator = animator
|
||||||
|
|
||||||
self.blurView = UIVisualEffectView(effect: nil)
|
self.blurView = UIVisualEffectView(effect: nil)
|
||||||
self.blurView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
self.blurView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||||
|
|
||||||
@ -27,16 +31,16 @@ class GamesPresentationController: UIPresentationController
|
|||||||
self.blurView.frame = CGRect(x: 0, y: 0, width: containerView.bounds.width, height: containerView.bounds.height)
|
self.blurView.frame = CGRect(x: 0, y: 0, width: containerView.bounds.width, height: containerView.bounds.height)
|
||||||
containerView.addSubview(self.blurView)
|
containerView.addSubview(self.blurView)
|
||||||
|
|
||||||
self.presentedViewController.transitionCoordinator?.animate(alongsideTransition: { (context) in
|
self.animator.addAnimations {
|
||||||
self.blurView.effect = UIBlurEffect(style: .dark)
|
self.blurView.effect = UIBlurEffect(style: .dark)
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func dismissalTransitionWillBegin()
|
override func dismissalTransitionWillBegin()
|
||||||
{
|
{
|
||||||
self.presentedViewController.transitionCoordinator?.animate(alongsideTransition: { (context) in
|
self.animator.addAnimations {
|
||||||
self.blurView.effect = nil
|
self.blurView.effect = nil
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func dismissalTransitionDidEnd(_ completed: Bool)
|
override func dismissalTransitionDidEnd(_ completed: Bool)
|
||||||
|
|||||||
@ -10,9 +10,9 @@ import UIKit
|
|||||||
|
|
||||||
class GamesStoryboardSegue: UIStoryboardSegue
|
class GamesStoryboardSegue: UIStoryboardSegue
|
||||||
{
|
{
|
||||||
fileprivate let animator: UIViewPropertyAnimator
|
private let animator: UIViewPropertyAnimator
|
||||||
|
|
||||||
fileprivate var isPresenting: Bool = true
|
private var isPresenting: Bool = true
|
||||||
|
|
||||||
override init(identifier: String?, source: UIViewController, destination: UIViewController)
|
override init(identifier: String?, source: UIViewController, destination: UIViewController)
|
||||||
{
|
{
|
||||||
@ -48,7 +48,7 @@ extension GamesStoryboardSegue: UIViewControllerTransitioningDelegate
|
|||||||
|
|
||||||
func presentationController(forPresented presentedViewController: UIViewController, presenting presentingViewController: UIViewController?, source: UIViewController) -> UIPresentationController?
|
func presentationController(forPresented presentedViewController: UIViewController, presenting presentingViewController: UIViewController?, source: UIViewController) -> UIPresentationController?
|
||||||
{
|
{
|
||||||
let presentationController = GamesPresentationController(presentedViewController: presentedViewController, presenting: presentingViewController)
|
let presentationController = GamesPresentationController(presentedViewController: presentedViewController, presenting: presentingViewController, animator: self.animator)
|
||||||
return presentationController
|
return presentationController
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -85,13 +85,15 @@ extension GamesStoryboardSegue: UIViewControllerAnimatedTransitioning
|
|||||||
snapshotView.alpha = 1.0
|
snapshotView.alpha = 1.0
|
||||||
transitionContext.containerView.addSubview(snapshotView)
|
transitionContext.containerView.addSubview(snapshotView)
|
||||||
|
|
||||||
// Ensures navigation controller toolbar (if visible) has been added to view heirachy, allowing us to add constraints
|
|
||||||
transitionContext.containerView.layoutIfNeeded()
|
|
||||||
|
|
||||||
// We add extra padding around the existing navigation bar and toolbar so they never appear to be detached from the edges of the screen during the overshooting of the spring animation
|
// We add extra padding around the existing navigation bar and toolbar so they never appear to be detached from the edges of the screen during the overshooting of the spring animation
|
||||||
var topPaddingToolbar: UIToolbar? = nil
|
var topPaddingToolbar: UIToolbar? = nil
|
||||||
var bottomPaddingToolbar: UIToolbar? = nil
|
var bottomPaddingToolbar: UIToolbar? = nil
|
||||||
|
|
||||||
|
// Must be wrapped in no-animation block to prevent iOS 11 search bar from not appearing.
|
||||||
|
UIView.performWithoutAnimation {
|
||||||
|
// Ensures navigation controller toolbar (if visible) has been added to view heirachy, allowing us to add constraints
|
||||||
|
transitionContext.containerView.layoutIfNeeded()
|
||||||
|
|
||||||
if let navigationController = transitionContext.destinationViewController as? UINavigationController
|
if let navigationController = transitionContext.destinationViewController as? UINavigationController
|
||||||
{
|
{
|
||||||
let padding: CGFloat = 44
|
let padding: CGFloat = 44
|
||||||
@ -101,12 +103,12 @@ extension GamesStoryboardSegue: UIViewControllerAnimatedTransitioning
|
|||||||
let topToolbar = UIToolbar(frame: CGRect.zero)
|
let topToolbar = UIToolbar(frame: CGRect.zero)
|
||||||
topToolbar.translatesAutoresizingMaskIntoConstraints = false
|
topToolbar.translatesAutoresizingMaskIntoConstraints = false
|
||||||
topToolbar.barStyle = navigationController.toolbar.barStyle
|
topToolbar.barStyle = navigationController.toolbar.barStyle
|
||||||
transitionContext.destinationView.insertSubview(topToolbar, belowSubview: navigationController.navigationBar)
|
transitionContext.destinationView.insertSubview(topToolbar, at: 1)
|
||||||
|
|
||||||
topToolbar.bottomAnchor.constraint(equalTo: navigationController.navigationBar.bottomAnchor).isActive = true
|
topToolbar.topAnchor.constraint(equalTo: navigationController.navigationBar.topAnchor, constant: -padding).isActive = true
|
||||||
topToolbar.centerXAnchor.constraint(equalTo: navigationController.navigationBar.centerXAnchor).isActive = true
|
topToolbar.bottomAnchor.constraint(equalTo: navigationController.topViewController!.topLayoutGuide.bottomAnchor).isActive = true
|
||||||
topToolbar.widthAnchor.constraint(equalTo: navigationController.navigationBar.widthAnchor, constant: padding * 2).isActive = true
|
topToolbar.leftAnchor.constraint(equalTo: navigationController.navigationBar.leftAnchor, constant: -padding).isActive = true
|
||||||
topToolbar.heightAnchor.constraint(equalTo: navigationController.navigationBar.heightAnchor, constant: padding).isActive = true
|
topToolbar.rightAnchor.constraint(equalTo: navigationController.navigationBar.rightAnchor, constant: padding).isActive = true
|
||||||
|
|
||||||
topPaddingToolbar = topToolbar
|
topPaddingToolbar = topToolbar
|
||||||
}
|
}
|
||||||
@ -119,13 +121,14 @@ extension GamesStoryboardSegue: UIViewControllerAnimatedTransitioning
|
|||||||
transitionContext.destinationView.insertSubview(bottomToolbar, belowSubview: navigationController.navigationBar)
|
transitionContext.destinationView.insertSubview(bottomToolbar, belowSubview: navigationController.navigationBar)
|
||||||
|
|
||||||
bottomToolbar.topAnchor.constraint(equalTo: navigationController.toolbar.topAnchor).isActive = true
|
bottomToolbar.topAnchor.constraint(equalTo: navigationController.toolbar.topAnchor).isActive = true
|
||||||
bottomToolbar.centerXAnchor.constraint(equalTo: navigationController.toolbar.centerXAnchor).isActive = true
|
bottomToolbar.bottomAnchor.constraint(equalTo: navigationController.toolbar.bottomAnchor, constant: padding).isActive = true
|
||||||
bottomToolbar.widthAnchor.constraint(equalTo: navigationController.toolbar.widthAnchor, constant: padding * 2).isActive = true
|
bottomToolbar.leftAnchor.constraint(equalTo: navigationController.toolbar.leftAnchor, constant: -padding).isActive = true
|
||||||
bottomToolbar.heightAnchor.constraint(equalTo: navigationController.toolbar.heightAnchor, constant: padding).isActive = true
|
bottomToolbar.rightAnchor.constraint(equalTo: navigationController.toolbar.rightAnchor, constant: padding).isActive = true
|
||||||
|
|
||||||
bottomPaddingToolbar = bottomToolbar
|
bottomPaddingToolbar = bottomToolbar
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.animator.addAnimations {
|
self.animator.addAnimations {
|
||||||
snapshotView.alpha = 0.0
|
snapshotView.alpha = 0.0
|
||||||
@ -163,3 +166,4 @@ extension GamesStoryboardSegue: UIViewControllerAnimatedTransitioning
|
|||||||
self.animator.startAnimation()
|
self.animator.startAnimation()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -10,9 +10,9 @@ import UIKit
|
|||||||
|
|
||||||
class InitialGamesStoryboardSegue: UIStoryboardSegue
|
class InitialGamesStoryboardSegue: UIStoryboardSegue
|
||||||
{
|
{
|
||||||
fileprivate let animator: UIViewPropertyAnimator
|
private let animator: UIViewPropertyAnimator
|
||||||
|
|
||||||
fileprivate var isPresenting: Bool = true
|
private var isPresenting: Bool = true
|
||||||
|
|
||||||
override init(identifier: String?, source: UIViewController, destination: UIViewController)
|
override init(identifier: String?, source: UIViewController, destination: UIViewController)
|
||||||
{
|
{
|
||||||
|
|||||||
39
Delta/Importing/Import Options/ClipboardImportOption.swift
Normal file
39
Delta/Importing/Import Options/ClipboardImportOption.swift
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
//
|
||||||
|
// ClipboardImportOption.swift
|
||||||
|
// Delta
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 5/1/17.
|
||||||
|
// Copyright © 2017 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import MobileCoreServices
|
||||||
|
|
||||||
|
import Roxas
|
||||||
|
|
||||||
|
struct ClipboardImportOption: ImportOption
|
||||||
|
{
|
||||||
|
let title = NSLocalizedString("Clipboard", comment: "")
|
||||||
|
let image: UIImage? = nil
|
||||||
|
|
||||||
|
func `import`(withCompletionHandler completionHandler: @escaping (Set<URL>?) -> Void)
|
||||||
|
{
|
||||||
|
guard UIPasteboard.general.hasImages else { return completionHandler([]) }
|
||||||
|
|
||||||
|
guard let data = UIPasteboard.general.data(forPasteboardType: kUTTypeImage as String) else { return completionHandler([]) }
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
let temporaryURL = FileManager.default.uniqueTemporaryURL()
|
||||||
|
try data.write(to: temporaryURL, options: .atomic)
|
||||||
|
|
||||||
|
completionHandler([temporaryURL])
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
print(error)
|
||||||
|
|
||||||
|
completionHandler([])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,42 @@
|
|||||||
|
//
|
||||||
|
// GamesDatabaseImportOption.swift
|
||||||
|
// Delta
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 5/1/17.
|
||||||
|
// Copyright © 2017 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
struct GamesDatabaseImportOption: ImportOption
|
||||||
|
{
|
||||||
|
let title = NSLocalizedString("Games Database", comment: "")
|
||||||
|
let image: UIImage? = nil
|
||||||
|
|
||||||
|
private let presentingViewController: UIViewController
|
||||||
|
|
||||||
|
init(presentingViewController: UIViewController)
|
||||||
|
{
|
||||||
|
self.presentingViewController = presentingViewController
|
||||||
|
}
|
||||||
|
|
||||||
|
func `import`(withCompletionHandler completionHandler: @escaping (Set<URL>?) -> Void)
|
||||||
|
{
|
||||||
|
let storyboard = UIStoryboard(name: "GamesDatabase", bundle: nil)
|
||||||
|
let navigationController = (storyboard.instantiateInitialViewController() as! UINavigationController)
|
||||||
|
|
||||||
|
let gamesDatabaseBrowserViewController = navigationController.topViewController as! GamesDatabaseBrowserViewController
|
||||||
|
gamesDatabaseBrowserViewController.selectionHandler = { (metadata) in
|
||||||
|
if let artworkURL = metadata.artworkURL
|
||||||
|
{
|
||||||
|
completionHandler([artworkURL])
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
completionHandler(nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.presentingViewController.present(navigationController, animated: true, completion: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,61 @@
|
|||||||
|
//
|
||||||
|
// PhotoLibraryImportOption.swift
|
||||||
|
// Delta
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 5/2/17.
|
||||||
|
// Copyright © 2017 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import MobileCoreServices
|
||||||
|
|
||||||
|
class PhotoLibraryImportOption: NSObject, ImportOption
|
||||||
|
{
|
||||||
|
let title = NSLocalizedString("Photo Library", comment: "")
|
||||||
|
let image: UIImage? = nil
|
||||||
|
|
||||||
|
private let presentingViewController: UIViewController
|
||||||
|
private var completionHandler: ((Set<URL>?) -> Void)?
|
||||||
|
|
||||||
|
init(presentingViewController: UIViewController)
|
||||||
|
{
|
||||||
|
self.presentingViewController = presentingViewController
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
}
|
||||||
|
|
||||||
|
func `import`(withCompletionHandler completionHandler: @escaping (Set<URL>?) -> Void)
|
||||||
|
{
|
||||||
|
self.completionHandler = completionHandler
|
||||||
|
|
||||||
|
let imagePickerController = UIImagePickerController()
|
||||||
|
imagePickerController.delegate = self
|
||||||
|
imagePickerController.sourceType = .photoLibrary
|
||||||
|
imagePickerController.mediaTypes = [kUTTypeImage as String]
|
||||||
|
imagePickerController.view.backgroundColor = .white
|
||||||
|
self.presentingViewController.present(imagePickerController, animated: true, completion: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension PhotoLibraryImportOption: UIImagePickerControllerDelegate, UINavigationControllerDelegate
|
||||||
|
{
|
||||||
|
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any])
|
||||||
|
{
|
||||||
|
guard let image = info[UIImagePickerControllerOriginalImage] as? UIImage, let data = UIImageJPEGRepresentation(image, 0.85) else {
|
||||||
|
self.completionHandler?([])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
let temporaryURL = FileManager.default.uniqueTemporaryURL()
|
||||||
|
try data.write(to: temporaryURL, options: .atomic)
|
||||||
|
|
||||||
|
self.completionHandler?([temporaryURL])
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
self.completionHandler?([])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
73
Delta/Importing/Import Options/iTunesImportOption.swift
Normal file
73
Delta/Importing/Import Options/iTunesImportOption.swift
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
//
|
||||||
|
// iTunesImportOption.swift
|
||||||
|
// Delta
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 5/1/17.
|
||||||
|
// Copyright © 2017 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
import DeltaCore
|
||||||
|
|
||||||
|
struct iTunesImportOption: ImportOption
|
||||||
|
{
|
||||||
|
let title = NSLocalizedString("iTunes", comment: "")
|
||||||
|
let image: UIImage? = nil
|
||||||
|
|
||||||
|
private let presentingViewController: UIViewController
|
||||||
|
|
||||||
|
init(presentingViewController: UIViewController)
|
||||||
|
{
|
||||||
|
self.presentingViewController = presentingViewController
|
||||||
|
}
|
||||||
|
|
||||||
|
func `import`(withCompletionHandler completionHandler: @escaping (Set<URL>?) -> Void)
|
||||||
|
{
|
||||||
|
let alertController = UIAlertController(title: NSLocalizedString("Import from iTunes?", comment: ""), message: NSLocalizedString("Delta will import the games and controller skins copied over via iTunes.", comment: ""), preferredStyle: .alert)
|
||||||
|
|
||||||
|
let importAction = UIAlertAction(title: NSLocalizedString("Import", comment: ""), style: .default) { action in
|
||||||
|
|
||||||
|
var importedURLs = Set<URL>()
|
||||||
|
|
||||||
|
let documentsDirectoryURL = DatabaseManager.defaultDirectoryURL().deletingLastPathComponent()
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
let contents = try FileManager.default.contentsOfDirectory(at: documentsDirectoryURL, includingPropertiesForKeys: nil, options: .skipsHiddenFiles)
|
||||||
|
|
||||||
|
let itemURLs = contents.filter { GameType(fileExtension: $0.pathExtension) != nil || $0.pathExtension.lowercased() == "zip" || $0.pathExtension.lowercased() == "deltaskin" }
|
||||||
|
|
||||||
|
for url in itemURLs
|
||||||
|
{
|
||||||
|
let destinationURL = FileManager.default.uniqueTemporaryURL().appendingPathExtension(url.pathExtension)
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
try FileManager.default.moveItem(at: url, to: destinationURL)
|
||||||
|
importedURLs.insert(destinationURL)
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
print("Error importing file at URL", url, error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
print(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
completionHandler(importedURLs)
|
||||||
|
}
|
||||||
|
alertController.addAction(importAction)
|
||||||
|
|
||||||
|
let cancelAction = UIAlertAction(title: NSLocalizedString("Cancel", comment: ""), style: .cancel) { action in
|
||||||
|
completionHandler(nil)
|
||||||
|
}
|
||||||
|
alertController.addAction(cancelAction)
|
||||||
|
|
||||||
|
self.presentingViewController.present(alertController, animated: true, completion: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
250
Delta/Importing/ImportController.swift
Normal file
250
Delta/Importing/ImportController.swift
Normal file
@ -0,0 +1,250 @@
|
|||||||
|
//
|
||||||
|
// ImportController.swift
|
||||||
|
// Delta
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 10/10/15.
|
||||||
|
// Copyright © 2015 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import MobileCoreServices
|
||||||
|
import ObjectiveC
|
||||||
|
|
||||||
|
import DeltaCore
|
||||||
|
|
||||||
|
import Roxas
|
||||||
|
|
||||||
|
protocol ImportControllerDelegate
|
||||||
|
{
|
||||||
|
func importController(_ importController: ImportController, didImportItemsAt urls: Set<URL>, errors: [Error])
|
||||||
|
|
||||||
|
/** Optional **/
|
||||||
|
func importControllerDidCancel(_ importController: ImportController)
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ImportControllerDelegate
|
||||||
|
{
|
||||||
|
func importControllerDidCancel(_ importController: ImportController)
|
||||||
|
{
|
||||||
|
// Empty Implementation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ImportController: NSObject
|
||||||
|
{
|
||||||
|
let documentTypes: Set<String>
|
||||||
|
|
||||||
|
var delegate: ImportControllerDelegate?
|
||||||
|
var importOptions: [ImportOption]?
|
||||||
|
|
||||||
|
private weak var presentingViewController: UIViewController?
|
||||||
|
|
||||||
|
// Store presentedViewController separately, since when we dismiss we don't know if it has already been dismissed.
|
||||||
|
// Calling dismiss on presentingViewController in that case would dismiss presentingViewController, which is bad.
|
||||||
|
private weak var presentedViewController: UIViewController?
|
||||||
|
|
||||||
|
private let importQueue: OperationQueue
|
||||||
|
private let fileCoordinator: NSFileCoordinator
|
||||||
|
|
||||||
|
init(documentTypes: Set<String>)
|
||||||
|
{
|
||||||
|
self.documentTypes = documentTypes
|
||||||
|
|
||||||
|
let dispatchQueue = DispatchQueue(label: "com.rileytestut.Delta.ImportController.dispatchQueue", qos: .userInitiated, attributes: .concurrent)
|
||||||
|
|
||||||
|
self.importQueue = OperationQueue()
|
||||||
|
self.importQueue.name = "com.rileytestut.Delta.ImportController.importQueue"
|
||||||
|
self.importQueue.underlyingQueue = dispatchQueue
|
||||||
|
|
||||||
|
self.fileCoordinator = NSFileCoordinator(filePresenter: nil)
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate func presentImportController(from presentingViewController: UIViewController, animated: Bool, completionHandler: (() -> Void)?)
|
||||||
|
{
|
||||||
|
self.presentingViewController = presentingViewController
|
||||||
|
|
||||||
|
#if IMPACTOR
|
||||||
|
|
||||||
|
let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
|
||||||
|
alertController.addAction(UIAlertAction.cancel)
|
||||||
|
|
||||||
|
if let importOptions = self.importOptions
|
||||||
|
{
|
||||||
|
for importOption in importOptions
|
||||||
|
{
|
||||||
|
alertController.add(importOption) { [unowned self] (urls) in
|
||||||
|
self.finish(with: urls, errors: [])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.presentedViewController = alertController
|
||||||
|
self.presentingViewController?.present(alertController, animated: true, completion: nil)
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
let documentMenuController = UIDocumentMenuViewController(documentTypes: Array(self.documentTypes), in: .import)
|
||||||
|
documentMenuController.delegate = self
|
||||||
|
|
||||||
|
if let reversedImportOptions = self.importOptions?.reversed()
|
||||||
|
{
|
||||||
|
for importOption in reversedImportOptions
|
||||||
|
{
|
||||||
|
documentMenuController.add(importOption, order: .first) { [unowned self] (urls) in
|
||||||
|
self.finish(with: urls, errors: [])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.presentedViewController = documentMenuController
|
||||||
|
self.presentingViewController?.present(documentMenuController, animated: true, completion: nil)
|
||||||
|
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func cancel()
|
||||||
|
{
|
||||||
|
self.finish(with: nil, errors: [])
|
||||||
|
}
|
||||||
|
|
||||||
|
private func finish(with urls: Set<URL>?, errors: [Error])
|
||||||
|
{
|
||||||
|
if let urls = urls
|
||||||
|
{
|
||||||
|
self.delegate?.importController(self, didImportItemsAt: urls, errors: errors)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
self.delegate?.importControllerDidCancel(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.presentedViewController?.dismiss(animated: true)
|
||||||
|
|
||||||
|
self.presentingViewController?.importController = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extension ImportController: UIDocumentMenuDelegate
|
||||||
|
{
|
||||||
|
func documentMenu(_ documentMenu: UIDocumentMenuViewController, didPickDocumentPicker documentPicker: UIDocumentPickerViewController)
|
||||||
|
{
|
||||||
|
if #available(iOS 11.0, *)
|
||||||
|
{
|
||||||
|
let cancelButton = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(ImportController.cancel))
|
||||||
|
|
||||||
|
let documentBrowserViewController = UIDocumentBrowserViewController(forOpeningFilesWithContentTypes: Array(self.documentTypes))
|
||||||
|
documentBrowserViewController.delegate = self
|
||||||
|
documentBrowserViewController.browserUserInterfaceStyle = .dark
|
||||||
|
documentBrowserViewController.allowsPickingMultipleItems = true
|
||||||
|
documentBrowserViewController.allowsDocumentCreation = false
|
||||||
|
documentBrowserViewController.additionalTrailingNavigationBarButtonItems = [cancelButton]
|
||||||
|
|
||||||
|
self.presentedViewController = documentBrowserViewController
|
||||||
|
self.presentingViewController?.present(documentBrowserViewController, animated: true, completion: nil)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
documentPicker.delegate = self
|
||||||
|
|
||||||
|
self.presentedViewController = documentPicker
|
||||||
|
self.presentingViewController?.present(documentPicker, animated: true, completion: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func documentMenuWasCancelled(_ documentMenu: UIDocumentMenuViewController)
|
||||||
|
{
|
||||||
|
self.finish(with: nil, errors: [])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ImportController: UIDocumentPickerDelegate
|
||||||
|
{
|
||||||
|
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentAt url: URL)
|
||||||
|
{
|
||||||
|
self.finish(with: [url], errors: [])
|
||||||
|
}
|
||||||
|
|
||||||
|
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL])
|
||||||
|
{
|
||||||
|
self.finish(with: Set(urls), errors: [])
|
||||||
|
}
|
||||||
|
|
||||||
|
func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController)
|
||||||
|
{
|
||||||
|
self.finish(with: nil, errors: [])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 11.0, *)
|
||||||
|
extension ImportController: UIDocumentBrowserViewControllerDelegate
|
||||||
|
{
|
||||||
|
func documentBrowser(_ controller: UIDocumentBrowserViewController, didPickDocumentURLs documentURLs: [URL])
|
||||||
|
{
|
||||||
|
var coordinatedURLs = Set<URL>()
|
||||||
|
var errors = [Error]()
|
||||||
|
|
||||||
|
let dispatchGroup = DispatchGroup()
|
||||||
|
|
||||||
|
for url in documentURLs
|
||||||
|
{
|
||||||
|
dispatchGroup.enter()
|
||||||
|
|
||||||
|
let intent = NSFileAccessIntent.readingIntent(with: url)
|
||||||
|
self.fileCoordinator.coordinate(with: [intent], queue: self.importQueue) { (error) in
|
||||||
|
if let error = error
|
||||||
|
{
|
||||||
|
errors.append(error)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
let temporaryURL = FileManager.default.temporaryDirectory.appendingPathComponent(url.lastPathComponent)
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
// Always access intent.url, as the system may have updated it when requesting access.
|
||||||
|
try FileManager.default.copyItem(at: intent.url, to: temporaryURL)
|
||||||
|
|
||||||
|
coordinatedURLs.insert(temporaryURL)
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
errors.append(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatchGroup.leave()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatchGroup.notify(queue: self.importQueue.underlyingQueue!) {
|
||||||
|
self.finish(with: coordinatedURLs, errors: errors)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var ImportControllerKey: UInt8 = 0
|
||||||
|
|
||||||
|
extension UIViewController
|
||||||
|
{
|
||||||
|
fileprivate(set) var importController: ImportController?
|
||||||
|
{
|
||||||
|
set
|
||||||
|
{
|
||||||
|
objc_setAssociatedObject(self, &ImportControllerKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||||
|
}
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return objc_getAssociatedObject(self, &ImportControllerKey) as? ImportController
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func present(_ importController: ImportController, animated: Bool, completion: (() -> Void)?)
|
||||||
|
{
|
||||||
|
self.importController = importController
|
||||||
|
|
||||||
|
importController.presentImportController(from: self, animated: animated, completionHandler: completion)
|
||||||
|
}
|
||||||
|
}
|
||||||
40
Delta/Importing/ImportOption.swift
Normal file
40
Delta/Importing/ImportOption.swift
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
//
|
||||||
|
// ImportOption.swift
|
||||||
|
// Delta
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 5/1/17.
|
||||||
|
// Copyright © 2017 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
import DeltaCore
|
||||||
|
|
||||||
|
extension UIDocumentMenuViewController
|
||||||
|
{
|
||||||
|
func add(_ importOption: ImportOption, order: UIDocumentMenuOrder, completionHandler: @escaping (Set<URL>?) -> Void)
|
||||||
|
{
|
||||||
|
self.addOption(withTitle: importOption.title, image: importOption.image, order: order) {
|
||||||
|
importOption.import(withCompletionHandler: completionHandler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension UIAlertController
|
||||||
|
{
|
||||||
|
func add(_ importOption: ImportOption, completionHandler: @escaping (Set<URL>?) -> Void)
|
||||||
|
{
|
||||||
|
let action = UIAlertAction(title: importOption.title, style: .default, handler: { action in
|
||||||
|
importOption.import(withCompletionHandler: completionHandler)
|
||||||
|
})
|
||||||
|
self.addAction(action)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol ImportOption
|
||||||
|
{
|
||||||
|
var title: String { get }
|
||||||
|
var image: UIImage? { get }
|
||||||
|
|
||||||
|
func `import`(withCompletionHandler completionHandler: @escaping (Set<URL>?) -> Void)
|
||||||
|
}
|
||||||
@ -10,10 +10,10 @@ import UIKit
|
|||||||
|
|
||||||
class LaunchViewController: UIViewController
|
class LaunchViewController: UIViewController
|
||||||
{
|
{
|
||||||
@IBOutlet fileprivate var gameViewContainerView: UIView!
|
@IBOutlet private var gameViewContainerView: UIView!
|
||||||
fileprivate var gameViewController: GameViewController!
|
private var gameViewController: GameViewController!
|
||||||
|
|
||||||
fileprivate var presentedGameViewController: Bool = false
|
private var presentedGameViewController: Bool = false
|
||||||
|
|
||||||
override var preferredStatusBarStyle: UIStatusBarStyle {
|
override var preferredStatusBarStyle: UIStatusBarStyle {
|
||||||
return self.gameViewController?.preferredStatusBarStyle ?? .lightContent
|
return self.gameViewController?.preferredStatusBarStyle ?? .lightContent
|
||||||
|
|||||||
@ -13,7 +13,10 @@ import DeltaCore
|
|||||||
|
|
||||||
import Roxas
|
import Roxas
|
||||||
|
|
||||||
private let CheatPrefixAttribute = "prefix"
|
private extension NSAttributedStringKey
|
||||||
|
{
|
||||||
|
static let cheatPrefix = NSAttributedStringKey("CheatPrefix")
|
||||||
|
}
|
||||||
|
|
||||||
class CheatTextView: UITextView
|
class CheatTextView: UITextView
|
||||||
{
|
{
|
||||||
@ -23,7 +26,7 @@ class CheatTextView: UITextView
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@NSCopying fileprivate var attributedFormat: NSAttributedString?
|
@NSCopying private var attributedFormat: NSAttributedString?
|
||||||
|
|
||||||
required init?(coder aDecoder: NSCoder)
|
required init?(coder aDecoder: NSCoder)
|
||||||
{
|
{
|
||||||
@ -44,9 +47,9 @@ extension CheatTextView
|
|||||||
|
|
||||||
if let format = self.cheatFormat, let font = self.font
|
if let format = self.cheatFormat, let font = self.font
|
||||||
{
|
{
|
||||||
let characterWidth = ("A" as NSString).size(attributes: [NSFontAttributeName: font]).width
|
let characterWidth = ("A" as NSString).size(withAttributes: [.font: font]).width
|
||||||
|
|
||||||
let width = characterWidth * CGFloat(format.format.characters.count)
|
let width = characterWidth * CGFloat(format.format.count)
|
||||||
self.textContainer.size = CGSize(width: width, height: 0)
|
self.textContainer.size = CGSize(width: width, height: 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -79,7 +82,7 @@ private extension CheatTextView
|
|||||||
|
|
||||||
if let prefixString = prefixString, prefixString.length > 0
|
if let prefixString = prefixString, prefixString.length > 0
|
||||||
{
|
{
|
||||||
attributedString.addAttribute(CheatPrefixAttribute, value: prefixString, range: NSRange(location: 0, length: 1))
|
attributedString.addAttribute(.cheatPrefix, value: prefixString, range: NSRange(location: 0, length: 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
attributedFormat.append(attributedString)
|
attributedFormat.append(attributedString)
|
||||||
@ -105,7 +108,7 @@ private extension CheatTextView
|
|||||||
|
|
||||||
extension CheatTextView: NSLayoutManagerDelegate
|
extension CheatTextView: NSLayoutManagerDelegate
|
||||||
{
|
{
|
||||||
func layoutManager(_ layoutManager: NSLayoutManager, shouldGenerateGlyphs glyphs: UnsafePointer<CGGlyph>, properties props: UnsafePointer<NSGlyphProperty>, characterIndexes charIndexes: UnsafePointer<Int>, font aFont: UIFont, forGlyphRange glyphRange: NSRange) -> Int
|
func layoutManager(_ layoutManager: NSLayoutManager, shouldGenerateGlyphs glyphs: UnsafePointer<CGGlyph>, properties props: UnsafePointer<NSLayoutManager.GlyphProperty>, characterIndexes charIndexes: UnsafePointer<Int>, font aFont: UIFont, forGlyphRange glyphRange: NSRange) -> Int
|
||||||
{
|
{
|
||||||
// Returning 0 = let the layoutManager do the normal logic
|
// Returning 0 = let the layoutManager do the normal logic
|
||||||
guard let attributedFormat = self.attributedFormat else { return 0 }
|
guard let attributedFormat = self.attributedFormat else { return 0 }
|
||||||
@ -118,7 +121,7 @@ extension CheatTextView: NSLayoutManagerDelegate
|
|||||||
|
|
||||||
// Allocate our replacement buffers
|
// Allocate our replacement buffers
|
||||||
let glyphBuffer = UnsafeMutablePointer<CGGlyph>.allocate(capacity: bufferSize)
|
let glyphBuffer = UnsafeMutablePointer<CGGlyph>.allocate(capacity: bufferSize)
|
||||||
let propertyBuffer = UnsafeMutablePointer<NSGlyphProperty>.allocate(capacity: bufferSize)
|
let propertyBuffer = UnsafeMutablePointer<NSLayoutManager.GlyphProperty>.allocate(capacity: bufferSize)
|
||||||
let characterBuffer = UnsafeMutablePointer<Int>.allocate(capacity: bufferSize)
|
let characterBuffer = UnsafeMutablePointer<Int>.allocate(capacity: bufferSize)
|
||||||
|
|
||||||
var offset = 0
|
var offset = 0
|
||||||
@ -128,10 +131,10 @@ extension CheatTextView: NSLayoutManagerDelegate
|
|||||||
// The index the actual character maps to in the cheat format
|
// The index the actual character maps to in the cheat format
|
||||||
let characterIndex = charIndexes[i] % attributedFormat.length
|
let characterIndex = charIndexes[i] % attributedFormat.length
|
||||||
|
|
||||||
if let prefix = attributedFormat.attributes(at: characterIndex, effectiveRange: nil)[CheatPrefixAttribute] as? String
|
if let prefix = attributedFormat.attributes(at: characterIndex, effectiveRange: nil)[.cheatPrefix] as? String
|
||||||
{
|
{
|
||||||
// If there is a prefix string, we insert the glyphs (and associated properties/character indexes) first
|
// If there is a prefix string, we insert the glyphs (and associated properties/character indexes) first
|
||||||
let prefixCount = prefix.characters.count
|
let prefixCount = prefix.count
|
||||||
|
|
||||||
for j in 0 ..< prefixCount
|
for j in 0 ..< prefixCount
|
||||||
{
|
{
|
||||||
|
|||||||
@ -51,7 +51,7 @@ struct CheatValidator
|
|||||||
// Remove newline characters (code should already be formatted)
|
// Remove newline characters (code should already be formatted)
|
||||||
let sanitizedCode = (cheat.code as NSString).replacingOccurrences(of: "\n", with: "")
|
let sanitizedCode = (cheat.code as NSString).replacingOccurrences(of: "\n", with: "")
|
||||||
|
|
||||||
if sanitizedCode.characters.count % self.format.format.characters.count != 0
|
if sanitizedCode.count % self.format.format.count != 0
|
||||||
{
|
{
|
||||||
throw Error.invalidCode
|
throw Error.invalidCode
|
||||||
}
|
}
|
||||||
|
|||||||
@ -29,7 +29,7 @@ class CheatsViewController: UITableViewController
|
|||||||
|
|
||||||
weak var delegate: CheatsViewControllerDelegate?
|
weak var delegate: CheatsViewControllerDelegate?
|
||||||
|
|
||||||
fileprivate let dataSource = RSTFetchedResultsTableViewDataSource<Cheat>(fetchedResultsController: NSFetchedResultsController())
|
private let dataSource = RSTFetchedResultsTableViewDataSource<Cheat>(fetchedResultsController: NSFetchedResultsController())
|
||||||
}
|
}
|
||||||
|
|
||||||
extension CheatsViewController
|
extension CheatsViewController
|
||||||
|
|||||||
@ -33,7 +33,7 @@ class EditCheatViewController: UITableViewController
|
|||||||
var game: Game! {
|
var game: Game! {
|
||||||
didSet {
|
didSet {
|
||||||
let deltaCore = Delta.core(for: self.game.type)!
|
let deltaCore = Delta.core(for: self.game.type)!
|
||||||
self.supportedCheatFormats = deltaCore.emulatorConfiguration.supportedCheatFormats
|
self.supportedCheatFormats = deltaCore.supportedCheatFormats.sorted() { $0.name < $1.name }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,19 +43,19 @@ class EditCheatViewController: UITableViewController
|
|||||||
|
|
||||||
var isPreviewing = false
|
var isPreviewing = false
|
||||||
|
|
||||||
fileprivate var supportedCheatFormats: [CheatFormat]!
|
private var supportedCheatFormats: [CheatFormat]!
|
||||||
|
|
||||||
fileprivate var selectedCheatFormat: CheatFormat {
|
private var selectedCheatFormat: CheatFormat {
|
||||||
let cheatFormat = self.supportedCheatFormats[self.typeSegmentedControl.selectedSegmentIndex]
|
let cheatFormat = self.supportedCheatFormats[self.typeSegmentedControl.selectedSegmentIndex]
|
||||||
return cheatFormat
|
return cheatFormat
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate var mutableCheat: Cheat!
|
private var mutableCheat: Cheat!
|
||||||
fileprivate var managedObjectContext = DatabaseManager.shared.newBackgroundContext()
|
private var managedObjectContext = DatabaseManager.shared.newBackgroundContext()
|
||||||
|
|
||||||
@IBOutlet fileprivate var nameTextField: UITextField!
|
@IBOutlet private var nameTextField: UITextField!
|
||||||
@IBOutlet fileprivate var typeSegmentedControl: UISegmentedControl!
|
@IBOutlet private var typeSegmentedControl: UISegmentedControl!
|
||||||
@IBOutlet fileprivate var codeTextView: CheatTextView!
|
@IBOutlet private var codeTextView: CheatTextView!
|
||||||
|
|
||||||
override var previewActionItems: [UIPreviewActionItem]
|
override var previewActionItems: [UIPreviewActionItem]
|
||||||
{
|
{
|
||||||
@ -132,7 +132,7 @@ extension EditCheatViewController
|
|||||||
|
|
||||||
// Update UI
|
// Update UI
|
||||||
|
|
||||||
if name.characters.count == 0
|
if name.count == 0
|
||||||
{
|
{
|
||||||
self.title = NSLocalizedString("Cheat", comment: "")
|
self.title = NSLocalizedString("Cheat", comment: "")
|
||||||
}
|
}
|
||||||
@ -224,7 +224,7 @@ private extension EditCheatViewController
|
|||||||
@IBAction func updateCheatName(_ sender: UITextField)
|
@IBAction func updateCheatName(_ sender: UITextField)
|
||||||
{
|
{
|
||||||
var title = sender.text ?? ""
|
var title = sender.text ?? ""
|
||||||
if title.characters.count == 0
|
if title.count == 0
|
||||||
{
|
{
|
||||||
title = NSLocalizedString("Cheat", comment: "")
|
title = NSLocalizedString("Cheat", comment: "")
|
||||||
}
|
}
|
||||||
@ -325,7 +325,7 @@ private extension EditCheatViewController
|
|||||||
sender.resignFirstResponder()
|
sender.resignFirstResponder()
|
||||||
}
|
}
|
||||||
|
|
||||||
func presentErrorAlert(title: String, message: String, handler: ((Void) -> Void)?)
|
func presentErrorAlert(title: String, message: String, handler: (() -> Void)?)
|
||||||
{
|
{
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
|
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
|
||||||
@ -382,7 +382,9 @@ extension EditCheatViewController: UITextViewDelegate
|
|||||||
|
|
||||||
// We need to manually add back the attributes when manually modifying the underlying text storage
|
// We need to manually add back the attributes when manually modifying the underlying text storage
|
||||||
// Otherwise, pasting text into an empty text view will result in the wrong font being used
|
// Otherwise, pasting text into an empty text view will result in the wrong font being used
|
||||||
let attributedString = NSAttributedString(string: sanitizedText, attributes: textView.typingAttributes)
|
let attributes = Dictionary(uniqueKeysWithValues: textView.typingAttributes.map { (key, value) in (NSAttributedStringKey(key), value) })
|
||||||
|
|
||||||
|
let attributedString = NSAttributedString(string: sanitizedText, attributes: attributes)
|
||||||
textView.textStorage.replaceCharacters(in: range, with: attributedString)
|
textView.textStorage.replaceCharacters(in: range, with: attributedString)
|
||||||
|
|
||||||
// We must add attributedString.length, not range.length, in case the attributed string's length differs
|
// We must add attributedString.length, not range.length, in case the attributed string's length differs
|
||||||
|
|||||||
176
Delta/Pause Menu/GridMenuViewController.swift
Normal file
176
Delta/Pause Menu/GridMenuViewController.swift
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
//
|
||||||
|
// GridMenuViewController.swift
|
||||||
|
// Delta
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 12/21/15.
|
||||||
|
// Copyright © 2015 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import Roxas
|
||||||
|
|
||||||
|
class GridMenuViewController: UICollectionViewController
|
||||||
|
{
|
||||||
|
var items: [MenuItem] {
|
||||||
|
get { return self.dataSource.items }
|
||||||
|
set { self.dataSource.items = newValue; self.updateItems() }
|
||||||
|
}
|
||||||
|
|
||||||
|
var isVibrancyEnabled = true
|
||||||
|
|
||||||
|
override var preferredContentSize: CGSize {
|
||||||
|
set { }
|
||||||
|
get { return self.collectionView?.contentSize ?? CGSize.zero }
|
||||||
|
}
|
||||||
|
|
||||||
|
private let dataSource = RSTArrayCollectionViewDataSource<MenuItem>(items: [])
|
||||||
|
|
||||||
|
private var prototypeCell = GridCollectionViewCell()
|
||||||
|
private var previousIndexPath: IndexPath? = nil
|
||||||
|
|
||||||
|
private var registeredKVOObservers = Set<NSKeyValueObservation>()
|
||||||
|
|
||||||
|
init()
|
||||||
|
{
|
||||||
|
let collectionViewLayout = GridCollectionViewLayout()
|
||||||
|
collectionViewLayout.itemSize = CGSize(width: 60, height: 80)
|
||||||
|
collectionViewLayout.minimumLineSpacing = 20
|
||||||
|
collectionViewLayout.minimumInteritemSpacing = 10
|
||||||
|
|
||||||
|
super.init(collectionViewLayout: collectionViewLayout)
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder aDecoder: NSCoder)
|
||||||
|
{
|
||||||
|
super.init(coder: aDecoder)
|
||||||
|
}
|
||||||
|
|
||||||
|
deinit
|
||||||
|
{
|
||||||
|
// Crashes on iOS 10 if not explicitly invalidated.
|
||||||
|
self.registeredKVOObservers.forEach { $0.invalidate() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension GridMenuViewController
|
||||||
|
{
|
||||||
|
override func viewDidLoad()
|
||||||
|
{
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
self.collectionView?.register(GridCollectionViewCell.self, forCellWithReuseIdentifier: RSTCellContentGenericCellIdentifier)
|
||||||
|
|
||||||
|
self.dataSource.cellConfigurationHandler = { [unowned self] (cell, item, indexPath) in
|
||||||
|
self.configure(cell as! GridCollectionViewCell, for: indexPath)
|
||||||
|
}
|
||||||
|
self.collectionView?.dataSource = self.dataSource
|
||||||
|
|
||||||
|
let collectionViewLayout = self.collectionViewLayout as! GridCollectionViewLayout
|
||||||
|
collectionViewLayout.itemWidth = 90
|
||||||
|
collectionViewLayout.usesEqualHorizontalSpacingDistributionForSingleRow = true
|
||||||
|
|
||||||
|
// Manually update prototype cell properties
|
||||||
|
self.prototypeCell.contentView.widthAnchor.constraint(equalToConstant: collectionViewLayout.itemWidth).isActive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewDidAppear(_ animated: Bool)
|
||||||
|
{
|
||||||
|
super.viewDidAppear(animated)
|
||||||
|
|
||||||
|
if let indexPath = self.previousIndexPath
|
||||||
|
{
|
||||||
|
UIView.animate(withDuration: 0.2) {
|
||||||
|
let item = self.items[indexPath.item]
|
||||||
|
item.isSelected = !item.isSelected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension GridMenuViewController
|
||||||
|
{
|
||||||
|
func configure(_ cell: GridCollectionViewCell, for indexPath: IndexPath)
|
||||||
|
{
|
||||||
|
let pauseItem = self.items[indexPath.item]
|
||||||
|
|
||||||
|
cell.maximumImageSize = CGSize(width: 60, height: 60)
|
||||||
|
|
||||||
|
cell.imageView.image = pauseItem.image
|
||||||
|
cell.imageView.contentMode = .center
|
||||||
|
cell.imageView.layer.borderWidth = 2
|
||||||
|
cell.imageView.layer.borderColor = self.view.tintColor.cgColor
|
||||||
|
cell.imageView.layer.cornerRadius = 10
|
||||||
|
|
||||||
|
cell.textLabel.text = pauseItem.text
|
||||||
|
cell.textLabel.textColor = self.view.tintColor
|
||||||
|
|
||||||
|
if pauseItem.isSelected
|
||||||
|
{
|
||||||
|
cell.imageView.tintColor = UIColor.black
|
||||||
|
cell.imageView.backgroundColor = self.view.tintColor
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cell.imageView.tintColor = self.view.tintColor
|
||||||
|
cell.imageView.backgroundColor = UIColor.clear
|
||||||
|
}
|
||||||
|
|
||||||
|
cell.isImageViewVibrancyEnabled = self.isVibrancyEnabled
|
||||||
|
cell.isTextLabelVibrancyEnabled = self.isVibrancyEnabled
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateItems()
|
||||||
|
{
|
||||||
|
self.registeredKVOObservers.removeAll()
|
||||||
|
|
||||||
|
for (index, item) in self.items.enumerated()
|
||||||
|
{
|
||||||
|
let observer = item.observe(\.isSelected, changeHandler: { [unowned self] (item, change) in
|
||||||
|
let indexPath = IndexPath(item: index, section: 0)
|
||||||
|
|
||||||
|
if let cell = self.collectionView?.cellForItem(at: indexPath) as? GridCollectionViewCell
|
||||||
|
{
|
||||||
|
self.configure(cell, for: indexPath)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
self.registeredKVOObservers.insert(observer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension GridMenuViewController: UICollectionViewDelegateFlowLayout
|
||||||
|
{
|
||||||
|
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
|
||||||
|
{
|
||||||
|
self.configure(self.prototypeCell, for: indexPath)
|
||||||
|
|
||||||
|
let size = self.prototypeCell.contentView.systemLayoutSizeFitting(UILayoutFittingCompressedSize)
|
||||||
|
return size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension GridMenuViewController
|
||||||
|
{
|
||||||
|
override func collectionView(_ collectionView: UICollectionView, didHighlightItemAt indexPath: IndexPath)
|
||||||
|
{
|
||||||
|
let item = self.items[indexPath.item]
|
||||||
|
item.isSelected = !item.isSelected
|
||||||
|
}
|
||||||
|
|
||||||
|
override func collectionView(_ collectionView: UICollectionView, didUnhighlightItemAt indexPath: IndexPath)
|
||||||
|
{
|
||||||
|
let item = self.items[indexPath.item]
|
||||||
|
item.isSelected = !item.isSelected
|
||||||
|
}
|
||||||
|
|
||||||
|
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
|
||||||
|
{
|
||||||
|
self.previousIndexPath = indexPath
|
||||||
|
|
||||||
|
let item = self.items[indexPath.item]
|
||||||
|
item.isSelected = !item.isSelected
|
||||||
|
item.action(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
35
Delta/Pause Menu/MenuItem.swift
Normal file
35
Delta/Pause Menu/MenuItem.swift
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
//
|
||||||
|
// MenuItem.swift
|
||||||
|
// Delta
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 1/30/16.
|
||||||
|
// Copyright © 2016 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
// Must be class for use with Objective-C generics :(
|
||||||
|
class MenuItem: NSObject
|
||||||
|
{
|
||||||
|
var text: String
|
||||||
|
var image: UIImage?
|
||||||
|
var action: ((MenuItem) -> Void)
|
||||||
|
|
||||||
|
@objc dynamic var isSelected = false
|
||||||
|
|
||||||
|
init(text: String, image: UIImage?, action: @escaping ((MenuItem) -> Void))
|
||||||
|
{
|
||||||
|
self.image = image
|
||||||
|
self.text = text
|
||||||
|
self.action = action
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension MenuItem
|
||||||
|
{
|
||||||
|
override func isEqual(_ object: Any?) -> Bool
|
||||||
|
{
|
||||||
|
guard let item = object as? MenuItem else { return false }
|
||||||
|
return item.image == self.image && item.text == self.text
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,31 +0,0 @@
|
|||||||
//
|
|
||||||
// PauseItem.swift
|
|
||||||
// Delta
|
|
||||||
//
|
|
||||||
// Created by Riley Testut on 1/30/16.
|
|
||||||
// Copyright © 2016 Riley Testut. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
// Must be class for use with Objective-C generics :(
|
|
||||||
class PauseItem: Equatable
|
|
||||||
{
|
|
||||||
var image: UIImage
|
|
||||||
var text: String
|
|
||||||
var action: ((PauseItem) -> Void)
|
|
||||||
|
|
||||||
var selected = false
|
|
||||||
|
|
||||||
init(image: UIImage, text: String, action: @escaping ((PauseItem) -> Void))
|
|
||||||
{
|
|
||||||
self.image = image
|
|
||||||
self.text = text
|
|
||||||
self.action = action
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ==(lhs: PauseItem, rhs: PauseItem) -> Bool
|
|
||||||
{
|
|
||||||
return (lhs.image == rhs.image) && (lhs.text == rhs.text)
|
|
||||||
}
|
|
||||||
@ -1,146 +0,0 @@
|
|||||||
//
|
|
||||||
// PauseMenuViewController.swift
|
|
||||||
// Delta
|
|
||||||
//
|
|
||||||
// Created by Riley Testut on 12/21/15.
|
|
||||||
// Copyright © 2015 Riley Testut. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
import Roxas
|
|
||||||
|
|
||||||
class PauseMenuViewController: UICollectionViewController
|
|
||||||
{
|
|
||||||
var items = [PauseItem]() {
|
|
||||||
didSet
|
|
||||||
{
|
|
||||||
guard oldValue != self.items else { return }
|
|
||||||
|
|
||||||
if self.items.count > 8
|
|
||||||
{
|
|
||||||
fatalError("PauseViewController only supports up to 8 items (for my sanity when laying out on a landscape iPhone 4s")
|
|
||||||
}
|
|
||||||
|
|
||||||
self.dataSource.items = self.items
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override var preferredContentSize: CGSize {
|
|
||||||
set { }
|
|
||||||
get { return self.collectionView?.contentSize ?? CGSize.zero }
|
|
||||||
}
|
|
||||||
|
|
||||||
fileprivate let dataSource = RSTArrayCollectionViewDataSource<PauseItem>(items: [])
|
|
||||||
|
|
||||||
fileprivate var prototypeCell = GridCollectionViewCell()
|
|
||||||
fileprivate var previousIndexPath: IndexPath? = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
extension PauseMenuViewController
|
|
||||||
{
|
|
||||||
override func viewDidLoad()
|
|
||||||
{
|
|
||||||
super.viewDidLoad()
|
|
||||||
|
|
||||||
self.dataSource.cellConfigurationHandler = { [unowned self] (cell, item, indexPath) in
|
|
||||||
self.configure(cell as! GridCollectionViewCell, for: indexPath)
|
|
||||||
}
|
|
||||||
self.collectionView?.dataSource = self.dataSource
|
|
||||||
|
|
||||||
let collectionViewLayout = self.collectionViewLayout as! GridCollectionViewLayout
|
|
||||||
collectionViewLayout.itemWidth = 90
|
|
||||||
collectionViewLayout.usesEqualHorizontalSpacingDistributionForSingleRow = true
|
|
||||||
|
|
||||||
// Manually update prototype cell properties
|
|
||||||
self.prototypeCell.contentView.widthAnchor.constraint(equalToConstant: collectionViewLayout.itemWidth).isActive = true
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewDidAppear(_ animated: Bool)
|
|
||||||
{
|
|
||||||
super.viewDidAppear(animated)
|
|
||||||
|
|
||||||
if let indexPath = self.previousIndexPath
|
|
||||||
{
|
|
||||||
UIView.animate(withDuration: 0.2) {
|
|
||||||
self.toggleSelectedStateForPauseItemAtIndexPath(indexPath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private extension PauseMenuViewController
|
|
||||||
{
|
|
||||||
func configure(_ cell: GridCollectionViewCell, for indexPath: IndexPath)
|
|
||||||
{
|
|
||||||
let pauseItem = self.items[(indexPath as NSIndexPath).item]
|
|
||||||
|
|
||||||
cell.maximumImageSize = CGSize(width: 60, height: 60)
|
|
||||||
|
|
||||||
cell.imageView.image = pauseItem.image
|
|
||||||
cell.imageView.contentMode = .center
|
|
||||||
cell.imageView.layer.borderWidth = 2
|
|
||||||
cell.imageView.layer.borderColor = UIColor.white.cgColor
|
|
||||||
cell.imageView.layer.cornerRadius = 10
|
|
||||||
|
|
||||||
cell.textLabel.text = pauseItem.text
|
|
||||||
cell.textLabel.textColor = UIColor.white
|
|
||||||
|
|
||||||
if pauseItem.selected
|
|
||||||
{
|
|
||||||
cell.imageView.tintColor = UIColor.black
|
|
||||||
cell.imageView.backgroundColor = UIColor.white
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cell.imageView.tintColor = UIColor.white
|
|
||||||
cell.imageView.backgroundColor = UIColor.clear
|
|
||||||
}
|
|
||||||
|
|
||||||
cell.isImageViewVibrancyEnabled = true
|
|
||||||
cell.isTextLabelVibrancyEnabled = true
|
|
||||||
}
|
|
||||||
|
|
||||||
func toggleSelectedStateForPauseItemAtIndexPath(_ indexPath: IndexPath)
|
|
||||||
{
|
|
||||||
let pauseItem = self.items[indexPath.item]
|
|
||||||
pauseItem.selected = !pauseItem.selected
|
|
||||||
|
|
||||||
let cell = self.collectionView!.cellForItem(at: indexPath) as! GridCollectionViewCell
|
|
||||||
self.configure(cell, for: indexPath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension PauseMenuViewController: UICollectionViewDelegateFlowLayout
|
|
||||||
{
|
|
||||||
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
|
|
||||||
{
|
|
||||||
self.configure(self.prototypeCell, for: indexPath)
|
|
||||||
|
|
||||||
let size = self.prototypeCell.contentView.systemLayoutSizeFitting(UILayoutFittingCompressedSize)
|
|
||||||
return size
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension PauseMenuViewController
|
|
||||||
{
|
|
||||||
override func collectionView(_ collectionView: UICollectionView, didHighlightItemAt indexPath: IndexPath)
|
|
||||||
{
|
|
||||||
self.toggleSelectedStateForPauseItemAtIndexPath(indexPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
override func collectionView(_ collectionView: UICollectionView, didUnhighlightItemAt indexPath: IndexPath)
|
|
||||||
{
|
|
||||||
self.toggleSelectedStateForPauseItemAtIndexPath(indexPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)
|
|
||||||
{
|
|
||||||
self.previousIndexPath = indexPath
|
|
||||||
|
|
||||||
self.toggleSelectedStateForPauseItemAtIndexPath(indexPath)
|
|
||||||
|
|
||||||
let pauseItem = self.items[indexPath.item]
|
|
||||||
pauseItem.action(pauseItem)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -18,16 +18,16 @@ class PauseViewController: UIViewController, PauseInfoProviding
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var pauseItems: [PauseItem] {
|
var pauseItems: [MenuItem] {
|
||||||
return [self.saveStateItem, self.loadStateItem, self.cheatCodesItem, self.fastForwardItem, self.sustainButtonsItem].flatMap { $0 }
|
return [self.saveStateItem, self.loadStateItem, self.cheatCodesItem, self.fastForwardItem, self.sustainButtonsItem].flatMap { $0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pause Items
|
/// Pause Items
|
||||||
var saveStateItem: PauseItem?
|
var saveStateItem: MenuItem?
|
||||||
var loadStateItem: PauseItem?
|
var loadStateItem: MenuItem?
|
||||||
var cheatCodesItem: PauseItem?
|
var cheatCodesItem: MenuItem?
|
||||||
var fastForwardItem: PauseItem?
|
var fastForwardItem: MenuItem?
|
||||||
var sustainButtonsItem: PauseItem?
|
var sustainButtonsItem: MenuItem?
|
||||||
|
|
||||||
/// PauseInfoProviding
|
/// PauseInfoProviding
|
||||||
var pauseText: String?
|
var pauseText: String?
|
||||||
@ -38,9 +38,9 @@ class PauseViewController: UIViewController, PauseInfoProviding
|
|||||||
/// Save States
|
/// Save States
|
||||||
weak var saveStatesViewControllerDelegate: SaveStatesViewControllerDelegate?
|
weak var saveStatesViewControllerDelegate: SaveStatesViewControllerDelegate?
|
||||||
|
|
||||||
fileprivate var saveStatesViewControllerMode = SaveStatesViewController.Mode.loading
|
private var saveStatesViewControllerMode = SaveStatesViewController.Mode.loading
|
||||||
|
|
||||||
fileprivate var pauseNavigationController: UINavigationController!
|
private var pauseNavigationController: UINavigationController!
|
||||||
|
|
||||||
/// UIViewController
|
/// UIViewController
|
||||||
override var preferredContentSize: CGSize {
|
override var preferredContentSize: CGSize {
|
||||||
@ -85,8 +85,8 @@ extension PauseViewController
|
|||||||
self.pauseNavigationController.navigationBar.tintColor = UIColor.deltaPurple
|
self.pauseNavigationController.navigationBar.tintColor = UIColor.deltaPurple
|
||||||
self.pauseNavigationController.view.backgroundColor = UIColor.clear
|
self.pauseNavigationController.view.backgroundColor = UIColor.clear
|
||||||
|
|
||||||
let pauseMenuViewController = self.pauseNavigationController.topViewController as! PauseMenuViewController
|
let gridMenuViewController = self.pauseNavigationController.topViewController as! GridMenuViewController
|
||||||
pauseMenuViewController.items = self.pauseItems
|
gridMenuViewController.items = self.pauseItems
|
||||||
|
|
||||||
case "saveStates":
|
case "saveStates":
|
||||||
let saveStatesViewController = segue.destination as! SaveStatesViewController
|
let saveStatesViewController = segue.destination as! SaveStatesViewController
|
||||||
@ -135,21 +135,21 @@ private extension PauseViewController
|
|||||||
|
|
||||||
guard self.emulatorCore != nil else { return }
|
guard self.emulatorCore != nil else { return }
|
||||||
|
|
||||||
self.saveStateItem = PauseItem(image: #imageLiteral(resourceName: "SaveSaveState"), text: NSLocalizedString("Save State", comment: ""), action: { [unowned self] _ in
|
self.saveStateItem = MenuItem(text: NSLocalizedString("Save State", comment: ""), image: #imageLiteral(resourceName: "SaveSaveState"), action: { [unowned self] _ in
|
||||||
self.saveStatesViewControllerMode = .saving
|
self.saveStatesViewControllerMode = .saving
|
||||||
self.performSegue(withIdentifier: "saveStates", sender: self)
|
self.performSegue(withIdentifier: "saveStates", sender: self)
|
||||||
})
|
})
|
||||||
|
|
||||||
self.loadStateItem = PauseItem(image: #imageLiteral(resourceName: "LoadSaveState"), text: NSLocalizedString("Load State", comment: ""), action: { [unowned self] _ in
|
self.loadStateItem = MenuItem(text: NSLocalizedString("Load State", comment: ""), image: #imageLiteral(resourceName: "LoadSaveState"), action: { [unowned self] _ in
|
||||||
self.saveStatesViewControllerMode = .loading
|
self.saveStatesViewControllerMode = .loading
|
||||||
self.performSegue(withIdentifier: "saveStates", sender: self)
|
self.performSegue(withIdentifier: "saveStates", sender: self)
|
||||||
})
|
})
|
||||||
|
|
||||||
self.cheatCodesItem = PauseItem(image: #imageLiteral(resourceName: "CheatCodes"), text: NSLocalizedString("Cheat Codes", comment: ""), action: { [unowned self] _ in
|
self.cheatCodesItem = MenuItem(text: NSLocalizedString("Cheat Codes", comment: ""), image: #imageLiteral(resourceName: "CheatCodes"), action: { [unowned self] _ in
|
||||||
self.performSegue(withIdentifier: "cheats", sender: self)
|
self.performSegue(withIdentifier: "cheats", sender: self)
|
||||||
})
|
})
|
||||||
|
|
||||||
self.fastForwardItem = PauseItem(image: #imageLiteral(resourceName: "FastForward"), text: NSLocalizedString("Fast Forward", comment: ""), action: { _ in })
|
self.fastForwardItem = MenuItem(text: NSLocalizedString("Fast Forward", comment: ""), image: #imageLiteral(resourceName: "FastForward"), action: { _ in })
|
||||||
self.sustainButtonsItem = PauseItem(image: #imageLiteral(resourceName: "SustainButtons"), text: NSLocalizedString("Sustain Buttons", comment: ""), action: { _ in })
|
self.sustainButtonsItem = MenuItem(text: NSLocalizedString("Sustain Buttons", comment: ""), image: #imageLiteral(resourceName: "SustainButtons"), action: { _ in })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,13 +17,17 @@ protocol PauseInfoProviding
|
|||||||
|
|
||||||
class PausePresentationController: UIPresentationController
|
class PausePresentationController: UIPresentationController
|
||||||
{
|
{
|
||||||
|
let presentationAnimator: UIViewPropertyAnimator
|
||||||
|
|
||||||
private let blurringView: UIVisualEffectView
|
private let blurringView: UIVisualEffectView
|
||||||
private let vibrancyView: UIVisualEffectView
|
private let vibrancyView: UIVisualEffectView
|
||||||
|
|
||||||
private var contentView: UIView!
|
private var contentView: UIView!
|
||||||
@IBOutlet private weak var pauseLabel: UILabel!
|
|
||||||
@IBOutlet private weak var pauseIconImageView: UIImageView!
|
// Must not be weak, or else may result in crash when deallocating.
|
||||||
@IBOutlet private weak var stackView: UIStackView!
|
@IBOutlet private var pauseLabel: UILabel!
|
||||||
|
@IBOutlet private var pauseIconImageView: UIImageView!
|
||||||
|
@IBOutlet private var stackView: UIStackView!
|
||||||
|
|
||||||
override var frameOfPresentedViewInContainerView: CGRect
|
override var frameOfPresentedViewInContainerView: CGRect
|
||||||
{
|
{
|
||||||
@ -45,8 +49,10 @@ class PausePresentationController: UIPresentationController
|
|||||||
return frame
|
return frame
|
||||||
}
|
}
|
||||||
|
|
||||||
override init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?)
|
init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?, presentationAnimator: UIViewPropertyAnimator)
|
||||||
{
|
{
|
||||||
|
self.presentationAnimator = presentationAnimator
|
||||||
|
|
||||||
self.blurringView = UIVisualEffectView(effect: nil)
|
self.blurringView = UIVisualEffectView(effect: nil)
|
||||||
self.vibrancyView = UIVisualEffectView(effect: nil)
|
self.vibrancyView = UIVisualEffectView(effect: nil)
|
||||||
|
|
||||||
@ -83,16 +89,18 @@ class PausePresentationController: UIPresentationController
|
|||||||
self.contentView.alpha = 0.0
|
self.contentView.alpha = 0.0
|
||||||
self.vibrancyView.contentView.addSubview(self.contentView)
|
self.vibrancyView.contentView.addSubview(self.contentView)
|
||||||
|
|
||||||
self.presentingViewController.transitionCoordinator?.animate(alongsideTransition: { context in
|
self.presentationAnimator.addAnimations {
|
||||||
|
|
||||||
let blurEffect = UIBlurEffect(style: .dark)
|
let blurEffect = UIBlurEffect(style: .dark)
|
||||||
|
|
||||||
self.blurringView.effect = blurEffect
|
self.blurringView.effect = blurEffect
|
||||||
self.vibrancyView.effect = UIVibrancyEffect(blurEffect: blurEffect)
|
self.vibrancyView.effect = UIVibrancyEffect(blurEffect: blurEffect)
|
||||||
|
|
||||||
self.contentView.alpha = 1.0
|
self.contentView.alpha = 1.0
|
||||||
|
}
|
||||||
|
|
||||||
}, completion: nil)
|
// I have absolutely no clue why animating with transition coordinator results in no animation on iOS 11.
|
||||||
|
// Spent far too long trying to fix it, so just use the presentation animator.
|
||||||
|
// self.presentingViewController.transitionCoordinator?.animate(alongsideTransition: { context in }, completion: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func dismissalTransitionWillBegin()
|
override func dismissalTransitionWillBegin()
|
||||||
|
|||||||
@ -12,7 +12,7 @@ class SaveStatesCollectionHeaderView: UICollectionReusableView
|
|||||||
{
|
{
|
||||||
let textLabel = UILabel()
|
let textLabel = UILabel()
|
||||||
|
|
||||||
fileprivate let vibrancyView = UIVisualEffectView(effect: UIVibrancyEffect(blurEffect: UIBlurEffect(style: .dark)))
|
private let vibrancyView = UIVisualEffectView(effect: UIVibrancyEffect(blurEffect: UIBlurEffect(style: .dark)))
|
||||||
|
|
||||||
var isTextLabelVibrancyEnabled = true {
|
var isTextLabelVibrancyEnabled = true {
|
||||||
didSet {
|
didSet {
|
||||||
|
|||||||
@ -29,6 +29,7 @@ extension SaveStatesViewController
|
|||||||
enum Section: Int
|
enum Section: Int
|
||||||
{
|
{
|
||||||
case auto
|
case auto
|
||||||
|
case quick
|
||||||
case general
|
case general
|
||||||
case locked
|
case locked
|
||||||
}
|
}
|
||||||
@ -58,29 +59,30 @@ class SaveStatesViewController: UICollectionViewController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate var vibrancyView: UIVisualEffectView!
|
private var vibrancyView: UIVisualEffectView!
|
||||||
fileprivate var placeholderView: RSTPlaceholderView!
|
private var placeholderView: RSTPlaceholderView!
|
||||||
|
|
||||||
fileprivate var prototypeCell = GridCollectionViewCell()
|
private var prototypeCell = GridCollectionViewCell()
|
||||||
fileprivate var prototypeCellWidthConstraint: NSLayoutConstraint!
|
private var prototypeCellWidthConstraint: NSLayoutConstraint!
|
||||||
fileprivate var prototypeHeader = SaveStatesCollectionHeaderView()
|
private var prototypeHeader = SaveStatesCollectionHeaderView()
|
||||||
|
|
||||||
fileprivate let dataSource = RSTFetchedResultsCollectionViewDataSource<SaveState>(fetchedResultsController: NSFetchedResultsController())
|
private let dataSource: RSTFetchedResultsCollectionViewPrefetchingDataSource<SaveState, UIImage>
|
||||||
|
|
||||||
fileprivate let imageOperationQueue = RSTOperationQueue()
|
private var emulatorCoreSaveState: SaveStateProtocol?
|
||||||
fileprivate let imageCache = NSCache<NSURL, UIImage>()
|
|
||||||
|
|
||||||
fileprivate var emulatorCoreSaveState: SaveStateProtocol?
|
private let dateFormatter: DateFormatter
|
||||||
|
|
||||||
fileprivate let dateFormatter: DateFormatter
|
|
||||||
|
|
||||||
required init?(coder aDecoder: NSCoder)
|
required init?(coder aDecoder: NSCoder)
|
||||||
{
|
{
|
||||||
|
self.dataSource = RSTFetchedResultsCollectionViewPrefetchingDataSource<SaveState, UIImage>(fetchedResultsController: NSFetchedResultsController())
|
||||||
|
|
||||||
self.dateFormatter = DateFormatter()
|
self.dateFormatter = DateFormatter()
|
||||||
self.dateFormatter.timeStyle = .short
|
self.dateFormatter.timeStyle = .short
|
||||||
self.dateFormatter.dateStyle = .short
|
self.dateFormatter.dateStyle = .short
|
||||||
|
|
||||||
super.init(coder: aDecoder)
|
super.init(coder: aDecoder)
|
||||||
|
|
||||||
|
self.prepareDataSource()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,21 +92,8 @@ extension SaveStatesViewController
|
|||||||
{
|
{
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
self.vibrancyView = UIVisualEffectView(effect: nil)
|
|
||||||
|
|
||||||
self.placeholderView = RSTPlaceholderView(frame: CGRect(x: 0, y: 0, width: self.vibrancyView.bounds.width, height: self.vibrancyView.bounds.height))
|
|
||||||
self.placeholderView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
|
||||||
self.placeholderView.textLabel.text = NSLocalizedString("No Save States", comment: "")
|
|
||||||
self.placeholderView.textLabel.textColor = UIColor.white
|
|
||||||
self.placeholderView.detailTextLabel.textColor = UIColor.white
|
|
||||||
self.vibrancyView.contentView.addSubview(self.placeholderView)
|
|
||||||
|
|
||||||
self.dataSource.proxy = self
|
|
||||||
self.dataSource.placeholderView = self.vibrancyView
|
|
||||||
self.dataSource.cellConfigurationHandler = { [unowned self] (cell, item, indexPath) in
|
|
||||||
self.configure(cell as! GridCollectionViewCell, for: indexPath)
|
|
||||||
}
|
|
||||||
self.collectionView?.dataSource = self.dataSource
|
self.collectionView?.dataSource = self.dataSource
|
||||||
|
self.collectionView?.prefetchDataSource = self.dataSource
|
||||||
|
|
||||||
let collectionViewLayout = self.collectionViewLayout as! GridCollectionViewLayout
|
let collectionViewLayout = self.collectionViewLayout as! GridCollectionViewLayout
|
||||||
let averageHorizontalInset = (collectionViewLayout.sectionInset.left + collectionViewLayout.sectionInset.right) / 2
|
let averageHorizontalInset = (collectionViewLayout.sectionInset.left + collectionViewLayout.sectionInset.right) / 2
|
||||||
@ -118,11 +107,11 @@ extension SaveStatesViewController
|
|||||||
{
|
{
|
||||||
case .saving:
|
case .saving:
|
||||||
self.title = NSLocalizedString("Save State", comment: "")
|
self.title = NSLocalizedString("Save State", comment: "")
|
||||||
placeholderView.detailTextLabel.text = NSLocalizedString("You can create a new save state by pressing the + button in the top right.", comment: "")
|
self.placeholderView.detailTextLabel.text = NSLocalizedString("You can create a new save state by pressing the + button in the top right.", comment: "")
|
||||||
|
|
||||||
case .loading:
|
case .loading:
|
||||||
self.title = NSLocalizedString("Load State", comment: "")
|
self.title = NSLocalizedString("Load State", comment: "")
|
||||||
placeholderView.detailTextLabel.text = NSLocalizedString("You can create a new save state by pressing the Save State option in the pause menu.", comment: "")
|
self.placeholderView.detailTextLabel.text = NSLocalizedString("You can create a new save state by pressing the Save State option in the pause menu.", comment: "")
|
||||||
self.navigationItem.rightBarButtonItem = nil
|
self.navigationItem.rightBarButtonItem = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,8 +147,55 @@ extension SaveStatesViewController
|
|||||||
|
|
||||||
private extension SaveStatesViewController
|
private extension SaveStatesViewController
|
||||||
{
|
{
|
||||||
//MARK: - Update -
|
func prepareDataSource()
|
||||||
|
{
|
||||||
|
self.dataSource.proxy = self
|
||||||
|
|
||||||
|
self.vibrancyView = UIVisualEffectView(effect: nil)
|
||||||
|
|
||||||
|
self.placeholderView = RSTPlaceholderView(frame: CGRect(x: 0, y: 0, width: self.vibrancyView.bounds.width, height: self.vibrancyView.bounds.height))
|
||||||
|
self.placeholderView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||||
|
self.placeholderView.textLabel.text = NSLocalizedString("No Save States", comment: "")
|
||||||
|
self.placeholderView.textLabel.textColor = UIColor.white
|
||||||
|
self.placeholderView.detailTextLabel.textColor = UIColor.white
|
||||||
|
self.vibrancyView.contentView.addSubview(self.placeholderView)
|
||||||
|
|
||||||
|
self.dataSource.placeholderView = self.vibrancyView
|
||||||
|
|
||||||
|
self.dataSource.cellConfigurationHandler = { [unowned self] (cell, item, indexPath) in
|
||||||
|
self.configure(cell as! GridCollectionViewCell, for: indexPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.dataSource.prefetchHandler = { [unowned self] (saveState, indexPath, completionHandler) in
|
||||||
|
let imageOperation = LoadImageURLOperation(url: saveState.imageFileURL)
|
||||||
|
imageOperation.resultHandler = { (image, error) in
|
||||||
|
completionHandler(image, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.isAppearing
|
||||||
|
{
|
||||||
|
imageOperation.start()
|
||||||
|
imageOperation.waitUntilFinished()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return imageOperation
|
||||||
|
}
|
||||||
|
|
||||||
|
self.dataSource.prefetchCompletionHandler = { (cell, image, indexPath, error) in
|
||||||
|
guard let image = image, let cell = cell as? GridCollectionViewCell else { return }
|
||||||
|
|
||||||
|
cell.imageView.backgroundColor = nil
|
||||||
|
cell.imageView.image = image
|
||||||
|
|
||||||
|
cell.isImageViewVibrancyEnabled = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension SaveStatesViewController
|
||||||
|
{
|
||||||
|
//MARK: - Update -
|
||||||
func updateDataSource()
|
func updateDataSource()
|
||||||
{
|
{
|
||||||
let fetchRequest: NSFetchRequest<SaveState> = SaveState.fetchRequest()
|
let fetchRequest: NSFetchRequest<SaveState> = SaveState.fetchRequest()
|
||||||
@ -194,7 +230,7 @@ private extension SaveStatesViewController
|
|||||||
|
|
||||||
//MARK: - Configure Views -
|
//MARK: - Configure Views -
|
||||||
|
|
||||||
func configure(_ cell: GridCollectionViewCell, for indexPath: IndexPath, ignoreExpensiveOperations ignoreOperations: Bool = false)
|
func configure(_ cell: GridCollectionViewCell, for indexPath: IndexPath)
|
||||||
{
|
{
|
||||||
let saveState = self.dataSource.item(at: indexPath)
|
let saveState = self.dataSource.item(at: indexPath)
|
||||||
|
|
||||||
@ -213,35 +249,9 @@ private extension SaveStatesViewController
|
|||||||
cell.isImageViewVibrancyEnabled = true
|
cell.isImageViewVibrancyEnabled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ignoreOperations
|
|
||||||
{
|
|
||||||
let imageOperation = LoadImageURLOperation(url: saveState.imageFileURL)
|
|
||||||
imageOperation.resultsCache = self.imageCache
|
|
||||||
imageOperation.resultHandler = { (image, error) in
|
|
||||||
|
|
||||||
if let image = image
|
|
||||||
{
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
cell.imageView.backgroundColor = nil
|
|
||||||
cell.imageView.image = image
|
|
||||||
|
|
||||||
cell.isImageViewVibrancyEnabled = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure initially visible cells have loaded their image before they appear to prevent potential flickering from placeholder to thumbnail
|
|
||||||
if self.isAppearing
|
|
||||||
{
|
|
||||||
imageOperation.isImmediate = true
|
|
||||||
}
|
|
||||||
|
|
||||||
self.imageOperationQueue.addOperation(imageOperation, forKey: indexPath as NSCopying)
|
|
||||||
}
|
|
||||||
|
|
||||||
let deltaCore = Delta.core(for: self.game.type)!
|
let deltaCore = Delta.core(for: self.game.type)!
|
||||||
|
|
||||||
let dimensions = deltaCore.emulatorConfiguration.videoBufferInfo.outputDimensions
|
let dimensions = deltaCore.videoFormat.dimensions
|
||||||
cell.maximumImageSize = CGSize(width: self.prototypeCellWidthConstraint.constant, height: (self.prototypeCellWidthConstraint.constant / dimensions.width) * dimensions.height)
|
cell.maximumImageSize = CGSize(width: self.prototypeCellWidthConstraint.constant, height: (self.prototypeCellWidthConstraint.constant / dimensions.width) * dimensions.height)
|
||||||
|
|
||||||
cell.textLabel.font = UIFont.preferredFont(forTextStyle: .subheadline)
|
cell.textLabel.font = UIFont.preferredFont(forTextStyle: .subheadline)
|
||||||
@ -259,6 +269,7 @@ private extension SaveStatesViewController
|
|||||||
switch section
|
switch section
|
||||||
{
|
{
|
||||||
case .auto: title = NSLocalizedString("Auto Save", comment: "")
|
case .auto: title = NSLocalizedString("Auto Save", comment: "")
|
||||||
|
case .quick: title = NSLocalizedString("Quick Save", comment: "")
|
||||||
case .general: title = NSLocalizedString("General", comment: "")
|
case .general: title = NSLocalizedString("General", comment: "")
|
||||||
case .locked: title = NSLocalizedString("Locked", comment: "")
|
case .locked: title = NSLocalizedString("Locked", comment: "")
|
||||||
}
|
}
|
||||||
@ -301,6 +312,7 @@ private extension SaveStatesViewController
|
|||||||
let game = backgroundContext.object(with: self.game.objectID) as! Game
|
let game = backgroundContext.object(with: self.game.objectID) as! Game
|
||||||
|
|
||||||
saveState = SaveState.insertIntoManagedObjectContext(backgroundContext)
|
saveState = SaveState.insertIntoManagedObjectContext(backgroundContext)
|
||||||
|
saveState.type = .general
|
||||||
saveState.game = game
|
saveState.game = game
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -365,7 +377,7 @@ private extension SaveStatesViewController
|
|||||||
func rename(_ saveState: SaveState, with name: String?)
|
func rename(_ saveState: SaveState, with name: String?)
|
||||||
{
|
{
|
||||||
var name = name
|
var name = name
|
||||||
if (name ?? "").characters.count == 0
|
if (name ?? "").count == 0
|
||||||
{
|
{
|
||||||
// When text is nil, we know to show the timestamp instead
|
// When text is nil, we know to show the timestamp instead
|
||||||
name = nil
|
name = nil
|
||||||
@ -471,6 +483,7 @@ private extension SaveStatesViewController
|
|||||||
switch saveState.type
|
switch saveState.type
|
||||||
{
|
{
|
||||||
case .auto: break
|
case .auto: break
|
||||||
|
case .quick: break
|
||||||
case .general:
|
case .general:
|
||||||
let lockAction = Action(title: NSLocalizedString("Lock", comment: ""), style: .default, action: { [unowned self] action in
|
let lockAction = Action(title: NSLocalizedString("Lock", comment: ""), style: .default, action: { [unowned self] action in
|
||||||
self.lockSaveState(saveState)
|
self.lockSaveState(saveState)
|
||||||
@ -549,13 +562,13 @@ private extension SaveStatesViewController
|
|||||||
//MARK: - 3D Touch -
|
//MARK: - 3D Touch -
|
||||||
extension SaveStatesViewController: UIViewControllerPreviewingDelegate
|
extension SaveStatesViewController: UIViewControllerPreviewingDelegate
|
||||||
{
|
{
|
||||||
fileprivate func prepareEmulatorCoreSaveState()
|
private func prepareEmulatorCoreSaveState()
|
||||||
{
|
{
|
||||||
guard let emulatorCore = self.emulatorCore else { return }
|
guard let emulatorCore = self.emulatorCore else { return }
|
||||||
|
|
||||||
// Store reference to current game state before we stop emulation so we can resume it if user decides to not load a save state
|
// Store reference to current game state before we stop emulation so we can resume it if user decides to not load a save state
|
||||||
|
|
||||||
let fileURL = FileManager.uniqueTemporaryURL()
|
let fileURL = FileManager.default.uniqueTemporaryURL()
|
||||||
self.emulatorCoreSaveState = emulatorCore.saveSaveState(to: fileURL)
|
self.emulatorCoreSaveState = emulatorCore.saveSaveState(to: fileURL)
|
||||||
|
|
||||||
if self.emulatorCoreSaveState != nil
|
if self.emulatorCoreSaveState != nil
|
||||||
@ -577,7 +590,7 @@ extension SaveStatesViewController: UIViewControllerPreviewingDelegate
|
|||||||
|
|
||||||
let saveState = self.dataSource.item(at: indexPath)
|
let saveState = self.dataSource.item(at: indexPath)
|
||||||
let actions = self.actionsForSaveState(saveState)?.previewActions ?? []
|
let actions = self.actionsForSaveState(saveState)?.previewActions ?? []
|
||||||
let previewImage = self.imageCache.object(forKey: saveState.imageFileURL as NSURL) ?? UIImage(contentsOfFile: saveState.imageFileURL.path)
|
let previewImage = self.dataSource.prefetchItemCache.object(forKey: saveState) ?? UIImage(contentsOfFile: saveState.imageFileURL.path)
|
||||||
|
|
||||||
let previewGameViewController = PreviewGameViewController()
|
let previewGameViewController = PreviewGameViewController()
|
||||||
previewGameViewController.game = self.game
|
previewGameViewController.game = self.game
|
||||||
@ -593,7 +606,7 @@ extension SaveStatesViewController: UIViewControllerPreviewingDelegate
|
|||||||
let gameViewController = viewControllerToCommit as! PreviewGameViewController
|
let gameViewController = viewControllerToCommit as! PreviewGameViewController
|
||||||
gameViewController.emulatorCore?.pause()
|
gameViewController.emulatorCore?.pause()
|
||||||
|
|
||||||
let fileURL = FileManager.uniqueTemporaryURL()
|
let fileURL = FileManager.default.uniqueTemporaryURL()
|
||||||
if let saveState = gameViewController.emulatorCore?.saveSaveState(to: fileURL)
|
if let saveState = gameViewController.emulatorCore?.saveSaveState(to: fileURL)
|
||||||
{
|
{
|
||||||
gameViewController.emulatorCore?.stop()
|
gameViewController.emulatorCore?.stop()
|
||||||
@ -634,11 +647,11 @@ extension SaveStatesViewController
|
|||||||
{
|
{
|
||||||
case .saving:
|
case .saving:
|
||||||
|
|
||||||
let section = self.correctedSectionForSectionIndex((indexPath as NSIndexPath).section)
|
let section = self.correctedSectionForSectionIndex(indexPath.section)
|
||||||
switch section
|
switch section
|
||||||
{
|
{
|
||||||
case .auto: break
|
case .auto: break
|
||||||
case .general:
|
case .quick, .general:
|
||||||
let backgroundContext = DatabaseManager.shared.newBackgroundContext()
|
let backgroundContext = DatabaseManager.shared.newBackgroundContext()
|
||||||
backgroundContext.performAndWait() {
|
backgroundContext.performAndWait() {
|
||||||
let temporarySaveState = backgroundContext.object(with: saveState.objectID) as! SaveState
|
let temporarySaveState = backgroundContext.object(with: saveState.objectID) as! SaveState
|
||||||
@ -655,12 +668,6 @@ extension SaveStatesViewController
|
|||||||
case .loading: self.loadSaveState(saveState)
|
case .loading: self.loadSaveState(saveState)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath)
|
|
||||||
{
|
|
||||||
let operation = self.imageOperationQueue[indexPath as NSCopying]
|
|
||||||
operation?.cancel()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//MARK: - <UICollectionViewDelegateFlowLayout> -
|
//MARK: - <UICollectionViewDelegateFlowLayout> -
|
||||||
@ -668,8 +675,7 @@ extension SaveStatesViewController: UICollectionViewDelegateFlowLayout
|
|||||||
{
|
{
|
||||||
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
|
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
|
||||||
{
|
{
|
||||||
// No need to load images from disk just to determine size, so we pass true for ignoreExpensiveOperations
|
self.configure(self.prototypeCell, for: indexPath)
|
||||||
self.configure(self.prototypeCell, for: indexPath, ignoreExpensiveOperations: true)
|
|
||||||
|
|
||||||
let size = self.prototypeCell.contentView.systemLayoutSizeFitting(UILayoutFittingCompressedSize)
|
let size = self.prototypeCell.contentView.systemLayoutSizeFitting(UILayoutFittingCompressedSize)
|
||||||
return size
|
return size
|
||||||
|
|||||||
@ -10,15 +10,15 @@ import UIKit
|
|||||||
|
|
||||||
class PauseStoryboardSegue: UIStoryboardSegue
|
class PauseStoryboardSegue: UIStoryboardSegue
|
||||||
{
|
{
|
||||||
fileprivate let animator: UIViewPropertyAnimator
|
private let animator: UIViewPropertyAnimator
|
||||||
fileprivate let presentationController: PausePresentationController
|
private let presentationController: PausePresentationController
|
||||||
|
|
||||||
override init(identifier: String?, source: UIViewController, destination: UIViewController)
|
override init(identifier: String?, source: UIViewController, destination: UIViewController)
|
||||||
{
|
{
|
||||||
let timingParameters = UISpringTimingParameters(mass: 3.0, stiffness: 750, damping: 65, initialVelocity: CGVector(dx: 0, dy: 0))
|
let timingParameters = UISpringTimingParameters(mass: 3.0, stiffness: 750, damping: 65, initialVelocity: CGVector(dx: 0, dy: 0))
|
||||||
self.animator = UIViewPropertyAnimator(duration: 0, timingParameters: timingParameters)
|
self.animator = UIViewPropertyAnimator(duration: 0, timingParameters: timingParameters)
|
||||||
|
|
||||||
self.presentationController = PausePresentationController(presentedViewController: destination, presenting: source)
|
self.presentationController = PausePresentationController(presentedViewController: destination, presenting: source, presentationAnimator: self.animator)
|
||||||
|
|
||||||
super.init(identifier: identifier, source: source, destination: destination)
|
super.init(identifier: identifier, source: source, destination: destination)
|
||||||
}
|
}
|
||||||
@ -29,6 +29,9 @@ class PauseStoryboardSegue: UIStoryboardSegue
|
|||||||
self.destination.modalPresentationStyle = .custom
|
self.destination.modalPresentationStyle = .custom
|
||||||
self.destination.modalPresentationCapturesStatusBarAppearance = true
|
self.destination.modalPresentationCapturesStatusBarAppearance = true
|
||||||
|
|
||||||
|
// Manually set tint color, since calling layoutIfNeeded will cause view to load, but with default system tint color.
|
||||||
|
self.destination.view.tintColor = .white
|
||||||
|
|
||||||
// We need to force layout of destinationViewController.view _before_ animateTransition(using:)
|
// We need to force layout of destinationViewController.view _before_ animateTransition(using:)
|
||||||
// Otherwise, we'll get "Unable to simultaneously satisfy constraints" errors
|
// Otherwise, we'll get "Unable to simultaneously satisfy constraints" errors
|
||||||
self.destination.view.frame = self.source.view.frame
|
self.destination.view.frame = self.source.view.frame
|
||||||
|
|||||||
@ -37,6 +37,13 @@ class PauseTransitionCoordinator: NSObject, UIViewControllerAnimatedTransitionin
|
|||||||
|
|
||||||
destinationViewController.view.layoutIfNeeded()
|
destinationViewController.view.layoutIfNeeded()
|
||||||
|
|
||||||
|
if let navigationController = destinationViewController.navigationController
|
||||||
|
{
|
||||||
|
// Layout before animation to prevent strange bar button item layout during animation.
|
||||||
|
navigationController.view.setNeedsLayout()
|
||||||
|
navigationController.view.layoutIfNeeded()
|
||||||
|
}
|
||||||
|
|
||||||
UIView.animate(withDuration: self.transitionDuration(using: transitionContext), delay:0, options:RSTSystemTransitionAnimationCurve, animations: {
|
UIView.animate(withDuration: self.transitionDuration(using: transitionContext), delay:0, options:RSTSystemTransitionAnimationCurve, animations: {
|
||||||
|
|
||||||
sourceViewController.view.frame.origin.y = self.presenting ? -sourceViewController.view.bounds.height : transitionContext.containerView.bounds.height
|
sourceViewController.view.frame.origin.y = self.presenting ? -sourceViewController.view.bounds.height : transitionContext.containerView.bounds.height
|
||||||
|
|||||||
@ -12,18 +12,9 @@ import DeltaCore
|
|||||||
|
|
||||||
import Roxas
|
import Roxas
|
||||||
|
|
||||||
extension ControllerSkinsViewController
|
|
||||||
{
|
|
||||||
enum Section: Int
|
|
||||||
{
|
|
||||||
case standard
|
|
||||||
case custom
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ControllerSkinsViewController: UITableViewController
|
class ControllerSkinsViewController: UITableViewController
|
||||||
{
|
{
|
||||||
var gameType: GameType! {
|
var system: System! {
|
||||||
didSet {
|
didSet {
|
||||||
self.updateDataSource()
|
self.updateDataSource()
|
||||||
}
|
}
|
||||||
@ -35,11 +26,16 @@ class ControllerSkinsViewController: UITableViewController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate let dataSource = RSTFetchedResultsTableViewDataSource<ControllerSkin>(fetchedResultsController: NSFetchedResultsController())
|
private let dataSource: RSTFetchedResultsTableViewPrefetchingDataSource<ControllerSkin, UIImage>
|
||||||
|
|
||||||
fileprivate let imageOperationQueue = RSTOperationQueue()
|
required init?(coder aDecoder: NSCoder)
|
||||||
|
{
|
||||||
|
self.dataSource = RSTFetchedResultsTableViewPrefetchingDataSource<ControllerSkin, UIImage>(fetchedResultsController: NSFetchedResultsController())
|
||||||
|
|
||||||
fileprivate let imageCache = NSCache<ControllerSkinImageCacheKey, UIImage>()
|
super.init(coder: aDecoder)
|
||||||
|
|
||||||
|
self.prepareDataSource()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ControllerSkinsViewController
|
extension ControllerSkinsViewController
|
||||||
@ -48,11 +44,8 @@ extension ControllerSkinsViewController
|
|||||||
{
|
{
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
self.dataSource.proxy = self
|
|
||||||
self.dataSource.cellConfigurationHandler = { [unowned self] (cell, item, indexPath) in
|
|
||||||
self.configure(cell as! ControllerSkinTableViewCell, for: indexPath)
|
|
||||||
}
|
|
||||||
self.tableView.dataSource = self.dataSource
|
self.tableView.dataSource = self.dataSource
|
||||||
|
self.tableView.prefetchDataSource = self.dataSource
|
||||||
}
|
}
|
||||||
|
|
||||||
override func didReceiveMemoryWarning()
|
override func didReceiveMemoryWarning()
|
||||||
@ -65,56 +58,52 @@ extension ControllerSkinsViewController
|
|||||||
private extension ControllerSkinsViewController
|
private extension ControllerSkinsViewController
|
||||||
{
|
{
|
||||||
//MARK: - Update
|
//MARK: - Update
|
||||||
func updateDataSource()
|
func prepareDataSource()
|
||||||
{
|
{
|
||||||
guard let gameType = self.gameType, let traits = self.traits else { return }
|
self.dataSource.proxy = self
|
||||||
|
self.dataSource.cellConfigurationHandler = { (cell, item, indexPath) in
|
||||||
let configuration = ControllerSkinConfigurations(traits: traits)
|
let cell = cell as! ControllerSkinTableViewCell
|
||||||
|
|
||||||
let fetchRequest: NSFetchRequest<ControllerSkin> = ControllerSkin.fetchRequest()
|
|
||||||
fetchRequest.predicate = NSPredicate(format: "%K == %@ AND (%K & %d) == %d", #keyPath(ControllerSkin.gameType), gameType.rawValue, #keyPath(ControllerSkin.supportedConfigurations), configuration.rawValue, configuration.rawValue)
|
|
||||||
fetchRequest.sortDescriptors = [NSSortDescriptor(key: #keyPath(ControllerSkin.isStandard), ascending: false), NSSortDescriptor(key: #keyPath(ControllerSkin.name), ascending: true)]
|
|
||||||
|
|
||||||
self.dataSource.fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: DatabaseManager.shared.viewContext, sectionNameKeyPath: #keyPath(ControllerSkin.name), cacheName: nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
//MARK: - Configure Cells
|
|
||||||
func configure(_ cell: ControllerSkinTableViewCell, for indexPath: IndexPath)
|
|
||||||
{
|
|
||||||
let controllerSkin = self.dataSource.item(at: indexPath)
|
|
||||||
|
|
||||||
cell.controllerSkinImageView.image = nil
|
cell.controllerSkinImageView.image = nil
|
||||||
cell.activityIndicatorView.startAnimating()
|
cell.activityIndicatorView.startAnimating()
|
||||||
|
}
|
||||||
|
|
||||||
let size = UIScreen.main.defaultControllerSkinSize
|
self.dataSource.prefetchHandler = { [unowned self] (controllerSkin, indexPath, completionHandler) in
|
||||||
|
let imageOperation = LoadControllerSkinImageOperation(controllerSkin: controllerSkin, traits: self.traits, size: UIScreen.main.defaultControllerSkinSize)
|
||||||
let imageOperation = LoadControllerSkinImageOperation(controllerSkin: controllerSkin, traits: self.traits, size: size)
|
|
||||||
imageOperation.resultsCache = self.imageCache
|
|
||||||
imageOperation.resultHandler = { (image, error) in
|
imageOperation.resultHandler = { (image, error) in
|
||||||
|
completionHandler(image, error)
|
||||||
guard let image = image else { return }
|
|
||||||
|
|
||||||
if !imageOperation.isImmediate
|
|
||||||
{
|
|
||||||
UIView.transition(with: cell.controllerSkinImageView, duration: 0.2, options: .transitionCrossDissolve, animations: {
|
|
||||||
cell.controllerSkinImageView.image = image
|
|
||||||
}, completion: nil)
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cell.controllerSkinImageView.image = image
|
|
||||||
}
|
|
||||||
|
|
||||||
cell.activityIndicatorView.stopAnimating()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure initially visible cells have loaded their image before they appear to prevent potential flickering from placeholder to thumbnail
|
// Ensure initially visible cells have loaded their image before they appear to prevent potential flickering from placeholder to thumbnail
|
||||||
if self.isAppearing
|
if self.isAppearing
|
||||||
{
|
{
|
||||||
imageOperation.isImmediate = true
|
imageOperation.start()
|
||||||
|
imageOperation.waitUntilFinished()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
self.imageOperationQueue.addOperation(imageOperation, forKey: indexPath as NSCopying)
|
return imageOperation
|
||||||
|
}
|
||||||
|
|
||||||
|
self.dataSource.prefetchCompletionHandler = { (cell, image, indexPath, error) in
|
||||||
|
guard let image = image, let cell = cell as? ControllerSkinTableViewCell else { return }
|
||||||
|
|
||||||
|
cell.controllerSkinImageView.image = image
|
||||||
|
cell.activityIndicatorView.stopAnimating()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateDataSource()
|
||||||
|
{
|
||||||
|
guard let system = self.system, let traits = self.traits else { return }
|
||||||
|
|
||||||
|
let configuration = ControllerSkinConfigurations(traits: traits)
|
||||||
|
|
||||||
|
let fetchRequest: NSFetchRequest<ControllerSkin> = ControllerSkin.fetchRequest()
|
||||||
|
fetchRequest.predicate = NSPredicate(format: "%K == %@ AND (%K & %d) == %d", #keyPath(ControllerSkin.gameType), system.gameType.rawValue, #keyPath(ControllerSkin.supportedConfigurations), configuration.rawValue, configuration.rawValue)
|
||||||
|
fetchRequest.sortDescriptors = [NSSortDescriptor(key: #keyPath(ControllerSkin.isStandard), ascending: false), NSSortDescriptor(key: #keyPath(ControllerSkin.name), ascending: true)]
|
||||||
|
|
||||||
|
self.dataSource.fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: DatabaseManager.shared.viewContext, sectionNameKeyPath: #keyPath(ControllerSkin.name), cacheName: nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,39 +116,12 @@ extension ControllerSkinsViewController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ControllerSkinsViewController: UITableViewDataSourcePrefetching
|
|
||||||
{
|
|
||||||
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath])
|
|
||||||
{
|
|
||||||
for indexPath in indexPaths
|
|
||||||
{
|
|
||||||
let controllerSkin = self.dataSource.item(at: indexPath)
|
|
||||||
|
|
||||||
let size = UIScreen.main.defaultControllerSkinSize
|
|
||||||
|
|
||||||
let imageOperation = LoadControllerSkinImageOperation(controllerSkin: controllerSkin, traits: self.traits, size: size)
|
|
||||||
imageOperation.resultsCache = self.imageCache
|
|
||||||
|
|
||||||
self.imageOperationQueue.addOperation(imageOperation, forKey: indexPath as NSCopying)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath])
|
|
||||||
{
|
|
||||||
for indexPath in indexPaths
|
|
||||||
{
|
|
||||||
let operation = self.imageOperationQueue[indexPath as NSCopying]
|
|
||||||
operation?.cancel()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ControllerSkinsViewController
|
extension ControllerSkinsViewController
|
||||||
{
|
{
|
||||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
|
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
|
||||||
{
|
{
|
||||||
let controllerSkin = self.dataSource.item(at: indexPath)
|
let controllerSkin = self.dataSource.item(at: indexPath)
|
||||||
Settings.setPreferredControllerSkin(controllerSkin, for: self.gameType, traits: self.traits)
|
Settings.setPreferredControllerSkin(controllerSkin, for: self.system, traits: self.traits)
|
||||||
|
|
||||||
_ = self.navigationController?.popViewController(animated: true)
|
_ = self.navigationController?.popViewController(animated: true)
|
||||||
}
|
}
|
||||||
@ -176,10 +138,4 @@ extension ControllerSkinsViewController
|
|||||||
|
|
||||||
return height
|
return height
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath)
|
|
||||||
{
|
|
||||||
let operation = self.imageOperationQueue[indexPath as NSCopying]
|
|
||||||
operation?.cancel()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// GameTypeControllerSkinsViewController.swift
|
// SystemControllerSkinsViewController.swift
|
||||||
// Delta
|
// Delta
|
||||||
//
|
//
|
||||||
// Created by Riley Testut on 9/30/16.
|
// Created by Riley Testut on 9/30/16.
|
||||||
@ -10,30 +10,30 @@ import UIKit
|
|||||||
|
|
||||||
import DeltaCore
|
import DeltaCore
|
||||||
|
|
||||||
extension GameTypeControllerSkinsViewController
|
extension SystemControllerSkinsViewController
|
||||||
{
|
{
|
||||||
fileprivate enum Section: Int
|
private enum Section: Int
|
||||||
{
|
{
|
||||||
case portrait
|
case portrait
|
||||||
case landscape
|
case landscape
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GameTypeControllerSkinsViewController: UITableViewController
|
class SystemControllerSkinsViewController: UITableViewController
|
||||||
{
|
{
|
||||||
var gameType: GameType!
|
var system: System!
|
||||||
|
|
||||||
@IBOutlet fileprivate var portraitImageView: UIImageView!
|
@IBOutlet private var portraitImageView: UIImageView!
|
||||||
@IBOutlet fileprivate var landscapeImageView: UIImageView!
|
@IBOutlet private var landscapeImageView: UIImageView!
|
||||||
}
|
}
|
||||||
|
|
||||||
extension GameTypeControllerSkinsViewController
|
extension SystemControllerSkinsViewController
|
||||||
{
|
{
|
||||||
override func viewDidLoad()
|
override func viewDidLoad()
|
||||||
{
|
{
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
self.title = self.gameType.localizedShortName
|
self.title = self.system.localizedShortName
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewWillAppear(_ animated: Bool)
|
override func viewWillAppear(_ animated: Bool)
|
||||||
@ -53,7 +53,7 @@ extension GameTypeControllerSkinsViewController
|
|||||||
guard let cell = sender as? UITableViewCell, let indexPath = self.tableView.indexPath(for: cell) else { return }
|
guard let cell = sender as? UITableViewCell, let indexPath = self.tableView.indexPath(for: cell) else { return }
|
||||||
|
|
||||||
let controllerSkinsViewController = segue.destination as! ControllerSkinsViewController
|
let controllerSkinsViewController = segue.destination as! ControllerSkinsViewController
|
||||||
controllerSkinsViewController.gameType = self.gameType
|
controllerSkinsViewController.system = self.system
|
||||||
|
|
||||||
var traits = DeltaCore.ControllerSkin.Traits.defaults(for: self.view)
|
var traits = DeltaCore.ControllerSkin.Traits.defaults(for: self.view)
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ extension GameTypeControllerSkinsViewController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension GameTypeControllerSkinsViewController
|
extension SystemControllerSkinsViewController
|
||||||
{
|
{
|
||||||
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
|
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
|
||||||
{
|
{
|
||||||
@ -98,15 +98,15 @@ extension GameTypeControllerSkinsViewController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension GameTypeControllerSkinsViewController
|
private extension SystemControllerSkinsViewController
|
||||||
{
|
{
|
||||||
func updateControllerSkins()
|
func updateControllerSkins()
|
||||||
{
|
{
|
||||||
let portraitTraits = DeltaCore.ControllerSkin.Traits(deviceType: .iphone, displayMode: DeltaCore.ControllerSkin.DisplayMode.fullScreen, orientation: .portrait)
|
let portraitTraits = DeltaCore.ControllerSkin.Traits(deviceType: .iphone, displayMode: DeltaCore.ControllerSkin.DisplayMode.fullScreen, orientation: .portrait)
|
||||||
let landscapeTraits = DeltaCore.ControllerSkin.Traits(deviceType: .iphone, displayMode: DeltaCore.ControllerSkin.DisplayMode.fullScreen, orientation: .landscape)
|
let landscapeTraits = DeltaCore.ControllerSkin.Traits(deviceType: .iphone, displayMode: DeltaCore.ControllerSkin.DisplayMode.fullScreen, orientation: .landscape)
|
||||||
|
|
||||||
let portraitControllerSkin = Settings.preferredControllerSkin(for: self.gameType, traits: portraitTraits)
|
let portraitControllerSkin = Settings.preferredControllerSkin(for: self.system, traits: portraitTraits)
|
||||||
let landscapeControllerSkin = Settings.preferredControllerSkin(for: self.gameType, traits: landscapeTraits)
|
let landscapeControllerSkin = Settings.preferredControllerSkin(for: self.system, traits: landscapeTraits)
|
||||||
|
|
||||||
self.portraitImageView.image = portraitControllerSkin?.image(for: portraitTraits, preferredSize: UIScreen.main.defaultControllerSkinSize)
|
self.portraitImageView.image = portraitControllerSkin?.image(for: portraitTraits, preferredSize: UIScreen.main.defaultControllerSkinSize)
|
||||||
self.landscapeImageView.image = landscapeControllerSkin?.image(for: landscapeTraits, preferredSize: UIScreen.main.defaultControllerSkinSize)
|
self.landscapeImageView.image = landscapeControllerSkin?.image(for: landscapeTraits, preferredSize: UIScreen.main.defaultControllerSkinSize)
|
||||||
508
Delta/Settings/Controllers/ControllerInputsViewController.swift
Normal file
508
Delta/Settings/Controllers/ControllerInputsViewController.swift
Normal file
@ -0,0 +1,508 @@
|
|||||||
|
//
|
||||||
|
// ControllerInputsViewController.swift
|
||||||
|
// Delta
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 7/1/17.
|
||||||
|
// Copyright © 2017 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import Roxas
|
||||||
|
|
||||||
|
import DeltaCore
|
||||||
|
|
||||||
|
import SMCalloutView
|
||||||
|
|
||||||
|
class ControllerInputsViewController: UIViewController
|
||||||
|
{
|
||||||
|
var gameController: GameController! {
|
||||||
|
didSet {
|
||||||
|
self.gameController.addReceiver(self, inputMapping: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var system: System = .snes {
|
||||||
|
didSet {
|
||||||
|
guard self.system != oldValue else { return }
|
||||||
|
self.updateSystem()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private lazy var managedObjectContext: NSManagedObjectContext = DatabaseManager.shared.newBackgroundContext()
|
||||||
|
private var inputMappings = [System: GameControllerInputMapping]()
|
||||||
|
|
||||||
|
private let supportedActionInputs: [ActionInput] = [.quickSave, .quickLoad, .fastForward]
|
||||||
|
|
||||||
|
private var gameViewController: DeltaCore.GameViewController!
|
||||||
|
private var actionsMenuViewController: GridMenuViewController!
|
||||||
|
|
||||||
|
private var calloutViews = [AnyInput: InputCalloutView]()
|
||||||
|
|
||||||
|
private var activeCalloutView: InputCalloutView?
|
||||||
|
|
||||||
|
@IBOutlet private var actionsMenuViewControllerHeightConstraint: NSLayoutConstraint!
|
||||||
|
@IBOutlet private var cancelTapGestureRecognizer: UITapGestureRecognizer!
|
||||||
|
|
||||||
|
override func viewDidLoad()
|
||||||
|
{
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
self.gameViewController.controllerView.addReceiver(self)
|
||||||
|
|
||||||
|
self.navigationController?.navigationBar.barStyle = .black
|
||||||
|
|
||||||
|
NSLayoutConstraint.activate([self.gameViewController.gameView.centerYAnchor.constraint(equalTo: self.actionsMenuViewController.view.centerYAnchor)])
|
||||||
|
|
||||||
|
self.preparePopoverMenuController()
|
||||||
|
self.updateSystem()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewDidLayoutSubviews()
|
||||||
|
{
|
||||||
|
super.viewDidLayoutSubviews()
|
||||||
|
|
||||||
|
if self.actionsMenuViewController.preferredContentSize.height > 0
|
||||||
|
{
|
||||||
|
self.actionsMenuViewControllerHeightConstraint.constant = self.actionsMenuViewController.preferredContentSize.height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewDidAppear(_ animated: Bool)
|
||||||
|
{
|
||||||
|
super.viewDidAppear(animated)
|
||||||
|
|
||||||
|
if self.calloutViews.isEmpty
|
||||||
|
{
|
||||||
|
self.prepareCallouts()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ControllerInputsViewController
|
||||||
|
{
|
||||||
|
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
|
||||||
|
{
|
||||||
|
guard let identifier = segue.identifier else { return }
|
||||||
|
|
||||||
|
switch identifier
|
||||||
|
{
|
||||||
|
case "embedGameViewController": self.gameViewController = segue.destination as! DeltaCore.GameViewController
|
||||||
|
case "embedActionsMenuViewController":
|
||||||
|
self.actionsMenuViewController = segue.destination as! GridMenuViewController
|
||||||
|
self.prepareActionsMenuViewController()
|
||||||
|
|
||||||
|
case "cancelControllerInputs": break
|
||||||
|
case "saveControllerInputs":
|
||||||
|
self.managedObjectContext.performAndWait {
|
||||||
|
self.managedObjectContext.saveWithErrorLogging()
|
||||||
|
}
|
||||||
|
|
||||||
|
default: break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension ControllerInputsViewController
|
||||||
|
{
|
||||||
|
func updateSystem()
|
||||||
|
{
|
||||||
|
guard self.isViewLoaded else { return }
|
||||||
|
|
||||||
|
// Update popoverMenuButton to display correctly on iOS 10.
|
||||||
|
if let popoverMenuButton = self.navigationItem.popoverMenuController?.popoverMenuButton
|
||||||
|
{
|
||||||
|
popoverMenuButton.title = self.system.localizedShortName
|
||||||
|
popoverMenuButton.bounds.size = popoverMenuButton.intrinsicContentSize
|
||||||
|
|
||||||
|
self.navigationController?.navigationBar.layoutIfNeeded()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update controller view's controller skin.
|
||||||
|
self.gameViewController.controllerView.controllerSkin = DeltaCore.ControllerSkin.standardControllerSkin(for: self.system.gameType)
|
||||||
|
|
||||||
|
// Fetch input mapping if it hasn't already been fetched.
|
||||||
|
if let gameController = self.gameController, let playerIndex = gameController.playerIndex, self.inputMappings[self.system] == nil
|
||||||
|
{
|
||||||
|
self.managedObjectContext.performAndWait {
|
||||||
|
let inputMapping = GameControllerInputMapping.inputMapping(for: gameController, gameType: self.system.gameType, in: self.managedObjectContext) ?? {
|
||||||
|
let deltaCoreInputMapping = gameController.defaultInputMapping as? DeltaCore.GameControllerInputMapping ?? DeltaCore.GameControllerInputMapping(gameControllerInputType: gameController.inputType)
|
||||||
|
|
||||||
|
let inputMapping = GameControllerInputMapping(inputMapping: deltaCoreInputMapping, context: self.managedObjectContext)
|
||||||
|
inputMapping.gameControllerInputType = gameController.inputType
|
||||||
|
inputMapping.gameType = self.system.gameType
|
||||||
|
inputMapping.playerIndex = Int16(playerIndex)
|
||||||
|
|
||||||
|
return inputMapping
|
||||||
|
}()
|
||||||
|
|
||||||
|
inputMapping.name = String.localizedStringWithFormat("Custom %@", gameController.name)
|
||||||
|
|
||||||
|
self.inputMappings[self.system] = inputMapping
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update callouts, if view is already on screen.
|
||||||
|
if self.view.window != nil
|
||||||
|
{
|
||||||
|
self.calloutViews.forEach { $1.dismissCallout(animated: true) }
|
||||||
|
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
|
||||||
|
self.calloutViews = [:]
|
||||||
|
self.prepareCallouts()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func preparePopoverMenuController()
|
||||||
|
{
|
||||||
|
let listMenuViewController = ListMenuViewController()
|
||||||
|
listMenuViewController.title = NSLocalizedString("Game System", comment: "")
|
||||||
|
|
||||||
|
let navigationController = UINavigationController(rootViewController: listMenuViewController)
|
||||||
|
|
||||||
|
let popoverMenuController = PopoverMenuController(popoverViewController: navigationController)
|
||||||
|
self.navigationItem.popoverMenuController = popoverMenuController
|
||||||
|
|
||||||
|
let items = System.supportedSystems.map { [unowned self, weak popoverMenuController, weak listMenuViewController] system -> MenuItem in
|
||||||
|
let item = MenuItem(text: system.localizedShortName, image: #imageLiteral(resourceName: "CheatCodes")) { [weak popoverMenuController, weak listMenuViewController] item in
|
||||||
|
listMenuViewController?.items.forEach { $0.isSelected = ($0 == item) }
|
||||||
|
popoverMenuController?.isActive = false
|
||||||
|
|
||||||
|
self.system = system
|
||||||
|
}
|
||||||
|
item.isSelected = (system == self.system)
|
||||||
|
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
listMenuViewController.items = items
|
||||||
|
}
|
||||||
|
|
||||||
|
func prepareActionsMenuViewController()
|
||||||
|
{
|
||||||
|
var items = [MenuItem]()
|
||||||
|
|
||||||
|
for input in self.supportedActionInputs
|
||||||
|
{
|
||||||
|
let image: UIImage
|
||||||
|
let text: String
|
||||||
|
|
||||||
|
switch input
|
||||||
|
{
|
||||||
|
case .quickSave:
|
||||||
|
image = #imageLiteral(resourceName: "SaveSaveState")
|
||||||
|
text = NSLocalizedString("Quick Save", comment: "")
|
||||||
|
|
||||||
|
case .quickLoad:
|
||||||
|
image = #imageLiteral(resourceName: "LoadSaveState")
|
||||||
|
text = NSLocalizedString("Quick Load", comment: "")
|
||||||
|
|
||||||
|
case .fastForward:
|
||||||
|
image = #imageLiteral(resourceName: "FastForward")
|
||||||
|
text = NSLocalizedString("Fast Forward", comment: "")
|
||||||
|
}
|
||||||
|
|
||||||
|
let item = MenuItem(text: text, image: image) { [unowned self] (item) in
|
||||||
|
guard let calloutView = self.calloutViews[AnyInput(input)] else { return }
|
||||||
|
self.toggle(calloutView)
|
||||||
|
}
|
||||||
|
|
||||||
|
items.append(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.actionsMenuViewController.items = items
|
||||||
|
self.actionsMenuViewController.isVibrancyEnabled = false
|
||||||
|
}
|
||||||
|
|
||||||
|
func prepareCallouts()
|
||||||
|
{
|
||||||
|
guard
|
||||||
|
let controllerView = self.gameViewController.controllerView,
|
||||||
|
let traits = controllerView.controllerSkinTraits,
|
||||||
|
let items = controllerView.controllerSkin?.items(for: traits),
|
||||||
|
let controllerViewInputMapping = controllerView.defaultInputMapping,
|
||||||
|
let inputMapping = self.inputMappings[self.system]
|
||||||
|
else { return }
|
||||||
|
|
||||||
|
// Implicit assumption that all skins used for controller input mapping don't have multiple items with same input.
|
||||||
|
let mappedInputs = items.flatMap { $0.inputs.allInputs.flatMap(controllerViewInputMapping.input(forControllerInput:)) } + (self.supportedActionInputs as [Input])
|
||||||
|
|
||||||
|
// Create callout view for each on-screen input.
|
||||||
|
for input in mappedInputs
|
||||||
|
{
|
||||||
|
let calloutView = InputCalloutView()
|
||||||
|
calloutView.delegate = self
|
||||||
|
self.calloutViews[AnyInput(input)] = calloutView
|
||||||
|
}
|
||||||
|
|
||||||
|
self.managedObjectContext.performAndWait {
|
||||||
|
// Update callout views with controller inputs that map to callout views' associated controller skin inputs.
|
||||||
|
for input in inputMapping.supportedControllerInputs
|
||||||
|
{
|
||||||
|
let mappedInput = self.mappedInput(for: input)
|
||||||
|
|
||||||
|
if let calloutView = self.calloutViews[mappedInput]
|
||||||
|
{
|
||||||
|
if let previousInput = calloutView.input
|
||||||
|
{
|
||||||
|
// Ensure the input we display has a higher priority.
|
||||||
|
calloutView.input = (input.displayPriority > previousInput.displayPriority) ? input : previousInput
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
calloutView.input = input
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Present only callout views that are associated with a controller input.
|
||||||
|
for calloutView in self.calloutViews.values
|
||||||
|
{
|
||||||
|
if let presentationRect = self.presentationRect(for: calloutView), calloutView.input != nil
|
||||||
|
{
|
||||||
|
calloutView.presentCallout(from: presentationRect, in: self.view, constrainedTo: self.view, animated: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension ControllerInputsViewController
|
||||||
|
{
|
||||||
|
func updateActiveCalloutView(with controllerInput: Input?)
|
||||||
|
{
|
||||||
|
guard let inputMapping = self.inputMappings[self.system] else { return }
|
||||||
|
|
||||||
|
guard let activeCalloutView = self.activeCalloutView else { return }
|
||||||
|
|
||||||
|
guard let input = self.calloutViews.first(where: { $0.value == activeCalloutView })?.key else { return }
|
||||||
|
|
||||||
|
if let controllerInput = controllerInput
|
||||||
|
{
|
||||||
|
for (_, calloutView) in self.calloutViews
|
||||||
|
{
|
||||||
|
guard let calloutInput = calloutView.input else { continue }
|
||||||
|
|
||||||
|
if calloutInput == controllerInput
|
||||||
|
{
|
||||||
|
// Hide callout views that previously displayed the controller input.
|
||||||
|
calloutView.input = nil
|
||||||
|
calloutView.dismissCallout(animated: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.managedObjectContext.performAndWait {
|
||||||
|
for supportedInput in inputMapping.supportedControllerInputs
|
||||||
|
{
|
||||||
|
let mappedInput = self.mappedInput(for: supportedInput)
|
||||||
|
|
||||||
|
if mappedInput == input
|
||||||
|
{
|
||||||
|
// Set all existing controller inputs that currently map to "input" to instead map to nil.
|
||||||
|
inputMapping.set(nil, forControllerInput: supportedInput)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let controllerInput = controllerInput
|
||||||
|
{
|
||||||
|
inputMapping.set(input, forControllerInput: controllerInput)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
activeCalloutView.input = controllerInput
|
||||||
|
|
||||||
|
self.toggle(activeCalloutView)
|
||||||
|
}
|
||||||
|
|
||||||
|
func toggle(_ calloutView: InputCalloutView)
|
||||||
|
{
|
||||||
|
if let activeCalloutView = self.activeCalloutView, activeCalloutView != calloutView
|
||||||
|
{
|
||||||
|
self.toggle(activeCalloutView)
|
||||||
|
}
|
||||||
|
|
||||||
|
let menuItem: MenuItem?
|
||||||
|
|
||||||
|
if let input = self.calloutViews.first(where: { $0.value == calloutView })?.key, let index = self.supportedActionInputs.index(where: { $0 == input })
|
||||||
|
{
|
||||||
|
menuItem = self.actionsMenuViewController.items[index]
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
menuItem = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch calloutView.state
|
||||||
|
{
|
||||||
|
case .normal:
|
||||||
|
calloutView.state = .listening
|
||||||
|
menuItem?.isSelected = true
|
||||||
|
self.activeCalloutView = calloutView
|
||||||
|
|
||||||
|
case .listening:
|
||||||
|
calloutView.state = .normal
|
||||||
|
menuItem?.isSelected = false
|
||||||
|
self.activeCalloutView = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
calloutView.dismissCallout(animated: true)
|
||||||
|
|
||||||
|
if let presentationRect = self.presentationRect(for: calloutView)
|
||||||
|
{
|
||||||
|
if calloutView.state == .listening || calloutView.input != nil
|
||||||
|
{
|
||||||
|
calloutView.presentCallout(from: presentationRect, in: self.view, constrainedTo: self.view, animated: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ControllerInputsViewController: UIGestureRecognizerDelegate
|
||||||
|
{
|
||||||
|
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool
|
||||||
|
{
|
||||||
|
return self.activeCalloutView != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction private func handleTapGesture(_ tapGestureRecognizer: UITapGestureRecognizer)
|
||||||
|
{
|
||||||
|
self.updateActiveCalloutView(with: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension ControllerInputsViewController
|
||||||
|
{
|
||||||
|
func mappedInput(for input: Input) -> AnyInput
|
||||||
|
{
|
||||||
|
guard let inputMapping = self.inputMappings[self.system] else {
|
||||||
|
fatalError("Input mapping for current system does not exist.")
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let mappedInput = inputMapping.input(forControllerInput: input) else {
|
||||||
|
fatalError("Mapped input for provided input does not exist.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if let standardInput = StandardGameControllerInput(input: mappedInput)
|
||||||
|
{
|
||||||
|
if let gameInput = standardInput.input(for: self.system.gameType)
|
||||||
|
{
|
||||||
|
return AnyInput(gameInput)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return AnyInput(mappedInput)
|
||||||
|
}
|
||||||
|
|
||||||
|
func presentationRect(for calloutView: InputCalloutView) -> CGRect?
|
||||||
|
{
|
||||||
|
guard let input = self.calloutViews.first(where: { $0.value == calloutView })?.key else { return nil }
|
||||||
|
|
||||||
|
guard
|
||||||
|
let controllerView = self.gameViewController.controllerView,
|
||||||
|
let traits = controllerView.controllerSkinTraits,
|
||||||
|
let items = controllerView.controllerSkin?.items(for: traits)
|
||||||
|
else { return nil }
|
||||||
|
|
||||||
|
if let item = items.first(where: { $0.inputs.allInputs.contains(where: { $0.stringValue == input.stringValue })})
|
||||||
|
{
|
||||||
|
// Input is a controller skin input.
|
||||||
|
|
||||||
|
let itemFrame: CGRect?
|
||||||
|
|
||||||
|
switch item.inputs
|
||||||
|
{
|
||||||
|
case .standard: itemFrame = item.frame
|
||||||
|
case let .directional(up, down, left, right):
|
||||||
|
|
||||||
|
switch input.stringValue
|
||||||
|
{
|
||||||
|
case up.stringValue:
|
||||||
|
itemFrame = CGRect(x: item.frame.minX + item.frame.width / 3,
|
||||||
|
y: item.frame.minY,
|
||||||
|
width: item.frame.width / 3,
|
||||||
|
height: item.frame.height / 3)
|
||||||
|
case down.stringValue:
|
||||||
|
itemFrame = CGRect(x: item.frame.minX + item.frame.width / 3,
|
||||||
|
y: item.frame.minY + (item.frame.height / 3) * 2,
|
||||||
|
width: item.frame.width / 3,
|
||||||
|
height: item.frame.height / 3)
|
||||||
|
|
||||||
|
case left.stringValue:
|
||||||
|
itemFrame = CGRect(x: item.frame.minX,
|
||||||
|
y: item.frame.minY + (item.frame.height / 3),
|
||||||
|
width: item.frame.width / 3,
|
||||||
|
height: item.frame.height / 3)
|
||||||
|
|
||||||
|
case right.stringValue:
|
||||||
|
itemFrame = CGRect(x: item.frame.minX + (item.frame.width / 3) * 2,
|
||||||
|
y: item.frame.minY + (item.frame.height / 3),
|
||||||
|
width: item.frame.width / 3,
|
||||||
|
height: item.frame.height / 3)
|
||||||
|
|
||||||
|
default: itemFrame = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let itemFrame = itemFrame
|
||||||
|
{
|
||||||
|
var presentationFrame = itemFrame.applying(CGAffineTransform(scaleX: controllerView.bounds.width, y: controllerView.bounds.height))
|
||||||
|
presentationFrame = self.view.convert(presentationFrame, from: controllerView)
|
||||||
|
|
||||||
|
return presentationFrame
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if let index = self.supportedActionInputs.index(where: { $0 == input })
|
||||||
|
{
|
||||||
|
// Input is an ActionInput.
|
||||||
|
|
||||||
|
let indexPath = IndexPath(item: index, section: 0)
|
||||||
|
|
||||||
|
if let attributes = self.actionsMenuViewController.collectionViewLayout.layoutAttributesForItem(at: indexPath)
|
||||||
|
{
|
||||||
|
let presentationFrame = self.view.convert(attributes.frame, from: self.actionsMenuViewController.view)
|
||||||
|
return presentationFrame
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Input is not an on-screen input.
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ControllerInputsViewController: GameControllerReceiver
|
||||||
|
{
|
||||||
|
func gameController(_ gameController: GameController, didActivate controllerInput: DeltaCore.Input)
|
||||||
|
{
|
||||||
|
guard self.isViewLoaded else { return }
|
||||||
|
|
||||||
|
switch gameController
|
||||||
|
{
|
||||||
|
case self.gameViewController.controllerView:
|
||||||
|
if let calloutView = self.calloutViews[AnyInput(controllerInput)]
|
||||||
|
{
|
||||||
|
self.toggle(calloutView)
|
||||||
|
}
|
||||||
|
|
||||||
|
case self.gameController: self.updateActiveCalloutView(with: controllerInput)
|
||||||
|
|
||||||
|
default: break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func gameController(_ gameController: GameController, didDeactivate input: DeltaCore.Input)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ControllerInputsViewController: SMCalloutViewDelegate
|
||||||
|
{
|
||||||
|
func calloutViewClicked(_ calloutView: SMCalloutView)
|
||||||
|
{
|
||||||
|
guard let calloutView = calloutView as? InputCalloutView else { return }
|
||||||
|
|
||||||
|
self.toggle(calloutView)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -9,39 +9,62 @@
|
|||||||
import UIKit
|
import UIKit
|
||||||
import DeltaCore
|
import DeltaCore
|
||||||
|
|
||||||
private enum ControllersSettingsSection: Int
|
import Roxas
|
||||||
|
|
||||||
|
extension ControllersSettingsViewController
|
||||||
{
|
{
|
||||||
|
private enum Section: Int
|
||||||
|
{
|
||||||
case none
|
case none
|
||||||
case localDevice
|
case localDevice
|
||||||
case externalControllers
|
case externalControllers
|
||||||
|
case customizeControls
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class LocalDeviceController: ExternalController
|
private class LocalDeviceController: NSObject, GameController
|
||||||
{
|
{
|
||||||
override var name: String {
|
var name: String {
|
||||||
return UIDevice.current.name
|
return UIDevice.current.name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var playerIndex: Int? {
|
||||||
|
set { Settings.localControllerPlayerIndex = newValue }
|
||||||
|
get { return Settings.localControllerPlayerIndex }
|
||||||
|
}
|
||||||
|
|
||||||
|
let inputType: GameControllerInputType = .standard
|
||||||
|
|
||||||
|
var defaultInputMapping: GameControllerInputMappingProtocol?
|
||||||
}
|
}
|
||||||
|
|
||||||
class ControllersSettingsViewController: UITableViewController
|
class ControllersSettingsViewController: UITableViewController
|
||||||
{
|
{
|
||||||
var playerIndex: Int? {
|
var playerIndex: Int! {
|
||||||
didSet
|
didSet {
|
||||||
|
self.title = NSLocalizedString("Player \(self.playerIndex + 1)", comment: "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var gameController: GameController? {
|
||||||
|
didSet {
|
||||||
|
// Order matters since localDeviceController changes Settings.localControllerPlayerIndex, which sends out NSNotification.
|
||||||
|
if oldValue == self.localDeviceController
|
||||||
{
|
{
|
||||||
if let playerIndex = self.playerIndex
|
self.gameController?.playerIndex = self.playerIndex
|
||||||
{
|
oldValue?.playerIndex = nil
|
||||||
self.title = NSLocalizedString("Player \(playerIndex + 1)", comment: "")
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
self.title = NSLocalizedString("Controllers", comment: "")
|
oldValue?.playerIndex = nil
|
||||||
|
self.gameController?.playerIndex = self.playerIndex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate var connectedControllers = ExternalControllerManager.shared.connectedControllers.sorted(by: { $0.playerIndex ?? NSIntegerMax < $1.playerIndex ?? NSIntegerMax })
|
private var connectedControllers = ExternalGameControllerManager.shared.connectedControllers.sorted(by: { $0.playerIndex ?? NSIntegerMax < $1.playerIndex ?? NSIntegerMax })
|
||||||
|
|
||||||
fileprivate lazy var localDeviceController: LocalDeviceController = {
|
private lazy var localDeviceController: LocalDeviceController = {
|
||||||
let device = LocalDeviceController()
|
let device = LocalDeviceController()
|
||||||
device.playerIndex = Settings.localControllerPlayerIndex
|
device.playerIndex = Settings.localControllerPlayerIndex
|
||||||
|
|
||||||
@ -52,146 +75,76 @@ class ControllersSettingsViewController: UITableViewController
|
|||||||
{
|
{
|
||||||
super.init(coder: aDecoder)
|
super.init(coder: aDecoder)
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(ControllersSettingsViewController.externalControllerDidConnect(_:)), name: .externalControllerDidConnect, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(ControllersSettingsViewController.externalGameControllerDidConnect(_:)), name: .externalGameControllerDidConnect, object: nil)
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(ControllersSettingsViewController.externalControllerDidDisconnect(_:)), name: .externalControllerDidDisconnect, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(ControllersSettingsViewController.externalGameControllerDidDisconnect(_:)), name: .externalGameControllerDidDisconnect, object: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewDidLoad()
|
override func viewDidLoad()
|
||||||
{
|
{
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
}
|
|
||||||
|
|
||||||
//MARK: - Storyboards -
|
let gameControllers = [self.localDeviceController as GameController] + self.connectedControllers
|
||||||
|
for gameController in gameControllers
|
||||||
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
|
|
||||||
{
|
{
|
||||||
guard let indexPath = self.tableView.indexPathForSelectedRow else { return }
|
if gameController.playerIndex == self.playerIndex
|
||||||
|
|
||||||
var controllers = self.connectedControllers
|
|
||||||
controllers.append(self.localDeviceController)
|
|
||||||
|
|
||||||
// Reset previous controller
|
|
||||||
if let playerIndex = self.playerIndex, let index = controllers.index(where: { $0.playerIndex == playerIndex })
|
|
||||||
{
|
{
|
||||||
let controller = controllers[index]
|
self.gameController = gameController
|
||||||
controller.playerIndex = nil
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ControllersSettingsSection(rawValue: (indexPath as NSIndexPath).section)!
|
|
||||||
{
|
|
||||||
case .none: break
|
|
||||||
case .localDevice: self.localDeviceController.playerIndex = self.playerIndex
|
|
||||||
case .externalControllers:
|
|
||||||
let controller = self.connectedControllers[(indexPath as NSIndexPath).row]
|
|
||||||
controller.playerIndex = self.playerIndex
|
|
||||||
}
|
|
||||||
|
|
||||||
// Updates in case we reset it above, as well as if we updated in the switch statement
|
|
||||||
Settings.localControllerPlayerIndex = self.localDeviceController.playerIndex
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private extension ControllersSettingsViewController
|
|
||||||
{
|
|
||||||
dynamic func externalControllerDidConnect(_ notification: Notification)
|
|
||||||
{
|
|
||||||
guard let controller = notification.object as? ExternalController else { return }
|
|
||||||
|
|
||||||
if let playerIndex = controller.playerIndex
|
|
||||||
{
|
|
||||||
self.connectedControllers.insert(controller, at: playerIndex)
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self.connectedControllers.append(controller)
|
|
||||||
}
|
|
||||||
|
|
||||||
if let index = self.connectedControllers.index(of: controller)
|
|
||||||
{
|
|
||||||
if self.connectedControllers.count == 1
|
|
||||||
{
|
|
||||||
self.tableView.insertSections(IndexSet(integer: ControllersSettingsSection.externalControllers.rawValue), with: .fade)
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self.tableView.insertRows(at: [IndexPath(row: index, section: ControllersSettingsSection.externalControllers.rawValue)], with: .automatic)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dynamic func externalControllerDidDisconnect(_ notification: Notification)
|
|
||||||
{
|
|
||||||
guard let controller = notification.object as? ExternalController else { return }
|
|
||||||
|
|
||||||
if let index = self.connectedControllers.index(of: controller)
|
|
||||||
{
|
|
||||||
self.connectedControllers.remove(at: index)
|
|
||||||
|
|
||||||
if self.connectedControllers.count == 0
|
|
||||||
{
|
|
||||||
self.tableView.deleteSections(IndexSet(integer: ControllersSettingsSection.externalControllers.rawValue), with: .fade)
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self.tableView.deleteRows(at: [IndexPath(row: index, section: ControllersSettingsSection.externalControllers.rawValue)], with: .automatic)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if controller.playerIndex == self.playerIndex
|
|
||||||
{
|
|
||||||
self.tableView.reloadSections(IndexSet(integer: ControllersSettingsSection.none.rawValue), with: .none)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ControllersSettingsViewController
|
extension ControllersSettingsViewController
|
||||||
{
|
{
|
||||||
override func numberOfSections(in tableView: UITableView) -> Int
|
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
|
||||||
{
|
{
|
||||||
if self.connectedControllers.count == 0
|
guard let identifier = segue.identifier else { return }
|
||||||
{
|
|
||||||
return 2
|
|
||||||
}
|
|
||||||
|
|
||||||
return 3
|
switch identifier
|
||||||
}
|
{
|
||||||
|
case "controllerInputsSegue":
|
||||||
|
let controllerInputsViewController = (segue.destination as! UINavigationController).topViewController as! ControllerInputsViewController
|
||||||
|
controllerInputsViewController.gameController = self.gameController
|
||||||
|
controllerInputsViewController.system = .snes
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
|
default: break
|
||||||
{
|
|
||||||
switch ControllersSettingsSection(rawValue: section)!
|
|
||||||
{
|
|
||||||
case .none: return 1
|
|
||||||
case .localDevice: return 1
|
|
||||||
case .externalControllers: return self.connectedControllers.count
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
|
@IBAction private func unwindFromControllerControlsViewController(_ segue: UIStoryboardSegue)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension ControllersSettingsViewController
|
||||||
|
{
|
||||||
|
func configure(_ cell: UITableViewCell, for indexPath: IndexPath)
|
||||||
{
|
{
|
||||||
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
|
|
||||||
cell.detailTextLabel?.text = nil
|
|
||||||
cell.accessoryType = .none
|
cell.accessoryType = .none
|
||||||
|
cell.detailTextLabel?.text = nil
|
||||||
|
cell.textLabel?.textColor = .darkText
|
||||||
|
|
||||||
if (indexPath as NSIndexPath).section == ControllersSettingsSection.none.rawValue
|
switch Section(rawValue: indexPath.section)!
|
||||||
{
|
{
|
||||||
|
case .none:
|
||||||
cell.textLabel?.text = NSLocalizedString("None", comment: "")
|
cell.textLabel?.text = NSLocalizedString("None", comment: "")
|
||||||
|
|
||||||
if Settings.localControllerPlayerIndex != self.playerIndex && !self.connectedControllers.contains(where: { $0.playerIndex == self.playerIndex })
|
if Settings.localControllerPlayerIndex != self.playerIndex && !self.connectedControllers.contains(where: { $0.playerIndex == self.playerIndex })
|
||||||
{
|
{
|
||||||
cell.accessoryType = .checkmark
|
cell.accessoryType = .checkmark
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
let controller: ExternalController
|
|
||||||
|
|
||||||
if (indexPath as NSIndexPath).section == ControllersSettingsSection.localDevice.rawValue
|
case .localDevice, .externalControllers:
|
||||||
|
let controller: GameController
|
||||||
|
|
||||||
|
if indexPath.section == Section.localDevice.rawValue
|
||||||
{
|
{
|
||||||
controller = self.localDeviceController
|
controller = self.localDeviceController
|
||||||
}
|
}
|
||||||
else if (indexPath as NSIndexPath).section == ControllersSettingsSection.externalControllers.rawValue
|
else if indexPath.section == Section.externalControllers.rawValue
|
||||||
{
|
{
|
||||||
controller = self.connectedControllers[(indexPath as NSIndexPath).row]
|
controller = self.connectedControllers[indexPath.row]
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -210,20 +163,211 @@ extension ControllersSettingsViewController
|
|||||||
{
|
{
|
||||||
cell.detailTextLabel?.text = NSLocalizedString("Player \(playerIndex + 1)", comment: "")
|
cell.detailTextLabel?.text = NSLocalizedString("Player \(playerIndex + 1)", comment: "")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case .customizeControls:
|
||||||
|
cell.textLabel?.text = NSLocalizedString("Customize Controls…", comment: "")
|
||||||
|
cell.textLabel?.textColor = self.view.tintColor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension ControllersSettingsViewController
|
||||||
|
{
|
||||||
|
@objc func externalGameControllerDidConnect(_ notification: Notification)
|
||||||
|
{
|
||||||
|
guard let controller = notification.object as? GameController else { return }
|
||||||
|
|
||||||
|
if let playerIndex = controller.playerIndex
|
||||||
|
{
|
||||||
|
// Keep connected controllers sorted.
|
||||||
|
|
||||||
|
self.connectedControllers.insert(controller, at: playerIndex)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
self.connectedControllers.append(controller)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let index = self.connectedControllers.index(where: { $0 == controller })
|
||||||
|
{
|
||||||
|
self.tableView.beginUpdates()
|
||||||
|
|
||||||
|
if self.connectedControllers.count == 1
|
||||||
|
{
|
||||||
|
self.tableView.insertSections(IndexSet(integer: Section.externalControllers.rawValue), with: .fade)
|
||||||
|
|
||||||
|
if self.connectedControllers.first?.playerIndex == self.playerIndex
|
||||||
|
{
|
||||||
|
self.tableView.insertSections(IndexSet(integer: Section.customizeControls.rawValue), with: .fade)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
self.tableView.insertRows(at: [IndexPath(row: index, section: Section.externalControllers.rawValue)], with: .automatic)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.tableView.endUpdates()
|
||||||
|
}
|
||||||
|
|
||||||
|
if controller.playerIndex == self.playerIndex
|
||||||
|
{
|
||||||
|
self.tableView.reloadSections(IndexSet(integer: Section.none.rawValue), with: .none)
|
||||||
|
self.tableView.reloadSections(IndexSet(integer: Section.localDevice.rawValue), with: .none)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func externalGameControllerDidDisconnect(_ notification: Notification)
|
||||||
|
{
|
||||||
|
guard let controller = notification.object as? GameController else { return }
|
||||||
|
|
||||||
|
if let index = self.connectedControllers.index(where: { $0 == controller })
|
||||||
|
{
|
||||||
|
self.connectedControllers.remove(at: index)
|
||||||
|
|
||||||
|
self.tableView.beginUpdates()
|
||||||
|
|
||||||
|
if self.connectedControllers.count == 0
|
||||||
|
{
|
||||||
|
self.tableView.deleteSections(IndexSet(integer: Section.externalControllers.rawValue), with: .fade)
|
||||||
|
|
||||||
|
if controller.playerIndex != nil
|
||||||
|
{
|
||||||
|
self.tableView.deleteSections(IndexSet(integer: Section.customizeControls.rawValue), with: .fade)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
self.tableView.deleteRows(at: [IndexPath(row: index, section: Section.externalControllers.rawValue)], with: .automatic)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.tableView.endUpdates()
|
||||||
|
}
|
||||||
|
|
||||||
|
if controller.playerIndex == self.playerIndex
|
||||||
|
{
|
||||||
|
self.tableView.reloadSections(IndexSet(integer: Section.none.rawValue), with: .none)
|
||||||
|
self.tableView.reloadSections(IndexSet(integer: Section.localDevice.rawValue), with: .none)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ControllersSettingsViewController
|
||||||
|
{
|
||||||
|
override func numberOfSections(in tableView: UITableView) -> Int
|
||||||
|
{
|
||||||
|
if self.connectedControllers.count == 0
|
||||||
|
{
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.gameController == nil || Settings.localControllerPlayerIndex == self.playerIndex
|
||||||
|
{
|
||||||
|
return 3
|
||||||
|
}
|
||||||
|
|
||||||
|
return 4
|
||||||
|
}
|
||||||
|
|
||||||
|
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
|
||||||
|
{
|
||||||
|
switch Section(rawValue: section)!
|
||||||
|
{
|
||||||
|
case .none: return 0
|
||||||
|
case .localDevice: return 1
|
||||||
|
case .externalControllers: return self.connectedControllers.count
|
||||||
|
case .customizeControls: return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
|
||||||
|
{
|
||||||
|
let cell = tableView.dequeueReusableCell(withIdentifier: RSTCellContentGenericCellIdentifier, for: indexPath)
|
||||||
|
|
||||||
|
self.configure(cell, for: indexPath)
|
||||||
|
|
||||||
return cell
|
return cell
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String?
|
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String?
|
||||||
{
|
{
|
||||||
switch ControllersSettingsSection(rawValue: section)!
|
switch Section(rawValue: section)!
|
||||||
{
|
{
|
||||||
case .none: return nil
|
case .none: return nil
|
||||||
case .localDevice: return NSLocalizedString("Local Device", comment: "")
|
case .localDevice: return NSLocalizedString("Local Device", comment: "")
|
||||||
case .externalControllers: return self.connectedControllers.count > 0 ? NSLocalizedString("External Controllers", comment: "") : ""
|
case .externalControllers: return self.connectedControllers.count > 0 ? NSLocalizedString("External Controllers", comment: "") : ""
|
||||||
|
case .customizeControls: return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat
|
||||||
|
{
|
||||||
|
if section == Section.none.rawValue
|
||||||
|
{
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return UITableViewAutomaticDimension
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ControllersSettingsViewController
|
||||||
|
{
|
||||||
|
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
|
||||||
|
{
|
||||||
|
let previousIndexPath: IndexPath?
|
||||||
|
|
||||||
|
if let gameController = self.gameController
|
||||||
|
{
|
||||||
|
if gameController == self.localDeviceController
|
||||||
|
{
|
||||||
|
previousIndexPath = IndexPath(row: 0, section: Section.localDevice.rawValue)
|
||||||
|
}
|
||||||
|
else if let row = self.connectedControllers.index(where: { $0 == gameController })
|
||||||
|
{
|
||||||
|
previousIndexPath = IndexPath(row: row, section: Section.externalControllers.rawValue)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
previousIndexPath = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
previousIndexPath = IndexPath(row: 0, section: Section.none.rawValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch Section(rawValue: indexPath.section)!
|
||||||
|
{
|
||||||
|
case .none: self.gameController = nil
|
||||||
|
case .localDevice: self.gameController = self.localDeviceController
|
||||||
|
case .externalControllers: self.gameController = self.connectedControllers[indexPath.row]
|
||||||
|
case .customizeControls:
|
||||||
|
guard let cell = tableView.cellForRow(at: indexPath) else { return }
|
||||||
|
self.performSegue(withIdentifier: "controllerInputsSegue", sender: cell)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.tableView.beginUpdates()
|
||||||
|
|
||||||
|
if let previousIndexPath = previousIndexPath, let cell = tableView.cellForRow(at: previousIndexPath)
|
||||||
|
{
|
||||||
|
// Must configure cell directly, or else a strange animation occurs when reloading row on iOS 11.
|
||||||
|
self.configure(cell, for: previousIndexPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.tableView.reloadRows(at: [indexPath], with: .none)
|
||||||
|
|
||||||
|
if self.numberOfSections(in: self.tableView) > self.tableView.numberOfSections
|
||||||
|
{
|
||||||
|
self.tableView.insertSections(IndexSet(integer: Section.customizeControls.rawValue), with: .fade)
|
||||||
|
}
|
||||||
|
else if self.numberOfSections(in: self.tableView) < self.tableView.numberOfSections
|
||||||
|
{
|
||||||
|
self.tableView.deleteSections(IndexSet(integer: Section.customizeControls.rawValue), with: .fade)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.tableView.endUpdates()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
84
Delta/Settings/Controllers/InputCalloutView.swift
Normal file
84
Delta/Settings/Controllers/InputCalloutView.swift
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
//
|
||||||
|
// InputCalloutView.swift
|
||||||
|
// Delta
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 7/9/17.
|
||||||
|
// Copyright © 2017 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SMCalloutView
|
||||||
|
|
||||||
|
import DeltaCore
|
||||||
|
|
||||||
|
extension InputCalloutView
|
||||||
|
{
|
||||||
|
enum State
|
||||||
|
{
|
||||||
|
case normal
|
||||||
|
case listening
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class InputCalloutView: SMCalloutView
|
||||||
|
{
|
||||||
|
var input: Input? {
|
||||||
|
didSet {
|
||||||
|
self.updateState()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var state: State = .normal {
|
||||||
|
didSet {
|
||||||
|
self.updateState()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private let textLabel: UILabel
|
||||||
|
|
||||||
|
init()
|
||||||
|
{
|
||||||
|
self.textLabel = UILabel()
|
||||||
|
self.textLabel.font = UIFont.boldSystemFont(ofSize: 18.0)
|
||||||
|
self.textLabel.textAlignment = .center
|
||||||
|
|
||||||
|
super.init(frame: CGRect.zero)
|
||||||
|
|
||||||
|
self.titleView = self.textLabel
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder aDecoder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override func tintColorDidChange()
|
||||||
|
{
|
||||||
|
super.tintColorDidChange()
|
||||||
|
|
||||||
|
self.updateTintColor()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension InputCalloutView
|
||||||
|
{
|
||||||
|
func updateState()
|
||||||
|
{
|
||||||
|
switch self.state
|
||||||
|
{
|
||||||
|
case .normal: self.textLabel.text = self.input?.localizedName
|
||||||
|
case .listening: self.textLabel.text = NSLocalizedString("Press Button", comment: "")
|
||||||
|
}
|
||||||
|
|
||||||
|
self.updateTintColor()
|
||||||
|
|
||||||
|
self.textLabel.sizeToFit()
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateTintColor()
|
||||||
|
{
|
||||||
|
switch self.state
|
||||||
|
{
|
||||||
|
case .normal: self.textLabel.textColor = self.tintColor
|
||||||
|
case .listening: self.textLabel.textColor = .red
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -9,8 +9,6 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
import DeltaCore
|
import DeltaCore
|
||||||
import SNESDeltaCore
|
|
||||||
import GBADeltaCore
|
|
||||||
|
|
||||||
import Roxas
|
import Roxas
|
||||||
|
|
||||||
@ -25,7 +23,7 @@ extension Settings
|
|||||||
{
|
{
|
||||||
case name
|
case name
|
||||||
|
|
||||||
case gameType
|
case system
|
||||||
case traits
|
case traits
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,12 +40,14 @@ struct Settings
|
|||||||
/// Controllers
|
/// Controllers
|
||||||
static var localControllerPlayerIndex: Int? = 0 {
|
static var localControllerPlayerIndex: Int? = 0 {
|
||||||
didSet {
|
didSet {
|
||||||
|
guard self.localControllerPlayerIndex != oldValue else { return }
|
||||||
NotificationCenter.default.post(name: .settingsDidChange, object: nil, userInfo: [NotificationUserInfoKey.name: Name.localControllerPlayerIndex])
|
NotificationCenter.default.post(name: .settingsDidChange, object: nil, userInfo: [NotificationUserInfoKey.name: Name.localControllerPlayerIndex])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static var translucentControllerSkinOpacity: CGFloat {
|
static var translucentControllerSkinOpacity: CGFloat {
|
||||||
set {
|
set {
|
||||||
|
guard newValue != self.translucentControllerSkinOpacity else { return }
|
||||||
UserDefaults.standard.translucentControllerSkinOpacity = newValue
|
UserDefaults.standard.translucentControllerSkinOpacity = newValue
|
||||||
NotificationCenter.default.post(name: .settingsDidChange, object: nil, userInfo: [NotificationUserInfoKey.name: Name.translucentControllerSkinOpacity])
|
NotificationCenter.default.post(name: .settingsDidChange, object: nil, userInfo: [NotificationUserInfoKey.name: Name.translucentControllerSkinOpacity])
|
||||||
}
|
}
|
||||||
@ -72,9 +72,9 @@ struct Settings
|
|||||||
UserDefaults.standard.register(defaults: defaults)
|
UserDefaults.standard.register(defaults: defaults)
|
||||||
}
|
}
|
||||||
|
|
||||||
static func preferredControllerSkin(for gameType: GameType, traits: DeltaCore.ControllerSkin.Traits) -> ControllerSkin?
|
static func preferredControllerSkin(for system: System, traits: DeltaCore.ControllerSkin.Traits) -> ControllerSkin?
|
||||||
{
|
{
|
||||||
guard let userDefaultsKey = self.preferredControllerSkinKey(for: gameType, traits: traits) else { return nil }
|
guard let userDefaultsKey = self.preferredControllerSkinKey(for: system, traits: traits) else { return nil }
|
||||||
|
|
||||||
let identifier = UserDefaults.standard.string(forKey: userDefaultsKey)
|
let identifier = UserDefaults.standard.string(forKey: userDefaultsKey)
|
||||||
|
|
||||||
@ -86,7 +86,7 @@ struct Settings
|
|||||||
|
|
||||||
if let identifier = identifier
|
if let identifier = identifier
|
||||||
{
|
{
|
||||||
fetchRequest.predicate = NSPredicate(format: "%K == %@ AND %K == %@", #keyPath(ControllerSkin.gameType), gameType.rawValue, #keyPath(ControllerSkin.identifier), identifier)
|
fetchRequest.predicate = NSPredicate(format: "%K == %@ AND %K == %@", #keyPath(ControllerSkin.gameType), system.gameType.rawValue, #keyPath(ControllerSkin.identifier), identifier)
|
||||||
|
|
||||||
if let controllerSkin = try DatabaseManager.shared.viewContext.fetch(fetchRequest).first
|
if let controllerSkin = try DatabaseManager.shared.viewContext.fetch(fetchRequest).first
|
||||||
{
|
{
|
||||||
@ -96,11 +96,11 @@ struct Settings
|
|||||||
|
|
||||||
// Controller skin doesn't exist, so fall back to standard controller skin
|
// Controller skin doesn't exist, so fall back to standard controller skin
|
||||||
|
|
||||||
fetchRequest.predicate = NSPredicate(format: "%K == %@ AND %K == YES", #keyPath(ControllerSkin.gameType), gameType.rawValue, #keyPath(ControllerSkin.isStandard))
|
fetchRequest.predicate = NSPredicate(format: "%K == %@ AND %K == YES", #keyPath(ControllerSkin.gameType), system.gameType.rawValue, #keyPath(ControllerSkin.isStandard))
|
||||||
|
|
||||||
if let controllerSkin = try DatabaseManager.shared.viewContext.fetch(fetchRequest).first
|
if let controllerSkin = try DatabaseManager.shared.viewContext.fetch(fetchRequest).first
|
||||||
{
|
{
|
||||||
Settings.setPreferredControllerSkin(controllerSkin, for: gameType, traits: traits)
|
Settings.setPreferredControllerSkin(controllerSkin, for: system, traits: traits)
|
||||||
return controllerSkin
|
return controllerSkin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -112,26 +112,29 @@ struct Settings
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
static func setPreferredControllerSkin(_ controllerSkin: ControllerSkin, for gameType: GameType, traits: DeltaCore.ControllerSkin.Traits)
|
static func setPreferredControllerSkin(_ controllerSkin: ControllerSkin, for system: System, traits: DeltaCore.ControllerSkin.Traits)
|
||||||
{
|
{
|
||||||
guard let userDefaultKey = self.preferredControllerSkinKey(for: gameType, traits: traits) else { return }
|
guard let userDefaultKey = self.preferredControllerSkinKey(for: system, traits: traits) else { return }
|
||||||
|
|
||||||
|
guard UserDefaults.standard.string(forKey: userDefaultKey) != controllerSkin.identifier else { return }
|
||||||
|
|
||||||
UserDefaults.standard.set(controllerSkin.identifier, forKey: userDefaultKey)
|
UserDefaults.standard.set(controllerSkin.identifier, forKey: userDefaultKey)
|
||||||
|
|
||||||
NotificationCenter.default.post(name: .settingsDidChange, object: controllerSkin, userInfo: [NotificationUserInfoKey.name: Name.preferredControllerSkin, NotificationUserInfoKey.gameType: gameType, NotificationUserInfoKey.traits: traits])
|
NotificationCenter.default.post(name: .settingsDidChange, object: controllerSkin, userInfo: [NotificationUserInfoKey.name: Name.preferredControllerSkin, NotificationUserInfoKey.system: system, NotificationUserInfoKey.traits: traits])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension Settings
|
private extension Settings
|
||||||
{
|
{
|
||||||
static func preferredControllerSkinKey(for gameType: GameType, traits: DeltaCore.ControllerSkin.Traits) -> String?
|
static func preferredControllerSkinKey(for system: System, traits: DeltaCore.ControllerSkin.Traits) -> String?
|
||||||
{
|
{
|
||||||
let systemName: String
|
let systemName: String
|
||||||
|
|
||||||
switch gameType
|
switch system
|
||||||
{
|
{
|
||||||
case GameType.snes: systemName = "snes"
|
case .snes: systemName = "snes"
|
||||||
case GameType.gba: systemName = "gba"
|
case .gba: systemName = "gba"
|
||||||
default: return nil
|
case .gbc: systemName = "gbc"
|
||||||
}
|
}
|
||||||
|
|
||||||
let orientation: String
|
let orientation: String
|
||||||
|
|||||||
@ -7,37 +7,49 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
import DeltaCore
|
import DeltaCore
|
||||||
|
|
||||||
extension SettingsViewController
|
import Roxas
|
||||||
|
|
||||||
|
private extension SettingsViewController
|
||||||
{
|
{
|
||||||
fileprivate enum Section: Int
|
enum Section: Int
|
||||||
{
|
{
|
||||||
case controllers
|
case controllers
|
||||||
case controllerSkins
|
case controllerSkins
|
||||||
case controllerOpacity
|
case controllerOpacity
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate enum Segue: String
|
enum Segue: String
|
||||||
{
|
{
|
||||||
case controllers = "controllersSegue"
|
case controllers = "controllersSegue"
|
||||||
case controllerSkins = "controllerSkinsSegue"
|
case controllerSkins = "controllerSkinsSegue"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ControllerSkinsRow: Int
|
||||||
|
{
|
||||||
|
case snes
|
||||||
|
case gba
|
||||||
|
case gbc
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SettingsViewController: UITableViewController
|
class SettingsViewController: UITableViewController
|
||||||
{
|
{
|
||||||
@IBOutlet fileprivate var controllerOpacityLabel: UILabel!
|
@IBOutlet private var controllerOpacityLabel: UILabel!
|
||||||
@IBOutlet fileprivate var controllerOpacitySlider: UISlider!
|
@IBOutlet private var controllerOpacitySlider: UISlider!
|
||||||
|
|
||||||
fileprivate var selectionFeedbackGenerator: UISelectionFeedbackGenerator?
|
private var selectionFeedbackGenerator: UISelectionFeedbackGenerator?
|
||||||
|
|
||||||
|
private var previousSelectedRowIndexPath: IndexPath?
|
||||||
|
|
||||||
required init?(coder aDecoder: NSCoder)
|
required init?(coder aDecoder: NSCoder)
|
||||||
{
|
{
|
||||||
super.init(coder: aDecoder)
|
super.init(coder: aDecoder)
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(SettingsViewController.externalControllerDidConnect(_:)), name: .externalControllerDidConnect, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(SettingsViewController.externalGameControllerDidConnect(_:)), name: .externalGameControllerDidConnect, object: nil)
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(SettingsViewController.externalControllerDidDisconnect(_:)), name: .externalControllerDidDisconnect, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(SettingsViewController.externalGameControllerDidDisconnect(_:)), name: .externalGameControllerDidDisconnect, object: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewDidLoad()
|
override func viewDidLoad()
|
||||||
@ -52,8 +64,15 @@ class SettingsViewController: UITableViewController
|
|||||||
{
|
{
|
||||||
super.viewWillAppear(animated)
|
super.viewWillAppear(animated)
|
||||||
|
|
||||||
if let indexPath = self.tableView.indexPathForSelectedRow
|
if let indexPath = self.previousSelectedRowIndexPath
|
||||||
{
|
{
|
||||||
|
if indexPath.section == Section.controllers.rawValue
|
||||||
|
{
|
||||||
|
// Update and temporarily re-select selected row.
|
||||||
|
self.tableView.reloadSections(IndexSet(integer: Section.controllers.rawValue), with: .none)
|
||||||
|
self.tableView.selectRow(at: indexPath, animated: false, scrollPosition: UITableViewScrollPosition.none)
|
||||||
|
}
|
||||||
|
|
||||||
self.tableView.deselectRow(at: indexPath, animated: true)
|
self.tableView.deselectRow(at: indexPath, animated: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -72,6 +91,8 @@ class SettingsViewController: UITableViewController
|
|||||||
let indexPath = self.tableView.indexPath(for: cell)
|
let indexPath = self.tableView.indexPath(for: cell)
|
||||||
else { return }
|
else { return }
|
||||||
|
|
||||||
|
self.previousSelectedRowIndexPath = indexPath
|
||||||
|
|
||||||
switch segueType
|
switch segueType
|
||||||
{
|
{
|
||||||
case Segue.controllers:
|
case Segue.controllers:
|
||||||
@ -79,13 +100,14 @@ class SettingsViewController: UITableViewController
|
|||||||
controllersSettingsViewController.playerIndex = indexPath.row
|
controllersSettingsViewController.playerIndex = indexPath.row
|
||||||
|
|
||||||
case Segue.controllerSkins:
|
case Segue.controllerSkins:
|
||||||
let gameTypeControllerSkinsViewController = segue.destination as! GameTypeControllerSkinsViewController
|
let systemControllerSkinsViewController = segue.destination as! SystemControllerSkinsViewController
|
||||||
|
|
||||||
switch indexPath.row
|
let row = ControllerSkinsRow(rawValue: indexPath.row)!
|
||||||
|
switch row
|
||||||
{
|
{
|
||||||
case 0: gameTypeControllerSkinsViewController.gameType = .snes
|
case .snes: systemControllerSkinsViewController.system = .snes
|
||||||
case 1: gameTypeControllerSkinsViewController.gameType = .gba
|
case .gba: systemControllerSkinsViewController.system = .gba
|
||||||
default: break
|
case .gbc: systemControllerSkinsViewController.system = .gbc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -100,18 +122,6 @@ private extension SettingsViewController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension SettingsViewController
|
|
||||||
{
|
|
||||||
@IBAction func unwindFromControllersSettingsViewController(_ segue: UIStoryboardSegue)
|
|
||||||
{
|
|
||||||
let indexPath = self.tableView.indexPathForSelectedRow
|
|
||||||
|
|
||||||
self.tableView.reloadSections(IndexSet(integer: Section.controllers.rawValue), with: .none)
|
|
||||||
|
|
||||||
self.tableView.selectRow(at: indexPath, animated: true, scrollPosition: UITableViewScrollPosition.none)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private extension SettingsViewController
|
private extension SettingsViewController
|
||||||
{
|
{
|
||||||
@IBAction func beginChangingControllerOpacity(with sender: UISlider)
|
@IBAction func beginChangingControllerOpacity(with sender: UISlider)
|
||||||
@ -143,12 +153,12 @@ private extension SettingsViewController
|
|||||||
|
|
||||||
private extension SettingsViewController
|
private extension SettingsViewController
|
||||||
{
|
{
|
||||||
dynamic func externalControllerDidConnect(_ notification: Notification)
|
@objc func externalGameControllerDidConnect(_ notification: Notification)
|
||||||
{
|
{
|
||||||
self.tableView.reloadSections(IndexSet(integer: Section.controllers.rawValue), with: .none)
|
self.tableView.reloadSections(IndexSet(integer: Section.controllers.rawValue), with: .none)
|
||||||
}
|
}
|
||||||
|
|
||||||
dynamic func externalControllerDidDisconnect(_ notification: Notification)
|
@objc func externalGameControllerDidDisconnect(_ notification: Notification)
|
||||||
{
|
{
|
||||||
self.tableView.reloadSections(IndexSet(integer: Section.controllers.rawValue), with: .none)
|
self.tableView.reloadSections(IndexSet(integer: Section.controllers.rawValue), with: .none)
|
||||||
}
|
}
|
||||||
@ -162,6 +172,7 @@ extension SettingsViewController
|
|||||||
switch section
|
switch section
|
||||||
{
|
{
|
||||||
case .controllers: return 1 // Temporarily hide other controller indexes until controller logic is finalized
|
case .controllers: return 1 // Temporarily hide other controller indexes until controller logic is finalized
|
||||||
|
case .controllerSkins: return System.supportedSystems.count
|
||||||
default: return super.tableView(tableView, numberOfRowsInSection: sectionIndex)
|
default: return super.tableView(tableView, numberOfRowsInSection: sectionIndex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -170,21 +181,26 @@ extension SettingsViewController
|
|||||||
{
|
{
|
||||||
let cell = super.tableView(tableView, cellForRowAt: indexPath)
|
let cell = super.tableView(tableView, cellForRowAt: indexPath)
|
||||||
|
|
||||||
if indexPath.section == Section.controllers.rawValue
|
let section = Section(rawValue: indexPath.section)!
|
||||||
|
switch section
|
||||||
{
|
{
|
||||||
|
case .controllers:
|
||||||
if indexPath.row == Settings.localControllerPlayerIndex
|
if indexPath.row == Settings.localControllerPlayerIndex
|
||||||
{
|
{
|
||||||
cell.detailTextLabel?.text = UIDevice.current.name
|
cell.detailTextLabel?.text = UIDevice.current.name
|
||||||
}
|
}
|
||||||
else if let index = ExternalControllerManager.shared.connectedControllers.index(where: { $0.playerIndex == indexPath.row })
|
else if let index = ExternalGameControllerManager.shared.connectedControllers.index(where: { $0.playerIndex == indexPath.row })
|
||||||
{
|
{
|
||||||
let controller = ExternalControllerManager.shared.connectedControllers[index]
|
let controller = ExternalGameControllerManager.shared.connectedControllers[index]
|
||||||
cell.detailTextLabel?.text = controller.name
|
cell.detailTextLabel?.text = controller.name
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
cell.detailTextLabel?.text = nil
|
cell.detailTextLabel?.text = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case .controllerSkins: cell.textLabel?.text = System.supportedSystems[indexPath.row].localizedName
|
||||||
|
default: break
|
||||||
}
|
}
|
||||||
|
|
||||||
return cell
|
return cell
|
||||||
|
|||||||
@ -60,6 +60,20 @@
|
|||||||
<string>com.pkware.zip-archive</string>
|
<string>com.pkware.zip-archive</string>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleTypeIconFiles</key>
|
||||||
|
<array/>
|
||||||
|
<key>CFBundleTypeName</key>
|
||||||
|
<string>GBC Game</string>
|
||||||
|
<key>CFBundleTypeRole</key>
|
||||||
|
<string>Viewer</string>
|
||||||
|
<key>LSHandlerRank</key>
|
||||||
|
<string>Owner</string>
|
||||||
|
<key>LSItemContentTypes</key>
|
||||||
|
<array>
|
||||||
|
<string>com.rileytestut.delta.game.gbc</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>$(EXECUTABLE_NAME)</string>
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
@ -112,6 +126,8 @@
|
|||||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
</array>
|
</array>
|
||||||
|
<key>NSPhotoLibraryUsageDescription</key>
|
||||||
|
<string>Press "OK" to allow Delta to use images from your Photo Library as game artwork.</string>
|
||||||
<key>UTExportedTypeDeclarations</key>
|
<key>UTExportedTypeDeclarations</key>
|
||||||
<array>
|
<array>
|
||||||
<dict>
|
<dict>
|
||||||
@ -179,6 +195,38 @@
|
|||||||
<string>deltaskin</string>
|
<string>deltaskin</string>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
|
<dict>
|
||||||
|
<key>UTTypeConformsTo</key>
|
||||||
|
<array>
|
||||||
|
<string>com.rileytestut.delta.game</string>
|
||||||
</array>
|
</array>
|
||||||
|
<key>UTTypeDescription</key>
|
||||||
|
<string>GBC Game</string>
|
||||||
|
<key>UTTypeIdentifier</key>
|
||||||
|
<string>com.rileytestut.delta.game.gbc</string>
|
||||||
|
<key>UTTypeTagSpecification</key>
|
||||||
|
<dict>
|
||||||
|
<key>public.filename-extension</key>
|
||||||
|
<array>
|
||||||
|
<string>gbc</string>
|
||||||
|
<string>gb</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>Fabric</key>
|
||||||
|
<dict>
|
||||||
|
<key>APIKey</key>
|
||||||
|
<string>d542629b4f6625cfd5564d27318550321272076d</string>
|
||||||
|
<key>Kits</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>KitInfo</key>
|
||||||
|
<dict/>
|
||||||
|
<key>KitName</key>
|
||||||
|
<string>Crashlytics</string>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
103
Delta/Systems/System.swift
Normal file
103
Delta/Systems/System.swift
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
//
|
||||||
|
// System.swift
|
||||||
|
// Delta
|
||||||
|
//
|
||||||
|
// Created by Riley Testut on 4/30/17.
|
||||||
|
// Copyright © 2017 Riley Testut. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import DeltaCore
|
||||||
|
|
||||||
|
import SNESDeltaCore
|
||||||
|
import GBADeltaCore
|
||||||
|
import GBCDeltaCore
|
||||||
|
|
||||||
|
extension GameType
|
||||||
|
{
|
||||||
|
init?(fileExtension: String)
|
||||||
|
{
|
||||||
|
switch fileExtension.lowercased()
|
||||||
|
{
|
||||||
|
case "smc", "sfc", "fig": self = .snes
|
||||||
|
case "gba": self = .gba
|
||||||
|
case "gbc", "gb": self = .gbc
|
||||||
|
default: return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum System
|
||||||
|
{
|
||||||
|
case snes
|
||||||
|
case gba
|
||||||
|
case gbc
|
||||||
|
|
||||||
|
static var supportedSystems: [System] {
|
||||||
|
return [.snes, .gba, .gbc]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension System
|
||||||
|
{
|
||||||
|
var localizedName: String {
|
||||||
|
switch self
|
||||||
|
{
|
||||||
|
case .snes: return NSLocalizedString("Super Nintendo", comment: "")
|
||||||
|
case .gba: return NSLocalizedString("Game Boy Advance", comment: "")
|
||||||
|
case .gbc: return NSLocalizedString("Game Boy Color", comment: "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var localizedShortName: String {
|
||||||
|
switch self
|
||||||
|
{
|
||||||
|
case .snes: return NSLocalizedString("SNES", comment: "")
|
||||||
|
case .gba: return NSLocalizedString("GBA", comment: "")
|
||||||
|
case .gbc: return NSLocalizedString("GBC", comment: "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var year: Int {
|
||||||
|
switch self
|
||||||
|
{
|
||||||
|
case .snes: return 1990
|
||||||
|
case .gba: return 2001
|
||||||
|
case .gbc: return 1998
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension System
|
||||||
|
{
|
||||||
|
var deltaCore: DeltaCoreProtocol {
|
||||||
|
switch self
|
||||||
|
{
|
||||||
|
case .snes: return SNES.core
|
||||||
|
case .gba: return GBA.core
|
||||||
|
case .gbc: return GBC.core
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension System
|
||||||
|
{
|
||||||
|
var gameType: GameType {
|
||||||
|
switch self
|
||||||
|
{
|
||||||
|
case .snes: return .snes
|
||||||
|
case .gba: return .gba
|
||||||
|
case .gbc: return .gbc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init?(gameType: GameType)
|
||||||
|
{
|
||||||
|
switch gameType
|
||||||
|
{
|
||||||
|
case GameType.snes: self = .snes
|
||||||
|
case GameType.gba: self = .gba
|
||||||
|
case GameType.gbc: self = .gbc
|
||||||
|
default: return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
External/Roxas
vendored
2
External/Roxas
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 9143d01afa25f07da8f1cdb5390b49f4efb1a75f
|
Subproject commit 192682bb389830944d257946d870e1d50ae4c42b
|
||||||
3
Podfile
3
Podfile
@ -7,4 +7,7 @@ target 'Delta' do
|
|||||||
pod 'FileMD5Hash', '~> 2.0.0'
|
pod 'FileMD5Hash', '~> 2.0.0'
|
||||||
pod 'SQLite.swift', '~> 0.11.0'
|
pod 'SQLite.swift', '~> 0.11.0'
|
||||||
pod 'SDWebImage', '~> 3.8'
|
pod 'SDWebImage', '~> 3.8'
|
||||||
|
pod 'Fabric', '~> 1.6.0'
|
||||||
|
pod 'Crashlytics', '~> 3.8.0'
|
||||||
|
pod 'SMCalloutView'
|
||||||
end
|
end
|
||||||
22
Podfile.lock
22
Podfile.lock
@ -1,22 +1,32 @@
|
|||||||
PODS:
|
PODS:
|
||||||
|
- Crashlytics (3.8.6):
|
||||||
|
- Fabric (~> 1.6.3)
|
||||||
|
- Fabric (1.6.13)
|
||||||
- FileMD5Hash (2.0.0)
|
- FileMD5Hash (2.0.0)
|
||||||
- SDWebImage (3.8.2):
|
- SDWebImage (3.8.2):
|
||||||
- SDWebImage/Core (= 3.8.2)
|
- SDWebImage/Core (= 3.8.2)
|
||||||
- SDWebImage/Core (3.8.2)
|
- SDWebImage/Core (3.8.2)
|
||||||
- SQLite.swift (0.11.3):
|
- SMCalloutView (2.1.5)
|
||||||
- SQLite.swift/standard (= 0.11.3)
|
- SQLite.swift (0.11.4):
|
||||||
- SQLite.swift/standard (0.11.3)
|
- SQLite.swift/standard (= 0.11.4)
|
||||||
|
- SQLite.swift/standard (0.11.4)
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
|
- Crashlytics (~> 3.8.0)
|
||||||
|
- Fabric (~> 1.6.0)
|
||||||
- FileMD5Hash (~> 2.0.0)
|
- FileMD5Hash (~> 2.0.0)
|
||||||
- SDWebImage (~> 3.8)
|
- SDWebImage (~> 3.8)
|
||||||
|
- SMCalloutView
|
||||||
- SQLite.swift (~> 0.11.0)
|
- SQLite.swift (~> 0.11.0)
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
|
Crashlytics: 95d05f4e4c19a771250c4bd9ce344d996de32bbf
|
||||||
|
Fabric: 2fb5676bc811af011a04513451f463dac6803206
|
||||||
FileMD5Hash: 3ed69cc19a21ff4d30ae8833fc104275ad2c7de0
|
FileMD5Hash: 3ed69cc19a21ff4d30ae8833fc104275ad2c7de0
|
||||||
SDWebImage: '098e97e6176540799c27e804c96653ee0833d13c'
|
SDWebImage: '098e97e6176540799c27e804c96653ee0833d13c'
|
||||||
SQLite.swift: 99b36c22084427f0abbeb957556ce1528cf10bb3
|
SMCalloutView: 5c0ee363dc8e7204b2fda17dfad38c93e9e23481
|
||||||
|
SQLite.swift: 3e3bee21da701b5b9f87c4a672cb54f233505692
|
||||||
|
|
||||||
PODFILE CHECKSUM: e583277236d810a5e080371e73349e52a4aaa25e
|
PODFILE CHECKSUM: a1fb0ce1f1bb0e73380460cc4f946d297a4d5ff1
|
||||||
|
|
||||||
COCOAPODS: 1.2.0
|
COCOAPODS: 1.2.1
|
||||||
|
|||||||
1
Pods/Crashlytics/Crashlytics.framework/README
generated
vendored
Normal file
1
Pods/Crashlytics/Crashlytics.framework/README
generated
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
We've now combined all our supported platforms into a single podspec. As a result, we moved our submit script to a new location for Cocoapods projects: ${PODS_ROOT}/Crashlytics/submit. To avoid breaking functionality that references the old location of the submit, we've placed this dummy script that calls to the correct location, while providing a helpful warning if it is invoked. This bridge for backwards compatibility will be removed in a future release, so please heed the warning!
|
||||||
6
Pods/Crashlytics/Crashlytics.framework/submit
generated
vendored
Executable file
6
Pods/Crashlytics/Crashlytics.framework/submit
generated
vendored
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
if [[ -z $PODS_ROOT ]]; then
|
||||||
|
echo "error: The submit binary delivered by cocoapods is in a new location, under '$"{"PODS_ROOT"}"/Crashlytics/submit'. This script was put in place for backwards compatibility, but it relies on PODS_ROOT, which does not have a value in your current setup. Please update the path to the submit binary to fix this issue."
|
||||||
|
else
|
||||||
|
echo "warning: The submit script is now located at '$"{"PODS_ROOT"}"/Crashlytics/submit'. To remove this warning, update your path to point to this new location."
|
||||||
|
sh "${PODS_ROOT}/Crashlytics/submit" "$@"
|
||||||
|
fi
|
||||||
39
Pods/Crashlytics/README.md
generated
Normal file
39
Pods/Crashlytics/README.md
generated
Normal file
@ -0,0 +1,39 @@
|
|||||||
|

|
||||||
|
|
||||||
|
Part of [Google Fabric](https://get.fabric.io), [Crashlytics](http://try.crashlytics.com/) offers the most powerful, yet lightest weight crash reporting solution for iOS. Crashlytics also provides real-time analytics through [Answers](https://answers.io/) and app distributions to testers using [Beta](http://try.crashlytics.com/beta/).
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
1. Visit [https://fabric.io/sign_up](https://fabric.io/sign_up) to create your Fabric account and to download Fabric.app.
|
||||||
|
|
||||||
|
1. Open Fabric.app, login and select the Crashlytics SDK.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
1. The Fabric app automatically detects when a project uses CocoaPods and gives you the option to install via the Podfile or Xcode.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
1. Select the Podfile option and follow the installation instructions to update your Podfile. **Note:** the Crashlytics Pod includes Answers. If you have Answers included as a separate Pod it should be removed from your Podfile to avoid duplicate symbol errors.
|
||||||
|
|
||||||
|
```
|
||||||
|
pod 'Fabric'
|
||||||
|
pod 'Crashlytics'
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Run `pod install`
|
||||||
|
|
||||||
|
1. Add a Run Script Build Phase and build your app.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
1. Initialize the SDK by inserting code outlined in the Fabric.app.
|
||||||
|
|
||||||
|
1. Run your app to finish the installation.
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
* [Documentation](https://docs.fabric.io/apple/crashlytics/overview.html)
|
||||||
|
* [Forums](https://stackoverflow.com/questions/tagged/google-fabric)
|
||||||
|
* [Website](http://try.crashlytics.com/)
|
||||||
|
* Follow us on Twitter: [@fabric](https://twitter.com/fabric) and [@crashlytics](https://twitter.com/crashlytics)
|
||||||
BIN
Pods/Crashlytics/iOS/Crashlytics.framework/Crashlytics
generated
vendored
Executable file
BIN
Pods/Crashlytics/iOS/Crashlytics.framework/Crashlytics
generated
vendored
Executable file
Binary file not shown.
31
Pods/Crashlytics/iOS/Crashlytics.framework/Headers/ANSCompatibility.h
generated
vendored
Normal file
31
Pods/Crashlytics/iOS/Crashlytics.framework/Headers/ANSCompatibility.h
generated
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
//
|
||||||
|
// ANSCompatibility.h
|
||||||
|
// AnswersKit
|
||||||
|
//
|
||||||
|
// Copyright (c) 2015 Crashlytics, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if !__has_feature(nullability)
|
||||||
|
#define nonnull
|
||||||
|
#define nullable
|
||||||
|
#define _Nullable
|
||||||
|
#define _Nonnull
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NS_ASSUME_NONNULL_BEGIN
|
||||||
|
#define NS_ASSUME_NONNULL_BEGIN
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NS_ASSUME_NONNULL_END
|
||||||
|
#define NS_ASSUME_NONNULL_END
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __has_feature(objc_generics)
|
||||||
|
#define ANS_GENERIC_NSARRAY(type) NSArray<type>
|
||||||
|
#define ANS_GENERIC_NSDICTIONARY(key_type,object_key) NSDictionary<key_type, object_key>
|
||||||
|
#else
|
||||||
|
#define ANS_GENERIC_NSARRAY(type) NSArray
|
||||||
|
#define ANS_GENERIC_NSDICTIONARY(key_type,object_key) NSDictionary
|
||||||
|
#endif
|
||||||
210
Pods/Crashlytics/iOS/Crashlytics.framework/Headers/Answers.h
generated
vendored
Normal file
210
Pods/Crashlytics/iOS/Crashlytics.framework/Headers/Answers.h
generated
vendored
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
//
|
||||||
|
// Answers.h
|
||||||
|
// Crashlytics
|
||||||
|
//
|
||||||
|
// Copyright (c) 2015 Crashlytics, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import "ANSCompatibility.h"
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class exposes the Answers Events API, allowing you to track key
|
||||||
|
* user user actions and metrics in your app.
|
||||||
|
*/
|
||||||
|
@interface Answers : NSObject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log a Sign Up event to see users signing up for your app in real-time, understand how
|
||||||
|
* many users are signing up with different methods and their success rate signing up.
|
||||||
|
*
|
||||||
|
* @param signUpMethodOrNil The method by which a user logged in, e.g. Twitter or Digits.
|
||||||
|
* @param signUpSucceededOrNil The ultimate success or failure of the login
|
||||||
|
* @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
|
||||||
|
*/
|
||||||
|
+ (void)logSignUpWithMethod:(nullable NSString *)signUpMethodOrNil
|
||||||
|
success:(nullable NSNumber *)signUpSucceededOrNil
|
||||||
|
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log an Log In event to see users logging into your app in real-time, understand how many
|
||||||
|
* users are logging in with different methods and their success rate logging into your app.
|
||||||
|
*
|
||||||
|
* @param loginMethodOrNil The method by which a user logged in, e.g. email, Twitter or Digits.
|
||||||
|
* @param loginSucceededOrNil The ultimate success or failure of the login
|
||||||
|
* @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
|
||||||
|
*/
|
||||||
|
+ (void)logLoginWithMethod:(nullable NSString *)loginMethodOrNil
|
||||||
|
success:(nullable NSNumber *)loginSucceededOrNil
|
||||||
|
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log a Share event to see users sharing from your app in real-time, letting you
|
||||||
|
* understand what content they're sharing from the type or genre down to the specific id.
|
||||||
|
*
|
||||||
|
* @param shareMethodOrNil The method by which a user shared, e.g. email, Twitter, SMS.
|
||||||
|
* @param contentNameOrNil The human readable name for this piece of content.
|
||||||
|
* @param contentTypeOrNil The type of content shared.
|
||||||
|
* @param contentIdOrNil The unique identifier for this piece of content. Useful for finding the top shared item.
|
||||||
|
* @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
|
||||||
|
*/
|
||||||
|
+ (void)logShareWithMethod:(nullable NSString *)shareMethodOrNil
|
||||||
|
contentName:(nullable NSString *)contentNameOrNil
|
||||||
|
contentType:(nullable NSString *)contentTypeOrNil
|
||||||
|
contentId:(nullable NSString *)contentIdOrNil
|
||||||
|
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log an Invite Event to track how users are inviting other users into
|
||||||
|
* your application.
|
||||||
|
*
|
||||||
|
* @param inviteMethodOrNil The method of invitation, e.g. GameCenter, Twitter, email.
|
||||||
|
* @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
|
||||||
|
*/
|
||||||
|
+ (void)logInviteWithMethod:(nullable NSString *)inviteMethodOrNil
|
||||||
|
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log a Purchase event to see your revenue in real-time, understand how many users are making purchases, see which
|
||||||
|
* items are most popular, and track plenty of other important purchase-related metrics.
|
||||||
|
*
|
||||||
|
* @param itemPriceOrNil The purchased item's price.
|
||||||
|
* @param currencyOrNil The ISO4217 currency code. Example: USD
|
||||||
|
* @param purchaseSucceededOrNil Was the purchase succesful or unsuccesful
|
||||||
|
* @param itemNameOrNil The human-readable form of the item's name. Example:
|
||||||
|
* @param itemTypeOrNil The type, or genre of the item. Example: Song
|
||||||
|
* @param itemIdOrNil The machine-readable, unique item identifier Example: SKU
|
||||||
|
* @param customAttributesOrNil A dictionary of custom attributes to associate with this purchase.
|
||||||
|
*/
|
||||||
|
+ (void)logPurchaseWithPrice:(nullable NSDecimalNumber *)itemPriceOrNil
|
||||||
|
currency:(nullable NSString *)currencyOrNil
|
||||||
|
success:(nullable NSNumber *)purchaseSucceededOrNil
|
||||||
|
itemName:(nullable NSString *)itemNameOrNil
|
||||||
|
itemType:(nullable NSString *)itemTypeOrNil
|
||||||
|
itemId:(nullable NSString *)itemIdOrNil
|
||||||
|
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log a Level Start Event to track where users are in your game.
|
||||||
|
*
|
||||||
|
* @param levelNameOrNil The level name
|
||||||
|
* @param customAttributesOrNil A dictionary of custom attributes to associate with this level start event.
|
||||||
|
*/
|
||||||
|
+ (void)logLevelStart:(nullable NSString *)levelNameOrNil
|
||||||
|
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log a Level End event to track how users are completing levels in your game.
|
||||||
|
*
|
||||||
|
* @param levelNameOrNil The name of the level completed, E.G. "1" or "Training"
|
||||||
|
* @param scoreOrNil The score the user completed the level with.
|
||||||
|
* @param levelCompletedSuccesfullyOrNil A boolean representing whether or not the level was completed succesfully.
|
||||||
|
* @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
|
||||||
|
*/
|
||||||
|
+ (void)logLevelEnd:(nullable NSString *)levelNameOrNil
|
||||||
|
score:(nullable NSNumber *)scoreOrNil
|
||||||
|
success:(nullable NSNumber *)levelCompletedSuccesfullyOrNil
|
||||||
|
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log an Add to Cart event to see users adding items to a shopping cart in real-time, understand how
|
||||||
|
* many users start the purchase flow, see which items are most popular, and track plenty of other important
|
||||||
|
* purchase-related metrics.
|
||||||
|
*
|
||||||
|
* @param itemPriceOrNil The purchased item's price.
|
||||||
|
* @param currencyOrNil The ISO4217 currency code. Example: USD
|
||||||
|
* @param itemNameOrNil The human-readable form of the item's name. Example:
|
||||||
|
* @param itemTypeOrNil The type, or genre of the item. Example: Song
|
||||||
|
* @param itemIdOrNil The machine-readable, unique item identifier Example: SKU
|
||||||
|
* @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
|
||||||
|
*/
|
||||||
|
+ (void)logAddToCartWithPrice:(nullable NSDecimalNumber *)itemPriceOrNil
|
||||||
|
currency:(nullable NSString *)currencyOrNil
|
||||||
|
itemName:(nullable NSString *)itemNameOrNil
|
||||||
|
itemType:(nullable NSString *)itemTypeOrNil
|
||||||
|
itemId:(nullable NSString *)itemIdOrNil
|
||||||
|
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log a Start Checkout event to see users moving through the purchase funnel in real-time, understand how many
|
||||||
|
* users are doing this and how much they're spending per checkout, and see how it related to other important
|
||||||
|
* purchase-related metrics.
|
||||||
|
*
|
||||||
|
* @param totalPriceOrNil The total price of the cart.
|
||||||
|
* @param currencyOrNil The ISO4217 currency code. Example: USD
|
||||||
|
* @param itemCountOrNil The number of items in the cart.
|
||||||
|
* @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
|
||||||
|
*/
|
||||||
|
+ (void)logStartCheckoutWithPrice:(nullable NSDecimalNumber *)totalPriceOrNil
|
||||||
|
currency:(nullable NSString *)currencyOrNil
|
||||||
|
itemCount:(nullable NSNumber *)itemCountOrNil
|
||||||
|
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log a Rating event to see users rating content within your app in real-time and understand what
|
||||||
|
* content is most engaging, from the type or genre down to the specific id.
|
||||||
|
*
|
||||||
|
* @param ratingOrNil The integer rating given by the user.
|
||||||
|
* @param contentNameOrNil The human readable name for this piece of content.
|
||||||
|
* @param contentTypeOrNil The type of content shared.
|
||||||
|
* @param contentIdOrNil The unique identifier for this piece of content. Useful for finding the top shared item.
|
||||||
|
* @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
|
||||||
|
*/
|
||||||
|
+ (void)logRating:(nullable NSNumber *)ratingOrNil
|
||||||
|
contentName:(nullable NSString *)contentNameOrNil
|
||||||
|
contentType:(nullable NSString *)contentTypeOrNil
|
||||||
|
contentId:(nullable NSString *)contentIdOrNil
|
||||||
|
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log a Content View event to see users viewing content within your app in real-time and
|
||||||
|
* understand what content is most engaging, from the type or genre down to the specific id.
|
||||||
|
*
|
||||||
|
* @param contentNameOrNil The human readable name for this piece of content.
|
||||||
|
* @param contentTypeOrNil The type of content shared.
|
||||||
|
* @param contentIdOrNil The unique identifier for this piece of content. Useful for finding the top shared item.
|
||||||
|
* @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
|
||||||
|
*/
|
||||||
|
+ (void)logContentViewWithName:(nullable NSString *)contentNameOrNil
|
||||||
|
contentType:(nullable NSString *)contentTypeOrNil
|
||||||
|
contentId:(nullable NSString *)contentIdOrNil
|
||||||
|
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log a Search event allows you to see users searching within your app in real-time and understand
|
||||||
|
* exactly what they're searching for.
|
||||||
|
*
|
||||||
|
* @param queryOrNil The user's query.
|
||||||
|
* @param customAttributesOrNil A dictionary of custom attributes to associate with this event.
|
||||||
|
*/
|
||||||
|
+ (void)logSearchWithQuery:(nullable NSString *)queryOrNil
|
||||||
|
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log a Custom Event to see user actions that are uniquely important for your app in real-time, to see how often
|
||||||
|
* they're performing these actions with breakdowns by different categories you add. Use a human-readable name for
|
||||||
|
* the name of the event, since this is how the event will appear in Answers.
|
||||||
|
*
|
||||||
|
* @param eventName The human-readable name for the event.
|
||||||
|
* @param customAttributesOrNil A dictionary of custom attributes to associate with this event. Attribute keys
|
||||||
|
* must be <code>NSString</code> and and values must be <code>NSNumber</code> or <code>NSString</code>.
|
||||||
|
* @discussion How we treat <code>NSNumbers</code>:
|
||||||
|
* We will provide information about the distribution of values over time.
|
||||||
|
*
|
||||||
|
* How we treat <code>NSStrings</code>:
|
||||||
|
* NSStrings are used as categorical data, allowing comparison across different category values.
|
||||||
|
* Strings are limited to a maximum length of 100 characters, attributes over this length will be
|
||||||
|
* truncated.
|
||||||
|
*
|
||||||
|
* When tracking the Tweet views to better understand user engagement, sending the tweet's length
|
||||||
|
* and the type of media present in the tweet allows you to track how tweet length and the type of media influence
|
||||||
|
* engagement.
|
||||||
|
*/
|
||||||
|
+ (void)logCustomEventWithName:(NSString *)eventName
|
||||||
|
customAttributes:(nullable ANS_GENERIC_NSDICTIONARY(NSString *, id) *)customAttributesOrNil;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
33
Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSAttributes.h
generated
vendored
Normal file
33
Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSAttributes.h
generated
vendored
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
//
|
||||||
|
// CLSAttributes.h
|
||||||
|
// Crashlytics
|
||||||
|
//
|
||||||
|
// Copyright (c) 2015 Crashlytics, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define CLS_DEPRECATED(x) __attribute__ ((deprecated(x)))
|
||||||
|
|
||||||
|
#if !__has_feature(nullability)
|
||||||
|
#define nonnull
|
||||||
|
#define nullable
|
||||||
|
#define _Nullable
|
||||||
|
#define _Nonnull
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NS_ASSUME_NONNULL_BEGIN
|
||||||
|
#define NS_ASSUME_NONNULL_BEGIN
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NS_ASSUME_NONNULL_END
|
||||||
|
#define NS_ASSUME_NONNULL_END
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __has_feature(objc_generics)
|
||||||
|
#define CLS_GENERIC_NSARRAY(type) NSArray<type>
|
||||||
|
#define CLS_GENERIC_NSDICTIONARY(key_type,object_key) NSDictionary<key_type, object_key>
|
||||||
|
#else
|
||||||
|
#define CLS_GENERIC_NSARRAY(type) NSArray
|
||||||
|
#define CLS_GENERIC_NSDICTIONARY(key_type,object_key) NSDictionary
|
||||||
|
#endif
|
||||||
64
Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSLogging.h
generated
vendored
Normal file
64
Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSLogging.h
generated
vendored
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
//
|
||||||
|
// CLSLogging.h
|
||||||
|
// Crashlytics
|
||||||
|
//
|
||||||
|
// Copyright (c) 2015 Crashlytics, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
#ifdef __OBJC__
|
||||||
|
#import "CLSAttributes.h"
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* The CLS_LOG macro provides as easy way to gather more information in your log messages that are
|
||||||
|
* sent with your crash data. CLS_LOG prepends your custom log message with the function name and
|
||||||
|
* line number where the macro was used. If your app was built with the DEBUG preprocessor macro
|
||||||
|
* defined CLS_LOG uses the CLSNSLog function which forwards your log message to NSLog and CLSLog.
|
||||||
|
* If the DEBUG preprocessor macro is not defined CLS_LOG uses CLSLog only.
|
||||||
|
*
|
||||||
|
* Example output:
|
||||||
|
* -[AppDelegate login:] line 134 $ login start
|
||||||
|
*
|
||||||
|
* If you would like to change this macro, create a new header file, unset our define and then define
|
||||||
|
* your own version. Make sure this new header file is imported after the Crashlytics header file.
|
||||||
|
*
|
||||||
|
* #undef CLS_LOG
|
||||||
|
* #define CLS_LOG(__FORMAT__, ...) CLSNSLog...
|
||||||
|
*
|
||||||
|
**/
|
||||||
|
#ifdef __OBJC__
|
||||||
|
#ifdef DEBUG
|
||||||
|
#define CLS_LOG(__FORMAT__, ...) CLSNSLog((@"%s line %d $ " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define CLS_LOG(__FORMAT__, ...) CLSLog((@"%s line %d $ " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Add logging that will be sent with your crash data. This logging will not show up in the system.log
|
||||||
|
* and will only be visible in your Crashlytics dashboard.
|
||||||
|
*
|
||||||
|
**/
|
||||||
|
|
||||||
|
#ifdef __OBJC__
|
||||||
|
OBJC_EXTERN void CLSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2);
|
||||||
|
OBJC_EXTERN void CLSLogv(NSString *format, va_list ap) NS_FORMAT_FUNCTION(1,0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Add logging that will be sent with your crash data. This logging will show up in the system.log
|
||||||
|
* and your Crashlytics dashboard. It is not recommended for Release builds.
|
||||||
|
*
|
||||||
|
**/
|
||||||
|
OBJC_EXTERN void CLSNSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2);
|
||||||
|
OBJC_EXTERN void CLSNSLogv(NSString *format, va_list ap) NS_FORMAT_FUNCTION(1,0);
|
||||||
|
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
|
#endif
|
||||||
103
Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSReport.h
generated
vendored
Normal file
103
Pods/Crashlytics/iOS/Crashlytics.framework/Headers/CLSReport.h
generated
vendored
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
//
|
||||||
|
// CLSReport.h
|
||||||
|
// Crashlytics
|
||||||
|
//
|
||||||
|
// Copyright (c) 2015 Crashlytics, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import "CLSAttributes.h"
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The CLSCrashReport protocol is deprecated. See the CLSReport class and the CrashyticsDelegate changes for details.
|
||||||
|
**/
|
||||||
|
@protocol CLSCrashReport <NSObject>
|
||||||
|
|
||||||
|
@property (nonatomic, copy, readonly) NSString *identifier;
|
||||||
|
@property (nonatomic, copy, readonly) NSDictionary *customKeys;
|
||||||
|
@property (nonatomic, copy, readonly) NSString *bundleVersion;
|
||||||
|
@property (nonatomic, copy, readonly) NSString *bundleShortVersionString;
|
||||||
|
@property (nonatomic, readonly, nullable) NSDate *crashedOnDate;
|
||||||
|
@property (nonatomic, copy, readonly) NSString *OSVersion;
|
||||||
|
@property (nonatomic, copy, readonly) NSString *OSBuildVersion;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The CLSReport exposes an interface to the phsyical report that Crashlytics has created. You can
|
||||||
|
* use this class to get information about the event, and can also set some values after the
|
||||||
|
* event has occured.
|
||||||
|
**/
|
||||||
|
@interface CLSReport : NSObject <CLSCrashReport>
|
||||||
|
|
||||||
|
- (instancetype)init NS_UNAVAILABLE;
|
||||||
|
+ (instancetype)new NS_UNAVAILABLE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the session identifier for the report.
|
||||||
|
**/
|
||||||
|
@property (nonatomic, copy, readonly) NSString *identifier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the custom key value data for the report.
|
||||||
|
**/
|
||||||
|
@property (nonatomic, copy, readonly) NSDictionary *customKeys;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the CFBundleVersion of the application that generated the report.
|
||||||
|
**/
|
||||||
|
@property (nonatomic, copy, readonly) NSString *bundleVersion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the CFBundleShortVersionString of the application that generated the report.
|
||||||
|
**/
|
||||||
|
@property (nonatomic, copy, readonly) NSString *bundleShortVersionString;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the date that the report was created.
|
||||||
|
**/
|
||||||
|
@property (nonatomic, copy, readonly) NSDate *dateCreated;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the os version that the application crashed on.
|
||||||
|
**/
|
||||||
|
@property (nonatomic, copy, readonly) NSString *OSVersion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the os build version that the application crashed on.
|
||||||
|
**/
|
||||||
|
@property (nonatomic, copy, readonly) NSString *OSBuildVersion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns YES if the report contains any crash information, otherwise returns NO.
|
||||||
|
**/
|
||||||
|
@property (nonatomic, assign, readonly) BOOL isCrash;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* You can use this method to set, after the event, additional custom keys. The rules
|
||||||
|
* and semantics for this method are the same as those documented in Crashlytics.h. Be aware
|
||||||
|
* that the maximum size and count of custom keys is still enforced, and you can overwrite keys
|
||||||
|
* and/or cause excess keys to be deleted by using this method.
|
||||||
|
**/
|
||||||
|
- (void)setObjectValue:(nullable id)value forKey:(NSString *)key;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Record an application-specific user identifier. See Crashlytics.h for details.
|
||||||
|
**/
|
||||||
|
@property (nonatomic, copy, nullable) NSString * userIdentifier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Record a user name. See Crashlytics.h for details.
|
||||||
|
**/
|
||||||
|
@property (nonatomic, copy, nullable) NSString * userName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Record a user email. See Crashlytics.h for details.
|
||||||
|
**/
|
||||||
|
@property (nonatomic, copy, nullable) NSString * userEmail;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user