commit
cfa31678ea
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -31,3 +31,6 @@
|
||||
[submodule "Cores/GPGXDeltaCore"]
|
||||
path = Cores/GPGXDeltaCore
|
||||
url = https://github.com/rileytestut/GPGXDeltaCore.git
|
||||
[submodule "External/CheatBase"]
|
||||
path = External/CheatBase
|
||||
url = https://github.com/rileytestut/CheatBase.git
|
||||
|
||||
@ -1 +1 @@
|
||||
Subproject commit e2b3f0e46b4c64670e13fd0466ebdac719f84555
|
||||
Subproject commit 81362dd5def310f4dd908513574c7e2fd10c7d75
|
||||
@ -1 +1 @@
|
||||
Subproject commit 8523e03358559cebaa36b67bd0a87698df238512
|
||||
Subproject commit c07950a584ed3153b90349059629db85b1c330ac
|
||||
@ -1 +1 @@
|
||||
Subproject commit 4313fa6670ab534e70d13532c2504761f849c432
|
||||
Subproject commit 338a3b32c01413999e390c681dc336fdcdff1ca1
|
||||
@ -1 +1 @@
|
||||
Subproject commit ad4ba365acf1800d372ebfaa98df86b9c1b23dce
|
||||
Subproject commit aed30506c77096c3e854c9a38fe9cc893e9480a1
|
||||
@ -1 +1 @@
|
||||
Subproject commit d6b33d89043898d521546d9062d12a505b7d2101
|
||||
Subproject commit 16f79982e468137c3bfe11a2dbff97423a5ce128
|
||||
@ -1 +1 @@
|
||||
Subproject commit e598f512b498e1b639a8d2134113169f4b8d0d26
|
||||
Subproject commit c581c6f51efd61dc5891fac22dbada7df347c003
|
||||
@ -1 +1 @@
|
||||
Subproject commit 78fa7db707655962a1077f4681c35fcf81510060
|
||||
Subproject commit e5221a06ff2ff830bf60302ef0678fe5e554a925
|
||||
@ -1 +1 @@
|
||||
Subproject commit 7539cbaac26a3d2ca9daf47ba22d1b0ebbc41a2b
|
||||
Subproject commit 2ef4c123b43565688f7b7d3aa5099559b633f7ba
|
||||
@ -3,7 +3,7 @@
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objectVersion = 52;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXAggregateTarget section */
|
||||
@ -165,6 +165,20 @@
|
||||
BFFDF03723E3BB2600931B96 /* libSnes9x.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BFFDF03623E3BB2600931B96 /* libSnes9x.a */; };
|
||||
BFFDF03F23E3C28A00931B96 /* libGambatte.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BFFDF03D23E3C0F000931B96 /* libGambatte.a */; };
|
||||
BFFDF04623E3D3A600931B96 /* libMupen64Plus.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BFFDF04523E3D3A600931B96 /* libMupen64Plus.a */; };
|
||||
D524F4A1273DE9A100D500B2 /* AltKit in Frameworks */ = {isa = PBXBuildFile; productRef = D524F4A0273DE9A100D500B2 /* AltKit */; };
|
||||
D524F4A3273DE9C000D500B2 /* ProcessInfo+JIT.swift in Sources */ = {isa = PBXBuildFile; fileRef = D524F4A2273DE9C000D500B2 /* ProcessInfo+JIT.swift */; };
|
||||
D524F4A5273DEBB400D500B2 /* ServerManager+Delta.swift in Sources */ = {isa = PBXBuildFile; fileRef = D524F4A4273DEBB400D500B2 /* ServerManager+Delta.swift */; };
|
||||
D5011C48281B6E8B00A0760B /* CharacterSet+Filename.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5011C47281B6E8B00A0760B /* CharacterSet+Filename.swift */; };
|
||||
D53415A5298C782A00FD67B1 /* ContributorsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D53415A4298C782A00FD67B1 /* ContributorsView.swift */; };
|
||||
D53415A7298C78BC00FD67B1 /* Contributors.plist in Resources */ = {isa = PBXBuildFile; fileRef = D53415A6298C78BC00FD67B1 /* Contributors.plist */; };
|
||||
D5864970297734280081477E /* CheatMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = D586496F297734280081477E /* CheatMetadata.swift */; };
|
||||
D586497229774ABD0081477E /* CheatBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = D586497129774ABD0081477E /* CheatBase.swift */; };
|
||||
D5864978297756CE0081477E /* CheatBaseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5864977297756CE0081477E /* CheatBaseView.swift */; };
|
||||
D5A98CE2284EF14B00E023E5 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5A98CE1284EF14B00E023E5 /* SceneDelegate.swift */; };
|
||||
D5AAF27729884F8600F21ACF /* CheatDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5AAF27629884F8600F21ACF /* CheatDevice.swift */; };
|
||||
D5D797E6298D946200738869 /* Contributor.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5D797E5298D946200738869 /* Contributor.swift */; };
|
||||
D5D797E9298DCC7300738869 /* cheatbase.zip in Resources */ = {isa = PBXBuildFile; fileRef = D5D797E7298DC9E200738869 /* cheatbase.zip */; };
|
||||
D5F82FB82981D3AC00B229AF /* LegacySearchBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F82FB72981D3AC00B229AF /* LegacySearchBar.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@ -358,6 +372,19 @@
|
||||
BFFDF03D23E3C0F000931B96 /* libGambatte.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libGambatte.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
BFFDF04523E3D3A600931B96 /* libMupen64Plus.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libMupen64Plus.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
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>"; };
|
||||
D524F4A2273DE9C000D500B2 /* ProcessInfo+JIT.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProcessInfo+JIT.swift"; sourceTree = "<group>"; };
|
||||
D524F4A4273DEBB400D500B2 /* ServerManager+Delta.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ServerManager+Delta.swift"; sourceTree = "<group>"; };
|
||||
D5011C47281B6E8B00A0760B /* CharacterSet+Filename.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CharacterSet+Filename.swift"; sourceTree = "<group>"; };
|
||||
D53415A4298C782A00FD67B1 /* ContributorsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContributorsView.swift; sourceTree = "<group>"; };
|
||||
D53415A6298C78BC00FD67B1 /* Contributors.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Contributors.plist; sourceTree = "<group>"; };
|
||||
D586496F297734280081477E /* CheatMetadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheatMetadata.swift; sourceTree = "<group>"; };
|
||||
D586497129774ABD0081477E /* CheatBase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheatBase.swift; sourceTree = "<group>"; };
|
||||
D5864977297756CE0081477E /* CheatBaseView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheatBaseView.swift; sourceTree = "<group>"; };
|
||||
D5A98CE1284EF14B00E023E5 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
|
||||
D5AAF27629884F8600F21ACF /* CheatDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheatDevice.swift; sourceTree = "<group>"; };
|
||||
D5D797E5298D946200738869 /* Contributor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Contributor.swift; sourceTree = "<group>"; };
|
||||
D5D797E7298DC9E200738869 /* cheatbase.zip */ = {isa = PBXFileReference; lastKnownFileType = archive.zip; path = cheatbase.zip; sourceTree = "<group>"; };
|
||||
D5F82FB72981D3AC00B229AF /* LegacySearchBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegacySearchBar.swift; sourceTree = "<group>"; };
|
||||
DC866E433B3BA9AE18ABA1EC /* libPods-Delta.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Delta.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
@ -373,6 +400,7 @@
|
||||
BFDCA1E6244EBAA900B8FBDB /* liblibDeSmuME.a in Frameworks */,
|
||||
BFCADF1E25D22FE2008D78FB /* Systems.framework in Frameworks */,
|
||||
BF69FBC923E3A8380051BEEA /* libNestopia.a in Frameworks */,
|
||||
D524F4A1273DE9A100D500B2 /* AltKit in Frameworks */,
|
||||
1FA4ABA79AB72914FE414A61 /* libPods-Delta.a in Frameworks */,
|
||||
BFFDF03F23E3C28A00931B96 /* libGambatte.a in Frameworks */,
|
||||
BFFDF03723E3BB2600931B96 /* libSnes9x.a in Frameworks */,
|
||||
@ -403,6 +431,9 @@
|
||||
BF647A6922FB8FCE0061D76D /* Bundle+SwizzleBundleID.swift */,
|
||||
BFD1EF3F2336BD8800D197CF /* UIDevice+Processor.swift */,
|
||||
BFE56E1823EB7BE00014FECD /* UIImage+SymbolFallback.swift */,
|
||||
D524F4A2273DE9C000D500B2 /* ProcessInfo+JIT.swift */,
|
||||
D524F4A4273DEBB400D500B2 /* ServerManager+Delta.swift */,
|
||||
D5011C47281B6E8B00A0760B /* CharacterSet+Filename.swift */,
|
||||
);
|
||||
path = Extensions;
|
||||
sourceTree = "<group>";
|
||||
@ -514,6 +545,7 @@
|
||||
BF59426D1E09BC5D0051894B /* DatabaseManager.swift */,
|
||||
BF5942711E09BC690051894B /* Model */,
|
||||
BF95E2751E49763D0030E7AD /* OpenVGDB */,
|
||||
D586496E297734060081477E /* Cheats */,
|
||||
);
|
||||
path = Database;
|
||||
sourceTree = "<group>";
|
||||
@ -718,6 +750,7 @@
|
||||
BF11734E1DA32CEC00047DF8 /* Controllers */,
|
||||
BF1DAD5B1D9F574900E752A7 /* Controller Skins */,
|
||||
BF48F74C219A16C100BC2FC1 /* Syncing */,
|
||||
D5D797E4298D944C00738869 /* Contributors */,
|
||||
);
|
||||
path = Settings;
|
||||
sourceTree = "<group>";
|
||||
@ -768,6 +801,8 @@
|
||||
children = (
|
||||
BF6BB2451BB73FE800CCF94A /* Assets.xcassets */,
|
||||
BF02D5D91DDEBB3000A5E131 /* openvgdb.sqlite */,
|
||||
D5D797E7298DC9E200738869 /* cheatbase.zip */,
|
||||
D53415A6298C78BC00FD67B1 /* Contributors.plist */,
|
||||
);
|
||||
path = Resources;
|
||||
sourceTree = "<group>";
|
||||
@ -817,6 +852,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BFFA71DC1AAC406100EE9DD1 /* AppDelegate.swift */,
|
||||
D5A98CE1284EF14B00E023E5 /* SceneDelegate.swift */,
|
||||
BFFA71E01AAC406100EE9DD1 /* Main.storyboard */,
|
||||
BFFC46211D59848000AF2CC6 /* Launch */,
|
||||
BF46894D1AAC469800A2586D /* Game Selection */,
|
||||
@ -877,6 +913,27 @@
|
||||
path = Launch;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D586496E297734060081477E /* Cheats */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D586496F297734280081477E /* CheatMetadata.swift */,
|
||||
D5AAF27629884F8600F21ACF /* CheatDevice.swift */,
|
||||
D586497129774ABD0081477E /* CheatBase.swift */,
|
||||
D5864977297756CE0081477E /* CheatBaseView.swift */,
|
||||
D5F82FB72981D3AC00B229AF /* LegacySearchBar.swift */,
|
||||
);
|
||||
path = Cheats;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D5D797E4298D944C00738869 /* Contributors */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D5D797E5298D946200738869 /* Contributor.swift */,
|
||||
D53415A4298C782A00FD67B1 /* ContributorsView.swift */,
|
||||
);
|
||||
path = Contributors;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FD1E8AE87FA2DB8793F7B937 /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -908,6 +965,7 @@
|
||||
);
|
||||
name = Delta;
|
||||
packageProductDependencies = (
|
||||
D524F4A0273DE9A100D500B2 /* AltKit */,
|
||||
);
|
||||
productName = Delta;
|
||||
productReference = BFFA71D71AAC406100EE9DD1 /* Delta.app */;
|
||||
@ -957,6 +1015,9 @@
|
||||
Base,
|
||||
);
|
||||
mainGroup = BFFA71CE1AAC406100EE9DD1;
|
||||
packageReferences = (
|
||||
D524F49F273DE9A100D500B2 /* XCRemoteSwiftPackageReference "AltKit" */,
|
||||
);
|
||||
productRefGroup = BFFA71D81AAC406100EE9DD1 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
@ -973,12 +1034,14 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
D5D797E9298DCC7300738869 /* cheatbase.zip in Resources */,
|
||||
BFFA71E21AAC406100EE9DD1 /* Main.storyboard in Resources */,
|
||||
BF6BF3211EB82362008E83CD /* GamesDatabase.storyboard in Resources */,
|
||||
BFAB9F88219A4B670080EC7D /* GoogleService-Info.plist in Resources */,
|
||||
BF3540001C5DA3C500C1184C /* PausePresentationControllerContentView.xib in Resources */,
|
||||
BF5E7F461B9A652600AE44F8 /* Settings.storyboard in Resources */,
|
||||
BF02D5DA1DDEBB3000A5E131 /* openvgdb.sqlite in Resources */,
|
||||
D53415A7298C78BC00FD67B1 /* Contributors.plist in Resources */,
|
||||
BF71CF8A1FE904B1001F1613 /* GameTableViewCell.xib in Resources */,
|
||||
BFFC46461D59861000AF2CC6 /* LaunchScreen.storyboard in Resources */,
|
||||
BF1F45AB21AF4B5800EF9895 /* SyncResultsViewController.storyboard in Resources */,
|
||||
@ -1114,6 +1177,7 @@
|
||||
BF525EEA1FF6CD12004AA849 /* DeepLink.swift in Sources */,
|
||||
BF8A334621A4926F00A42FD4 /* GameSyncStatusViewController.swift in Sources */,
|
||||
BF59427E1E09BC830051894B /* Game.swift in Sources */,
|
||||
D5011C48281B6E8B00A0760B /* CharacterSet+Filename.swift in Sources */,
|
||||
BFE593CC21F3F8C2003412A6 /* _GameSave.swift in Sources */,
|
||||
BF63A1A321A4AAAE00EE8F61 /* RecordSyncStatusViewController.swift in Sources */,
|
||||
BFAA1FED1B8AA4FA00495943 /* Settings.swift in Sources */,
|
||||
@ -1137,8 +1201,10 @@
|
||||
BF5942951E09BD1A0051894B /* NSManagedObjectContext+Conveniences.swift in Sources */,
|
||||
BFC3628021ADE2BA00EF2BE6 /* UIAlertController+Error.swift in Sources */,
|
||||
BF353FF91C5D870B00C1184C /* MenuItem.swift in Sources */,
|
||||
D5D797E6298D946200738869 /* Contributor.swift in Sources */,
|
||||
BFDB3418219E4B1700595A62 /* SyncStatusViewController.swift in Sources */,
|
||||
BF18B61F1E2985F900F70067 /* UIAlertController+Importing.swift in Sources */,
|
||||
D586497229774ABD0081477E /* CheatBase.swift in Sources */,
|
||||
BFDD04F11D5E2C27002D450E /* GameCollectionViewController.swift in Sources */,
|
||||
BFE4269E1D9C68E600DC913F /* SaveStatesStoryboardSegue.swift in Sources */,
|
||||
BFFA71DD1AAC406100EE9DD1 /* AppDelegate.swift in Sources */,
|
||||
@ -1146,8 +1212,11 @@
|
||||
BF59426B1E09BBD00051894B /* GridCollectionViewLayout.swift in Sources */,
|
||||
BF6424851F5CBDC900D6AB44 /* UIView+ParentViewController.swift in Sources */,
|
||||
BF04E6FF1DB8625C000F35D3 /* ControllerSkinsViewController.swift in Sources */,
|
||||
D53415A5298C782A00FD67B1 /* ContributorsView.swift in Sources */,
|
||||
BF5942891E09BC8B0051894B /* _GameCollection.swift in Sources */,
|
||||
BF34FA111CF1899D006624C7 /* CheatTextView.swift in Sources */,
|
||||
D524F4A5273DEBB400D500B2 /* ServerManager+Delta.swift in Sources */,
|
||||
D5AAF27729884F8600F21ACF /* CheatDevice.swift in Sources */,
|
||||
BF1F45AD21AF57BA00EF9895 /* HarmonyMetadataKey+Keys.swift in Sources */,
|
||||
BFD1EF402336BD8800D197CF /* UIDevice+Processor.swift in Sources */,
|
||||
BF71CF871FE90006001F1613 /* AppIconShortcutsViewController.swift in Sources */,
|
||||
@ -1156,6 +1225,7 @@
|
||||
BF6BF31C1EB821A0008E83CD /* GamesDatabaseImportOption.swift in Sources */,
|
||||
BFFA4C091E8A24D600D87934 /* GameTableViewCell.swift in Sources */,
|
||||
BFFC46231D5984A000AF2CC6 /* LaunchViewController.swift in Sources */,
|
||||
D5864970297734280081477E /* CheatMetadata.swift in Sources */,
|
||||
BF8DDD241F4F6C880088A21B /* InputCalloutView.swift in Sources */,
|
||||
BF5942701E09BC5D0051894B /* GamesDatabase.swift in Sources */,
|
||||
BF34FA071CF0F510006624C7 /* EditCheatViewController.swift in Sources */,
|
||||
@ -1167,6 +1237,7 @@
|
||||
BFFC461F1D59823500AF2CC6 /* GamesStoryboardSegue.swift in Sources */,
|
||||
BF2B98E61C97E32F00F6D57D /* SaveStatesCollectionHeaderView.swift in Sources */,
|
||||
BFC9B7391CEFCD34008629BB /* CheatsViewController.swift in Sources */,
|
||||
D5864978297756CE0081477E /* CheatBaseView.swift in Sources */,
|
||||
BF353FFF1C5DA3C500C1184C /* PausePresentationController.swift in Sources */,
|
||||
BFFC464C1D5998D600AF2CC6 /* CheatTableViewCell.swift in Sources */,
|
||||
BF5942941E09BD1A0051894B /* NSManagedObject+Conveniences.swift in Sources */,
|
||||
@ -1175,14 +1246,17 @@
|
||||
BF797A2D1C2D339F00F1A000 /* UILabel+FontSize.swift in Sources */,
|
||||
BF80E1D21F13117000847008 /* ControllerInputsViewController.swift in Sources */,
|
||||
BF5942641E09BBB10051894B /* LoadControllerSkinImageOperation.swift in Sources */,
|
||||
D5A98CE2284EF14B00E023E5 /* SceneDelegate.swift in Sources */,
|
||||
BFE56E1923EB7BE00014FECD /* UIImage+SymbolFallback.swift in Sources */,
|
||||
BF6EE5E91F7C5F860051AD6C /* _GameControllerInputMapping.swift in Sources */,
|
||||
BF5942871E09BC8B0051894B /* _ControllerSkin.swift in Sources */,
|
||||
BF95E2791E4982A10030E7AD /* GamesDatabaseBrowserViewController.swift in Sources */,
|
||||
D524F4A3273DE9C000D500B2 /* ProcessInfo+JIT.swift in Sources */,
|
||||
BFDCA1E9244F7E1000B8FBDB /* Delta5ToDelta6.xcmappingmodel in Sources */,
|
||||
BFD097211D3A01B8005A44C2 /* SaveStatesViewController.swift in Sources */,
|
||||
BF6EE5EB1F7C5F8F0051AD6C /* GameControllerInputMapping.swift in Sources */,
|
||||
BF8A333421A484A000A42FD4 /* BadgedTableViewCell.swift in Sources */,
|
||||
D5F82FB82981D3AC00B229AF /* LegacySearchBar.swift in Sources */,
|
||||
BF3540021C5DA3D500C1184C /* PauseStoryboardSegue.swift in Sources */,
|
||||
BF107EC41BF413F000E0C32C /* GamesViewController.swift in Sources */,
|
||||
BF3D6C512202865F0083E05A /* Delta2ToDelta3.xcmappingmodel in Sources */,
|
||||
@ -1407,7 +1481,8 @@
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.rileytestut.Delta;
|
||||
PRODUCT_NAME = Delta;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
@ -1422,11 +1497,11 @@
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 43;
|
||||
CURRENT_PROJECT_VERSION = 61;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
INFOPLIST_FILE = "Delta/Supporting Files/Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
MARKETING_VERSION = 1.3;
|
||||
MARKETING_VERSION = 1.4;
|
||||
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" -DDEBUG";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.rileytestut.Delta;
|
||||
PROVISIONING_PROFILE = "";
|
||||
@ -1438,6 +1513,7 @@
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Delta/Supporting Files/Delta-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@ -1451,21 +1527,23 @@
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 43;
|
||||
CURRENT_PROJECT_VERSION = 61;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
INFOPLIST_FILE = "Delta/Supporting Files/Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
MARKETING_VERSION = 1.3;
|
||||
MARKETING_VERSION = 1.4;
|
||||
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" -DIMPACTOR";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.rileytestut.Delta;
|
||||
PROVISIONING_PROFILE = "";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
STRIP_INSTALLED_PRODUCT = YES;
|
||||
STRIP_STYLE = "non-global";
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_INCLUDE_PATHS = "$(inherited) \"$(SRCROOT)/Cores/GPGXDeltaCore/Sources/GPGXBridge\"";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Delta/Supporting Files/Delta-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
@ -1510,6 +1588,25 @@
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCRemoteSwiftPackageReference section */
|
||||
D524F49F273DE9A100D500B2 /* XCRemoteSwiftPackageReference "AltKit" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/rileytestut/AltKit.git";
|
||||
requirement = {
|
||||
kind = revision;
|
||||
revision = 2fd376df1c79ec06a5c80cc8933da027f65b3148;
|
||||
};
|
||||
};
|
||||
/* End XCRemoteSwiftPackageReference section */
|
||||
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
D524F4A0273DE9A100D500B2 /* AltKit */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = D524F49F273DE9A100D500B2 /* XCRemoteSwiftPackageReference "AltKit" */;
|
||||
productName = AltKit;
|
||||
};
|
||||
/* End XCSwiftPackageProductDependency section */
|
||||
|
||||
/* Begin XCVersionGroup section */
|
||||
BF4828811F9027B600028B97 /* Delta.xcdatamodeld */ = {
|
||||
isa = XCVersionGroup;
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
LastUpgradeVersion = "1020"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "NO"
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
|
||||
@ -1,12 +1,21 @@
|
||||
{
|
||||
"object": {
|
||||
"pins": [
|
||||
{
|
||||
"package": "AltKit",
|
||||
"repositoryURL": "https://github.com/rileytestut/AltKit.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "2fd376df1c79ec06a5c80cc8933da027f65b3148",
|
||||
"version": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "DeltaCore",
|
||||
"repositoryURL": "https://github.com/rileytestut/DeltaCore.git",
|
||||
"state": {
|
||||
"branch": "main",
|
||||
"revision": "eeefb30bd78fb130f1113e823afbce6f4f767cfb",
|
||||
"revision": "27a574a46238817084f80d811b3e6c2884d9cdc0",
|
||||
"version": null
|
||||
}
|
||||
},
|
||||
@ -15,8 +24,8 @@
|
||||
"repositoryURL": "https://github.com/weichsel/ZIPFoundation.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "ec32d62d412578542c0ffb7a6ce34d3e64b43b94",
|
||||
"version": "0.9.11"
|
||||
"revision": "f6a22e7da26314b38bf9befce34ae8e4b2543090",
|
||||
"version": "0.9.15"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@ -10,6 +10,7 @@ import UIKit
|
||||
|
||||
import DeltaCore
|
||||
import Harmony
|
||||
import AltKit
|
||||
|
||||
import Fabric
|
||||
import Crashlytics
|
||||
@ -63,6 +64,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate
|
||||
// Controllers
|
||||
ExternalGameControllerManager.shared.startMonitoring()
|
||||
|
||||
// JIT
|
||||
ServerManager.shared.prepare()
|
||||
|
||||
// Notifications
|
||||
let center = CFNotificationCenterGetDarwinNotifyCenter()
|
||||
CFNotificationCenterAddObserver(center, nil, ReceivedApplicationState, CFNotificationName.altstoreRequestAppState.rawValue, nil, .deliverImmediately)
|
||||
@ -110,6 +114,24 @@ class AppDelegate: UIResponder, UIApplicationDelegate
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 13, *)
|
||||
extension AppDelegate
|
||||
{
|
||||
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration
|
||||
{
|
||||
// Called when a new scene session is being created.
|
||||
// Use this method to select a configuration to create the new scene with.
|
||||
return UISceneConfiguration(name: "Main", sessionRole: connectingSceneSession.role)
|
||||
}
|
||||
|
||||
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>)
|
||||
{
|
||||
// Called when the user discards a scene session.
|
||||
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
|
||||
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
|
||||
}
|
||||
}
|
||||
|
||||
private extension AppDelegate
|
||||
{
|
||||
func registerCores()
|
||||
|
||||
@ -46,6 +46,9 @@
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
</navigationItem>
|
||||
<connections>
|
||||
<outlet property="importButton" destination="FeA-O5-xd2" id="A44-3S-Okz"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="JYx-xE-nis" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="18121" 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="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="ssH-mM-uG6">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18092"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
|
||||
<capability name="Named colors" minToolsVersion="9.0"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
||||
@ -279,26 +279,26 @@
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection headerTitle="Haptic Feedback" footerTitle="When enabled, your device will vibrate in response to touch screen controls." id="aJ4-xT-aZL">
|
||||
<tableViewSection headerTitle="Game Audio" footerTitle="When enabled, Delta will only play game audio if your device isn't silenced." id="aJ4-xT-aZL">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" reuseIdentifier="SwitchCell" id="mRV-Bi-pZ8" customClass="SwitchTableViewCell">
|
||||
<rect key="frame" x="0.0" y="765" width="375" height="44"/>
|
||||
<rect key="frame" x="0.0" y="794.5" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="mRV-Bi-pZ8" id="RtM-ok-LGu">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Buttons" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="mue-uO-1xE">
|
||||
<rect key="frame" x="8" y="11.5" width="302" height="21"/>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Respect Silent Mode" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="mue-uO-1xE">
|
||||
<rect key="frame" x="16" y="11.5" width="286" height="21"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="mlO-iy-zU2">
|
||||
<rect key="frame" x="318" y="6.5" width="51" height="31"/>
|
||||
<rect key="frame" x="310" y="6.5" width="51" height="31"/>
|
||||
<color key="onTintColor" name="Purple"/>
|
||||
<connections>
|
||||
<action selector="toggleButtonHapticFeedbackEnabled:" destination="eHi-aO-uGS" eventType="primaryActionTriggered" id="au3-JT-77n"/>
|
||||
<action selector="toggleRespectSilentMode:" destination="eHi-aO-uGS" eventType="primaryActionTriggered" id="dyA-el-uRa"/>
|
||||
</connections>
|
||||
</switch>
|
||||
</subviews>
|
||||
@ -311,33 +311,67 @@
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" reuseIdentifier="SwitchCell" id="2Kn-Xu-sTo" customClass="SwitchTableViewCell">
|
||||
<rect key="frame" x="0.0" y="809" width="375" height="44"/>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection headerTitle="Haptic Feedback" footerTitle="When enabled, your device will vibrate in response to touch screen controls." id="avK-bg-nm6">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" reuseIdentifier="SwitchCell" id="ZUx-5m-gPq" customClass="SwitchTableViewCell">
|
||||
<rect key="frame" x="0.0" y="930" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="2Kn-Xu-sTo" id="2pb-gv-gY2">
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="ZUx-5m-gPq" id="7fY-cO-dht">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Control Sticks" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="UoA-qC-cKc">
|
||||
<rect key="frame" x="8" y="11.5" width="302" height="21"/>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Buttons" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="XPP-qe-q75">
|
||||
<rect key="frame" x="16" y="11.5" width="286" height="21"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="s5f-Uz-GpH">
|
||||
<rect key="frame" x="318" y="6.5" width="51" height="31"/>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="g7m-wj-ueP">
|
||||
<rect key="frame" x="310" y="6.5" width="51" height="31"/>
|
||||
<color key="onTintColor" name="Purple"/>
|
||||
<connections>
|
||||
<action selector="toggleThumbstickHapticFeedbackEnabled:" destination="eHi-aO-uGS" eventType="primaryActionTriggered" id="ELm-nK-kTr"/>
|
||||
<action selector="toggleButtonHapticFeedbackEnabled:" destination="eHi-aO-uGS" eventType="primaryActionTriggered" id="JkH-Ha-NqO"/>
|
||||
</connections>
|
||||
</switch>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="s5f-Uz-GpH" firstAttribute="centerY" secondItem="2pb-gv-gY2" secondAttribute="centerY" id="1yP-Wt-XbP"/>
|
||||
<constraint firstAttribute="trailingMargin" secondItem="s5f-Uz-GpH" secondAttribute="trailing" id="3XM-jj-6TV"/>
|
||||
<constraint firstItem="UoA-qC-cKc" firstAttribute="centerY" secondItem="2pb-gv-gY2" secondAttribute="centerY" id="AMH-D3-llc"/>
|
||||
<constraint firstItem="UoA-qC-cKc" firstAttribute="leading" secondItem="2pb-gv-gY2" secondAttribute="leadingMargin" id="Fi5-Td-wMi"/>
|
||||
<constraint firstItem="s5f-Uz-GpH" firstAttribute="leading" secondItem="UoA-qC-cKc" secondAttribute="trailing" constant="8" symbolic="YES" id="L7N-F1-Maf"/>
|
||||
<constraint firstItem="XPP-qe-q75" firstAttribute="centerY" secondItem="7fY-cO-dht" secondAttribute="centerY" id="ML4-2L-ivB"/>
|
||||
<constraint firstItem="XPP-qe-q75" firstAttribute="leading" secondItem="7fY-cO-dht" secondAttribute="leadingMargin" id="RcM-9w-cj3"/>
|
||||
<constraint firstItem="g7m-wj-ueP" firstAttribute="leading" secondItem="XPP-qe-q75" secondAttribute="trailing" constant="8" symbolic="YES" id="U9Z-lS-tZm"/>
|
||||
<constraint firstAttribute="trailingMargin" secondItem="g7m-wj-ueP" secondAttribute="trailing" id="W92-fx-sPH"/>
|
||||
<constraint firstItem="g7m-wj-ueP" firstAttribute="centerY" secondItem="7fY-cO-dht" secondAttribute="centerY" id="mKM-Rs-8FG"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" reuseIdentifier="SwitchCell" id="p47-ru-O9r" customClass="SwitchTableViewCell">
|
||||
<rect key="frame" x="0.0" y="974" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="p47-ru-O9r" id="o9R-cP-Tge">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Control Sticks" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="MDR-bb-TYm">
|
||||
<rect key="frame" x="16" y="11.5" width="286" height="21"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="19A-ef-F3V">
|
||||
<rect key="frame" x="310" y="6.5" width="51" height="31"/>
|
||||
<color key="onTintColor" name="Purple"/>
|
||||
<connections>
|
||||
<action selector="toggleThumbstickHapticFeedbackEnabled:" destination="eHi-aO-uGS" eventType="primaryActionTriggered" id="pHq-hK-bcL"/>
|
||||
</connections>
|
||||
</switch>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="19A-ef-F3V" firstAttribute="centerY" secondItem="o9R-cP-Tge" secondAttribute="centerY" id="7S9-gQ-Elk"/>
|
||||
<constraint firstAttribute="trailingMargin" secondItem="19A-ef-F3V" secondAttribute="trailing" id="IVw-V1-wRU"/>
|
||||
<constraint firstItem="19A-ef-F3V" firstAttribute="leading" secondItem="MDR-bb-TYm" secondAttribute="trailing" constant="8" symbolic="YES" id="hPx-zJ-0qA"/>
|
||||
<constraint firstItem="MDR-bb-TYm" firstAttribute="centerY" secondItem="o9R-cP-Tge" secondAttribute="centerY" id="m0V-TQ-ajv"/>
|
||||
<constraint firstItem="MDR-bb-TYm" firstAttribute="leading" secondItem="o9R-cP-Tge" secondAttribute="leadingMargin" id="pSi-wU-tje"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
@ -502,15 +536,38 @@
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Riley Testut" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Pum-dL-hGn">
|
||||
<rect key="frame" x="16" y="13" width="84" height="19.5"/>
|
||||
<rect key="frame" x="16" y="12" width="88" height="20.5"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Developer" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="WQ6-m7-zhh">
|
||||
<rect key="frame" x="267.5" y="13" width="74" height="19.5"/>
|
||||
<rect key="frame" x="262.5" y="12" width="78" height="20.5"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" systemColor="secondaryLabelColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="DetailCell" textLabel="cht-lO-kpR" detailTextLabel="0pG-CT-ZWR" style="IBUITableViewCellStyleValue1" id="CV9-Df-mUX">
|
||||
<rect key="frame" x="0.0" y="1590.5" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="CV9-Df-mUX" id="gLC-z2-rMU">
|
||||
<rect key="frame" x="0.0" y="0.0" width="348.5" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Shane Gill" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="cht-lO-kpR">
|
||||
<rect key="frame" x="16" y="12" width="76" height="20.5"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Operations" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="0pG-CT-ZWR">
|
||||
<rect key="frame" x="256.5" y="12" width="84" height="20.5"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" systemColor="secondaryLabelColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
@ -527,13 +584,13 @@
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Caroline Moore" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="gWx-Xn-5Nf">
|
||||
<rect key="frame" x="16" y="13" width="110.5" height="19.5"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Graphic Designer" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="09x-GX-cpy">
|
||||
<rect key="frame" x="215.5" y="13" width="126" height="19.5"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" systemColor="secondaryLabelColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
@ -550,13 +607,13 @@
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Grant Gliner" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="zro-BX-EY9">
|
||||
<rect key="frame" x="16" y="13" width="87.5" height="19.5"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Icon Guy" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="e45-FD-ug2">
|
||||
<rect key="frame" x="277.5" y="13" width="64" height="19.5"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" systemColor="secondaryLabelColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
@ -573,19 +630,35 @@
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Chris Rittenhouse" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="w1i-mR-wOF">
|
||||
<rect key="frame" x="16" y="13" width="129" height="19.5"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Genesis Skin" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="jRO-48-iRO">
|
||||
<rect key="frame" x="248" y="13" width="93.5" height="19.5"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" systemColor="secondaryLabelColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="DetailCell" textLabel="g59-8E-zW7" style="IBUITableViewCellStyleDefault" id="hkv-lx-68h">
|
||||
<rect key="frame" x="0.0" y="1766.5" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="hkv-lx-68h" id="bNT-kB-3cI">
|
||||
<rect key="frame" x="0.0" y="0.0" width="348.5" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" ambiguous="YES" text="Contributors" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="g59-8E-zW7">
|
||||
<rect key="frame" x="16" y="0.0" width="324.5" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="DetailCell" textLabel="2K3-IL-94S" style="IBUITableViewCellStyleDefault" id="j7p-ZK-mHq">
|
||||
<rect key="frame" x="0.0" y="1567.5" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
@ -621,12 +694,13 @@
|
||||
</barButtonItem>
|
||||
</navigationItem>
|
||||
<connections>
|
||||
<outlet property="buttonHapticFeedbackEnabledSwitch" destination="mlO-iy-zU2" id="ObD-uL-Si0"/>
|
||||
<outlet property="buttonHapticFeedbackEnabledSwitch" destination="g7m-wj-ueP" id="nOj-sc-foi"/>
|
||||
<outlet property="controllerOpacityLabel" destination="zaz-yD-CYG" id="eUW-u9-xxx"/>
|
||||
<outlet property="controllerOpacitySlider" destination="whi-If-wFf" id="6Cx-HY-xLG"/>
|
||||
<outlet property="previewsEnabledSwitch" destination="OJE-9e-9i3" id="Ndg-eN-PPs"/>
|
||||
<outlet property="respectSilentModeSwitch" destination="mlO-iy-zU2" id="TDT-cx-kCf"/>
|
||||
<outlet property="syncingServiceLabel" destination="kLY-5g-v8n" id="zzx-qM-q1g"/>
|
||||
<outlet property="thumbstickHapticFeedbackEnabledSwitch" destination="s5f-Uz-GpH" id="ffp-3M-FDL"/>
|
||||
<outlet property="thumbstickHapticFeedbackEnabledSwitch" destination="19A-ef-F3V" id="K03-kQ-lv3"/>
|
||||
<outlet property="versionLabel" destination="Str-BY-agW" id="gU2-L0-pYt"/>
|
||||
<segue destination="uBz-mm-mXr" kind="show" identifier="controllersSegue" id="MLY-hF-UB8"/>
|
||||
<segue destination="56e-ul-z6v" kind="show" identifier="controllerSkinsSegue" id="GNM-Gt-YFf"/>
|
||||
@ -1048,6 +1122,7 @@
|
||||
<objects>
|
||||
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="0QR-U9-gtx" customClass="RSTNavigationController" sceneMemberID="viewController">
|
||||
<toolbarItems/>
|
||||
<value key="contentSizeForViewInPopover" type="size" width="375" height="667"/>
|
||||
<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="0.0" width="375" height="44"/>
|
||||
@ -1861,10 +1936,48 @@ Delta uses OpenVGDB to provide automatic artwork for imported games.</string>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection headerTitle="Performance" footerTitle="When enabled, Delta will automatically enable JIT when on the same WiFi network as AltServer." id="MyM-jI-qtY" userLabel="Performance">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" reuseIdentifier="SwitchCell" id="uXJ-qB-E2e" customClass="SwitchTableViewCell">
|
||||
<rect key="frame" x="0.0" y="284" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="uXJ-qB-E2e" id="mkz-9w-FlW">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Use AltJIT" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="XPc-dG-N6R">
|
||||
<rect key="frame" x="16" y="11.5" width="286" height="21"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="4Y4-gY-MaX">
|
||||
<rect key="frame" x="310" y="6.5" width="51" height="31"/>
|
||||
<color key="onTintColor" name="Purple"/>
|
||||
<connections>
|
||||
<action selector="toggleAltJITEnabled:" destination="OwL-4c-EEA" eventType="valueChanged" id="lfe-xg-J5o"/>
|
||||
<action selector="togglePreviewsEnabled:" destination="eHi-aO-uGS" eventType="primaryActionTriggered" id="BKJ-dV-k7X"/>
|
||||
</connections>
|
||||
</switch>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailingMargin" secondItem="4Y4-gY-MaX" secondAttribute="trailing" id="6Rs-qs-gFM"/>
|
||||
<constraint firstItem="XPc-dG-N6R" firstAttribute="leading" secondItem="mkz-9w-FlW" secondAttribute="leadingMargin" id="BBM-mW-kez"/>
|
||||
<constraint firstItem="4Y4-gY-MaX" firstAttribute="centerY" secondItem="mkz-9w-FlW" secondAttribute="centerY" id="CY7-Kt-ggC"/>
|
||||
<constraint firstItem="4Y4-gY-MaX" firstAttribute="leading" secondItem="XPc-dG-N6R" secondAttribute="trailing" constant="8" symbolic="YES" id="Qce-jc-vX3"/>
|
||||
<constraint firstItem="XPc-dG-N6R" firstAttribute="centerY" secondItem="mkz-9w-FlW" secondAttribute="centerY" id="Xzp-aR-4Dz"/>
|
||||
</constraints>
|
||||
</tableViewCellContentView>
|
||||
<connections>
|
||||
<outlet property="switchView" destination="4Y4-gY-MaX" id="NDr-Y9-9RS"/>
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
</cells>
|
||||
</tableViewSection>
|
||||
<tableViewSection headerTitle="DS BIOS Files" footerTitle="Delta requires these BIOS files in order to play Nintendo DS games. Tap one to import it from Files." id="ObZ-cl-5sV">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="gray" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="Cell" textLabel="Bfp-iA-wWE" detailTextLabel="Xpe-sd-GRy" style="IBUITableViewCellStyleValue1" id="ian-1X-SOi">
|
||||
<rect key="frame" x="0.0" y="284" width="375" height="44"/>
|
||||
<rect key="frame" x="0.0" y="408" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="ian-1X-SOi" id="nE9-5Y-G2k">
|
||||
<rect key="frame" x="0.0" y="0.0" width="349.5" height="44"/>
|
||||
@ -1887,7 +2000,7 @@ Delta uses OpenVGDB to provide automatic artwork for imported games.</string>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="gray" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="Cell" textLabel="fgD-y0-55T" detailTextLabel="b8s-cx-IgW" style="IBUITableViewCellStyleValue1" id="dwT-0T-gFD">
|
||||
<rect key="frame" x="0.0" y="328" width="375" height="44"/>
|
||||
<rect key="frame" x="0.0" y="452" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="dwT-0T-gFD" id="KEg-18-WYZ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="349.5" height="44"/>
|
||||
@ -1910,7 +2023,7 @@ Delta uses OpenVGDB to provide automatic artwork for imported games.</string>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="gray" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="Cell" textLabel="SIu-zD-qfE" detailTextLabel="CW8-h0-YST" style="IBUITableViewCellStyleValue1" id="Tll-tX-97T">
|
||||
<rect key="frame" x="0.0" y="372" width="375" height="44"/>
|
||||
<rect key="frame" x="0.0" y="496" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Tll-tX-97T" id="4ca-AM-jBE">
|
||||
<rect key="frame" x="0.0" y="0.0" width="349.5" height="44"/>
|
||||
@ -1937,7 +2050,7 @@ Delta uses OpenVGDB to provide automatic artwork for imported games.</string>
|
||||
<tableViewSection headerTitle="DSi BIOS Files" footerTitle="Delta requires these BIOS files in order to play Nintendo DSi games. Tap one to import it from Files." id="SAz-hG-O4G">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="gray" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="Cell" textLabel="QKd-rm-101" detailTextLabel="pJb-OR-TGs" style="IBUITableViewCellStyleValue1" id="jwN-8n-xgM">
|
||||
<rect key="frame" x="0.0" y="496" width="375" height="44"/>
|
||||
<rect key="frame" x="0.0" y="620" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="jwN-8n-xgM" id="b29-V1-qt3">
|
||||
<rect key="frame" x="0.0" y="0.0" width="349.5" height="44"/>
|
||||
@ -1960,7 +2073,7 @@ Delta uses OpenVGDB to provide automatic artwork for imported games.</string>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="gray" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="Cell" textLabel="6OX-32-izR" detailTextLabel="2bc-8r-epB" style="IBUITableViewCellStyleValue1" id="3ka-mn-QqR">
|
||||
<rect key="frame" x="0.0" y="540" width="375" height="44"/>
|
||||
<rect key="frame" x="0.0" y="664" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="3ka-mn-QqR" id="4ya-QS-s5w">
|
||||
<rect key="frame" x="0.0" y="0.0" width="349.5" height="44"/>
|
||||
@ -1983,7 +2096,7 @@ Delta uses OpenVGDB to provide automatic artwork for imported games.</string>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="gray" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="Cell" textLabel="Czu-qN-Luq" detailTextLabel="eVw-7r-BEf" style="IBUITableViewCellStyleValue1" id="4FF-9a-jq2">
|
||||
<rect key="frame" x="0.0" y="584" width="375" height="44"/>
|
||||
<rect key="frame" x="0.0" y="708" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="4FF-9a-jq2" id="emb-d3-TgY">
|
||||
<rect key="frame" x="0.0" y="0.0" width="349.5" height="44"/>
|
||||
@ -2006,7 +2119,7 @@ Delta uses OpenVGDB to provide automatic artwork for imported games.</string>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="gray" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="Cell" textLabel="VWi-nu-uMv" detailTextLabel="8JD-uT-eoA" style="IBUITableViewCellStyleValue1" id="we3-0h-uKq">
|
||||
<rect key="frame" x="0.0" y="628" width="375" height="44"/>
|
||||
<rect key="frame" x="0.0" y="752" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="we3-0h-uKq" id="cG1-a2-Nez">
|
||||
<rect key="frame" x="0.0" y="0.0" width="349.5" height="44"/>
|
||||
@ -2033,7 +2146,7 @@ Delta uses OpenVGDB to provide automatic artwork for imported games.</string>
|
||||
<tableViewSection headerTitle="" footerTitle="Changing cores may improve performance at the cost of additional features." id="hbM-mL-bIr">
|
||||
<cells>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="gray" indentationWidth="10" reuseIdentifier="ChangeCell" textLabel="Jmx-Jw-278" style="IBUITableViewCellStyleDefault" id="p1T-4d-fC3">
|
||||
<rect key="frame" x="0.0" y="737.5" width="375" height="44"/>
|
||||
<rect key="frame" x="0.0" y="861.5" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="p1T-4d-fC3" id="3bs-Bp-nd4">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
|
||||
|
||||
@ -21,7 +21,8 @@ class ListMenuViewController: UITableViewController
|
||||
|
||||
override var preferredContentSize: CGSize {
|
||||
get {
|
||||
let navigationBarHeight = self.navigationController?.navigationBar.bounds.height ?? 0.0
|
||||
// Don't include navigation bar height in calculation (as of iOS 13).
|
||||
let navigationBarHeight = 0.0 // self.navigationController?.navigationBar.bounds.height ?? 0.0
|
||||
return CGSize(width: 0, height: (self.tableView.rowHeight * CGFloat(self.items.count)) + navigationBarHeight)
|
||||
}
|
||||
set {}
|
||||
|
||||
@ -38,11 +38,23 @@ extension UINavigationBar
|
||||
private var _defaultTitleTextAttributes: [NSAttributedString.Key: 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
|
||||
guard let contentView = self.subviews.first(where: { NSStringFromClass(type(of: $0)).contains("ContentView") || NSStringFromClass(type(of: $0)).contains("ItemView") })
|
||||
else { return nil }
|
||||
|
||||
let containerView: UIView
|
||||
|
||||
if #available(iOS 16, *)
|
||||
{
|
||||
guard let titleControl = contentView.subviews.first(where: { NSStringFromClass(type(of: $0)).contains("Title") }) else { return nil }
|
||||
containerView = titleControl
|
||||
}
|
||||
else
|
||||
{
|
||||
containerView = contentView
|
||||
}
|
||||
|
||||
guard let titleLabel = containerView.subviews.first(where: { $0 is UILabel }) as? UILabel else { return nil }
|
||||
|
||||
let textAttributes = titleLabel.attributedText?.attributes(at: 0, effectiveRange: nil)
|
||||
return textAttributes
|
||||
}
|
||||
|
||||
143
Delta/Database/Cheats/CheatBase.swift
Normal file
143
Delta/Database/Cheats/CheatBase.swift
Normal file
@ -0,0 +1,143 @@
|
||||
//
|
||||
// CheatBase.swift
|
||||
// Delta
|
||||
//
|
||||
// Created by Riley Testut on 1/17/23.
|
||||
// Copyright © 2023 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SQLite
|
||||
|
||||
import Roxas
|
||||
|
||||
private extension UserDefaults
|
||||
{
|
||||
@NSManaged var previousCheatBaseVersion: Int
|
||||
}
|
||||
|
||||
extension ExpressionType
|
||||
{
|
||||
static var cheatID: SQLite.Expression<Int> {
|
||||
return SQLite.Expression<Int>("cheatID")
|
||||
}
|
||||
|
||||
static var cheatName: SQLite.Expression<String> {
|
||||
return SQLite.Expression<String>("cheatName")
|
||||
}
|
||||
|
||||
static var cheatDescription: SQLite.Expression<String?> {
|
||||
return SQLite.Expression<String?>("cheatDescription")
|
||||
}
|
||||
|
||||
static var cheatCode: SQLite.Expression<String> {
|
||||
return SQLite.Expression<String>("cheatCode")
|
||||
}
|
||||
|
||||
static var cheatDeviceID: SQLite.Expression<Int> {
|
||||
return SQLite.Expression<Int>("cheatDeviceID")
|
||||
}
|
||||
|
||||
static var cheatActivation: SQLite.Expression<String?> {
|
||||
return SQLite.Expression<String?>("cheatActivation")
|
||||
}
|
||||
|
||||
static var cheatCategoryID: SQLite.Expression<Int> {
|
||||
return SQLite.Expression<Int>("cheatCategoryID")
|
||||
}
|
||||
|
||||
static var cheatCategoryName: SQLite.Expression<String> {
|
||||
return SQLite.Expression<String>("cheatCategory")
|
||||
}
|
||||
|
||||
static var cheatCategoryDescription: SQLite.Expression<String> {
|
||||
return SQLite.Expression<String>("cheatCategoryDescription")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension Table
|
||||
{
|
||||
static var cheats: Table {
|
||||
return Table("CHEATS")
|
||||
}
|
||||
|
||||
static var cheatCategories: Table {
|
||||
return Table("CHEAT_CATEGORIES")
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 14, *)
|
||||
class CheatBase: GamesDatabase
|
||||
{
|
||||
static let cheatsVersion = 1
|
||||
static var previousCheatsVersion: Int? {
|
||||
return UserDefaults.standard.previousCheatBaseVersion
|
||||
}
|
||||
|
||||
private let connection: Connection
|
||||
|
||||
override init() throws
|
||||
{
|
||||
let fileURL = DatabaseManager.cheatBaseURL
|
||||
guard FileManager.default.fileExists(atPath: fileURL.path) else { throw GamesDatabase.Error.doesNotExist }
|
||||
|
||||
self.connection = try Connection(fileURL.path)
|
||||
|
||||
try super.init()
|
||||
|
||||
UserDefaults.standard.previousCheatBaseVersion = CheatBase.cheatsVersion
|
||||
}
|
||||
|
||||
func cheats(for game: Game) async throws -> [CheatMetadata]?
|
||||
{
|
||||
let metadata = await withCheckedContinuation { continuation in
|
||||
if let context = game.managedObjectContext
|
||||
{
|
||||
context.perform {
|
||||
let metadata = self.metadata(for: game)
|
||||
continuation.resume(returning: metadata)
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
let metadata = self.metadata(for: game)
|
||||
continuation.resume(returning: metadata)
|
||||
}
|
||||
}
|
||||
|
||||
guard let romIDValue = metadata?.romID else { return nil }
|
||||
|
||||
let cheatID = Expression<Any>.cheatID
|
||||
let cheatName = Expression<Any>.cheatName
|
||||
let cheatCode = Expression<Any>.cheatCode
|
||||
let cheatDescription = Expression<Any>.cheatDescription
|
||||
let cheatActivation = Expression<Any>.cheatActivation
|
||||
let cheatDeviceID = Expression<Any>.cheatDeviceID
|
||||
|
||||
let categoryID = Expression<Any>.cheatCategoryID
|
||||
let categoryName = Expression<Any>.cheatCategoryName
|
||||
let categoryDescription = Expression<Any>.cheatCategoryDescription
|
||||
|
||||
let romID = Expression<Any>.romID
|
||||
|
||||
let query = Table.cheats.select(cheatID, cheatName, cheatCode, cheatDescription, cheatActivation, cheatDeviceID, Table.cheats[categoryID], categoryName, categoryDescription)
|
||||
.filter(romID == romIDValue)
|
||||
.join(Table.cheatCategories, on: Table.cheats[categoryID] == Table.cheatCategories[categoryID])
|
||||
.order(cheatName)
|
||||
|
||||
let rows = try self.connection.prepare(query)
|
||||
|
||||
let results = rows.compactMap { (row) -> CheatMetadata? in
|
||||
guard case let deviceID = Int16(row[cheatDeviceID]), let device = CheatDevice(rawValue: deviceID) else { return nil }
|
||||
|
||||
let id = row[Table.cheats[categoryID]]
|
||||
|
||||
let category = CheatCategory(id: id, name: row[categoryName], categoryDescription: row[categoryDescription])
|
||||
let metadata = CheatMetadata(id: row[cheatID], name: row[cheatName], code: row[cheatCode], description: row[cheatDescription], activationHint: row[cheatActivation], device: device, category: category)
|
||||
return metadata
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
}
|
||||
273
Delta/Database/Cheats/CheatBaseView.swift
Normal file
273
Delta/Database/Cheats/CheatBaseView.swift
Normal file
@ -0,0 +1,273 @@
|
||||
//
|
||||
// CheatBaseView.swift
|
||||
// Delta
|
||||
//
|
||||
// Created by Riley Testut on 1/17/23.
|
||||
// Copyright © 2023 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
import enum SQLite.Result
|
||||
|
||||
@available(iOS 14, *)
|
||||
extension CheatBaseView
|
||||
{
|
||||
private class ViewModel: ObservableObject
|
||||
{
|
||||
@Published
|
||||
var allCheats: [CheatMetadata]? {
|
||||
didSet {
|
||||
guard let cheats = allCheats else {
|
||||
self.cheatsByCategory = nil
|
||||
return
|
||||
}
|
||||
|
||||
let cheatsByCategory = Dictionary(grouping: cheats, by: { $0.category }).sorted { $0.key.id < $1.key.id }
|
||||
self.cheatsByCategory = cheatsByCategory
|
||||
}
|
||||
}
|
||||
|
||||
@Published
|
||||
private(set) var cheatsByCategory: [(CheatCategory, [CheatMetadata])]?
|
||||
|
||||
@Published
|
||||
private(set) var error: Error?
|
||||
|
||||
@Published
|
||||
var searchText: String = "" {
|
||||
didSet {
|
||||
self.searchCheats()
|
||||
}
|
||||
}
|
||||
|
||||
@Published
|
||||
private(set) var filteredCheats: [CheatMetadata]?
|
||||
|
||||
@MainActor
|
||||
func fetchCheats(for game: Game) async
|
||||
{
|
||||
guard self.allCheats == nil else { return }
|
||||
|
||||
do
|
||||
{
|
||||
let database = try CheatBase()
|
||||
let cheats = try await database.cheats(for: game) ?? []
|
||||
self.allCheats = cheats
|
||||
}
|
||||
catch
|
||||
{
|
||||
self.error = error
|
||||
}
|
||||
}
|
||||
|
||||
private func searchCheats()
|
||||
{
|
||||
if let cheats = self.allCheats, !self.searchText.isEmpty
|
||||
{
|
||||
let predicate = NSPredicate(forSearchingForText: self.searchText, inValuesForKeyPaths: [#keyPath(CheatMetadata.name), #keyPath(CheatMetadata.cheatDescription)])
|
||||
|
||||
let filteredCheats = cheats.filter { predicate.evaluate(with: $0) }
|
||||
self.filteredCheats = filteredCheats
|
||||
}
|
||||
else
|
||||
{
|
||||
self.filteredCheats = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 14, *)
|
||||
struct CheatBaseView: View
|
||||
{
|
||||
let game: Game?
|
||||
|
||||
var cancellationHandler: (() -> Void)?
|
||||
var selectionHandler: ((CheatMetadata) -> Void)?
|
||||
|
||||
@StateObject
|
||||
private var viewModel = ViewModel()
|
||||
|
||||
@State
|
||||
private var activationHintCheat: CheatMetadata?
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
ZStack {
|
||||
if let cheats = viewModel.allCheats, !cheats.isEmpty
|
||||
{
|
||||
// Only show List if there is at least one cheat for this game.
|
||||
cheatList()
|
||||
}
|
||||
|
||||
// Place above List
|
||||
placeholderView()
|
||||
}
|
||||
.alert(item: $activationHintCheat) { cheat in
|
||||
Alert(title: Text("How to Activate"),
|
||||
message: Text(cheat.activationHint ?? ""),
|
||||
dismissButton: .default(Text("OK")))
|
||||
}
|
||||
.navigationTitle(Text(game?.name ?? "CheatBase"))
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .cancellationAction) {
|
||||
Button("Cancel") {
|
||||
cancellationHandler?()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
Task {
|
||||
guard let game = self.game else { return }
|
||||
await viewModel.fetchCheats(for: game)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func cheatList() -> some View
|
||||
{
|
||||
VStack {
|
||||
if #unavailable(iOS 15)
|
||||
{
|
||||
LegacySearchBar(text: $viewModel.searchText)
|
||||
}
|
||||
|
||||
let listView = List {
|
||||
if let filteredCheats = viewModel.filteredCheats
|
||||
{
|
||||
ForEach(filteredCheats) { cheat in
|
||||
cell(for: cheat)
|
||||
}
|
||||
}
|
||||
else if let cheats = viewModel.cheatsByCategory
|
||||
{
|
||||
ForEach(cheats, id: \.0.id) { (category, cheats) in
|
||||
Section {
|
||||
DisclosureGroup {
|
||||
ForEach(cheats) { cheat in
|
||||
cell(for: cheat)
|
||||
}
|
||||
} label: {
|
||||
Text(category.name)
|
||||
}
|
||||
} footer: {
|
||||
Text(category.categoryDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if #available(iOS 15, *)
|
||||
{
|
||||
listView.searchable(text: $viewModel.searchText)
|
||||
}
|
||||
else
|
||||
{
|
||||
listView
|
||||
}
|
||||
}
|
||||
.listStyle(.insetGrouped)
|
||||
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .top)
|
||||
}
|
||||
|
||||
|
||||
private func cell(for cheat: CheatMetadata) -> some View
|
||||
{
|
||||
ZStack(alignment: .leading) {
|
||||
Button(action: { choose(cheat) }) {}
|
||||
|
||||
HStack {
|
||||
// Name + Description
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text(cheat.name)
|
||||
|
||||
if let description = cheat.cheatDescription
|
||||
{
|
||||
Text(description)
|
||||
.font(.caption)
|
||||
}
|
||||
}
|
||||
|
||||
// Activation Hint
|
||||
if cheat.activationHint != nil
|
||||
{
|
||||
Spacer()
|
||||
|
||||
Button(action: { activationHintCheat = cheat }) {
|
||||
Image(systemName: "info.circle")
|
||||
}
|
||||
.buttonStyle(.borderless)
|
||||
}
|
||||
}
|
||||
.multilineTextAlignment(.leading)
|
||||
}
|
||||
}
|
||||
|
||||
private func placeholderView() -> some View
|
||||
{
|
||||
VStack(spacing: 8) {
|
||||
if let error = viewModel.error
|
||||
{
|
||||
Text("Unable to Load Cheats")
|
||||
.font(.title)
|
||||
|
||||
if let error = error as? SQLite.Result
|
||||
{
|
||||
// SQLite.Result implements CustomStringConvertible.description, but not localizedDescription.
|
||||
Text(String(describing: error))
|
||||
.font(.callout)
|
||||
}
|
||||
else
|
||||
{
|
||||
Text(error.localizedDescription)
|
||||
.font(.callout)
|
||||
}
|
||||
}
|
||||
else if let filteredCheats = viewModel.filteredCheats, filteredCheats.isEmpty
|
||||
{
|
||||
Text("Cheat Not Found")
|
||||
.font(.title)
|
||||
|
||||
Text("Please make sure the name is correct, or try searching for another cheat.")
|
||||
.font(.callout)
|
||||
}
|
||||
else if let cheats = viewModel.allCheats, cheats.isEmpty
|
||||
{
|
||||
Text("No Cheats")
|
||||
.font(.title)
|
||||
|
||||
Text("There are no cheats for this game in Delta's CheatBase. Please try a different game.")
|
||||
.font(.callout)
|
||||
}
|
||||
else if viewModel.allCheats == nil
|
||||
{
|
||||
ProgressView()
|
||||
.progressViewStyle(.circular)
|
||||
}
|
||||
}
|
||||
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .center)
|
||||
.foregroundColor(.gray)
|
||||
.padding()
|
||||
}
|
||||
|
||||
init(game: Game, cheats: [CheatMetadata]? = nil)
|
||||
{
|
||||
self.game = game
|
||||
|
||||
let viewModel = ViewModel()
|
||||
viewModel.allCheats = cheats
|
||||
self._viewModel = StateObject(wrappedValue: viewModel)
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 14, *)
|
||||
private extension CheatBaseView
|
||||
{
|
||||
func choose(_ cheatMetadata: CheatMetadata)
|
||||
{
|
||||
self.selectionHandler?(cheatMetadata)
|
||||
}
|
||||
}
|
||||
111
Delta/Database/Cheats/CheatDevice.swift
Normal file
111
Delta/Database/Cheats/CheatDevice.swift
Normal file
@ -0,0 +1,111 @@
|
||||
//
|
||||
// CheatDevice.swift
|
||||
// Delta
|
||||
//
|
||||
// Created by Riley Testut on 1/30/23.
|
||||
// Copyright © 2023 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
import DeltaCore
|
||||
import NESDeltaCore
|
||||
|
||||
@objc
|
||||
enum CheatDevice: Int16
|
||||
{
|
||||
case famicomGameGenie = 1
|
||||
case famicomRaw = 2
|
||||
case famicomRawCompare = 3
|
||||
|
||||
case gbGameGenie = 4
|
||||
|
||||
case gbaActionReplayMax = 5
|
||||
case gbaCodeBreaker = 6
|
||||
case gbaGameShark = 7
|
||||
|
||||
case gbcGameShark = 8
|
||||
|
||||
case n64GameShark = 9
|
||||
|
||||
case dsActionReplay = 10
|
||||
case dsCodeBreaker = 11
|
||||
|
||||
case nesGameGenie = 12
|
||||
case nesRaw = 13
|
||||
case nesRawCompare = 14
|
||||
|
||||
case snesActionReplay = 15
|
||||
case snesGameGenie = 16
|
||||
|
||||
case gameGearActionReplay = 17
|
||||
case gameGearGameGenie = 18
|
||||
|
||||
case masterSystemActionReplay = 19
|
||||
case masterSystemGameGenie = 20
|
||||
|
||||
case cdActionReplay10 = 21
|
||||
case cdActionReplay8 = 22
|
||||
|
||||
case genesisActionReplay10 = 23
|
||||
case genesisActionReplay8 = 24
|
||||
}
|
||||
|
||||
extension CheatDevice
|
||||
{
|
||||
var cheatType: CheatType? {
|
||||
switch self
|
||||
{
|
||||
case .snesActionReplay, .gbaActionReplayMax, .dsActionReplay, .gameGearActionReplay, .masterSystemActionReplay, .genesisActionReplay8, .genesisActionReplay10, .cdActionReplay8, .cdActionReplay10:
|
||||
return .actionReplay
|
||||
|
||||
case .n64GameShark, .gbcGameShark, .gbaGameShark:
|
||||
return .gameShark
|
||||
|
||||
case .famicomGameGenie, .snesGameGenie, .gbGameGenie, .gameGearGameGenie, .masterSystemGameGenie:
|
||||
return .gameGenie
|
||||
|
||||
case .nesGameGenie:
|
||||
return CheatType(rawValue: DeltaCore.CheatType.gameGenie8.rawValue)
|
||||
|
||||
case .gbaCodeBreaker, .dsCodeBreaker:
|
||||
return .codeBreaker
|
||||
|
||||
case .famicomRaw, .famicomRawCompare:
|
||||
return nil
|
||||
|
||||
case .nesRaw, .nesRawCompare:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var gameType: GameType? {
|
||||
switch self
|
||||
{
|
||||
case .famicomGameGenie, .famicomRaw, .famicomRawCompare: return .nes
|
||||
case .nesGameGenie, .nesRaw, .nesRawCompare: return .nes
|
||||
case .snesActionReplay, .snesGameGenie: return .snes
|
||||
case .n64GameShark: return .n64
|
||||
case .gbGameGenie, .gbcGameShark: return .gbc
|
||||
case .gbaActionReplayMax, .gbaGameShark, .gbaCodeBreaker: return .gba
|
||||
case .dsActionReplay, .dsCodeBreaker: return .ds
|
||||
case .genesisActionReplay8, .genesisActionReplay10: return .genesis
|
||||
case .cdActionReplay8, .cdActionReplay10: return .genesis
|
||||
|
||||
// Not yet supported
|
||||
case .gameGearActionReplay, .gameGearGameGenie: return nil
|
||||
case .masterSystemActionReplay, .masterSystemGameGenie: return nil
|
||||
}
|
||||
}
|
||||
|
||||
var cheatFormat: CheatFormat? {
|
||||
guard
|
||||
let cheatType = self.cheatType,
|
||||
let gameType = self.gameType,
|
||||
let deltaCore = Delta.core(for: gameType)
|
||||
else { return nil }
|
||||
|
||||
let cheatFormat = deltaCore.supportedCheatFormats.first { $0.type == cheatType }
|
||||
return cheatFormat
|
||||
}
|
||||
}
|
||||
45
Delta/Database/Cheats/CheatMetadata.swift
Normal file
45
Delta/Database/Cheats/CheatMetadata.swift
Normal file
@ -0,0 +1,45 @@
|
||||
//
|
||||
// CheatMetadata.swift
|
||||
// Delta
|
||||
//
|
||||
// Created by Riley Testut on 1/17/23.
|
||||
// Copyright © 2023 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
import DeltaCore
|
||||
|
||||
struct CheatCategory: Identifiable, Hashable
|
||||
{
|
||||
var id: Int
|
||||
|
||||
var name: String
|
||||
var categoryDescription: String
|
||||
}
|
||||
|
||||
@objcMembers // @objcMembers required for NSPredicate-based filtering.
|
||||
final class CheatMetadata: NSObject, Identifiable
|
||||
{
|
||||
let id: Int
|
||||
|
||||
let name: String
|
||||
let code: String
|
||||
|
||||
let cheatDescription: String?
|
||||
let activationHint: String?
|
||||
|
||||
let device: CheatDevice
|
||||
let category: CheatCategory
|
||||
|
||||
init(id: Int, name: String, code: String, description: String?, activationHint: String?, device: CheatDevice, category: CheatCategory)
|
||||
{
|
||||
self.id = id
|
||||
self.name = name
|
||||
self.code = code
|
||||
self.cheatDescription = description
|
||||
self.activationHint = activationHint
|
||||
self.device = device
|
||||
self.category = category
|
||||
}
|
||||
}
|
||||
51
Delta/Database/Cheats/LegacySearchBar.swift
Normal file
51
Delta/Database/Cheats/LegacySearchBar.swift
Normal file
@ -0,0 +1,51 @@
|
||||
//
|
||||
// LegacySearchBar.swift
|
||||
// Delta
|
||||
//
|
||||
// Created by Riley Testut on 1/25/23.
|
||||
// Copyright © 2023 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SwiftUI
|
||||
|
||||
@available(iOS 13, *)
|
||||
struct LegacySearchBar: UIViewRepresentable
|
||||
{
|
||||
class Coordinator: NSObject, UISearchBarDelegate
|
||||
{
|
||||
@Binding
|
||||
var text: String
|
||||
|
||||
init(text: Binding<String>)
|
||||
{
|
||||
self._text = text
|
||||
}
|
||||
|
||||
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String)
|
||||
{
|
||||
self.text = searchText
|
||||
}
|
||||
}
|
||||
|
||||
@Binding
|
||||
var text: String
|
||||
|
||||
func makeUIView(context: Context) -> UISearchBar
|
||||
{
|
||||
let searchBar = UISearchBar(frame: .zero)
|
||||
searchBar.delegate = context.coordinator
|
||||
searchBar.placeholder = NSLocalizedString("Search", comment: "")
|
||||
return searchBar
|
||||
}
|
||||
|
||||
func updateUIView(_ uiView: UISearchBar, context: Context)
|
||||
{
|
||||
uiView.text = self.text
|
||||
}
|
||||
|
||||
func makeCoordinator() -> Coordinator
|
||||
{
|
||||
return Coordinator(text: $text)
|
||||
}
|
||||
}
|
||||
@ -255,10 +255,27 @@ private extension DatabaseManager
|
||||
|
||||
do
|
||||
{
|
||||
if !FileManager.default.fileExists(atPath: DatabaseManager.gamesDatabaseURL.path)
|
||||
if !FileManager.default.fileExists(atPath: DatabaseManager.gamesDatabaseURL.path) || GamesDatabase.version != GamesDatabase.previousVersion
|
||||
{
|
||||
guard let bundleURL = Bundle.main.url(forResource: "openvgdb", withExtension: "sqlite") else { throw GamesDatabase.Error.doesNotExist }
|
||||
try FileManager.default.copyItem(at: bundleURL, to: DatabaseManager.gamesDatabaseURL)
|
||||
try FileManager.default.copyItem(at: bundleURL, to: DatabaseManager.gamesDatabaseURL, shouldReplace: true)
|
||||
}
|
||||
|
||||
if #available(iOS 14, *), !FileManager.default.fileExists(atPath: DatabaseManager.cheatBaseURL.path) || CheatBase.cheatsVersion != CheatBase.previousCheatsVersion
|
||||
{
|
||||
guard let archiveURL = Bundle.main.url(forResource: "cheatbase", withExtension: "zip") else { throw GamesDatabase.Error.doesNotExist }
|
||||
|
||||
let temporaryDirectoryURL = FileManager.default.uniqueTemporaryURL()
|
||||
try FileManager.default.createDirectory(at: temporaryDirectoryURL, withIntermediateDirectories: true)
|
||||
defer {
|
||||
try? FileManager.default.removeItem(at: temporaryDirectoryURL)
|
||||
}
|
||||
|
||||
// Unzip to temporaryDirectoryURL first to ensure we don't accidentally unzip other items into DatabaseManager.cheatBaseURL directory (e.g. __MACOSX directory).
|
||||
try FileManager.default.unzipItem(at: archiveURL, to: temporaryDirectoryURL, skipCRC32: true) // skipCRC32 to avoid ~10 second extraction.
|
||||
|
||||
let extractedDatabaseURL = temporaryDirectoryURL.appendingPathComponent("cheatbase.sqlite")
|
||||
try FileManager.default.copyItem(at: extractedDatabaseURL, to: DatabaseManager.cheatBaseURL, shouldReplace: true)
|
||||
}
|
||||
|
||||
self.gamesDatabase = try GamesDatabase()
|
||||
@ -618,6 +635,12 @@ extension DatabaseManager
|
||||
let gamesDatabaseURL = self.defaultDirectoryURL().appendingPathComponent("openvgdb.sqlite")
|
||||
return gamesDatabaseURL
|
||||
}
|
||||
|
||||
class var cheatBaseURL: URL
|
||||
{
|
||||
let gamesDatabaseURL = self.defaultDirectoryURL().appendingPathComponent("cheatbase.sqlite")
|
||||
return gamesDatabaseURL
|
||||
}
|
||||
|
||||
class var gamesDirectoryURL: URL
|
||||
{
|
||||
|
||||
@ -71,11 +71,6 @@ extension ControllerSkin: ControllerSkinProtocol
|
||||
return self.controllerSkin?.thumbstick(for: item, traits: traits, preferredSize: preferredSize)
|
||||
}
|
||||
|
||||
public func inputs(for traits: DeltaCore.ControllerSkin.Traits, at point: CGPoint) -> [Input]?
|
||||
{
|
||||
return self.controllerSkin?.inputs(for: traits, at: point)
|
||||
}
|
||||
|
||||
public func items(for traits: DeltaCore.ControllerSkin.Traits) -> [DeltaCore.ControllerSkin.Item]?
|
||||
{
|
||||
return self.controllerSkin?.items(for: traits)
|
||||
|
||||
@ -45,12 +45,16 @@ public class Game: _Game, GameProtocol
|
||||
// Recreate the stored URL relative to current sandbox location.
|
||||
artworkURL = URL(fileURLWithPath: unwrappedArtworkURL.relativePath, relativeTo: DatabaseManager.gamesDirectoryURL)
|
||||
}
|
||||
else if unwrappedArtworkURL.host?.lowercased() == "img.gamefaqs.net", var components = URLComponents(url: unwrappedArtworkURL, resolvingAgainstBaseURL: false)
|
||||
else if let host = unwrappedArtworkURL.host?.lowercased(), host == "img.gamefaqs.net" || host == "gamefaqs1.cbsistatic.com",
|
||||
var components = URLComponents(url: unwrappedArtworkURL, resolvingAgainstBaseURL: false)
|
||||
{
|
||||
// Quick fix for broken album artwork URLs due to host change.
|
||||
components.host = "gamefaqs1.cbsistatic.com"
|
||||
components.host = "gamefaqs.gamespot.com"
|
||||
components.scheme = "https"
|
||||
|
||||
let updatedPath = "/a" + components.path
|
||||
components.path = updatedPath
|
||||
|
||||
if let url = components.url
|
||||
{
|
||||
artworkURL = url
|
||||
|
||||
@ -11,15 +11,17 @@ import Foundation
|
||||
// Must be an NSObject subclass so it can be used with RSTCellContentDataSource.
|
||||
class GameMetadata: NSObject
|
||||
{
|
||||
let identifier: Int
|
||||
let releaseID: Int
|
||||
let romID: Int
|
||||
|
||||
let name: String?
|
||||
let artworkURL: URL?
|
||||
|
||||
init(identifier: Int, name: String?, artworkURL: URL?)
|
||||
init(releaseID: Int, romID: Int, name: String?, artworkURL: URL?)
|
||||
{
|
||||
self.releaseID = releaseID
|
||||
self.romID = romID
|
||||
self.name = name
|
||||
self.identifier = identifier
|
||||
self.artworkURL = artworkURL
|
||||
}
|
||||
}
|
||||
@ -27,13 +29,13 @@ class GameMetadata: NSObject
|
||||
extension GameMetadata
|
||||
{
|
||||
override var hash: Int {
|
||||
return self.identifier.hashValue
|
||||
return self.releaseID.hashValue ^ self.romID.hashValue
|
||||
}
|
||||
|
||||
override func isEqual(_ object: Any?) -> Bool
|
||||
{
|
||||
guard let metadata = object as? GameMetadata else { return false }
|
||||
|
||||
return self.identifier == metadata.identifier
|
||||
return self.releaseID == metadata.releaseID && self.romID == metadata.romID
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,16 +57,26 @@ extension VirtualTable
|
||||
|
||||
extension GamesDatabase
|
||||
{
|
||||
enum Error: Swift.Error
|
||||
enum Error: LocalizedError
|
||||
{
|
||||
case doesNotExist
|
||||
case connection(Swift.Error)
|
||||
|
||||
var errorDescription: String? {
|
||||
switch self
|
||||
{
|
||||
case .doesNotExist:
|
||||
return NSLocalizedString("The SQLite database could not be found.", comment: "")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class GamesDatabase
|
||||
{
|
||||
static let version = -1
|
||||
static let version = 3
|
||||
static var previousVersion: Int? {
|
||||
return UserDefaults.standard.previousGamesDatabaseVersion
|
||||
}
|
||||
|
||||
private let connection: Connection
|
||||
|
||||
@ -80,7 +90,7 @@ class GamesDatabase
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw Error.connection(error)
|
||||
throw error
|
||||
}
|
||||
|
||||
self.invalidateVirtualTableIfNeeded()
|
||||
@ -89,10 +99,11 @@ class GamesDatabase
|
||||
func metadataResults(forGameName gameName: String) -> [GameMetadata]
|
||||
{
|
||||
let releaseID = Expression<Any>.releaseID
|
||||
let romID = Expression<Any>.romID
|
||||
let name = Expression<Any>.name
|
||||
let artworkAddress = Expression<Any>.artworkAddress
|
||||
|
||||
let query = VirtualTable.search.select(releaseID, name, artworkAddress).filter(name.match(gameName + "*"))
|
||||
let query = VirtualTable.search.select(releaseID, romID, name, artworkAddress).filter(name.match(gameName + "*"))
|
||||
|
||||
do
|
||||
{
|
||||
@ -111,7 +122,7 @@ class GamesDatabase
|
||||
}
|
||||
|
||||
|
||||
let metadata = GameMetadata(identifier: row[releaseID], name: row[name], artworkURL: artworkURL)
|
||||
let metadata = GameMetadata(releaseID: row[releaseID], romID: row[romID], name: row[name], artworkURL: artworkURL)
|
||||
return metadata
|
||||
}
|
||||
|
||||
@ -145,7 +156,7 @@ class GamesDatabase
|
||||
let romID = Expression<Any>.romID
|
||||
|
||||
let gameHash = game.identifier.uppercased()
|
||||
let query = Table.roms.select(releaseID, name, artworkAddress).filter(sha1Hash == gameHash).join(Table.releases, on: Table.roms[romID] == Table.releases[romID])
|
||||
let query = Table.roms.select(releaseID, name, artworkAddress, Table.roms[romID]).filter(sha1Hash == gameHash).join(Table.releases, on: Table.roms[romID] == Table.releases[romID])
|
||||
|
||||
do
|
||||
{
|
||||
@ -161,7 +172,7 @@ class GamesDatabase
|
||||
artworkURL = nil
|
||||
}
|
||||
|
||||
let metadata = GameMetadata(identifier: row[releaseID], name: row[name], artworkURL: artworkURL)
|
||||
let metadata = GameMetadata(releaseID: row[releaseID], romID: row[Table.roms[romID]], name: row[name], artworkURL: artworkURL)
|
||||
return metadata
|
||||
}
|
||||
}
|
||||
@ -197,12 +208,13 @@ private extension GamesDatabase
|
||||
let name = Expression<Any>.name
|
||||
let artworkAddress = Expression<Any>.artworkAddress
|
||||
let releaseID = Expression<Any>.releaseID
|
||||
let romID = Expression<Any>.romID
|
||||
|
||||
do
|
||||
{
|
||||
try self.connection.run(VirtualTable.search.create(.FTS4([releaseID, name, artworkAddress], tokenize: .Unicode61())))
|
||||
try self.connection.run(VirtualTable.search.create(.FTS4([releaseID, romID, name, artworkAddress], tokenize: .Unicode61())))
|
||||
|
||||
let update = VirtualTable.search.insert(Table.releases.select(releaseID, name, artworkAddress))
|
||||
let update = VirtualTable.search.insert(Table.releases.select(releaseID, romID, name, artworkAddress))
|
||||
_ = try self.connection.run(update)
|
||||
}
|
||||
catch
|
||||
|
||||
@ -15,6 +15,8 @@ extension UIActivity.ActivityType
|
||||
|
||||
class CopyDeepLinkActivity: UIActivity
|
||||
{
|
||||
private var deepLink: URL?
|
||||
|
||||
override class var activityCategory: UIActivity.Category {
|
||||
return .action
|
||||
}
|
||||
@ -28,7 +30,7 @@ class CopyDeepLinkActivity: UIActivity
|
||||
}
|
||||
|
||||
override var activityImage: UIImage? {
|
||||
return UIImage(named: "Link")
|
||||
return UIImage(symbolNameIfAvailable: "link") ?? UIImage(named: "Link")
|
||||
}
|
||||
|
||||
override func canPerform(withActivityItems activityItems: [Any]) -> Bool
|
||||
@ -47,7 +49,19 @@ class CopyDeepLinkActivity: UIActivity
|
||||
{
|
||||
guard let game = activityItems.first(where: { $0 is Game }) as? Game else { return }
|
||||
|
||||
let deepLink = URL(action: .launchGame(identifier: game.identifier))
|
||||
UIPasteboard.general.url = deepLink
|
||||
self.deepLink = URL(action: .launchGame(identifier: game.identifier))
|
||||
}
|
||||
|
||||
override func perform()
|
||||
{
|
||||
if let deepLink = self.deepLink
|
||||
{
|
||||
UIPasteboard.general.url = deepLink
|
||||
self.activityDidFinish(true)
|
||||
}
|
||||
else
|
||||
{
|
||||
self.activityDidFinish(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,8 +23,16 @@ extension UIViewController
|
||||
struct DeepLinkController
|
||||
{
|
||||
private var window: UIWindow? {
|
||||
guard let delegate = UIApplication.shared.delegate, let window = delegate.window else { return nil }
|
||||
return window
|
||||
if #available(iOS 13, *)
|
||||
{
|
||||
guard let delegate = UIApplication.shared.connectedScenes.lazy.compactMap({ $0.delegate as? UIWindowSceneDelegate }).first, let window = delegate.window else { return nil }
|
||||
return window
|
||||
}
|
||||
else
|
||||
{
|
||||
guard let delegate = UIApplication.shared.delegate, let window = delegate.window else { return nil }
|
||||
return window
|
||||
}
|
||||
}
|
||||
|
||||
private var topViewController: UIViewController? {
|
||||
|
||||
@ -10,11 +10,13 @@ import UIKit
|
||||
|
||||
import DeltaCore
|
||||
import GBADeltaCore
|
||||
import MelonDSDeltaCore
|
||||
import Systems
|
||||
|
||||
import struct DSDeltaCore.DS
|
||||
|
||||
import Roxas
|
||||
import AltKit
|
||||
|
||||
private var kvoContext = 0
|
||||
|
||||
@ -110,6 +112,7 @@ class GameViewController: DeltaCore.GameViewController
|
||||
}
|
||||
|
||||
self.updateControllers()
|
||||
self.updateAudio()
|
||||
|
||||
self.presentedGyroAlert = false
|
||||
}
|
||||
@ -167,6 +170,8 @@ class GameViewController: DeltaCore.GameViewController
|
||||
private var isGyroActive = false
|
||||
private var presentedGyroAlert = false
|
||||
|
||||
private var presentedJITAlert = false
|
||||
|
||||
override var shouldAutorotate: Bool {
|
||||
return !self.isGyroActive
|
||||
}
|
||||
@ -205,6 +210,8 @@ class GameViewController: DeltaCore.GameViewController
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(GameViewController.didDeactivateGyro(with:)), name: GBA.didDeactivateGyroNotification, object: nil)
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(GameViewController.emulationDidQuit(with:)), name: EmulatorCore.emulationDidQuitNotification, object: nil)
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(GameViewController.didEnableJIT(with:)), name: ServerManager.didEnableJITNotification, object: nil)
|
||||
}
|
||||
|
||||
deinit
|
||||
@ -330,12 +337,18 @@ extension GameViewController
|
||||
|
||||
UserDefaults.standard.desmumeDeprecatedAlertCount += 1
|
||||
}
|
||||
else if self.emulatorCore?.deltaCore == MelonDS.core, ProcessInfo.processInfo.isJITAvailable
|
||||
{
|
||||
self.showJITEnabledAlert()
|
||||
}
|
||||
}
|
||||
|
||||
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator)
|
||||
{
|
||||
super.viewWillTransition(to: size, with: coordinator)
|
||||
|
||||
guard UIApplication.shared.applicationState != .background else { return }
|
||||
|
||||
coordinator.animate(alongsideTransition: { (context) in
|
||||
self.updateControllerSkin()
|
||||
}, completion: nil)
|
||||
@ -474,6 +487,13 @@ extension GameViewController
|
||||
}
|
||||
|
||||
self._isLoadingSaveState = false
|
||||
|
||||
if self.emulatorCore?.deltaCore == MelonDS.core, ProcessInfo.processInfo.isJITAvailable
|
||||
{
|
||||
self.transitionCoordinator?.animate(alongsideTransition: nil, completion: { (context) in
|
||||
self.showJITEnabledAlert()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
case "unwindToGames":
|
||||
@ -646,12 +666,14 @@ private extension GameViewController
|
||||
else if let controllerSkin = DeltaCore.ControllerSkin.standardControllerSkin(for: game.type), controllerSkin.hasTouchScreen(for: traits)
|
||||
{
|
||||
var touchControllerSkin = TouchControllerSkin(controllerSkin: controllerSkin)
|
||||
touchControllerSkin.layoutGuide = self.view.safeAreaLayoutGuide
|
||||
|
||||
switch traits.orientation
|
||||
if self.view.bounds.width > self.view.bounds.height
|
||||
{
|
||||
case .portrait: touchControllerSkin.screenLayoutAxis = .vertical
|
||||
case .landscape: touchControllerSkin.screenLayoutAxis = .horizontal
|
||||
touchControllerSkin.screenLayoutAxis = .horizontal
|
||||
}
|
||||
else
|
||||
{
|
||||
touchControllerSkin.screenLayoutAxis = .vertical
|
||||
}
|
||||
|
||||
self.controllerView.controllerSkin = touchControllerSkin
|
||||
@ -914,6 +936,16 @@ extension GameViewController: CheatsViewControllerDelegate
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - Audio -
|
||||
/// Audio
|
||||
private extension GameViewController
|
||||
{
|
||||
func updateAudio()
|
||||
{
|
||||
self.emulatorCore?.audioManager.respectsSilentMode = Settings.respectSilentMode
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - Sustain Buttons -
|
||||
private extension GameViewController
|
||||
{
|
||||
@ -934,6 +966,8 @@ private extension GameViewController
|
||||
UIView.animate(withDuration: 0.4) {
|
||||
self.sustainButtonsBlurView.effect = blurEffect
|
||||
self.sustainButtonsBackgroundView.alpha = 1.0
|
||||
} completion: { _ in
|
||||
self.controllerView.becomeFirstResponder()
|
||||
}
|
||||
}
|
||||
|
||||
@ -1061,6 +1095,8 @@ extension GameViewController: GameViewControllerDelegate
|
||||
else if self.presentedViewController == nil
|
||||
{
|
||||
self.pauseEmulation()
|
||||
self.controllerView.resignFirstResponder()
|
||||
|
||||
self.performSegue(withIdentifier: "pause", sender: gameController)
|
||||
}
|
||||
}
|
||||
@ -1085,6 +1121,48 @@ private extension GameViewController
|
||||
toastView.presentationEdge = .top
|
||||
toastView.show(in: self.view, duration: duration)
|
||||
}
|
||||
|
||||
func showJITEnabledAlert()
|
||||
{
|
||||
guard !self.presentedJITAlert, self.presentedViewController == nil, self.game != nil else { return }
|
||||
self.presentedJITAlert = true
|
||||
|
||||
func presentToastView()
|
||||
{
|
||||
let detailText: String?
|
||||
let duration: TimeInterval
|
||||
|
||||
if UserDefaults.standard.jitEnabledAlertCount < 3
|
||||
{
|
||||
detailText = NSLocalizedString("You can now Fast Forward DS games up to 3x speed.", comment: "")
|
||||
duration = 5.0
|
||||
}
|
||||
else
|
||||
{
|
||||
detailText = nil
|
||||
duration = 2.0
|
||||
}
|
||||
|
||||
let toastView = RSTToastView(text: NSLocalizedString("JIT Compilation Enabled", comment: ""), detailText: detailText)
|
||||
toastView.edgeOffset.vertical = 8
|
||||
self.show(toastView, duration: duration)
|
||||
|
||||
UserDefaults.standard.jitEnabledAlertCount += 1
|
||||
}
|
||||
|
||||
DispatchQueue.main.async {
|
||||
if let transitionCoordinator = self.transitionCoordinator
|
||||
{
|
||||
transitionCoordinator.animate(alongsideTransition: nil) { (context) in
|
||||
presentToastView()
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
presentToastView()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - Notifications -
|
||||
@ -1127,9 +1205,13 @@ private extension GameViewController
|
||||
self.updateControllerSkin()
|
||||
}
|
||||
|
||||
case .translucentControllerSkinOpacity: self.controllerView.translucentControllerSkinOpacity = Settings.translucentControllerSkinOpacity
|
||||
case .translucentControllerSkinOpacity:
|
||||
self.controllerView.translucentControllerSkinOpacity = Settings.translucentControllerSkinOpacity
|
||||
|
||||
case .syncingService: break
|
||||
case .respectSilentMode:
|
||||
self.updateAudio()
|
||||
|
||||
case .syncingService, .isAltJITEnabled: break
|
||||
}
|
||||
}
|
||||
|
||||
@ -1207,6 +1289,60 @@ private extension GameViewController
|
||||
self.isGyroActive = false
|
||||
}
|
||||
|
||||
@objc func didEnableJIT(with notification: Notification)
|
||||
{
|
||||
DispatchQueue.main.async {
|
||||
self.showJITEnabledAlert()
|
||||
}
|
||||
|
||||
DispatchQueue.global(qos: .utility).async {
|
||||
guard let emulatorCore = self.emulatorCore, let emulatorBridge = emulatorCore.deltaCore.emulatorBridge as? MelonDSEmulatorBridge, !emulatorBridge.isJITEnabled
|
||||
else { return }
|
||||
|
||||
guard emulatorCore.state != .stopped else {
|
||||
// Emulator core is not running, which means we can set
|
||||
// isJITEnabled to true without resetting the core.
|
||||
emulatorBridge.isJITEnabled = true
|
||||
return
|
||||
}
|
||||
|
||||
let isVideoEnabled = emulatorCore.videoManager.isEnabled
|
||||
emulatorCore.videoManager.isEnabled = false
|
||||
|
||||
let isRunning = (emulatorCore.state == .running)
|
||||
if isRunning
|
||||
{
|
||||
self.pauseEmulation()
|
||||
}
|
||||
|
||||
let temporaryFileURL = FileManager.default.uniqueTemporaryURL()
|
||||
|
||||
let saveState = emulatorCore.saveSaveState(to: temporaryFileURL)
|
||||
emulatorCore.stop()
|
||||
|
||||
emulatorBridge.isJITEnabled = true
|
||||
|
||||
emulatorCore.start()
|
||||
emulatorCore.pause()
|
||||
|
||||
do
|
||||
{
|
||||
try emulatorCore.load(saveState)
|
||||
}
|
||||
catch
|
||||
{
|
||||
print("Failed to load save state after enabling JIT.", error)
|
||||
}
|
||||
|
||||
if isRunning
|
||||
{
|
||||
self.resumeEmulation()
|
||||
}
|
||||
|
||||
emulatorCore.videoManager.isEnabled = isVideoEnabled
|
||||
}
|
||||
}
|
||||
|
||||
@objc func emulationDidQuit(with notification: Notification)
|
||||
{
|
||||
DispatchQueue.main.async {
|
||||
@ -1231,4 +1367,6 @@ private extension GameViewController
|
||||
private extension UserDefaults
|
||||
{
|
||||
@NSManaged var desmumeDeprecatedAlertCount: Int
|
||||
|
||||
@NSManaged var jitEnabledAlertCount: Int
|
||||
}
|
||||
|
||||
22
Delta/Extensions/CharacterSet+Filename.swift
Normal file
22
Delta/Extensions/CharacterSet+Filename.swift
Normal file
@ -0,0 +1,22 @@
|
||||
//
|
||||
// CharacterSet+Filename.swift
|
||||
// Delta
|
||||
//
|
||||
// Created by Riley Testut on 4/28/22.
|
||||
// Copyright © 2022 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension CharacterSet
|
||||
{
|
||||
// Different than .urlPathAllowed
|
||||
// Copied from https://stackoverflow.com/a/39443252
|
||||
static var urlFilenameAllowed: CharacterSet {
|
||||
var illegalCharacters = CharacterSet(charactersIn: ":/")
|
||||
illegalCharacters.formUnion(.newlines)
|
||||
illegalCharacters.formUnion(.illegalCharacters)
|
||||
illegalCharacters.formUnion(.controlCharacters)
|
||||
return illegalCharacters.inverted
|
||||
}
|
||||
}
|
||||
41
Delta/Extensions/ProcessInfo+JIT.swift
Normal file
41
Delta/Extensions/ProcessInfo+JIT.swift
Normal file
@ -0,0 +1,41 @@
|
||||
//
|
||||
// ProcessInfo+JIT.swift
|
||||
// Delta
|
||||
//
|
||||
// Created by Riley Testut on 9/14/21.
|
||||
// Copyright © 2021 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
private let CS_OPS_STATUS: UInt32 = 0 /* OK */
|
||||
private let CS_DEBUGGED: UInt32 = 0x10000000 /* Process is or has been attached to debugger. */
|
||||
|
||||
@_silgen_name("csops")
|
||||
func csops(_ pid: pid_t, _ ops: UInt32, _ useraddr: UnsafeMutableRawPointer?, _ usersize: Int) -> Int
|
||||
|
||||
extension ProcessInfo
|
||||
{
|
||||
static var isJITDisabled = false
|
||||
|
||||
var isDebugging: Bool {
|
||||
var flags: UInt32 = 0
|
||||
let result = csops(getpid(), CS_OPS_STATUS, &flags, MemoryLayout<UInt32>.size)
|
||||
|
||||
let isDebugging = result == 0 && (flags & CS_DEBUGGED == CS_DEBUGGED)
|
||||
return isDebugging
|
||||
}
|
||||
|
||||
var isJITAvailable: Bool {
|
||||
guard UIDevice.current.supportsJIT && !ProcessInfo.isJITDisabled else { return false }
|
||||
|
||||
let ios14_4 = OperatingSystemVersion(majorVersion: 14, minorVersion: 4, patchVersion: 0)
|
||||
if #available(iOS 14.2, *), !ProcessInfo.processInfo.isOperatingSystemAtLeast(ios14_4)
|
||||
{
|
||||
// JIT is always available on supported devices running iOS 14.2 - 14.3.
|
||||
return true
|
||||
}
|
||||
|
||||
return self.isDebugging
|
||||
}
|
||||
}
|
||||
104
Delta/Extensions/ServerManager+Delta.swift
Normal file
104
Delta/Extensions/ServerManager+Delta.swift
Normal file
@ -0,0 +1,104 @@
|
||||
//
|
||||
// ServerManager+Delta.swift
|
||||
// Delta
|
||||
//
|
||||
// Created by Riley Testut on 9/15/21.
|
||||
// Copyright © 2021 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import AltKit
|
||||
|
||||
extension ServerManager
|
||||
{
|
||||
static let didEnableJITNotification = Notification.Name("didEnableJITNotification")
|
||||
}
|
||||
|
||||
extension ServerManager
|
||||
{
|
||||
func prepare()
|
||||
{
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(ServerManager.didChangeJITMode(_:)), name: .settingsDidChange, object: nil)
|
||||
|
||||
#if DEBUG
|
||||
if ProcessInfo.processInfo.isDebugging
|
||||
{
|
||||
// Debugger is attached at app launch, so we assume
|
||||
// we're connected to Xcode for debugging purposes.
|
||||
// In that case, we manually treat JIT as unavailable
|
||||
// until AltServer is discovered to simulate real-world use.
|
||||
ProcessInfo.isJITDisabled = true
|
||||
}
|
||||
#endif
|
||||
|
||||
self.start()
|
||||
}
|
||||
}
|
||||
|
||||
private extension ServerManager
|
||||
{
|
||||
func start()
|
||||
{
|
||||
guard Settings.isAltJITEnabled && !ProcessInfo.processInfo.isJITAvailable else { return }
|
||||
|
||||
self.startDiscovering()
|
||||
self.autoconnect()
|
||||
}
|
||||
|
||||
func autoconnect()
|
||||
{
|
||||
self.autoconnect { result in
|
||||
switch result
|
||||
{
|
||||
case .failure(let error):
|
||||
print("Could not auto-connect to server.", error)
|
||||
self.autoconnect()
|
||||
|
||||
case .success(let connection):
|
||||
func finish(result: Result<Void, Error>)
|
||||
{
|
||||
switch result
|
||||
{
|
||||
case .failure(ALTServerError.unknownRequest), .failure(ALTServerError.deviceNotFound):
|
||||
// Try connecting to a different server.
|
||||
self.autoconnect()
|
||||
|
||||
case .failure(let error):
|
||||
print("Could not enable JIT compilation.", error)
|
||||
|
||||
case .success:
|
||||
print("Successfully enabled JIT compilation!")
|
||||
|
||||
NotificationCenter.default.post(name: ServerManager.didEnableJITNotification, object: nil)
|
||||
self.stopDiscovering()
|
||||
}
|
||||
|
||||
connection.disconnect()
|
||||
}
|
||||
|
||||
if ProcessInfo.isJITDisabled
|
||||
{
|
||||
ProcessInfo.isJITDisabled = false
|
||||
finish(result: .success(()))
|
||||
}
|
||||
else
|
||||
{
|
||||
connection.enableUnsignedCodeExecution(completion: finish)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc func didChangeJITMode(_ notification: Notification)
|
||||
{
|
||||
guard let name = notification.userInfo?[Settings.NotificationUserInfoKey.name] as? Settings.Name, name == Settings.Name.isAltJITEnabled else { return }
|
||||
|
||||
if Settings.isAltJITEnabled
|
||||
{
|
||||
self.start()
|
||||
}
|
||||
else
|
||||
{
|
||||
self.stopDiscovering()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -26,12 +26,9 @@ extension UIDevice
|
||||
}
|
||||
|
||||
var supportsJIT: Bool {
|
||||
// As of iOS 14.4 beta 2, JIT is no longer supported :(
|
||||
// Hopefully this change is reversed before the public release...
|
||||
let ios14_4 = OperatingSystemVersion(majorVersion: 14, minorVersion: 4, patchVersion: 0)
|
||||
guard #available(iOS 14.2, *), !ProcessInfo.processInfo.isOperatingSystemAtLeast(ios14_4) else { return false }
|
||||
guard #available(iOS 14.0, *) else { return false }
|
||||
|
||||
// JIT is supported on devices with an A12 processor or better running iOS 14.2 or later.
|
||||
// JIT is supported on devices with an A12 processor or better running iOS 14.0 or later.
|
||||
// ARKit 3 is only supported by devices with an A12 processor or better, according to the documentation.
|
||||
return ARBodyTrackingConfiguration.isSupported
|
||||
}
|
||||
|
||||
@ -66,6 +66,8 @@ class GameCollectionViewController: UICollectionViewController
|
||||
private weak var _previewTransitionViewController: PreviewGameViewController?
|
||||
private weak var _previewTransitionDestinationViewController: UIViewController?
|
||||
|
||||
private weak var _popoverSourceView: UIView?
|
||||
|
||||
private var _renameAction: UIAlertAction?
|
||||
private var _changingArtworkGame: Game?
|
||||
private var _importingSaveFileGame: Game?
|
||||
@ -93,10 +95,6 @@ extension GameCollectionViewController
|
||||
self.collectionView?.prefetchDataSource = self.dataSource
|
||||
self.collectionView?.delegate = self
|
||||
|
||||
let layout = self.collectionViewLayout as! GridCollectionViewLayout
|
||||
layout.itemWidth = 90
|
||||
layout.minimumInteritemSpacing = 12
|
||||
|
||||
if #available(iOS 13, *) {}
|
||||
else
|
||||
{
|
||||
@ -105,6 +103,8 @@ extension GameCollectionViewController
|
||||
let longPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(GameCollectionViewController.handleLongPressGesture(_:)))
|
||||
self.collectionView?.addGestureRecognizer(longPressGestureRecognizer)
|
||||
}
|
||||
|
||||
self.update()
|
||||
}
|
||||
|
||||
override func viewWillDisappear(_ animated: Bool)
|
||||
@ -131,6 +131,13 @@ extension GameCollectionViewController
|
||||
super.didReceiveMemoryWarning()
|
||||
// Dispose of any resources that can be recreated.
|
||||
}
|
||||
|
||||
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?)
|
||||
{
|
||||
super.traitCollectionDidChange(previousTraitCollection)
|
||||
|
||||
self.update()
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - Segues -
|
||||
@ -180,7 +187,7 @@ extension GameCollectionViewController
|
||||
emulatorBridge.systemType = .ds
|
||||
}
|
||||
|
||||
emulatorBridge.isJITEnabled = UIDevice.current.supportsJIT
|
||||
emulatorBridge.isJITEnabled = ProcessInfo.processInfo.isJITAvailable
|
||||
}
|
||||
|
||||
if let saveState = self.activeSaveState
|
||||
@ -224,6 +231,26 @@ extension GameCollectionViewController
|
||||
//MARK: - Private Methods -
|
||||
private extension GameCollectionViewController
|
||||
{
|
||||
func update()
|
||||
{
|
||||
let layout = self.collectionViewLayout as! GridCollectionViewLayout
|
||||
|
||||
switch self.traitCollection.horizontalSizeClass
|
||||
{
|
||||
case .regular:
|
||||
layout.itemWidth = 150
|
||||
layout.minimumInteritemSpacing = 25 // 30 == only 3 games per line for iPad mini 6 in portrait
|
||||
|
||||
case .unspecified, .compact:
|
||||
layout.itemWidth = 90
|
||||
layout.minimumInteritemSpacing = 12
|
||||
|
||||
@unknown default: break
|
||||
}
|
||||
|
||||
self.collectionView.reloadData()
|
||||
}
|
||||
|
||||
//MARK: - Data Source
|
||||
func prepareDataSource()
|
||||
{
|
||||
@ -284,7 +311,19 @@ private extension GameCollectionViewController
|
||||
|
||||
cell.imageView.image = #imageLiteral(resourceName: "BoxArt")
|
||||
|
||||
cell.maximumImageSize = CGSize(width: 90, height: 90)
|
||||
if self.traitCollection.horizontalSizeClass == .regular
|
||||
{
|
||||
let fontDescriptor = UIFontDescriptor.preferredFontDescriptor(withTextStyle: .subheadline).withSymbolicTraits(.traitBold)!
|
||||
cell.textLabel.font = UIFont(descriptor: fontDescriptor, size: 0)
|
||||
}
|
||||
else
|
||||
{
|
||||
cell.textLabel.font = UIFont.preferredFont(forTextStyle: .caption1)
|
||||
}
|
||||
|
||||
let layout = self.collectionViewLayout as! GridCollectionViewLayout
|
||||
cell.maximumImageSize = CGSize(width: layout.itemWidth, height: layout.itemWidth)
|
||||
|
||||
cell.textLabel.text = game.name
|
||||
cell.textLabel.textColor = UIColor.gray
|
||||
cell.tintColor = cell.textLabel.textColor
|
||||
@ -484,7 +523,9 @@ private extension GameCollectionViewController
|
||||
|
||||
func delete(_ game: Game)
|
||||
{
|
||||
let confirmationAlertController = UIAlertController(title: NSLocalizedString("Are you sure you want to delete this game? All associated data, such as saves, save states, and cheat codes, will also be deleted.", comment: ""), message: nil, preferredStyle: .actionSheet)
|
||||
let confirmationAlertController = UIAlertController(title: NSLocalizedString("Are you sure you want to delete this game?", comment: ""),
|
||||
message: NSLocalizedString("All associated data, such as saves, save states, and cheat codes, will also be deleted.", comment: ""),
|
||||
preferredStyle: .alert)
|
||||
confirmationAlertController.addAction(UIAlertAction(title: NSLocalizedString("Delete Game", comment: ""), style: .destructive, handler: { action in
|
||||
|
||||
DatabaseManager.shared.performBackgroundTask { (context) in
|
||||
@ -554,6 +595,7 @@ private extension GameCollectionViewController
|
||||
let importController = ImportController(documentTypes: [kUTTypeImage as String])
|
||||
importController.delegate = self
|
||||
importController.importOptions = [clipboardImportOption, photoLibraryImportOption, gamesDatabaseImportOption]
|
||||
importController.sourceView = self._popoverSourceView
|
||||
self.present(importController, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
@ -663,26 +705,36 @@ private extension GameCollectionViewController
|
||||
|
||||
func share(_ game: Game)
|
||||
{
|
||||
let temporaryDirectory = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
|
||||
let symbolicURL = temporaryDirectory.appendingPathComponent(game.name + "." + game.fileURL.pathExtension)
|
||||
let temporaryDirectory = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true)
|
||||
|
||||
let sanitizedName = game.name.components(separatedBy: .urlFilenameAllowed.inverted).joined()
|
||||
let temporaryURL = temporaryDirectory.appendingPathComponent(sanitizedName + "." + game.fileURL.pathExtension, isDirectory: false)
|
||||
|
||||
do
|
||||
{
|
||||
try FileManager.default.createDirectory(at: temporaryDirectory, withIntermediateDirectories: true, attributes: nil)
|
||||
|
||||
// Create a symbolic link so we can control the file name used when sharing.
|
||||
|
||||
// Make a temporary copy so we can control the filename used when sharing.
|
||||
// Otherwise, if we just passed in game.fileURL to UIActivityViewController, the file name would be the game's SHA1 hash.
|
||||
try FileManager.default.createSymbolicLink(at: symbolicURL, withDestinationURL: game.fileURL)
|
||||
try FileManager.default.copyItem(at: game.fileURL, to: temporaryURL, shouldReplace: true)
|
||||
}
|
||||
catch
|
||||
{
|
||||
print(error)
|
||||
let alertController = UIAlertController(title: NSLocalizedString("Could Not Share Game", comment: ""), error: error)
|
||||
self.present(alertController, animated: true, completion: nil)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
let copyDeepLinkActivity = CopyDeepLinkActivity()
|
||||
|
||||
let activityViewController = UIActivityViewController(activityItems: [symbolicURL, game], applicationActivities: [copyDeepLinkActivity])
|
||||
let activityViewController = UIActivityViewController(activityItems: [temporaryURL, game], applicationActivities: [copyDeepLinkActivity])
|
||||
activityViewController.popoverPresentationController?.sourceView = self._popoverSourceView?.superview
|
||||
activityViewController.popoverPresentationController?.sourceRect = self._popoverSourceView?.frame ?? .zero
|
||||
activityViewController.completionWithItemsHandler = { (activityType, finished, returnedItems, error) in
|
||||
// Make sure the user either shared the game or cancelled before deleting temporaryDirectory.
|
||||
guard finished || activityType == nil else { return }
|
||||
|
||||
do
|
||||
{
|
||||
try FileManager.default.removeItem(at: temporaryDirectory)
|
||||
@ -692,6 +744,7 @@ private extension GameCollectionViewController
|
||||
print(error)
|
||||
}
|
||||
}
|
||||
|
||||
self.present(activityViewController, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
@ -747,8 +800,7 @@ private extension GameCollectionViewController
|
||||
{
|
||||
do
|
||||
{
|
||||
let illegalCharacterSet = CharacterSet(charactersIn: "\"\\/?<>:*|")
|
||||
let sanitizedFilename = game.name.components(separatedBy: illegalCharacterSet).joined() + "." + game.gameSaveURL.pathExtension
|
||||
let sanitizedFilename = game.name.components(separatedBy: .urlFilenameAllowed.inverted).joined()
|
||||
|
||||
let temporaryURL = FileManager.default.temporaryDirectory.appendingPathComponent(sanitizedFilename)
|
||||
try FileManager.default.copyItem(at: game.gameSaveURL, to: temporaryURL, shouldReplace: true)
|
||||
@ -807,6 +859,9 @@ extension GameCollectionViewController: UIViewControllerPreviewingDelegate
|
||||
|
||||
previewingContext.sourceRect = layoutAttributes.frame
|
||||
|
||||
let cell = collectionView.cellForItem(at: indexPath)
|
||||
self._popoverSourceView = cell
|
||||
|
||||
let game = self.dataSource.item(at: indexPath)
|
||||
|
||||
let gameViewController = self.makePreviewGameViewController(for: game)
|
||||
@ -843,7 +898,7 @@ extension GameCollectionViewController: UIViewControllerPreviewingDelegate
|
||||
emulatorBridge.systemType = .ds
|
||||
}
|
||||
|
||||
emulatorBridge.isJITEnabled = UIDevice.current.supportsJIT
|
||||
emulatorBridge.isJITEnabled = ProcessInfo.processInfo.isJITAvailable
|
||||
}
|
||||
|
||||
let actions = self.actions(for: game).previewActions
|
||||
@ -974,6 +1029,9 @@ extension GameCollectionViewController
|
||||
let game = self.dataSource.item(at: indexPath)
|
||||
let actions = self.actions(for: game)
|
||||
|
||||
let cell = self.collectionView.cellForItem(at: indexPath)
|
||||
self._popoverSourceView = cell
|
||||
|
||||
return UIContextMenuConfiguration(identifier: indexPath as NSIndexPath, previewProvider: { [weak self] in
|
||||
guard let self = self else { return nil }
|
||||
|
||||
|
||||
@ -47,6 +47,7 @@ class GamesViewController: UIViewController
|
||||
private let fetchedResultsController: NSFetchedResultsController<NSFetchRequestResult>
|
||||
|
||||
private var searchController: RSTSearchController?
|
||||
private lazy var importController: ImportController = self.makeImportController()
|
||||
|
||||
private var syncingToastView: RSTToastView? {
|
||||
didSet {
|
||||
@ -58,6 +59,8 @@ class GamesViewController: UIViewController
|
||||
}
|
||||
private var syncingProgressObservation: NSKeyValueObservation?
|
||||
|
||||
@IBOutlet private var importButton: UIBarButtonItem!
|
||||
|
||||
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
|
||||
fatalError("initWithNibName: not implemented")
|
||||
}
|
||||
@ -114,10 +117,16 @@ extension GamesViewController
|
||||
let navigationBarAppearance = navigationController.navigationBar.standardAppearance.copy()
|
||||
navigationBarAppearance.backgroundEffect = UIBlurEffect(style: .dark)
|
||||
navigationController.navigationBar.standardAppearance = navigationBarAppearance
|
||||
navigationController.navigationBar.scrollEdgeAppearance = navigationBarAppearance
|
||||
|
||||
let toolbarAppearance = navigationController.toolbar.standardAppearance.copy()
|
||||
toolbarAppearance.backgroundEffect = UIBlurEffect(style: .dark)
|
||||
navigationController.toolbar.standardAppearance = toolbarAppearance
|
||||
navigationController.toolbar.standardAppearance = toolbarAppearance
|
||||
|
||||
if #available(iOS 15, *)
|
||||
{
|
||||
navigationController.toolbar.scrollEdgeAppearance = toolbarAppearance
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -126,6 +135,22 @@ extension GamesViewController
|
||||
}
|
||||
}
|
||||
|
||||
if #available(iOS 14, *)
|
||||
{
|
||||
self.importController.presentingViewController = self
|
||||
|
||||
let importActions = self.importController.makeActions().menuActions
|
||||
let importMenu = UIMenu(title: NSLocalizedString("Import From…", comment: ""), image: UIImage(systemName: "square.and.arrow.down"), children: importActions)
|
||||
self.importButton.menu = importMenu
|
||||
|
||||
self.importButton.action = nil
|
||||
self.importButton.target = nil
|
||||
}
|
||||
else
|
||||
{
|
||||
self.importController.barButtonItem = self.importButton
|
||||
}
|
||||
|
||||
self.prepareSearchController()
|
||||
|
||||
self.updateTheme()
|
||||
@ -352,8 +377,8 @@ private extension GamesViewController
|
||||
/// Importing
|
||||
extension GamesViewController: ImportControllerDelegate
|
||||
{
|
||||
@IBAction private func importFiles()
|
||||
{
|
||||
private func makeImportController() -> ImportController
|
||||
{
|
||||
var documentTypes = Set(System.registeredSystems.map { $0.gameType.rawValue })
|
||||
documentTypes.insert(kUTTypeZipArchive as String)
|
||||
documentTypes.insert("com.rileytestut.delta.skin")
|
||||
@ -373,7 +398,13 @@ extension GamesViewController: ImportControllerDelegate
|
||||
let importController = ImportController(documentTypes: documentTypes)
|
||||
importController.delegate = self
|
||||
importController.importOptions = [itunesImportOption]
|
||||
self.present(importController, animated: true, completion: nil)
|
||||
|
||||
return importController
|
||||
}
|
||||
|
||||
@IBAction private func importFiles()
|
||||
{
|
||||
self.present(self.importController, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
func importController(_ importController: ImportController, didImportItemsAt urls: Set<URL>, errors: [Error])
|
||||
|
||||
@ -13,7 +13,7 @@ import DeltaCore
|
||||
struct iTunesImportOption: ImportOption
|
||||
{
|
||||
let title = NSLocalizedString("iTunes", comment: "")
|
||||
let image: UIImage? = nil
|
||||
let image: UIImage? = UIImage(symbolNameIfAvailable: "music.note")
|
||||
|
||||
private let presentingViewController: UIViewController
|
||||
|
||||
|
||||
@ -37,7 +37,10 @@ class ImportController: NSObject
|
||||
var delegate: ImportControllerDelegate?
|
||||
var importOptions: [ImportOption]?
|
||||
|
||||
private weak var presentingViewController: UIViewController?
|
||||
weak var presentingViewController: UIViewController?
|
||||
|
||||
weak var barButtonItem: UIBarButtonItem?
|
||||
weak var sourceView: UIView?
|
||||
|
||||
// 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.
|
||||
@ -61,26 +64,54 @@ class ImportController: NSObject
|
||||
super.init()
|
||||
}
|
||||
|
||||
func makeActions() -> [Action]
|
||||
{
|
||||
assert(self.presentingViewController != nil, "presentingViewController must be set before calling makeActions()")
|
||||
|
||||
var actions = (self.importOptions ?? []).map { (option) -> Action in
|
||||
let action = Action(title: option.title, style: .default, image: option.image) { _ in
|
||||
option.import { importedURLs in
|
||||
self.finish(with: importedURLs, errors: [])
|
||||
}
|
||||
}
|
||||
|
||||
return action
|
||||
}
|
||||
|
||||
let filesAction = Action(title: NSLocalizedString("Files", comment: ""), style: .default, image: UIImage(symbolNameIfAvailable: "doc")) { action in
|
||||
self.presentDocumentBrowser()
|
||||
}
|
||||
actions.append(filesAction)
|
||||
|
||||
return actions
|
||||
}
|
||||
|
||||
fileprivate func presentImportController(from presentingViewController: UIViewController, animated: Bool, completionHandler: (() -> Void)?)
|
||||
{
|
||||
self.presentingViewController = presentingViewController
|
||||
|
||||
let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
|
||||
alertController.addAction(UIAlertAction.cancel)
|
||||
let actions = self.makeActions()
|
||||
|
||||
if let importOptions = self.importOptions
|
||||
if actions.count > 1
|
||||
{
|
||||
for importOption in importOptions
|
||||
let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
|
||||
alertController.addAction(UIAlertAction.cancel)
|
||||
|
||||
let alertActions = actions.map { UIAlertAction($0) }
|
||||
for action in alertActions
|
||||
{
|
||||
alertController.add(importOption) { [unowned self] (urls) in
|
||||
self.finish(with: urls, errors: [])
|
||||
}
|
||||
alertController.addAction(action)
|
||||
}
|
||||
|
||||
let filesAction = UIAlertAction(title: NSLocalizedString("Files", comment: ""), style: .default) { (action) in
|
||||
self.presentDocumentBrowser()
|
||||
if let sourceView = self.sourceView
|
||||
{
|
||||
alertController.popoverPresentationController?.sourceView = sourceView.superview
|
||||
alertController.popoverPresentationController?.sourceRect = sourceView.frame
|
||||
}
|
||||
else
|
||||
{
|
||||
alertController.popoverPresentationController?.barButtonItem = self.barButtonItem
|
||||
}
|
||||
alertController.addAction(filesAction)
|
||||
|
||||
self.presentedViewController = alertController
|
||||
self.presentingViewController?.present(alertController, animated: true, completion: nil)
|
||||
@ -198,7 +229,7 @@ private var ImportControllerKey: UInt8 = 0
|
||||
|
||||
extension UIViewController
|
||||
{
|
||||
fileprivate(set) var importController: ImportController?
|
||||
fileprivate var importController: ImportController?
|
||||
{
|
||||
set
|
||||
{
|
||||
|
||||
@ -12,13 +12,26 @@ import DeltaCore
|
||||
|
||||
extension CheatValidator
|
||||
{
|
||||
enum Error: Swift.Error
|
||||
enum Error: LocalizedError
|
||||
{
|
||||
case invalidCode
|
||||
case invalidName
|
||||
case invalidGame
|
||||
case duplicateName
|
||||
case duplicateCode
|
||||
case unknownCheatType
|
||||
|
||||
var errorDescription: String? {
|
||||
switch self
|
||||
{
|
||||
case .invalidCode: return NSLocalizedString("The cheat code isn't in the correct format.", comment: "")
|
||||
case .invalidName: return NSLocalizedString("The name of this cheat is invalid.", comment: "")
|
||||
case .invalidGame: return NSLocalizedString("There is no associated game with this cheat.", comment: "")
|
||||
case .duplicateName: return NSLocalizedString("A cheat already exists with this name.", comment: "")
|
||||
case .duplicateCode: return NSLocalizedString("A cheat already exists with this code.", comment: "")
|
||||
case .unknownCheatType: return NSLocalizedString("Delta does not support this cheat type.", comment: "")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -8,8 +8,10 @@
|
||||
|
||||
import UIKit
|
||||
import CoreData
|
||||
import SwiftUI
|
||||
|
||||
import DeltaCore
|
||||
import MelonDSDeltaCore
|
||||
|
||||
import Roxas
|
||||
|
||||
@ -30,6 +32,8 @@ class CheatsViewController: UITableViewController
|
||||
weak var delegate: CheatsViewControllerDelegate?
|
||||
|
||||
private let dataSource = RSTFetchedResultsTableViewDataSource<Cheat>(fetchedResultsController: NSFetchedResultsController())
|
||||
|
||||
private var cheatBaseCheats: [CheatMetadata]?
|
||||
}
|
||||
|
||||
extension CheatsViewController
|
||||
@ -61,6 +65,21 @@ extension CheatsViewController
|
||||
self.tableView.separatorEffect = vibrancyEffect
|
||||
|
||||
self.registerForPreviewing(with: self, sourceView: self.tableView)
|
||||
|
||||
if #available(iOS 14, *)
|
||||
{
|
||||
self.updateAddCheatMenu()
|
||||
}
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool)
|
||||
{
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
if #available(iOS 14, *), self.cheatBaseCheats == nil
|
||||
{
|
||||
self.fetchCheatBaseCheats()
|
||||
}
|
||||
}
|
||||
|
||||
override func didReceiveMemoryWarning()
|
||||
@ -91,6 +110,37 @@ private extension CheatsViewController
|
||||
|
||||
self.dataSource.fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: DatabaseManager.shared.viewContext, sectionNameKeyPath: nil, cacheName: nil)
|
||||
}
|
||||
|
||||
@available(iOS 14, *) @MainActor
|
||||
func updateAddCheatMenu()
|
||||
{
|
||||
// CheatBase only contains DS cheats for now, so hide option completely for other systems.
|
||||
guard self.game.type == .ds else { return }
|
||||
|
||||
var searchCheatBaseTitle = NSLocalizedString("Search CheatBase", comment: "")
|
||||
var attributes: UIMenuElement.Attributes = []
|
||||
|
||||
if let cheats = self.cheatBaseCheats, cheats.isEmpty
|
||||
{
|
||||
searchCheatBaseTitle = NSLocalizedString("No Cheats in CheatBase", comment: "")
|
||||
attributes = [.disabled]
|
||||
}
|
||||
|
||||
let addCheatMenu = UIMenu(children: [
|
||||
UIAction(title: NSLocalizedString("New Cheat Code", comment: ""), image: UIImage(systemName: "square.and.pencil")) { [weak self] _ in
|
||||
self?.addCheat()
|
||||
},
|
||||
|
||||
UIAction(title: searchCheatBaseTitle, image: UIImage(systemName: "magnifyingglass"), attributes: attributes) { [weak self] _ in
|
||||
self?.searchCheatBase()
|
||||
},
|
||||
])
|
||||
|
||||
self.navigationItem.rightBarButtonItem?.target = nil
|
||||
self.navigationItem.rightBarButtonItem?.action = nil
|
||||
|
||||
self.navigationItem.rightBarButtonItem?.menu = addCheatMenu
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - Managing Cheats -
|
||||
@ -103,6 +153,78 @@ private extension CheatsViewController
|
||||
editCheatViewController.presentWithPresentingViewController(self)
|
||||
}
|
||||
|
||||
@available(iOS 14, *)
|
||||
func fetchCheatBaseCheats()
|
||||
{
|
||||
Task {
|
||||
do
|
||||
{
|
||||
let cheatBase = try CheatBase()
|
||||
let cheats = try await cheatBase.cheats(for: self.game) ?? []
|
||||
self.cheatBaseCheats = cheats
|
||||
|
||||
self.updateAddCheatMenu()
|
||||
}
|
||||
catch
|
||||
{
|
||||
print("[RSTLog] Failed to prefetch cheats from CheatBase:", error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 14, *)
|
||||
func searchCheatBase()
|
||||
{
|
||||
var rootView = CheatBaseView(game: self.game, cheats: self.cheatBaseCheats)
|
||||
rootView.cancellationHandler = { [weak self] in
|
||||
self?.presentedViewController?.dismiss(animated: true)
|
||||
}
|
||||
|
||||
rootView.selectionHandler = { [weak self] cheatMetadata in
|
||||
self?.saveCheatMetadata(cheatMetadata)
|
||||
self?.presentedViewController?.dismiss(animated: true)
|
||||
}
|
||||
|
||||
let hostingController = UIHostingController(rootView: rootView)
|
||||
self.present(hostingController, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
func saveCheatMetadata(_ cheatMetadata: CheatMetadata)
|
||||
{
|
||||
DatabaseManager.shared.performBackgroundTask { context in
|
||||
do
|
||||
{
|
||||
guard let cheatType = cheatMetadata.device.cheatType, let cheatFormat = cheatMetadata.device.cheatFormat else { throw CheatValidator.Error.unknownCheatType }
|
||||
|
||||
let cheat = Cheat(context: context)
|
||||
cheat.name = cheatMetadata.name
|
||||
cheat.type = cheatType
|
||||
cheat.isEnabled = true
|
||||
|
||||
let sanitizedCode = cheatMetadata.code.components(separatedBy: .whitespacesAndNewlines).joined()
|
||||
let formattedCode = sanitizedCode.formatted(with: cheatFormat)
|
||||
cheat.code = formattedCode
|
||||
|
||||
let game = context.object(with: self.game.objectID) as! Game
|
||||
cheat.game = game
|
||||
|
||||
let validator = CheatValidator(format: cheatFormat, managedObjectContext: context)
|
||||
try validator.validate(cheat)
|
||||
|
||||
self.delegate?.cheatsViewController(self, activateCheat: cheat)
|
||||
|
||||
try context.save()
|
||||
}
|
||||
catch
|
||||
{
|
||||
DispatchQueue.main.async {
|
||||
let alertController = UIAlertController(title: NSLocalizedString("Unable to Add Cheat", comment: ""), error: error)
|
||||
self.present(alertController, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func deleteCheat(_ cheat: Cheat)
|
||||
{
|
||||
self.delegate?.cheatsViewController(self, deactivateCheat: cheat)
|
||||
|
||||
@ -93,14 +93,6 @@ extension SaveStatesViewController
|
||||
self.collectionView?.dataSource = self.dataSource
|
||||
self.collectionView?.prefetchDataSource = self.dataSource
|
||||
|
||||
let collectionViewLayout = self.collectionViewLayout as! GridCollectionViewLayout
|
||||
let averageHorizontalInset = (collectionViewLayout.sectionInset.left + collectionViewLayout.sectionInset.right) / 2
|
||||
let portraitScreenWidth = UIScreen.main.coordinateSpace.convert(UIScreen.main.bounds, to: UIScreen.main.fixedCoordinateSpace).width
|
||||
|
||||
// Use dimensions that allow two cells to fill the screen horizontally with padding in portrait mode
|
||||
// We'll keep the same size for landscape orientation, which will allow more to fit
|
||||
collectionViewLayout.itemWidth = floor((portraitScreenWidth - (averageHorizontalInset * 3)) / 2)
|
||||
|
||||
switch self.mode
|
||||
{
|
||||
case .saving:
|
||||
@ -113,8 +105,7 @@ extension SaveStatesViewController
|
||||
self.navigationItem.rightBarButtonItems?.removeFirst()
|
||||
}
|
||||
|
||||
// Manually update prototype cell properties
|
||||
self.prototypeCellWidthConstraint = self.prototypeCell.contentView.widthAnchor.constraint(equalToConstant: collectionViewLayout.itemWidth)
|
||||
self.prototypeCellWidthConstraint = self.prototypeCell.contentView.widthAnchor.constraint(equalToConstant: 0)
|
||||
self.prototypeCellWidthConstraint.isActive = true
|
||||
|
||||
self.prepareEmulatorCoreSaveState()
|
||||
@ -238,6 +229,26 @@ private extension SaveStatesViewController
|
||||
}
|
||||
|
||||
self.sortButton.transform = CGAffineTransform.identity.rotated(by: Settings.sortSaveStatesByOldestFirst ? 0 : .pi)
|
||||
|
||||
let collectionViewLayout = self.collectionViewLayout as! GridCollectionViewLayout
|
||||
|
||||
if self.traitCollection.horizontalSizeClass == .regular
|
||||
{
|
||||
collectionViewLayout.itemWidth = 180
|
||||
collectionViewLayout.minimumInteritemSpacing = 30
|
||||
}
|
||||
else
|
||||
{
|
||||
let averageHorizontalInset = (collectionViewLayout.sectionInset.left + collectionViewLayout.sectionInset.right) / 2
|
||||
let portraitScreenWidth = UIScreen.main.coordinateSpace.convert(UIScreen.main.bounds, to: UIScreen.main.fixedCoordinateSpace).width
|
||||
|
||||
// Use dimensions that allow two cells to fill the screen horizontally with padding in portrait mode
|
||||
// We'll keep the same size for landscape orientation, which will allow more to fit
|
||||
collectionViewLayout.itemWidth = floor((portraitScreenWidth - (averageHorizontalInset * 3)) / 2)
|
||||
}
|
||||
|
||||
// Manually update prototype cell properties
|
||||
self.prototypeCellWidthConstraint.constant = collectionViewLayout.itemWidth
|
||||
}
|
||||
|
||||
//MARK: - Configure Views -
|
||||
|
||||
190
Delta/SceneDelegate.swift
Normal file
190
Delta/SceneDelegate.swift
Normal file
@ -0,0 +1,190 @@
|
||||
//
|
||||
// SceneDelegate.swift
|
||||
// Delta
|
||||
//
|
||||
// Created by Riley Testut on 6/6/22.
|
||||
// Copyright © 2022 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
import DeltaCore
|
||||
import Harmony
|
||||
|
||||
@objc(SceneDelegate) @available(iOS 13, *)
|
||||
class SceneDelegate: UIResponder, UIWindowSceneDelegate
|
||||
{
|
||||
var window: UIWindow? {
|
||||
get {
|
||||
if _window == nil
|
||||
{
|
||||
_window = GameWindow()
|
||||
}
|
||||
|
||||
return _window
|
||||
}
|
||||
set {
|
||||
_window = newValue as? GameWindow
|
||||
}
|
||||
}
|
||||
private var _window: GameWindow?
|
||||
|
||||
private let deepLinkController = DeepLinkController()
|
||||
|
||||
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions)
|
||||
{
|
||||
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
|
||||
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
|
||||
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
|
||||
|
||||
self.window?.tintColor = .deltaPurple
|
||||
|
||||
if let context = connectionOptions.urlContexts.first
|
||||
{
|
||||
self.handle(.url(context.url))
|
||||
}
|
||||
|
||||
if let shortcutItem = connectionOptions.shortcutItem
|
||||
{
|
||||
self.handle(.shortcut(shortcutItem))
|
||||
}
|
||||
|
||||
self.window?.makeKeyAndVisible()
|
||||
}
|
||||
|
||||
func sceneDidDisconnect(_ scene: UIScene)
|
||||
{
|
||||
// Called as the scene is being released by the system.
|
||||
// This occurs shortly after the scene enters the background, or when its session is discarded.
|
||||
// Release any resources associated with this scene that can be re-created the next time the scene connects.
|
||||
// The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).
|
||||
}
|
||||
|
||||
func sceneDidBecomeActive(_ scene: UIScene)
|
||||
{
|
||||
// Called when the scene has moved from an inactive state to an active state.
|
||||
// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
|
||||
}
|
||||
|
||||
func sceneWillResignActive(_ scene: UIScene)
|
||||
{
|
||||
// Called when the scene will move from an active state to an inactive state.
|
||||
// This may occur due to temporary interruptions (ex. an incoming phone call).
|
||||
}
|
||||
|
||||
func sceneWillEnterForeground(_ scene: UIScene)
|
||||
{
|
||||
// Called as the scene transitions from the background to the foreground.
|
||||
// Use this method to undo the changes made on entering the background.
|
||||
}
|
||||
|
||||
func sceneDidEnterBackground(_ scene: UIScene)
|
||||
{
|
||||
// Called as the scene transitions from the foreground to the background.
|
||||
// Use this method to save data, release shared resources, and store enough scene-specific state information
|
||||
// to restore the scene back to its current state.
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 13, *)
|
||||
extension SceneDelegate
|
||||
{
|
||||
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>)
|
||||
{
|
||||
guard let context = URLContexts.first else { return }
|
||||
self.handle(.url(context.url))
|
||||
}
|
||||
|
||||
func windowScene(_ windowScene: UIWindowScene, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void)
|
||||
{
|
||||
self.handle(.shortcut(shortcutItem))
|
||||
completionHandler(true)
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 13, *)
|
||||
private extension SceneDelegate
|
||||
{
|
||||
func handle(_ deepLink: DeepLink)
|
||||
{
|
||||
guard DatabaseManager.shared.isStarted else {
|
||||
// Wait until DatabaseManager is ready before handling deep link.
|
||||
|
||||
// NotificationCenter.default.notifications requires iOS 15 or later :(
|
||||
// _ = await NotificationCenter.default.notifications(named: DatabaseManager.didStartNotification).first(where: { _ in true })
|
||||
|
||||
var observer: NSObjectProtocol?
|
||||
observer = NotificationCenter.default.addObserver(forName: DatabaseManager.didStartNotification, object: DatabaseManager.shared, queue: .main) { [weak observer] _ in
|
||||
observer.map { NotificationCenter.default.removeObserver($0) }
|
||||
self.handle(deepLink)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
DispatchQueue.main.async {
|
||||
// DeepLinkController expects to be called from main thread.
|
||||
|
||||
switch deepLink
|
||||
{
|
||||
case .shortcut:
|
||||
_ = self.deepLinkController.handle(deepLink)
|
||||
|
||||
case .url(let url):
|
||||
if url.isFileURL
|
||||
{
|
||||
if GameType(fileExtension: url.pathExtension) != nil || url.pathExtension.lowercased() == "zip"
|
||||
{
|
||||
self.importGame(at: url)
|
||||
}
|
||||
else if url.pathExtension.lowercased() == "deltaskin"
|
||||
{
|
||||
self.importControllerSkin(at: url)
|
||||
}
|
||||
}
|
||||
else if url.scheme?.hasPrefix("db-") == true
|
||||
{
|
||||
_ = DropboxService.shared.handleDropboxURL(url)
|
||||
}
|
||||
else if url.scheme?.lowercased() == "delta"
|
||||
{
|
||||
_ = self.deepLinkController.handle(deepLink)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func importGame(at url: URL)
|
||||
{
|
||||
DatabaseManager.shared.importGames(at: [url]) { (games, errors) in
|
||||
if errors.count > 0
|
||||
{
|
||||
let alertController = UIAlertController.alertController(for: .games, with: errors)
|
||||
self.present(alertController)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func importControllerSkin(at url: URL)
|
||||
{
|
||||
DatabaseManager.shared.importControllerSkins(at: [url]) { (games, errors) in
|
||||
if errors.count > 0
|
||||
{
|
||||
let alertController = UIAlertController.alertController(for: .controllerSkins, with: errors)
|
||||
self.present(alertController)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func present(_ alertController: UIAlertController)
|
||||
{
|
||||
var rootViewController = self.window?.rootViewController
|
||||
|
||||
while rootViewController?.presentedViewController != nil
|
||||
{
|
||||
rootViewController = rootViewController?.presentedViewController
|
||||
}
|
||||
|
||||
rootViewController?.present(alertController, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
@ -14,7 +14,7 @@ import DeltaCore
|
||||
import Roxas
|
||||
|
||||
@objc(SwitchTableViewCell)
|
||||
private class SwitchTableViewCell: UITableViewCell
|
||||
class SwitchTableViewCell: UITableViewCell
|
||||
{
|
||||
@IBOutlet var switchView: UISwitch!
|
||||
}
|
||||
|
||||
45
Delta/Settings/Contributors/Contributor.swift
Normal file
45
Delta/Settings/Contributors/Contributor.swift
Normal file
@ -0,0 +1,45 @@
|
||||
//
|
||||
// Contributor.swift
|
||||
// Delta
|
||||
//
|
||||
// Created by Riley Testut on 2/3/23.
|
||||
// Copyright © 2023 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct Contributor: Identifiable, Decodable
|
||||
{
|
||||
var name: String
|
||||
|
||||
var id: String {
|
||||
// Use names as identifiers for now.
|
||||
return self.name
|
||||
}
|
||||
|
||||
var url: URL? {
|
||||
guard let link = self.link, let url = URL(string: link) else { return nil }
|
||||
return url
|
||||
}
|
||||
private var link: String?
|
||||
|
||||
var linkName: String?
|
||||
|
||||
var contributions: [Contribution]
|
||||
}
|
||||
|
||||
struct Contribution: Identifiable, Decodable
|
||||
{
|
||||
var name: String
|
||||
|
||||
var id: String {
|
||||
// Use names as identifiers for now.
|
||||
return self.name
|
||||
}
|
||||
|
||||
var url: URL? {
|
||||
guard let link = self.link, let url = URL(string: link) else { return nil }
|
||||
return url
|
||||
}
|
||||
private var link: String?
|
||||
}
|
||||
200
Delta/Settings/Contributors/ContributorsView.swift
Normal file
200
Delta/Settings/Contributors/ContributorsView.swift
Normal file
@ -0,0 +1,200 @@
|
||||
//
|
||||
// ContributionsView.swift
|
||||
// Delta
|
||||
//
|
||||
// Created by Riley Testut on 2/2/23.
|
||||
// Copyright © 2023 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import SafariServices
|
||||
|
||||
@available(iOS 14, *)
|
||||
private extension NavigationLink where Label == EmptyView, Destination == EmptyView
|
||||
{
|
||||
// Copied from https://stackoverflow.com/a/66891173
|
||||
static var empty: NavigationLink {
|
||||
self.init(destination: EmptyView(), label: { EmptyView() })
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 14, *)
|
||||
extension ContributorsView
|
||||
{
|
||||
fileprivate class ViewModel: ObservableObject
|
||||
{
|
||||
@Published
|
||||
var contributors: [Contributor]?
|
||||
|
||||
@Published
|
||||
var error: Error?
|
||||
|
||||
@Published
|
||||
var webViewURL: URL?
|
||||
|
||||
weak var hostingController: UIViewController?
|
||||
|
||||
func loadContributors()
|
||||
{
|
||||
guard self.contributors == nil else { return }
|
||||
|
||||
do
|
||||
{
|
||||
let fileURL = Bundle.main.url(forResource: "Contributors", withExtension: "plist")!
|
||||
let data = try Data(contentsOf: fileURL)
|
||||
|
||||
let contributors = try PropertyListDecoder().decode([Contributor].self, from: data)
|
||||
self.contributors = contributors
|
||||
}
|
||||
catch
|
||||
{
|
||||
self.error = error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static func makeViewController() -> UIHostingController<some View>
|
||||
{
|
||||
let viewModel = ViewModel()
|
||||
let contributorsView = ContributorsView(viewModel: viewModel)
|
||||
|
||||
let hostingController = UIHostingController(rootView: contributorsView)
|
||||
hostingController.title = NSLocalizedString("Contributors", comment: "")
|
||||
|
||||
viewModel.hostingController = hostingController
|
||||
|
||||
return hostingController
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 14, *)
|
||||
struct ContributorsView: View
|
||||
{
|
||||
@StateObject
|
||||
private var viewModel: ViewModel
|
||||
|
||||
@State
|
||||
private var showErrorAlert: Bool = false
|
||||
|
||||
var body: some View {
|
||||
List {
|
||||
Section(content: {}, footer: {
|
||||
Text("These individuals have contributed to the open-source Delta project on GitHub.\n\nThank you to all our contributors, your help is much appreciated 💜")
|
||||
.font(.subheadline)
|
||||
})
|
||||
|
||||
ForEach(viewModel.contributors ?? []) { contributor in
|
||||
Section {
|
||||
// First row = contributor
|
||||
ContributionCell(name: Text(contributor.name).bold(), url: contributor.url, linkName: contributor.linkName) { webViewURL in
|
||||
viewModel.webViewURL = webViewURL
|
||||
}
|
||||
|
||||
// Remaining rows = contributions
|
||||
ForEach(contributor.contributions) { contribution in
|
||||
ContributionCell(name: Text(contribution.name), url: contribution.url) { webViewURL in
|
||||
viewModel.webViewURL = webViewURL
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.listStyle(.grouped) // TODO: Change to .insetGrouped once we drop iOS 13 support.
|
||||
.environmentObject(viewModel)
|
||||
.alert(isPresented: $showErrorAlert) {
|
||||
Alert(title: Text("Unable to Load Contributors"), message: Text(viewModel.error?.localizedDescription ?? ""), dismissButton: .default(Text("OK")) {
|
||||
guard let hostingController = viewModel.hostingController else { return }
|
||||
hostingController.navigationController?.popViewController(animated: true)
|
||||
})
|
||||
}
|
||||
.onReceive(viewModel.$error) { error in
|
||||
guard error != nil else { return }
|
||||
showErrorAlert = true
|
||||
}
|
||||
.onReceive(viewModel.$webViewURL) { webViewURL in
|
||||
guard let webViewURL else { return }
|
||||
openURL(webViewURL)
|
||||
}
|
||||
.onAppear {
|
||||
viewModel.loadContributors()
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate init(contributors: [Contributor]? = nil, viewModel: ViewModel = ViewModel())
|
||||
{
|
||||
if let contributors
|
||||
{
|
||||
// Don't overwrite passed-in viewModel.contributors if contributors is nil.
|
||||
viewModel.contributors = contributors
|
||||
}
|
||||
|
||||
self._viewModel = StateObject(wrappedValue: viewModel)
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 14, *)
|
||||
struct ContributionCell: View
|
||||
{
|
||||
var name: Text
|
||||
var url: URL?
|
||||
var linkName: String?
|
||||
|
||||
var action: (URL) -> Void
|
||||
|
||||
var body: some View {
|
||||
|
||||
let body = Button {
|
||||
guard let url else { return }
|
||||
|
||||
Task { @MainActor in
|
||||
// Dispatch Task to avoid "Publishing changes from within view updates is not allowed, this will cause undefined behavior." runtime error on iOS 16.
|
||||
self.action(url)
|
||||
}
|
||||
|
||||
} label: {
|
||||
HStack {
|
||||
self.name
|
||||
.font(.system(size: 17)) // Match Settings screen
|
||||
|
||||
Spacer()
|
||||
|
||||
if let linkName
|
||||
{
|
||||
Text(linkName)
|
||||
.font(.system(size: 17)) // Match Settings screen
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
|
||||
if url != nil
|
||||
{
|
||||
NavigationLink.empty
|
||||
.fixedSize()
|
||||
}
|
||||
}
|
||||
}
|
||||
.accentColor(.primary)
|
||||
|
||||
if url != nil
|
||||
{
|
||||
body
|
||||
}
|
||||
else
|
||||
{
|
||||
// No URL to open, so disable cell highlighting.
|
||||
body.buttonStyle(.plain)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 14, *)
|
||||
private extension ContributorsView
|
||||
{
|
||||
func openURL(_ url: URL)
|
||||
{
|
||||
guard let hostingController = viewModel.hostingController else { return }
|
||||
|
||||
let safariViewController = SFSafariViewController(url: url)
|
||||
safariViewController.preferredControlTintColor = .deltaPurple
|
||||
hostingController.present(safariViewController, animated: true)
|
||||
}
|
||||
}
|
||||
@ -40,6 +40,8 @@ class ControllerInputsViewController: UIViewController
|
||||
|
||||
private var activeCalloutView: InputCalloutView?
|
||||
|
||||
private var _didLayoutSubviews = false
|
||||
|
||||
@IBOutlet private var actionsMenuViewControllerHeightConstraint: NSLayoutConstraint!
|
||||
@IBOutlet private var cancelTapGestureRecognizer: UITapGestureRecognizer!
|
||||
|
||||
@ -65,7 +67,15 @@ class ControllerInputsViewController: UIViewController
|
||||
|
||||
self.gameViewController.controllerView.addReceiver(self)
|
||||
|
||||
self.navigationController?.navigationBar.barStyle = .black
|
||||
if let navigationController = self.navigationController, #available(iOS 13, *)
|
||||
{
|
||||
navigationController.overrideUserInterfaceStyle = .dark
|
||||
navigationController.navigationBar.scrollEdgeAppearance = navigationController.navigationBar.standardAppearance // Fixes invisible navigation bar on iPad.
|
||||
}
|
||||
else
|
||||
{
|
||||
self.navigationController?.navigationBar.barStyle = .black
|
||||
}
|
||||
|
||||
NSLayoutConstraint.activate([self.gameViewController.gameView.centerYAnchor.constraint(equalTo: self.actionsMenuViewController.view.centerYAnchor)])
|
||||
|
||||
@ -81,6 +91,23 @@ class ControllerInputsViewController: UIViewController
|
||||
{
|
||||
self.actionsMenuViewControllerHeightConstraint.constant = self.actionsMenuViewController.preferredContentSize.height
|
||||
}
|
||||
|
||||
if let window = self.view.window, !_didLayoutSubviews
|
||||
{
|
||||
var traits = DeltaCore.ControllerSkin.Traits.defaults(for: window)
|
||||
traits.orientation = .portrait
|
||||
|
||||
if traits.device == .ipad
|
||||
{
|
||||
// Use standard iPhone skins instead of iPad skins.
|
||||
traits.device = .iphone
|
||||
traits.displayType = .standard
|
||||
}
|
||||
|
||||
self.gameViewController.controllerView.overrideControllerSkinTraits = traits
|
||||
|
||||
_didLayoutSubviews = true
|
||||
}
|
||||
}
|
||||
|
||||
override func viewDidAppear(_ animated: Bool)
|
||||
@ -91,6 +118,9 @@ class ControllerInputsViewController: UIViewController
|
||||
{
|
||||
self.prepareCallouts()
|
||||
}
|
||||
|
||||
// controllerView must be first responder to receive keyboard presses.
|
||||
self.gameViewController.controllerView.becomeFirstResponder()
|
||||
}
|
||||
}
|
||||
|
||||
@ -183,6 +213,10 @@ private extension ControllerInputsViewController
|
||||
listMenuViewController.title = NSLocalizedString("Game System", comment: "")
|
||||
|
||||
let navigationController = UINavigationController(rootViewController: listMenuViewController)
|
||||
if #available(iOS 13, *)
|
||||
{
|
||||
navigationController.navigationBar.scrollEdgeAppearance = navigationController.navigationBar.standardAppearance
|
||||
}
|
||||
|
||||
let popoverMenuController = PopoverMenuController(popoverViewController: navigationController)
|
||||
self.navigationItem.popoverMenuController = popoverMenuController
|
||||
@ -403,6 +437,7 @@ private extension ControllerInputsViewController
|
||||
}
|
||||
|
||||
let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
|
||||
alertController.popoverPresentationController?.barButtonItem = sender
|
||||
alertController.addAction(.cancel)
|
||||
alertController.addAction(UIAlertAction(title: NSLocalizedString("Reset Controls to Defaults", comment: ""), style: .destructive, handler: { (action) in
|
||||
reset()
|
||||
@ -418,6 +453,12 @@ extension ControllerInputsViewController: UIGestureRecognizerDelegate
|
||||
return self.activeCalloutView != nil
|
||||
}
|
||||
|
||||
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool
|
||||
{
|
||||
// Necessary to prevent other gestures (e.g. GameViewController's resumeEmulationIfNeeded() tap gesture) from cancelling tap.
|
||||
return true
|
||||
}
|
||||
|
||||
@IBAction private func handleTapGesture(_ tapGestureRecognizer: UITapGestureRecognizer)
|
||||
{
|
||||
self.updateActiveCalloutView(with: nil)
|
||||
@ -532,13 +573,19 @@ extension ControllerInputsViewController: GameControllerReceiver
|
||||
{
|
||||
func gameController(_ gameController: GameController, didActivate controllerInput: DeltaCore.Input, value: Double)
|
||||
{
|
||||
guard self.isViewLoaded else { return }
|
||||
guard self.isViewLoaded, value > 0.9 else { return }
|
||||
|
||||
switch gameController
|
||||
{
|
||||
case self.gameViewController.controllerView:
|
||||
if let calloutView = self.calloutViews[AnyInput(controllerInput)]
|
||||
{
|
||||
if controllerInput.isContinuous
|
||||
{
|
||||
// Make sure we only toggle calloutView once in a single gesture.
|
||||
guard calloutView.state == .normal else { break }
|
||||
}
|
||||
|
||||
self.toggle(calloutView)
|
||||
}
|
||||
|
||||
@ -562,3 +609,15 @@ extension ControllerInputsViewController: SMCalloutViewDelegate
|
||||
self.toggle(calloutView)
|
||||
}
|
||||
}
|
||||
|
||||
extension ControllerInputsViewController: UIAdaptivePresentationControllerDelegate
|
||||
{
|
||||
func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle
|
||||
{
|
||||
switch (traitCollection.horizontalSizeClass, traitCollection.verticalSizeClass)
|
||||
{
|
||||
case (.regular, .regular): return .formSheet // Regular width and height, so display as form sheet
|
||||
default: return .fullScreen // Compact width and/or height, so display full screen
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,9 +108,18 @@ extension ControllersSettingsViewController
|
||||
switch identifier
|
||||
{
|
||||
case "controllerInputsSegue":
|
||||
let controllerInputsViewController = (segue.destination as! UINavigationController).topViewController as! ControllerInputsViewController
|
||||
let navigationController = segue.destination as! UINavigationController
|
||||
|
||||
let controllerInputsViewController = navigationController.topViewController as! ControllerInputsViewController
|
||||
controllerInputsViewController.gameController = self.gameController
|
||||
|
||||
if self.view.traitCollection.userInterfaceIdiom == .pad
|
||||
{
|
||||
// For now, only iPads can display ControllerInputsViewController as a form sheet.
|
||||
navigationController.modalPresentationStyle = .formSheet
|
||||
navigationController.presentationController?.delegate = controllerInputsViewController
|
||||
}
|
||||
|
||||
default: break
|
||||
}
|
||||
|
||||
@ -296,6 +305,8 @@ extension ControllersSettingsViewController
|
||||
{
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
|
||||
{
|
||||
let previousGameController = self.gameController
|
||||
|
||||
switch Section(rawValue: indexPath.section)!
|
||||
{
|
||||
case .localDevice: self.gameController = self.localDeviceController
|
||||
@ -310,7 +321,7 @@ extension ControllersSettingsViewController
|
||||
|
||||
let previousIndexPath: IndexPath?
|
||||
|
||||
if let gameController = self.gameController
|
||||
if let gameController = previousGameController
|
||||
{
|
||||
if gameController == self.localDeviceController
|
||||
{
|
||||
|
||||
@ -23,18 +23,18 @@ private extension MelonDSCoreSettingsViewController
|
||||
enum Section: Int
|
||||
{
|
||||
case general
|
||||
case performance
|
||||
case dsBIOS
|
||||
case dsiBIOS
|
||||
case changeCore
|
||||
}
|
||||
|
||||
@available(iOS 13, *)
|
||||
enum BIOSError: LocalizedError
|
||||
{
|
||||
case unknownSize(URL)
|
||||
case incorrectHash(URL, hash: String, expectedHash: String)
|
||||
case unsupportedHash(URL, hash: String)
|
||||
|
||||
@available(iOS 13, *)
|
||||
case incorrectSize(URL, size: Int, validSizes: Set<ClosedRange<Measurement<UnitInformationStorage>>>)
|
||||
|
||||
private static let byteFormatter: ByteCountFormatter = {
|
||||
@ -150,6 +150,13 @@ private extension MelonDSCoreSettingsViewController
|
||||
|
||||
switch section
|
||||
{
|
||||
case .performance:
|
||||
// Hide AltJIT section for public builds.
|
||||
guard isBeta else { return true }
|
||||
|
||||
guard Settings.preferredCore(for: .ds) == MelonDS.core else { return true }
|
||||
return !UIDevice.current.supportsJIT
|
||||
|
||||
case .dsBIOS where Settings.preferredCore(for: .ds) == DS.core:
|
||||
// Using DeSmuME core, which doesn't require BIOS.
|
||||
return true
|
||||
@ -253,6 +260,11 @@ private extension MelonDSCoreSettingsViewController
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction func toggleAltJITEnabled(_ sender: UISwitch)
|
||||
{
|
||||
Settings.isAltJITEnabled = sender.isOn
|
||||
}
|
||||
|
||||
@objc func willEnterForeground(_ notification: Notification)
|
||||
{
|
||||
self.tableView.reloadData()
|
||||
@ -301,6 +313,10 @@ extension MelonDSCoreSettingsViewController
|
||||
|
||||
cell.contentView.isHidden = (item == nil)
|
||||
|
||||
case .performance:
|
||||
let cell = cell as! SwitchTableViewCell
|
||||
cell.switchView.isOn = Settings.isAltJITEnabled
|
||||
|
||||
case .dsBIOS:
|
||||
let bios = DSBIOS.allCases[indexPath.row]
|
||||
|
||||
@ -379,6 +395,8 @@ extension MelonDSCoreSettingsViewController
|
||||
|
||||
case .changeCore:
|
||||
self.changeCore()
|
||||
|
||||
case .performance: break
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -38,6 +38,8 @@ extension Settings
|
||||
case syncingService
|
||||
case isButtonHapticFeedbackEnabled
|
||||
case isThumbstickHapticFeedbackEnabled
|
||||
case isAltJITEnabled
|
||||
case respectSilentMode
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,8 +62,18 @@ struct Settings
|
||||
#keyPath(UserDefaults.isThumbstickHapticFeedbackEnabled): true,
|
||||
#keyPath(UserDefaults.sortSaveStatesByOldestFirst): true,
|
||||
#keyPath(UserDefaults.isPreviewsEnabled): true,
|
||||
#keyPath(UserDefaults.isAltJITEnabled): false,
|
||||
#keyPath(UserDefaults.respectSilentMode): true,
|
||||
Settings.preferredCoreSettingsKey(for: .ds): MelonDS.core.identifier] as [String : Any]
|
||||
UserDefaults.standard.register(defaults: defaults)
|
||||
|
||||
#if !BETA
|
||||
// Manually set MelonDS as preferred DS core in case DeSmuME is cached from a previous version.
|
||||
UserDefaults.standard.set(MelonDS.core.identifier, forKey: Settings.preferredCoreSettingsKey(for: .ds))
|
||||
|
||||
// Manually disable AltJIT for public builds.
|
||||
UserDefaults.standard.isAltJITEnabled = false
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@ -190,6 +202,28 @@ extension Settings
|
||||
}
|
||||
}
|
||||
|
||||
static var isAltJITEnabled: Bool {
|
||||
get {
|
||||
let isAltJITEnabled = UserDefaults.standard.isAltJITEnabled
|
||||
return isAltJITEnabled
|
||||
}
|
||||
set {
|
||||
UserDefaults.standard.isAltJITEnabled = newValue
|
||||
NotificationCenter.default.post(name: .settingsDidChange, object: nil, userInfo: [NotificationUserInfoKey.name: Name.isAltJITEnabled])
|
||||
}
|
||||
}
|
||||
|
||||
static var respectSilentMode: Bool {
|
||||
get {
|
||||
let respectSilentMode = UserDefaults.standard.respectSilentMode
|
||||
return respectSilentMode
|
||||
}
|
||||
set {
|
||||
UserDefaults.standard.respectSilentMode = newValue
|
||||
NotificationCenter.default.post(name: .settingsDidChange, object: nil, userInfo: [NotificationUserInfoKey.name: Name.respectSilentMode])
|
||||
}
|
||||
}
|
||||
|
||||
static func preferredCore(for gameType: GameType) -> DeltaCoreProtocol?
|
||||
{
|
||||
let key = self.preferredCoreSettingsKey(for: gameType)
|
||||
@ -385,4 +419,8 @@ private extension UserDefaults
|
||||
@NSManaged var sortSaveStatesByOldestFirst: Bool
|
||||
|
||||
@NSManaged var isPreviewsEnabled: Bool
|
||||
|
||||
@NSManaged var isAltJITEnabled: Bool
|
||||
|
||||
@NSManaged var respectSilentMode: Bool
|
||||
}
|
||||
|
||||
@ -20,6 +20,7 @@ private extension SettingsViewController
|
||||
case controllers
|
||||
case controllerSkins
|
||||
case controllerOpacity
|
||||
case gameAudio
|
||||
case hapticFeedback
|
||||
case syncing
|
||||
case hapticTouch
|
||||
@ -44,9 +45,11 @@ private extension SettingsViewController
|
||||
enum CreditsRow: Int, CaseIterable
|
||||
{
|
||||
case riley
|
||||
case shane
|
||||
case caroline
|
||||
case grant
|
||||
case litRitt
|
||||
case contributors
|
||||
case softwareLicenses
|
||||
}
|
||||
}
|
||||
@ -56,6 +59,7 @@ class SettingsViewController: UITableViewController
|
||||
@IBOutlet private var controllerOpacityLabel: UILabel!
|
||||
@IBOutlet private var controllerOpacitySlider: UISlider!
|
||||
|
||||
@IBOutlet private var respectSilentModeSwitch: UISwitch!
|
||||
@IBOutlet private var buttonHapticFeedbackEnabledSwitch: UISwitch!
|
||||
@IBOutlet private var thumbstickHapticFeedbackEnabledSwitch: UISwitch!
|
||||
@IBOutlet private var previewsEnabledSwitch: UISwitch!
|
||||
@ -160,6 +164,8 @@ private extension SettingsViewController
|
||||
self.controllerOpacitySlider.value = Float(Settings.translucentControllerSkinOpacity)
|
||||
self.updateControllerOpacityLabel()
|
||||
|
||||
self.respectSilentModeSwitch.isOn = Settings.respectSilentMode
|
||||
|
||||
self.syncingServiceLabel.text = Settings.syncingService?.localizedName
|
||||
|
||||
do
|
||||
@ -248,6 +254,11 @@ private extension SettingsViewController
|
||||
Settings.isPreviewsEnabled = sender.isOn
|
||||
}
|
||||
|
||||
@IBAction func toggleRespectSilentMode(_ sender: UISwitch)
|
||||
{
|
||||
Settings.respectSilentMode = sender.isOn
|
||||
}
|
||||
|
||||
func openTwitter(username: String)
|
||||
{
|
||||
let twitterAppURL = URL(string: "twitter://user?screen_name=" + username)!
|
||||
@ -269,6 +280,13 @@ private extension SettingsViewController
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 14, *)
|
||||
func showContributors()
|
||||
{
|
||||
let hostingController = ContributorsView.makeViewController()
|
||||
self.navigationController?.pushViewController(hostingController, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
private extension SettingsViewController
|
||||
@ -290,7 +308,7 @@ private extension SettingsViewController
|
||||
self.tableView.selectRow(at: selectedIndexPath, animated: true, scrollPosition: .none)
|
||||
}
|
||||
|
||||
case .localControllerPlayerIndex, .preferredControllerSkin, .translucentControllerSkinOpacity, .isButtonHapticFeedbackEnabled, .isThumbstickHapticFeedbackEnabled: break
|
||||
case .localControllerPlayerIndex, .preferredControllerSkin, .translucentControllerSkinOpacity, .respectSilentMode, .isButtonHapticFeedbackEnabled, .isThumbstickHapticFeedbackEnabled, .isAltJITEnabled: break
|
||||
}
|
||||
}
|
||||
|
||||
@ -367,7 +385,7 @@ extension SettingsViewController
|
||||
let preferredCore = Settings.preferredCore(for: .ds)
|
||||
cell.detailTextLabel?.text = preferredCore?.metadata?.name.value ?? preferredCore?.name ?? NSLocalizedString("Unknown", comment: "")
|
||||
|
||||
case .controllerOpacity, .hapticFeedback, .hapticTouch, .patreon, .credits: break
|
||||
case .controllerOpacity, .gameAudio, .hapticFeedback, .hapticTouch, .patreon, .credits: break
|
||||
}
|
||||
|
||||
return cell
|
||||
@ -383,7 +401,7 @@ extension SettingsViewController
|
||||
case .controllers: self.performSegue(withIdentifier: Segue.controllers.rawValue, sender: cell)
|
||||
case .controllerSkins: self.performSegue(withIdentifier: Segue.controllerSkins.rawValue, sender: cell)
|
||||
case .cores: self.performSegue(withIdentifier: Segue.dsSettings.rawValue, sender: cell)
|
||||
case .controllerOpacity, .hapticFeedback, .hapticTouch, .syncing: break
|
||||
case .controllerOpacity, .gameAudio, .hapticFeedback, .hapticTouch, .syncing: break
|
||||
case .patreon:
|
||||
let patreonURL = URL(string: "altstore://patreon")!
|
||||
|
||||
@ -404,9 +422,14 @@ extension SettingsViewController
|
||||
switch row
|
||||
{
|
||||
case .riley: self.openTwitter(username: "rileytestut")
|
||||
case .shane: self.openTwitter(username: "shanegillio")
|
||||
case .caroline: self.openTwitter(username: "1carolinemoore")
|
||||
case .grant: self.openTwitter(username: "grantgliner")
|
||||
case .litRitt: self.openTwitter(username: "litritt_z")
|
||||
case .litRitt: self.openTwitter(username: "lit_ritt")
|
||||
case .contributors:
|
||||
guard #available(iOS 14, *) else { return }
|
||||
self.showContributors()
|
||||
|
||||
case .softwareLicenses: break
|
||||
}
|
||||
}
|
||||
@ -414,13 +437,35 @@ extension SettingsViewController
|
||||
|
||||
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
|
||||
{
|
||||
primary:
|
||||
switch Section(rawValue: indexPath.section)!
|
||||
{
|
||||
#if !BETA
|
||||
case .credits where indexPath.row == CreditsRow.litRitt.rawValue: return 0.0
|
||||
#endif
|
||||
default: return super.tableView(tableView, heightForRowAt: indexPath)
|
||||
case .credits:
|
||||
let row = CreditsRow(rawValue: indexPath.row)!
|
||||
switch row
|
||||
{
|
||||
case .grant:
|
||||
// Hide row on iOS 14 and above
|
||||
guard #available(iOS 14, *) else { break primary }
|
||||
return 0.0
|
||||
|
||||
case .litRitt:
|
||||
// Hide row on iOS 14 and above
|
||||
guard #available(iOS 14, *) else { break primary }
|
||||
return 0.0
|
||||
|
||||
case .contributors:
|
||||
// Hide row on iOS 13 and below
|
||||
guard #unavailable(iOS 14) else { break primary }
|
||||
return 0.0
|
||||
|
||||
default: break
|
||||
}
|
||||
|
||||
default: break
|
||||
}
|
||||
|
||||
return super.tableView(tableView, heightForRowAt: indexPath)
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String?
|
||||
|
||||
@ -203,6 +203,27 @@
|
||||
<string>Delta uses your microphone to emulate the Nintendo DS microphone.</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>Press "OK" to allow Delta to use images from your Photo Library as game artwork.</string>
|
||||
<key>UIApplicationSceneManifest</key>
|
||||
<dict>
|
||||
<key>UIApplicationSupportsMultipleScenes</key>
|
||||
<false/>
|
||||
<key>UISceneConfigurations</key>
|
||||
<dict>
|
||||
<key>UIWindowSceneSessionRoleApplication</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UISceneConfigurationName</key>
|
||||
<string>Main</string>
|
||||
<key>UISceneDelegateClassName</key>
|
||||
<string>SceneDelegate</string>
|
||||
<key>UISceneStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array/>
|
||||
<key>UIFileSharingEnabled</key>
|
||||
@ -215,6 +236,8 @@
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
</array>
|
||||
<key>UIRequiresFullScreen</key>
|
||||
<false/>
|
||||
<key>UIStatusBarStyle</key>
|
||||
<string>UIStatusBarStyleLightContent</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
@ -223,6 +246,13 @@
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportsDocumentBrowser</key>
|
||||
<true/>
|
||||
<key>UTExportedTypeDeclarations</key>
|
||||
@ -400,5 +430,13 @@
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
<key>NSBonjourServices</key>
|
||||
<array>
|
||||
<string>_altserver._tcp</string>
|
||||
</array>
|
||||
<key>ALTDeviceID</key>
|
||||
<string>00008110-000A68390A82801E</string>
|
||||
<key>NSLocalNetworkUsageDescription</key>
|
||||
<string>Delta uses the local network to communicate with AltServer and enable JIT.</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@ -72,7 +72,7 @@ extension DeltaCoreProtocol
|
||||
case GBA.core: return 3
|
||||
case N64.core where UIDevice.current.hasA11ProcessorOrBetter: return 3
|
||||
case N64.core where UIDevice.current.hasA9ProcessorOrBetter: return 1.5
|
||||
case MelonDS.core where UIDevice.current.supportsJIT: return 3
|
||||
case MelonDS.core where ProcessInfo.processInfo.isJITAvailable: return 3
|
||||
case MelonDS.core where UIDevice.current.hasA11ProcessorOrBetter: return 1.5
|
||||
case GPGX.core: return 4
|
||||
default: return 1
|
||||
|
||||
1
External/CheatBase
vendored
Submodule
1
External/CheatBase
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit df52576e6fd1185747f41b022d9664ad0593608a
|
||||
2
External/Harmony
vendored
2
External/Harmony
vendored
@ -1 +1 @@
|
||||
Subproject commit db5fbd829ac5aa6e7e249eb9fabf07bbb75fabd9
|
||||
Subproject commit 7234d6626a49e56ddceaaec0c04cc4f4f43b572c
|
||||
@ -138,7 +138,7 @@ SPEC CHECKSUMS:
|
||||
GTMSessionFetcher: b3503b20a988c4e20cc189aa798fd18220133f52
|
||||
Harmony: cea514db17c41c22f78f54b17d2135935b5e9b96
|
||||
MelonDSDeltaCore: 3de2a2e2ebcd630a6dd756818b5a26dde7afa726
|
||||
N64DeltaCore: 6b0f07f2078193a15d736f153ce701661853fa36
|
||||
N64DeltaCore: c513f36f88d421e79fda3cd7963040db84d3ab51
|
||||
NESDeltaCore: 41ab438dd78d51d4636aacb7d9a7336ea3d4728c
|
||||
Roxas: 1990039f843f5dc284918dc82375feb80020ef62
|
||||
SDWebImage: a72e880a8fe0f7fc31efe15aaed443c074d2a80c
|
||||
@ -148,6 +148,6 @@ SPEC CHECKSUMS:
|
||||
SwiftyDropbox: 378b4425a2e8d0cb24c7b0f2e3af72bfbaaf1e73
|
||||
ZIPFoundation: b1f0de4eed33e74a676f76e12559ab6b75990197
|
||||
|
||||
PODFILE CHECKSUM: 2f9219b1db18479e650fc2159f58160b4cc13aad
|
||||
PODFILE CHECKSUM: 68bb9968a91dd904a9f75286dfb3c2536917eb87
|
||||
|
||||
COCOAPODS: 1.10.1
|
||||
COCOAPODS: 1.11.2
|
||||
|
||||
5
Pods/Local Podspecs/N64DeltaCore.podspec.json
generated
5
Pods/Local Podspecs/N64DeltaCore.podspec.json
generated
@ -51,6 +51,11 @@
|
||||
"CLANG_ENABLE_MODULES": "NO",
|
||||
"GCC_PREPROCESSOR_DEFINITIONS": "STATIC_LIBRARY=1"
|
||||
},
|
||||
"script_phases": {
|
||||
"name": "Get GlideN64 Revision.h",
|
||||
"script": "\"${PODS_TARGET_SRCROOT}/Mupen64Plus/GLideN64/src/getRevision.sh\"",
|
||||
"execution_position": "before_compile"
|
||||
},
|
||||
"dependencies": {
|
||||
"DeltaCore": [
|
||||
|
||||
|
||||
6
Pods/Manifest.lock
generated
6
Pods/Manifest.lock
generated
@ -138,7 +138,7 @@ SPEC CHECKSUMS:
|
||||
GTMSessionFetcher: b3503b20a988c4e20cc189aa798fd18220133f52
|
||||
Harmony: cea514db17c41c22f78f54b17d2135935b5e9b96
|
||||
MelonDSDeltaCore: 3de2a2e2ebcd630a6dd756818b5a26dde7afa726
|
||||
N64DeltaCore: 6b0f07f2078193a15d736f153ce701661853fa36
|
||||
N64DeltaCore: c513f36f88d421e79fda3cd7963040db84d3ab51
|
||||
NESDeltaCore: 41ab438dd78d51d4636aacb7d9a7336ea3d4728c
|
||||
Roxas: 1990039f843f5dc284918dc82375feb80020ef62
|
||||
SDWebImage: a72e880a8fe0f7fc31efe15aaed443c074d2a80c
|
||||
@ -148,6 +148,6 @@ SPEC CHECKSUMS:
|
||||
SwiftyDropbox: 378b4425a2e8d0cb24c7b0f2e3af72bfbaaf1e73
|
||||
ZIPFoundation: b1f0de4eed33e74a676f76e12559ab6b75990197
|
||||
|
||||
PODFILE CHECKSUM: 2f9219b1db18479e650fc2159f58160b4cc13aad
|
||||
PODFILE CHECKSUM: 68bb9968a91dd904a9f75286dfb3c2536917eb87
|
||||
|
||||
COCOAPODS: 1.10.1
|
||||
COCOAPODS: 1.11.2
|
||||
|
||||
14293
Pods/Pods.xcodeproj/project.pbxproj
generated
14293
Pods/Pods.xcodeproj/project.pbxproj
generated
File diff suppressed because it is too large
Load Diff
@ -1,10 +0,0 @@
|
||||
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Crashlytics
|
||||
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Crashlytics/iOS" "${PODS_ROOT}/Fabric/iOS"
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||
PODS_BUILD_DIR = ${BUILD_DIR}
|
||||
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||
PODS_ROOT = ${SRCROOT}
|
||||
PODS_TARGET_SRCROOT = ${PODS_ROOT}/Crashlytics
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
|
||||
SKIP_INSTALL = YES
|
||||
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
|
||||
10
Pods/Target Support Files/Fabric/Fabric.xcconfig
generated
10
Pods/Target Support Files/Fabric/Fabric.xcconfig
generated
@ -1,10 +0,0 @@
|
||||
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Fabric
|
||||
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Fabric/iOS"
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||
PODS_BUILD_DIR = ${BUILD_DIR}
|
||||
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||
PODS_ROOT = ${SRCROOT}
|
||||
PODS_TARGET_SRCROOT = ${PODS_ROOT}/Fabric
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
|
||||
SKIP_INSTALL = YES
|
||||
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
|
||||
@ -4,7 +4,7 @@ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
|
||||
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Crashlytics/iOS" "${PODS_ROOT}/Fabric/iOS" "${PODS_ROOT}/GoogleSignIn/Frameworks"
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 STATIC_LIBRARY=1 STATIC_LIBRARY=1 JIT_ENABLED=1 STATIC_LIBRARY=1 MUPENPLUSAPI TXFILTER_LIB OS_IOS GLESX GL_ERROR_DEBUG GL_DEBUG GLESX PNG_ARM_NEON_OPT=0
|
||||
HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/DSDeltaCore" "${PODS_ROOT}/Headers/Public/DeltaCore" "${PODS_ROOT}/Headers/Public/GBADeltaCore" "${PODS_ROOT}/Headers/Public/GBCDeltaCore" "${PODS_ROOT}/Headers/Public/GTMSessionFetcher" "${PODS_ROOT}/Headers/Public/GoogleAPIClientForREST" "${PODS_ROOT}/Headers/Public/GoogleToolboxForMac" "${PODS_ROOT}/Headers/Public/Harmony" "${PODS_ROOT}/Headers/Public/MelonDSDeltaCore" "${PODS_ROOT}/Headers/Public/N64DeltaCore" "${PODS_ROOT}/Headers/Public/NESDeltaCore" "${PODS_ROOT}/Headers/Public/Roxas" "${PODS_ROOT}/Headers/Public/SDWebImage" "${PODS_ROOT}/Headers/Public/SMCalloutView" "${PODS_ROOT}/Headers/Public/SNESDeltaCore" "${PODS_ROOT}/Headers/Public/SQLite.swift" "${PODS_ROOT}/Headers/Public/SwiftyDropbox" "${PODS_CONFIGURATION_BUILD_DIR}" "$(PODS_ROOT)/Headers/Private/DSDeltaCore/desmume/desmume/src/libretro-common/include" "${PODS_CONFIGURATION_BUILD_DIR}" "$(PODS_ROOT)/Headers/Private/GBADeltaCore/SFML/include" "$(PODS_ROOT)/Headers/Private/GBADeltaCore/SFML/src" "${PODS_CONFIGURATION_BUILD_DIR}" "${PODS_CONFIGURATION_BUILD_DIR}" "$(PODS_ROOT)/Headers/Private/MelonDSDeltaCore/melonDS/src" "${PODS_CONFIGURATION_BUILD_DIR}" "$(PODS_ROOT)/Headers/Private/N64DeltaCore/Mupen64Plus/mupen64plus-core/subprojects/**" "$(PODS_ROOT)/Headers/Private/N64DeltaCore/libMupen64Plus/SDL" "${PODS_CONFIGURATION_BUILD_DIR}" "$(PODS_ROOT)/Headers/Private/N64DeltaCore/Mupen64Plus/mupen64plus-core/subprojects/**" "$(PODS_ROOT)/Headers/Private/N64DeltaCore/libMupen64Plus/SDL" "$(PODS_ROOT)/Headers/Private/N64DeltaCore/Mupen64Plus/GLideN64/src" "$(PODS_ROOT)/Headers/Private/N64DeltaCore/Mupen64Plus/GLideN64/src/inc" "$(PODS_ROOT)/Headers/Private/N64DeltaCore/Mupen64Plus/GLideN64/src" "$(PODS_ROOT)/Headers/Private/N64DeltaCore/Mupen64Plus/GLideN64/src/osal" "$(PODS_ROOT)/Headers/Private/N64DeltaCore/Mupen64Plus/libpng"
|
||||
LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/DSDeltaCore" "${PODS_CONFIGURATION_BUILD_DIR}/DeltaCore" "${PODS_CONFIGURATION_BUILD_DIR}/GBADeltaCore" "${PODS_CONFIGURATION_BUILD_DIR}/GBCDeltaCore" "${PODS_CONFIGURATION_BUILD_DIR}/GTMSessionFetcher" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleAPIClientForREST" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleToolboxForMac" "${PODS_CONFIGURATION_BUILD_DIR}/Harmony" "${PODS_CONFIGURATION_BUILD_DIR}/MelonDSDeltaCore" "${PODS_CONFIGURATION_BUILD_DIR}/N64DeltaCore" "${PODS_CONFIGURATION_BUILD_DIR}/NESDeltaCore" "${PODS_CONFIGURATION_BUILD_DIR}/Roxas" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/SMCalloutView" "${PODS_CONFIGURATION_BUILD_DIR}/SNESDeltaCore" "${PODS_CONFIGURATION_BUILD_DIR}/SQLite.swift" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyDropbox" "${PODS_CONFIGURATION_BUILD_DIR}/ZIPFoundation"
|
||||
LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/DSDeltaCore" "${PODS_CONFIGURATION_BUILD_DIR}/DeltaCore" "${PODS_CONFIGURATION_BUILD_DIR}/GBADeltaCore" "${PODS_CONFIGURATION_BUILD_DIR}/GBCDeltaCore" "${PODS_CONFIGURATION_BUILD_DIR}/GTMSessionFetcher" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleAPIClientForREST" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleToolboxForMac" "${PODS_CONFIGURATION_BUILD_DIR}/Harmony" "${PODS_CONFIGURATION_BUILD_DIR}/MelonDSDeltaCore" "${PODS_CONFIGURATION_BUILD_DIR}/N64DeltaCore" "${PODS_CONFIGURATION_BUILD_DIR}/NESDeltaCore" "${PODS_CONFIGURATION_BUILD_DIR}/Roxas" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/SMCalloutView" "${PODS_CONFIGURATION_BUILD_DIR}/SNESDeltaCore" "${PODS_CONFIGURATION_BUILD_DIR}/SQLite.swift" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyDropbox" "${PODS_CONFIGURATION_BUILD_DIR}/ZIPFoundation" /usr/lib/swift
|
||||
OTHER_CFLAGS = $(inherited) -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.modulemap" -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/DSDeltaCore/DSDeltaCore.modulemap" -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/DeltaCore/DeltaCore.modulemap" -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/GBADeltaCore/GBADeltaCore.modulemap" -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/GBCDeltaCore/GBCDeltaCore.modulemap" -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/Harmony/Harmony.modulemap" -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/MelonDSDeltaCore/MelonDSDeltaCore.modulemap" -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/N64DeltaCore/N64DeltaCore.modulemap" -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/NESDeltaCore/NESDeltaCore.modulemap" -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/SNESDeltaCore/SNESDeltaCore.modulemap" -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/SQLite.swift/SQLite.modulemap" -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/SwiftyDropbox/SwiftyDropbox.modulemap" -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/ZIPFoundation/ZIPFoundation.modulemap" -fmodule-map-file="${PODS_ROOT}/Headers/Public/GTMSessionFetcher/GTMSessionFetcher.modulemap" -fmodule-map-file="${PODS_ROOT}/Headers/Public/GoogleAPIClientForREST/GoogleAPIClientForREST.modulemap" -fmodule-map-file="${PODS_ROOT}/Headers/Public/GoogleToolboxForMac/GoogleToolboxForMac.modulemap" -fmodule-map-file="${PODS_ROOT}/Headers/Public/Roxas/Roxas.modulemap" -fmodule-map-file="${PODS_ROOT}/Headers/Public/SDWebImage/SDWebImage.modulemap" -fmodule-map-file="${PODS_ROOT}/Headers/Public/SMCalloutView/SMCalloutView.modulemap" -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/DSDeltaCore" -isystem "${PODS_ROOT}/Headers/Public/GBADeltaCore" -isystem "${PODS_ROOT}/Headers/Public/GBCDeltaCore" -isystem "${PODS_ROOT}/Headers/Public/Harmony" -isystem "${PODS_ROOT}/Headers/Public/MelonDSDeltaCore" -isystem "${PODS_ROOT}/Headers/Public/N64DeltaCore" -isystem "${PODS_ROOT}/Headers/Public/NESDeltaCore" -isystem "${PODS_ROOT}/Headers/Public/SNESDeltaCore" -iframework "${PODS_ROOT}/Crashlytics/iOS" -iframework "${PODS_ROOT}/Fabric/iOS" -iframework "${PODS_ROOT}/GoogleSignIn/Frameworks" -DHOST_DARWIN -DDESMUME_COCOA -DHAVE_OPENGL -DHAVE_LIBZ -DANDROID -fexceptions -ftree-vectorize -DCOMPRESS_MT -DIOS -DOBJ_C -marm -fvisibility=hidden -DSTATIC_LIBRARY=1 -DSTATIC_LIBRARY -DHAVE_CSTDINT -fno-strict-aliasing -DGCC -pthread -fPIC -D__unix__ -ffast-math -fno-strict-aliasing -DGCC -pthread -fPIC -D__unix__ -ffast-math -D__VEC4_OPT -fvisibility=hidden -funsigned-char -ffast-math -finline -fno-builtin -fno-common -fomit-frame-pointer -funroll-loops -fstrict-aliasing -DHAVE_STDINT_H -DARM -DSTATIC_LIBRARY=1
|
||||
OTHER_LDFLAGS = $(inherited) -ObjC -Wl,-exported_symbol,_Video_PluginStartup,-exported_symbol,_Video_PluginShutdown,-exported_symbol,_Video_PluginGetVersion,-exported_symbol,_Video_RomOpen,-exported_symbol,_Video_RomClosed,-exported_symbol,_ConfigGetSharedDataFilepath,-exported_symbol,_ConfigGetUserConfigPath,-exported_symbol,_ConfigGetUserCachePath,-exported_symbol,_ConfigGetUserDataPath,-exported_symbol,_ConfigOpenSection,-exported_symbol,_ConfigDeleteSection,-exported_symbol,_ConfigSaveSection,-exported_symbol,_ConfigSaveFile,-exported_symbol,_ConfigSetDefaultInt,-exported_symbol,_ConfigSetDefaultFloat,-exported_symbol,_ConfigSetDefaultBool,-exported_symbol,_ConfigSetDefaultString,-exported_symbol,_ConfigGetParamInt,-exported_symbol,_ConfigGetParamFloat,-exported_symbol,_ConfigGetParamBool,-exported_symbol,_ConfigGetParamString,-exported_symbol,_ConfigExternalGetParameter,-exported_symbol,_ConfigExternalOpen,-exported_symbol,_ConfigExternalClose,-exported_symbol,_VidExt_Init,-exported_symbol,_VidExt_Quit,-exported_symbol,_VidExt_ListFullscreenModes,-exported_symbol,_VidExt_SetVideoMode,-exported_symbol,_VidExt_SetCaption,-exported_symbol,_VidExt_ToggleFullScreen,-exported_symbol,_VidExt_ResizeWindow,-exported_symbol,_VidExt_GL_GetProcAddress,-exported_symbol,_VidExt_GL_SetAttribute,-exported_symbol,_VidExt_GL_GetAttribute,-exported_symbol,_VidExt_GL_SwapBuffers,-exported_symbol,_ChangeWindow,-exported_symbol,_InitiateGFX,-exported_symbol,_MoveScreen,-exported_symbol,_ProcessDList,-exported_symbol,_ProcessRDPList,-exported_symbol,_ShowCFB,-exported_symbol,_UpdateScreen,-exported_symbol,_ViStatusChanged,-exported_symbol,_ViWidthChanged,-exported_symbol,_ReadScreen2,-exported_symbol,_SetRenderingCallback,-exported_symbol,_FBRead,-exported_symbol,_FBWrite,-exported_symbol,_FBGetFrameBufferInfo,-exported_symbol,_ResizeVideoOutput,-exported_symbol,_RSP_PluginStartup,-exported_symbol,_RSP_PluginShutdown,-exported_symbol,_RSP_PluginGetVersion,-exported_symbol,_DoRspCycles,-exported_symbol,_InitiateRSP,-exported_symbol,_RSP_RomClosed,-exported_symbol,_CoreGetAPIVersions,-exported_symbol,_ConfigGetParameter,-exported_symbol,_ConfigSetParameter,-exported_symbol,_CoreDoCommand -l"Alamofire" -l"DSDeltaCore" -l"GBADeltaCore" -l"GBCDeltaCore" -l"GTMSessionFetcher" -l"GoogleAPIClientForREST" -l"GoogleToolboxForMac" -l"Harmony" -l"MelonDSDeltaCore" -l"N64DeltaCore" -l"NESDeltaCore" -l"Roxas" -l"SDWebImage" -l"SMCalloutView" -l"SNESDeltaCore" -l"SQLite.swift" -l"SwiftyDropbox" -l"ZIPFoundation" -l"c++" -l"sqlite3" -l"z" -framework "CoreGraphics" -framework "CoreText" -framework "Crashlytics" -framework "Fabric" -framework "Foundation" -framework "GoogleSignIn" -framework "ImageIO" -framework "LocalAuthentication" -framework "SafariServices" -framework "Security" -framework "SystemConfiguration" -framework "UIKit" -framework "WebKit"
|
||||
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -Xcc -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.modulemap" -Xcc -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/DSDeltaCore/DSDeltaCore.modulemap" -Xcc -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/DeltaCore/DeltaCore.modulemap" -Xcc -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/GBADeltaCore/GBADeltaCore.modulemap" -Xcc -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/GBCDeltaCore/GBCDeltaCore.modulemap" -Xcc -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/Harmony/Harmony.modulemap" -Xcc -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/MelonDSDeltaCore/MelonDSDeltaCore.modulemap" -Xcc -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/N64DeltaCore/N64DeltaCore.modulemap" -Xcc -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/NESDeltaCore/NESDeltaCore.modulemap" -Xcc -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/SNESDeltaCore/SNESDeltaCore.modulemap" -Xcc -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/SQLite.swift/SQLite.modulemap" -Xcc -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/SwiftyDropbox/SwiftyDropbox.modulemap" -Xcc -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/ZIPFoundation/ZIPFoundation.modulemap" -Xcc -fmodule-map-file="${PODS_ROOT}/Headers/Public/GTMSessionFetcher/GTMSessionFetcher.modulemap" -Xcc -fmodule-map-file="${PODS_ROOT}/Headers/Public/GoogleAPIClientForREST/GoogleAPIClientForREST.modulemap" -Xcc -fmodule-map-file="${PODS_ROOT}/Headers/Public/GoogleToolboxForMac/GoogleToolboxForMac.modulemap" -Xcc -fmodule-map-file="${PODS_ROOT}/Headers/Public/Roxas/Roxas.modulemap" -Xcc -fmodule-map-file="${PODS_ROOT}/Headers/Public/SDWebImage/SDWebImage.modulemap" -Xcc -fmodule-map-file="${PODS_ROOT}/Headers/Public/SMCalloutView/SMCalloutView.modulemap"
|
||||
|
||||
@ -4,7 +4,7 @@ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
|
||||
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Crashlytics/iOS" "${PODS_ROOT}/Fabric/iOS" "${PODS_ROOT}/GoogleSignIn/Frameworks"
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 STATIC_LIBRARY=1 STATIC_LIBRARY=1 JIT_ENABLED=1 STATIC_LIBRARY=1 MUPENPLUSAPI TXFILTER_LIB OS_IOS GLESX GL_ERROR_DEBUG GL_DEBUG GLESX PNG_ARM_NEON_OPT=0
|
||||
HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/DSDeltaCore" "${PODS_ROOT}/Headers/Public/DeltaCore" "${PODS_ROOT}/Headers/Public/GBADeltaCore" "${PODS_ROOT}/Headers/Public/GBCDeltaCore" "${PODS_ROOT}/Headers/Public/GTMSessionFetcher" "${PODS_ROOT}/Headers/Public/GoogleAPIClientForREST" "${PODS_ROOT}/Headers/Public/GoogleToolboxForMac" "${PODS_ROOT}/Headers/Public/Harmony" "${PODS_ROOT}/Headers/Public/MelonDSDeltaCore" "${PODS_ROOT}/Headers/Public/N64DeltaCore" "${PODS_ROOT}/Headers/Public/NESDeltaCore" "${PODS_ROOT}/Headers/Public/Roxas" "${PODS_ROOT}/Headers/Public/SDWebImage" "${PODS_ROOT}/Headers/Public/SMCalloutView" "${PODS_ROOT}/Headers/Public/SNESDeltaCore" "${PODS_ROOT}/Headers/Public/SQLite.swift" "${PODS_ROOT}/Headers/Public/SwiftyDropbox" "${PODS_CONFIGURATION_BUILD_DIR}" "$(PODS_ROOT)/Headers/Private/DSDeltaCore/desmume/desmume/src/libretro-common/include" "${PODS_CONFIGURATION_BUILD_DIR}" "$(PODS_ROOT)/Headers/Private/GBADeltaCore/SFML/include" "$(PODS_ROOT)/Headers/Private/GBADeltaCore/SFML/src" "${PODS_CONFIGURATION_BUILD_DIR}" "${PODS_CONFIGURATION_BUILD_DIR}" "$(PODS_ROOT)/Headers/Private/MelonDSDeltaCore/melonDS/src" "${PODS_CONFIGURATION_BUILD_DIR}" "$(PODS_ROOT)/Headers/Private/N64DeltaCore/Mupen64Plus/mupen64plus-core/subprojects/**" "$(PODS_ROOT)/Headers/Private/N64DeltaCore/libMupen64Plus/SDL" "${PODS_CONFIGURATION_BUILD_DIR}" "$(PODS_ROOT)/Headers/Private/N64DeltaCore/Mupen64Plus/mupen64plus-core/subprojects/**" "$(PODS_ROOT)/Headers/Private/N64DeltaCore/libMupen64Plus/SDL" "$(PODS_ROOT)/Headers/Private/N64DeltaCore/Mupen64Plus/GLideN64/src" "$(PODS_ROOT)/Headers/Private/N64DeltaCore/Mupen64Plus/GLideN64/src/inc" "$(PODS_ROOT)/Headers/Private/N64DeltaCore/Mupen64Plus/GLideN64/src" "$(PODS_ROOT)/Headers/Private/N64DeltaCore/Mupen64Plus/GLideN64/src/osal" "$(PODS_ROOT)/Headers/Private/N64DeltaCore/Mupen64Plus/libpng"
|
||||
LIBRARY_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/DSDeltaCore" "${PODS_CONFIGURATION_BUILD_DIR}/DeltaCore" "${PODS_CONFIGURATION_BUILD_DIR}/GBADeltaCore" "${PODS_CONFIGURATION_BUILD_DIR}/GBCDeltaCore" "${PODS_CONFIGURATION_BUILD_DIR}/GTMSessionFetcher" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleAPIClientForREST" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleToolboxForMac" "${PODS_CONFIGURATION_BUILD_DIR}/Harmony" "${PODS_CONFIGURATION_BUILD_DIR}/MelonDSDeltaCore" "${PODS_CONFIGURATION_BUILD_DIR}/N64DeltaCore" "${PODS_CONFIGURATION_BUILD_DIR}/NESDeltaCore" "${PODS_CONFIGURATION_BUILD_DIR}/Roxas" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/SMCalloutView" "${PODS_CONFIGURATION_BUILD_DIR}/SNESDeltaCore" "${PODS_CONFIGURATION_BUILD_DIR}/SQLite.swift" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyDropbox" "${PODS_CONFIGURATION_BUILD_DIR}/ZIPFoundation"
|
||||
LIBRARY_SEARCH_PATHS = $(inherited) "${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/DSDeltaCore" "${PODS_CONFIGURATION_BUILD_DIR}/DeltaCore" "${PODS_CONFIGURATION_BUILD_DIR}/GBADeltaCore" "${PODS_CONFIGURATION_BUILD_DIR}/GBCDeltaCore" "${PODS_CONFIGURATION_BUILD_DIR}/GTMSessionFetcher" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleAPIClientForREST" "${PODS_CONFIGURATION_BUILD_DIR}/GoogleToolboxForMac" "${PODS_CONFIGURATION_BUILD_DIR}/Harmony" "${PODS_CONFIGURATION_BUILD_DIR}/MelonDSDeltaCore" "${PODS_CONFIGURATION_BUILD_DIR}/N64DeltaCore" "${PODS_CONFIGURATION_BUILD_DIR}/NESDeltaCore" "${PODS_CONFIGURATION_BUILD_DIR}/Roxas" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/SMCalloutView" "${PODS_CONFIGURATION_BUILD_DIR}/SNESDeltaCore" "${PODS_CONFIGURATION_BUILD_DIR}/SQLite.swift" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyDropbox" "${PODS_CONFIGURATION_BUILD_DIR}/ZIPFoundation" /usr/lib/swift
|
||||
OTHER_CFLAGS = $(inherited) -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.modulemap" -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/DSDeltaCore/DSDeltaCore.modulemap" -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/DeltaCore/DeltaCore.modulemap" -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/GBADeltaCore/GBADeltaCore.modulemap" -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/GBCDeltaCore/GBCDeltaCore.modulemap" -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/Harmony/Harmony.modulemap" -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/MelonDSDeltaCore/MelonDSDeltaCore.modulemap" -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/N64DeltaCore/N64DeltaCore.modulemap" -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/NESDeltaCore/NESDeltaCore.modulemap" -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/SNESDeltaCore/SNESDeltaCore.modulemap" -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/SQLite.swift/SQLite.modulemap" -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/SwiftyDropbox/SwiftyDropbox.modulemap" -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/ZIPFoundation/ZIPFoundation.modulemap" -fmodule-map-file="${PODS_ROOT}/Headers/Public/GTMSessionFetcher/GTMSessionFetcher.modulemap" -fmodule-map-file="${PODS_ROOT}/Headers/Public/GoogleAPIClientForREST/GoogleAPIClientForREST.modulemap" -fmodule-map-file="${PODS_ROOT}/Headers/Public/GoogleToolboxForMac/GoogleToolboxForMac.modulemap" -fmodule-map-file="${PODS_ROOT}/Headers/Public/Roxas/Roxas.modulemap" -fmodule-map-file="${PODS_ROOT}/Headers/Public/SDWebImage/SDWebImage.modulemap" -fmodule-map-file="${PODS_ROOT}/Headers/Public/SMCalloutView/SMCalloutView.modulemap" -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/DSDeltaCore" -isystem "${PODS_ROOT}/Headers/Public/GBADeltaCore" -isystem "${PODS_ROOT}/Headers/Public/GBCDeltaCore" -isystem "${PODS_ROOT}/Headers/Public/Harmony" -isystem "${PODS_ROOT}/Headers/Public/MelonDSDeltaCore" -isystem "${PODS_ROOT}/Headers/Public/N64DeltaCore" -isystem "${PODS_ROOT}/Headers/Public/NESDeltaCore" -isystem "${PODS_ROOT}/Headers/Public/SNESDeltaCore" -iframework "${PODS_ROOT}/Crashlytics/iOS" -iframework "${PODS_ROOT}/Fabric/iOS" -iframework "${PODS_ROOT}/GoogleSignIn/Frameworks" -DHOST_DARWIN -DDESMUME_COCOA -DHAVE_OPENGL -DHAVE_LIBZ -DANDROID -fexceptions -ftree-vectorize -DCOMPRESS_MT -DIOS -DOBJ_C -marm -fvisibility=hidden -DSTATIC_LIBRARY=1 -DSTATIC_LIBRARY -DHAVE_CSTDINT -fno-strict-aliasing -DGCC -pthread -fPIC -D__unix__ -ffast-math -fno-strict-aliasing -DGCC -pthread -fPIC -D__unix__ -ffast-math -D__VEC4_OPT -fvisibility=hidden -funsigned-char -ffast-math -finline -fno-builtin -fno-common -fomit-frame-pointer -funroll-loops -fstrict-aliasing -DHAVE_STDINT_H -DARM -DSTATIC_LIBRARY=1
|
||||
OTHER_LDFLAGS = $(inherited) -ObjC -Wl,-exported_symbol,_Video_PluginStartup,-exported_symbol,_Video_PluginShutdown,-exported_symbol,_Video_PluginGetVersion,-exported_symbol,_Video_RomOpen,-exported_symbol,_Video_RomClosed,-exported_symbol,_ConfigGetSharedDataFilepath,-exported_symbol,_ConfigGetUserConfigPath,-exported_symbol,_ConfigGetUserCachePath,-exported_symbol,_ConfigGetUserDataPath,-exported_symbol,_ConfigOpenSection,-exported_symbol,_ConfigDeleteSection,-exported_symbol,_ConfigSaveSection,-exported_symbol,_ConfigSaveFile,-exported_symbol,_ConfigSetDefaultInt,-exported_symbol,_ConfigSetDefaultFloat,-exported_symbol,_ConfigSetDefaultBool,-exported_symbol,_ConfigSetDefaultString,-exported_symbol,_ConfigGetParamInt,-exported_symbol,_ConfigGetParamFloat,-exported_symbol,_ConfigGetParamBool,-exported_symbol,_ConfigGetParamString,-exported_symbol,_ConfigExternalGetParameter,-exported_symbol,_ConfigExternalOpen,-exported_symbol,_ConfigExternalClose,-exported_symbol,_VidExt_Init,-exported_symbol,_VidExt_Quit,-exported_symbol,_VidExt_ListFullscreenModes,-exported_symbol,_VidExt_SetVideoMode,-exported_symbol,_VidExt_SetCaption,-exported_symbol,_VidExt_ToggleFullScreen,-exported_symbol,_VidExt_ResizeWindow,-exported_symbol,_VidExt_GL_GetProcAddress,-exported_symbol,_VidExt_GL_SetAttribute,-exported_symbol,_VidExt_GL_GetAttribute,-exported_symbol,_VidExt_GL_SwapBuffers,-exported_symbol,_ChangeWindow,-exported_symbol,_InitiateGFX,-exported_symbol,_MoveScreen,-exported_symbol,_ProcessDList,-exported_symbol,_ProcessRDPList,-exported_symbol,_ShowCFB,-exported_symbol,_UpdateScreen,-exported_symbol,_ViStatusChanged,-exported_symbol,_ViWidthChanged,-exported_symbol,_ReadScreen2,-exported_symbol,_SetRenderingCallback,-exported_symbol,_FBRead,-exported_symbol,_FBWrite,-exported_symbol,_FBGetFrameBufferInfo,-exported_symbol,_ResizeVideoOutput,-exported_symbol,_RSP_PluginStartup,-exported_symbol,_RSP_PluginShutdown,-exported_symbol,_RSP_PluginGetVersion,-exported_symbol,_DoRspCycles,-exported_symbol,_InitiateRSP,-exported_symbol,_RSP_RomClosed,-exported_symbol,_CoreGetAPIVersions,-exported_symbol,_ConfigGetParameter,-exported_symbol,_ConfigSetParameter,-exported_symbol,_CoreDoCommand -l"Alamofire" -l"DSDeltaCore" -l"GBADeltaCore" -l"GBCDeltaCore" -l"GTMSessionFetcher" -l"GoogleAPIClientForREST" -l"GoogleToolboxForMac" -l"Harmony" -l"MelonDSDeltaCore" -l"N64DeltaCore" -l"NESDeltaCore" -l"Roxas" -l"SDWebImage" -l"SMCalloutView" -l"SNESDeltaCore" -l"SQLite.swift" -l"SwiftyDropbox" -l"ZIPFoundation" -l"c++" -l"sqlite3" -l"z" -framework "CoreGraphics" -framework "CoreText" -framework "Crashlytics" -framework "Fabric" -framework "Foundation" -framework "GoogleSignIn" -framework "ImageIO" -framework "LocalAuthentication" -framework "SafariServices" -framework "Security" -framework "SystemConfiguration" -framework "UIKit" -framework "WebKit"
|
||||
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -Xcc -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.modulemap" -Xcc -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/DSDeltaCore/DSDeltaCore.modulemap" -Xcc -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/DeltaCore/DeltaCore.modulemap" -Xcc -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/GBADeltaCore/GBADeltaCore.modulemap" -Xcc -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/GBCDeltaCore/GBCDeltaCore.modulemap" -Xcc -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/Harmony/Harmony.modulemap" -Xcc -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/MelonDSDeltaCore/MelonDSDeltaCore.modulemap" -Xcc -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/N64DeltaCore/N64DeltaCore.modulemap" -Xcc -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/NESDeltaCore/NESDeltaCore.modulemap" -Xcc -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/SNESDeltaCore/SNESDeltaCore.modulemap" -Xcc -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/SQLite.swift/SQLite.modulemap" -Xcc -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/SwiftyDropbox/SwiftyDropbox.modulemap" -Xcc -fmodule-map-file="${PODS_CONFIGURATION_BUILD_DIR}/ZIPFoundation/ZIPFoundation.modulemap" -Xcc -fmodule-map-file="${PODS_ROOT}/Headers/Public/GTMSessionFetcher/GTMSessionFetcher.modulemap" -Xcc -fmodule-map-file="${PODS_ROOT}/Headers/Public/GoogleAPIClientForREST/GoogleAPIClientForREST.modulemap" -Xcc -fmodule-map-file="${PODS_ROOT}/Headers/Public/GoogleToolboxForMac/GoogleToolboxForMac.modulemap" -Xcc -fmodule-map-file="${PODS_ROOT}/Headers/Public/Roxas/Roxas.modulemap" -Xcc -fmodule-map-file="${PODS_ROOT}/Headers/Public/SDWebImage/SDWebImage.modulemap" -Xcc -fmodule-map-file="${PODS_ROOT}/Headers/Public/SMCalloutView/SMCalloutView.modulemap"
|
||||
|
||||
70
Resources/Contributors.plist
Normal file
70
Resources/Contributors.plist
Normal file
@ -0,0 +1,70 @@
|
||||
<?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">
|
||||
<array>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>Grant Gliner</string>
|
||||
<key>link</key>
|
||||
<string>https://twitter.com/GrantGliner</string>
|
||||
<key>linkName</key>
|
||||
<string>@GrantGliner</string>
|
||||
<key>contributions</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>Pause Menu Icons</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>Eric Lewis</string>
|
||||
<key>link</key>
|
||||
<string>https://github.com/ericlewis</string>
|
||||
<key>linkName</key>
|
||||
<string>github.com/ericlewis</string>
|
||||
<key>contributions</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>Dark Mode</string>
|
||||
<key>link</key>
|
||||
<string>https://github.com/rileytestut/Delta/pull/82</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>Chris Rittenhouse</string>
|
||||
<key>link</key>
|
||||
<string>https://www.litritt.com</string>
|
||||
<key>linkName</key>
|
||||
<string>litritt.com</string>
|
||||
<key>contributions</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>Genesis Skin</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>Noah Keck</string>
|
||||
<key>link</key>
|
||||
<string>https://github.com/noah978</string>
|
||||
<key>linkName</key>
|
||||
<string>github.com/noah978</string>
|
||||
<key>contributions</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>CheatBase</string>
|
||||
<key>link</key>
|
||||
<string>https://github.com/CheatBase/CheatBase</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
</plist>
|
||||
BIN
Resources/cheatbase.zip
Normal file
BIN
Resources/cheatbase.zip
Normal file
Binary file not shown.
BIN
Resources/openvgdb.sqlite
Executable file → Normal file
BIN
Resources/openvgdb.sqlite
Executable file → Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user