From 77da71cd62a09da31e4dc31fa1d347da4740fb7e Mon Sep 17 00:00:00 2001 From: Riley Testut Date: Thu, 23 Apr 2020 17:18:59 -0700 Subject: [PATCH] =?UTF-8?q?Adds=20=E2=80=9CHome=20Screen=E2=80=9D=20DS=20g?= =?UTF-8?q?ame=20to=20boot=20into=20DS=20main=20menu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Delta.xcodeproj/project.pbxproj | 4 + Delta/Database/DatabaseManager.swift | 69 ++++++++++++++++-- Delta/Database/Model/Human/Game.swift | 9 +++ Delta/Database/Model/Human/GameSave.swift | 7 ++ Delta/Database/Model/Human/SaveState.swift | 6 +- .../ControllerSkin+Configuring.swift | 12 +++ .../GameCollectionViewController.swift | 1 + Resources/DS.png | Bin 0 -> 12511 bytes 8 files changed, 100 insertions(+), 8 deletions(-) create mode 100644 Resources/DS.png diff --git a/Delta.xcodeproj/project.pbxproj b/Delta.xcodeproj/project.pbxproj index dbcbb81..2ba383d 100644 --- a/Delta.xcodeproj/project.pbxproj +++ b/Delta.xcodeproj/project.pbxproj @@ -125,6 +125,7 @@ BFCEA67E1D56FF640061A534 /* UIViewControllerContextTransitioning+Conveniences.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFCEA67D1D56FF640061A534 /* UIViewControllerContextTransitioning+Conveniences.swift */; }; BFD097211D3A01B8005A44C2 /* SaveStatesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF3540041C5DA70400C1184C /* SaveStatesViewController.swift */; }; BFD1EF402336BD8800D197CF /* UIDevice+Processor.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD1EF3F2336BD8800D197CF /* UIDevice+Processor.swift */; }; + BFDB0FEC24464758001C727C /* DS.png in Resources */ = {isa = PBXBuildFile; fileRef = BFDB0FEB24464757001C727C /* DS.png */; }; BFDB3418219E4B1700595A62 /* SyncStatusViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDB3417219E4B1700595A62 /* SyncStatusViewController.swift */; }; BFDCA1E6244EBAA900B8FBDB /* liblibDeSmuME.a in Frameworks */ = {isa = PBXBuildFile; fileRef = BFDCA1E5244EBAA900B8FBDB /* liblibDeSmuME.a */; }; BFDD04F11D5E2C27002D450E /* GameCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDD04F01D5E2C27002D450E /* GameCollectionViewController.swift */; }; @@ -281,6 +282,7 @@ BFC9B7381CEFCD34008629BB /* CheatsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CheatsViewController.swift; sourceTree = ""; }; BFCEA67D1D56FF640061A534 /* UIViewControllerContextTransitioning+Conveniences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewControllerContextTransitioning+Conveniences.swift"; sourceTree = ""; }; BFD1EF3F2336BD8800D197CF /* UIDevice+Processor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIDevice+Processor.swift"; sourceTree = ""; }; + BFDB0FEB24464757001C727C /* DS.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = DS.png; sourceTree = ""; }; BFDB3417219E4B1700595A62 /* SyncStatusViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncStatusViewController.swift; sourceTree = ""; }; BFDCA1E5244EBAA900B8FBDB /* liblibDeSmuME.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = liblibDeSmuME.a; sourceTree = BUILT_PRODUCTS_DIR; }; BFDD04F01D5E2C27002D450E /* GameCollectionViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameCollectionViewController.swift; sourceTree = ""; }; @@ -721,6 +723,7 @@ isa = PBXGroup; children = ( BF6BB2451BB73FE800CCF94A /* Assets.xcassets */, + BFDB0FEB24464757001C727C /* DS.png */, BF02D5D91DDEBB3000A5E131 /* openvgdb.sqlite */, ); path = Resources; @@ -920,6 +923,7 @@ BF6BF3211EB82362008E83CD /* GamesDatabase.storyboard in Resources */, BFAB9F88219A4B670080EC7D /* GoogleService-Info.plist in Resources */, BF3540001C5DA3C500C1184C /* PausePresentationControllerContentView.xib in Resources */, + BFDB0FEC24464758001C727C /* DS.png in Resources */, BF5E7F461B9A652600AE44F8 /* Settings.storyboard in Resources */, BF02D5DA1DDEBB3000A5E131 /* openvgdb.sqlite in Resources */, BF71CF8A1FE904B1001F1613 /* GameTableViewCell.xib in Resources */, diff --git a/Delta/Database/DatabaseManager.swift b/Delta/Database/DatabaseManager.swift index e79c49d..1358e29 100644 --- a/Delta/Database/DatabaseManager.swift +++ b/Delta/Database/DatabaseManager.swift @@ -14,6 +14,7 @@ import DeltaCore import Harmony import Roxas import ZIPFoundation +import MelonDSDeltaCore extension DatabaseManager { @@ -74,6 +75,66 @@ extension DatabaseManager } } } + + func prepare(_ core: DeltaCoreProtocol, in context: NSManagedObjectContext) + { + guard let system = System(gameType: core.gameType) else { return } + + if let skin = ControllerSkin(system: system, context: context) + { + print("Updated default skin (\(skin.identifier)) for system:", system) + } + else + { + print("Failed to update default skin for system:", system) + } + + switch system + { + case .ds where core == MelonDS.core: + let predicate = NSPredicate(format: "%K == %@", #keyPath(Game.identifier), Game.melonDSBIOSIdentifier) + if let _ = Game.instancesWithPredicate(predicate, inManagedObjectContext: context, type: Game.self).first + { + // Game already exists, so don't do anything. + break + } + + let game = Game(context: context) + game.identifier = Game.melonDSBIOSIdentifier + game.type = .ds + game.filename = "melonDS-BIOS" + + game.name = NSLocalizedString("Home Screen", comment: "") + + if let sourceURL = Bundle.main.url(forResource: "DS", withExtension: "png") + { + do + { + let destinationURL = DatabaseManager.artworkURL(for: game) + try FileManager.default.copyItem(at: sourceURL, to: destinationURL, shouldReplace: true) + game.artworkURL = destinationURL + } + catch + { + print("Failed to copy default DS home screen artwork.", error) + } + } + + let gameCollection = GameCollection(context: context) + gameCollection.identifier = GameType.ds.rawValue + gameCollection.index = Int16(System.ds.year) + gameCollection.games.insert(game) + + case .ds: + let predicate = NSPredicate(format: "%K == %@", #keyPath(Game.identifier), Game.melonDSBIOSIdentifier) + if let game = Game.instancesWithPredicate(predicate, inManagedObjectContext: context, type: Game.self).first + { + context.delete(game) + } + + default: break + } + } } //MARK: - Update - @@ -113,13 +174,7 @@ private extension DatabaseManager for system in System.allCases { - guard let deltaControllerSkin = DeltaCore.ControllerSkin.standardControllerSkin(for: system.gameType) else { continue } - - let controllerSkin = ControllerSkin(context: context) - controllerSkin.isStandard = true - controllerSkin.filename = deltaControllerSkin.fileURL.lastPathComponent - - controllerSkin.configure(with: deltaControllerSkin) + self.prepare(system.deltaCore, in: context) } do diff --git a/Delta/Database/Model/Human/Game.swift b/Delta/Database/Model/Human/Game.swift index 77c4017..8dcc86a 100644 --- a/Delta/Database/Model/Human/Game.swift +++ b/Delta/Database/Model/Human/Game.swift @@ -11,6 +11,11 @@ import Foundation import DeltaCore import Harmony +public extension Game +{ + static let melonDSBIOSIdentifier = "com.rileytestut.MelonDSDeltaCore.BIOS" +} + @objc(Game) public class Game: _Game, GameProtocol { @@ -151,4 +156,8 @@ extension Game: Syncable public var syncableLocalizedName: String? { return self.name } + + public var isSyncingEnabled: Bool { + return self.identifier != Game.melonDSBIOSIdentifier + } } diff --git a/Delta/Database/Model/Human/GameSave.swift b/Delta/Database/Model/Human/GameSave.swift index da86b8b..caa122b 100644 --- a/Delta/Database/Model/Human/GameSave.swift +++ b/Delta/Database/Model/Human/GameSave.swift @@ -57,4 +57,11 @@ extension GameSave: Syncable public var syncableLocalizedName: String? { return self.game?.name } + + public var isSyncingEnabled: Bool { + // self.game may be nil if being downloaded, so don't enforce it. + // guard let identifier = self.game?.identifier else { return false } + + return self.game?.identifier != Game.melonDSBIOSIdentifier + } } diff --git a/Delta/Database/Model/Human/SaveState.swift b/Delta/Database/Model/Human/SaveState.swift index f676648..6456773 100644 --- a/Delta/Database/Model/Human/SaveState.swift +++ b/Delta/Database/Model/Human/SaveState.swift @@ -123,7 +123,11 @@ extension SaveState: Syncable } public var isSyncingEnabled: Bool { - return self.type != .auto && self.type != .quick + // self.game may be nil if being downloaded, so don't enforce it. + // guard let identifier = self.game?.identifier else { return false } + + let isSyncingEnabled = (self.type != .auto && self.type != .quick) && (self.game?.identifier != Game.melonDSBIOSIdentifier) + return isSyncingEnabled } public var syncableMetadata: [HarmonyMetadataKey : String] { diff --git a/Delta/Extensions/ControllerSkin+Configuring.swift b/Delta/Extensions/ControllerSkin+Configuring.swift index d715540..6dcecbf 100644 --- a/Delta/Extensions/ControllerSkin+Configuring.swift +++ b/Delta/Extensions/ControllerSkin+Configuring.swift @@ -10,6 +10,18 @@ import DeltaCore extension ControllerSkin { + convenience init?(system: System, context: NSManagedObjectContext) + { + guard let deltaControllerSkin = DeltaCore.ControllerSkin.standardControllerSkin(for: system.gameType) else { return nil } + + self.init(context: context) + + self.isStandard = true + self.filename = deltaControllerSkin.fileURL.lastPathComponent + + self.configure(with: deltaControllerSkin) + } + func configure(with skin: DeltaCore.ControllerSkin) { // Manually copy values to be stored in database. diff --git a/Delta/Game Selection/GameCollectionViewController.swift b/Delta/Game Selection/GameCollectionViewController.swift index 31ffe34..28709b4 100644 --- a/Delta/Game Selection/GameCollectionViewController.swift +++ b/Delta/Game Selection/GameCollectionViewController.swift @@ -440,6 +440,7 @@ private extension GameCollectionViewController switch game.type { case GameType.unknown: return [cancelAction, renameAction, changeArtworkAction, shareAction, deleteAction] + case .ds where game.identifier == Game.melonDSBIOSIdentifier: return [cancelAction, renameAction, changeArtworkAction, changeControllerSkinAction, saveStatesAction] default: return [cancelAction, renameAction, changeArtworkAction, changeControllerSkinAction, shareAction, saveStatesAction, importSaveFile, deleteAction] } } diff --git a/Resources/DS.png b/Resources/DS.png new file mode 100644 index 0000000000000000000000000000000000000000..2c36ce6c22b4fa804fa629b69d6e3fb9a5b4fb58 GIT binary patch literal 12511 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 literal 0 HcmV?d00001