From 1a3fc3724aa2d38e717a8dcbfcc6c8a808a693b6 Mon Sep 17 00:00:00 2001 From: t895 Date: Wed, 24 Jan 2024 12:33:34 -0500 Subject: [PATCH 1/4] frontend_common: Remove key rederivation and keep key check --- src/frontend_common/content_manager.h | 7 ++ src/yuzu/main.cpp | 114 ++------------------------ src/yuzu/main.h | 8 +- src/yuzu/main.ui | 5 -- 4 files changed, 13 insertions(+), 121 deletions(-) diff --git a/src/frontend_common/content_manager.h b/src/frontend_common/content_manager.h index 0b0fee73e..fb92a1695 100644 --- a/src/frontend_common/content_manager.h +++ b/src/frontend_common/content_manager.h @@ -368,4 +368,11 @@ inline GameVerificationResult VerifyGameContents( return GameVerificationResult::Success; } +/** + * Checks if the keys required for decrypting firmware and games are available + */ +inline bool AreKeysPresent() { + return !Core::Crypto::KeyManager::Instance().BaseDeriveNecessary(); +} + } // namespace ContentManager diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index d8b0beadf..bb0fbef7c 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -423,7 +423,7 @@ GMainWindow::GMainWindow(std::unique_ptr config_, bool has_broken_vulk RemoveCachedContents(); // Gen keys if necessary - OnReinitializeKeys(ReinitializeKeyBehavior::NoWarning); + OnCheckFirmwareDecryption(); game_list->LoadCompatibilityList(); game_list->PopulateAsync(UISettings::values.game_dirs); @@ -1574,8 +1574,6 @@ void GMainWindow::ConnectMenuEvents() { connect(multiplayer_state, &MultiplayerState::SaveConfig, this, &GMainWindow::OnSaveConfig); // Tools - connect_menu(ui->action_Rederive, std::bind(&GMainWindow::OnReinitializeKeys, this, - ReinitializeKeyBehavior::Warning)); connect_menu(ui->action_Load_Album, &GMainWindow::OnAlbum); connect_menu(ui->action_Load_Cabinet_Nickname_Owner, [this]() { OnCabinet(Service::NFP::CabinetMode::StartNicknameAndOwnerSettings); }); @@ -4551,122 +4549,20 @@ void GMainWindow::OnMouseActivity() { } } -void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { - if (behavior == ReinitializeKeyBehavior::Warning) { - const auto res = QMessageBox::information( - this, tr("Confirm Key Rederivation"), - tr("You are about to force rederive all of your keys. \nIf you do not know what " - "this " - "means or what you are doing, \nthis is a potentially destructive action. " - "\nPlease " - "make sure this is what you want \nand optionally make backups.\n\nThis will " - "delete " - "your autogenerated key files and re-run the key derivation module."), - QMessageBox::StandardButtons{QMessageBox::Ok, QMessageBox::Cancel}); - - if (res == QMessageBox::Cancel) - return; - - const auto keys_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::KeysDir); - - Common::FS::RemoveFile(keys_dir / "prod.keys_autogenerated"); - Common::FS::RemoveFile(keys_dir / "console.keys_autogenerated"); - Common::FS::RemoveFile(keys_dir / "title.keys_autogenerated"); - } - - Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); - bool all_keys_present{true}; - - if (keys.BaseDeriveNecessary()) { - Core::Crypto::PartitionDataManager pdm{vfs->OpenDirectory("", FileSys::Mode::Read)}; - - const auto function = [this, &keys, &pdm] { - keys.PopulateFromPartitionData(pdm); - - system->GetFileSystemController().CreateFactories(*vfs); - keys.DeriveETicket(pdm, system->GetContentProvider()); - }; - - QString errors; - if (!pdm.HasFuses()) { - errors += tr("Missing fuses"); - } - if (!pdm.HasBoot0()) { - errors += tr(" - Missing BOOT0"); - } - if (!pdm.HasPackage2()) { - errors += tr(" - Missing BCPKG2-1-Normal-Main"); - } - if (!pdm.HasProdInfo()) { - errors += tr(" - Missing PRODINFO"); - } - if (!errors.isEmpty()) { - all_keys_present = false; - QMessageBox::warning( - this, tr("Derivation Components Missing"), - tr("Encryption keys are missing. " - "
Please follow the yuzu " - "quickstart guide to get all your keys, firmware and " - "games.

(%1)") - .arg(errors)); - } - - QProgressDialog prog(this); - prog.setRange(0, 0); - prog.setLabelText(tr("Deriving keys...\nThis may take up to a minute depending \non your " - "system's performance.")); - prog.setWindowTitle(tr("Deriving Keys")); - - prog.show(); - - auto future = QtConcurrent::run(function); - while (!future.isFinished()) { - QCoreApplication::processEvents(); - } - - prog.close(); - } - +void GMainWindow::OnCheckFirmwareDecryption() { system->GetFileSystemController().CreateFactories(*vfs); - - if (all_keys_present && !this->CheckSystemArchiveDecryption()) { - LOG_WARNING(Frontend, "Mii model decryption failed"); + if (!ContentManager::AreKeysPresent()) { QMessageBox::warning( - this, tr("System Archive Decryption Failed"), - tr("Encryption keys failed to decrypt firmware. " + this, tr("Derivation Components Missing"), + tr("Encryption keys are missing. " "
Please follow the yuzu " "quickstart guide to get all your keys, firmware and " "games.")); } - SetFirmwareVersion(); - - if (behavior == ReinitializeKeyBehavior::Warning) { - game_list->PopulateAsync(UISettings::values.game_dirs); - } - UpdateMenuState(); } -bool GMainWindow::CheckSystemArchiveDecryption() { - constexpr u64 MiiModelId = 0x0100000000000802; - - auto bis_system = system->GetFileSystemController().GetSystemNANDContents(); - if (!bis_system) { - // Not having system BIS files is not an error. - return true; - } - - auto mii_nca = bis_system->GetEntry(MiiModelId, FileSys::ContentRecordType::Data); - if (!mii_nca) { - // Not having the Mii model is not an error. - return true; - } - - // Return whether we are able to decrypt the RomFS of the Mii model. - return mii_nca->GetRomFS().get() != nullptr; -} - bool GMainWindow::CheckFirmwarePresence() { constexpr u64 MiiEditId = static_cast(Service::AM::Applets::AppletProgramId::MiiEdit); diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 280fae5c3..6b72094ff 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -125,11 +125,6 @@ enum class EmulatedDirectoryTarget { SDMC, }; -enum class ReinitializeKeyBehavior { - NoWarning, - Warning, -}; - namespace VkDeviceInfo { class Record; } @@ -400,7 +395,7 @@ private slots: void OnMiiEdit(); void OnOpenControllerMenu(); void OnCaptureScreenshot(); - void OnReinitializeKeys(ReinitializeKeyBehavior behavior); + void OnCheckFirmwareDecryption(); void OnLanguageChanged(const QString& locale); void OnMouseActivity(); bool OnShutdownBegin(); @@ -441,7 +436,6 @@ private: void LoadTranslation(); void OpenPerGameConfiguration(u64 title_id, const std::string& file_name); bool CheckDarkMode(); - bool CheckSystemArchiveDecryption(); bool CheckFirmwarePresence(); void SetFirmwareVersion(); void ConfigureFilesystemProvider(const std::string& filepath); diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index e53f9951e..6a6b0821f 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui @@ -224,11 +224,6 @@ &Stop - - - &Reinitialize keys... - - &Verify Installed Contents From 97ca160b08537314ca6ba953a382aebda3da06b9 Mon Sep 17 00:00:00 2001 From: t895 Date: Wed, 24 Jan 2024 12:34:50 -0500 Subject: [PATCH 2/4] frontend_common: Consistently use references Was swapping between references and pointers for no reason. Just unify them here since each of these utility functions will need their parameters to be alive. --- src/android/app/src/main/jni/native.cpp | 15 +++---- src/frontend_common/content_manager.h | 56 ++++++++++++------------- src/yuzu/main.cpp | 11 +++-- 3 files changed, 41 insertions(+), 41 deletions(-) diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 963f57380..4c3644cc5 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -463,8 +463,8 @@ int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject }; return static_cast( - ContentManager::InstallNSP(&EmulationSession::GetInstance().System(), - EmulationSession::GetInstance().System().GetFilesystem().get(), + ContentManager::InstallNSP(EmulationSession::GetInstance().System(), + *EmulationSession::GetInstance().System().GetFilesystem(), GetJString(env, j_file), callback)); } @@ -819,7 +819,7 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeUpdate(JNIEnv* env, jobject job void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeDLC(JNIEnv* env, jobject jobj, jstring jprogramId) { auto program_id = EmulationSession::GetProgramId(env, jprogramId); - ContentManager::RemoveAllDLC(&EmulationSession::GetInstance().System(), program_id); + ContentManager::RemoveAllDLC(EmulationSession::GetInstance().System(), program_id); } void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeMod(JNIEnv* env, jobject jobj, jstring jprogramId, @@ -829,8 +829,9 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeMod(JNIEnv* env, jobject jobj, program_id, GetJString(env, jname)); } -jobject Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEnv* env, jobject jobj, - jobject jcallback) { +jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEnv* env, + jobject jobj, + jobject jcallback) { auto jlambdaClass = env->GetObjectClass(jcallback); auto jlambdaInvokeMethod = env->GetMethodID( jlambdaClass, "invoke", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); @@ -842,7 +843,7 @@ jobject Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEnv* en auto& session = EmulationSession::GetInstance(); std::vector result = ContentManager::VerifyInstalledContents( - &session.System(), session.GetContentProvider(), callback); + session.System(), *session.GetContentProvider(), callback); jobjectArray jresult = env->NewObjectArray(result.size(), IDCache::GetStringClass(), ToJString(env, "")); for (size_t i = 0; i < result.size(); ++i) { @@ -863,7 +864,7 @@ jint Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyGameContents(JNIEnv* env, jobje }; auto& session = EmulationSession::GetInstance(); return static_cast( - ContentManager::VerifyGameContents(&session.System(), GetJString(env, jpath), callback)); + ContentManager::VerifyGameContents(session.System(), GetJString(env, jpath), callback)); } jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject jobj, diff --git a/src/frontend_common/content_manager.h b/src/frontend_common/content_manager.h index fb92a1695..1cbaa73f7 100644 --- a/src/frontend_common/content_manager.h +++ b/src/frontend_common/content_manager.h @@ -47,14 +47,14 @@ inline bool RemoveDLC(const Service::FileSystem::FileSystemController& fs_contro /** * \brief Removes all DLC for a game - * \param system Raw pointer to the system instance + * \param system Reference to the system instance * \param program_id Program ID for the game that will have all of its DLC removed * \return Number of DLC removed */ -inline size_t RemoveAllDLC(Core::System* system, const u64 program_id) { +inline size_t RemoveAllDLC(Core::System& system, const u64 program_id) { size_t count{}; - const auto& fs_controller = system->GetFileSystemController(); - const auto dlc_entries = system->GetContentProvider().ListEntriesFilter( + const auto& fs_controller = system.GetFileSystemController(); + const auto dlc_entries = system.GetContentProvider().ListEntriesFilter( FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); std::vector program_dlc_entries; @@ -124,15 +124,15 @@ inline bool RemoveMod(const Service::FileSystem::FileSystemController& fs_contro /** * \brief Installs an NSP - * \param system Raw pointer to the system instance - * \param vfs Raw pointer to the VfsFilesystem instance in Core::System + * \param system Reference to the system instance + * \param vfs Reference to the VfsFilesystem instance in Core::System * \param filename Path to the NSP file * \param callback Callback to report the progress of the installation. The first size_t * parameter is the total size of the virtual file and the second is the current progress. If you * return true to the callback, it will cancel the installation as soon as possible. * \return [InstallResult] representing how the installation finished */ -inline InstallResult InstallNSP(Core::System* system, FileSys::VfsFilesystem* vfs, +inline InstallResult InstallNSP(Core::System& system, FileSys::VfsFilesystem& vfs, const std::string& filename, const std::function& callback) { const auto copy = [callback](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest, @@ -159,7 +159,7 @@ inline InstallResult InstallNSP(Core::System* system, FileSys::VfsFilesystem* vf }; std::shared_ptr nsp; - FileSys::VirtualFile file = vfs->OpenFile(filename, FileSys::Mode::Read); + FileSys::VirtualFile file = vfs.OpenFile(filename, FileSys::Mode::Read); if (boost::to_lower_copy(file->GetName()).ends_with(std::string("nsp"))) { nsp = std::make_shared(file); if (nsp->IsExtractedType()) { @@ -173,7 +173,7 @@ inline InstallResult InstallNSP(Core::System* system, FileSys::VfsFilesystem* vf return InstallResult::Failure; } const auto res = - system->GetFileSystemController().GetUserNANDContents()->InstallEntry(*nsp, true, copy); + system.GetFileSystemController().GetUserNANDContents()->InstallEntry(*nsp, true, copy); switch (res) { case FileSys::InstallResult::Success: return InstallResult::Success; @@ -188,17 +188,17 @@ inline InstallResult InstallNSP(Core::System* system, FileSys::VfsFilesystem* vf /** * \brief Installs an NCA - * \param vfs Raw pointer to the VfsFilesystem instance in Core::System + * \param vfs Reference to the VfsFilesystem instance in Core::System * \param filename Path to the NCA file - * \param registered_cache Raw pointer to the registered cache that the NCA will be installed to + * \param registered_cache Reference to the registered cache that the NCA will be installed to * \param title_type Type of NCA package to install * \param callback Callback to report the progress of the installation. The first size_t * parameter is the total size of the virtual file and the second is the current progress. If you * return true to the callback, it will cancel the installation as soon as possible. * \return [InstallResult] representing how the installation finished */ -inline InstallResult InstallNCA(FileSys::VfsFilesystem* vfs, const std::string& filename, - FileSys::RegisteredCache* registered_cache, +inline InstallResult InstallNCA(FileSys::VfsFilesystem& vfs, const std::string& filename, + FileSys::RegisteredCache& registered_cache, const FileSys::TitleType title_type, const std::function& callback) { const auto copy = [callback](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest, @@ -224,7 +224,7 @@ inline InstallResult InstallNCA(FileSys::VfsFilesystem* vfs, const std::string& return true; }; - const auto nca = std::make_shared(vfs->OpenFile(filename, FileSys::Mode::Read)); + const auto nca = std::make_shared(vfs.OpenFile(filename, FileSys::Mode::Read)); const auto id = nca->GetStatus(); // Game updates necessary are missing base RomFS @@ -233,7 +233,7 @@ inline InstallResult InstallNCA(FileSys::VfsFilesystem* vfs, const std::string& return InstallResult::Failure; } - const auto res = registered_cache->InstallEntry(*nca, title_type, true, copy); + const auto res = registered_cache.InstallEntry(*nca, title_type, true, copy); if (res == FileSys::InstallResult::Success) { return InstallResult::Success; } else if (res == FileSys::InstallResult::OverwriteExisting) { @@ -245,19 +245,19 @@ inline InstallResult InstallNCA(FileSys::VfsFilesystem* vfs, const std::string& /** * \brief Verifies the installed contents for a given ManualContentProvider - * \param system Raw pointer to the system instance - * \param provider Raw pointer to the content provider that's tracking indexed games + * \param system Reference to the system instance + * \param provider Reference to the content provider that's tracking indexed games * \param callback Callback to report the progress of the installation. The first size_t * parameter is the total size of the installed contents and the second is the current progress. If * you return true to the callback, it will cancel the installation as soon as possible. * \return A list of entries that failed to install. Returns an empty vector if successful. */ inline std::vector VerifyInstalledContents( - Core::System* system, FileSys::ManualContentProvider* provider, + Core::System& system, FileSys::ManualContentProvider& provider, const std::function& callback) { // Get content registries. - auto bis_contents = system->GetFileSystemController().GetSystemNANDContents(); - auto user_contents = system->GetFileSystemController().GetUserNANDContents(); + auto bis_contents = system.GetFileSystemController().GetSystemNANDContents(); + auto user_contents = system.GetFileSystemController().GetUserNANDContents(); std::vector content_providers; if (bis_contents) { @@ -309,11 +309,11 @@ inline std::vector VerifyInstalledContents( const auto title_id = nca.GetTitleId(); std::string title_name = "unknown"; - const auto control = provider->GetEntry(FileSys::GetBaseTitleID(title_id), - FileSys::ContentRecordType::Control); + const auto control = provider.GetEntry(FileSys::GetBaseTitleID(title_id), + FileSys::ContentRecordType::Control); if (control && control->GetStatus() == Loader::ResultStatus::Success) { - const FileSys::PatchManager pm{title_id, system->GetFileSystemController(), - *provider}; + const FileSys::PatchManager pm{title_id, system.GetFileSystemController(), + provider}; const auto [nacp, logo] = pm.ParseControlNCA(*control); if (nacp) { title_name = nacp->GetApplicationName(); @@ -335,7 +335,7 @@ inline std::vector VerifyInstalledContents( /** * \brief Verifies the contents of a given game - * \param system Raw pointer to the system instance + * \param system Reference to the system instance * \param game_path Patch to the game file * \param callback Callback to report the progress of the installation. The first size_t * parameter is the total size of the installed contents and the second is the current progress. If @@ -343,10 +343,10 @@ inline std::vector VerifyInstalledContents( * \return GameVerificationResult representing how the verification process finished */ inline GameVerificationResult VerifyGameContents( - Core::System* system, const std::string& game_path, + Core::System& system, const std::string& game_path, const std::function& callback) { - const auto loader = Loader::GetLoader( - *system, system->GetFilesystem()->OpenFile(game_path, FileSys::Mode::Read)); + const auto loader = + Loader::GetLoader(system, system.GetFilesystem()->OpenFile(game_path, FileSys::Mode::Read)); if (loader == nullptr) { return GameVerificationResult::NotImplemented; } diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index bb0fbef7c..e14410f7d 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -2499,7 +2499,7 @@ void GMainWindow::RemoveUpdateContent(u64 program_id, InstalledEntryType type) { } void GMainWindow::RemoveAddOnContent(u64 program_id, InstalledEntryType type) { - const size_t count = ContentManager::RemoveAllDLC(system.get(), program_id); + const size_t count = ContentManager::RemoveAllDLC(*system, program_id); if (count == 0) { QMessageBox::warning(this, GetGameListErrorRemoving(type), tr("There are no DLC installed for this title.")); @@ -2796,8 +2796,7 @@ void GMainWindow::OnGameListVerifyIntegrity(const std::string& game_path) { return progress.wasCanceled(); }; - const auto result = - ContentManager::VerifyGameContents(system.get(), game_path, QtProgressCallback); + const auto result = ContentManager::VerifyGameContents(*system, game_path, QtProgressCallback); progress.close(); switch (result) { case ContentManager::GameVerificationResult::Success: @@ -3266,7 +3265,7 @@ void GMainWindow::OnMenuInstallToNAND() { return false; }; future = QtConcurrent::run([this, &file, progress_callback] { - return ContentManager::InstallNSP(system.get(), vfs.get(), file.toStdString(), + return ContentManager::InstallNSP(*system, *vfs, file.toStdString(), progress_callback); }); @@ -3369,7 +3368,7 @@ ContentManager::InstallResult GMainWindow::InstallNCA(const QString& filename) { } return false; }; - return ContentManager::InstallNCA(vfs.get(), filename.toStdString(), registered_cache, + return ContentManager::InstallNCA(*vfs, filename.toStdString(), *registered_cache, static_cast(index), progress_callback); } @@ -4119,7 +4118,7 @@ void GMainWindow::OnVerifyInstalledContents() { }; const std::vector result = - ContentManager::VerifyInstalledContents(system.get(), provider.get(), QtProgressCallback); + ContentManager::VerifyInstalledContents(*system, *provider, QtProgressCallback); progress.close(); if (result.empty()) { From bc317a9807dec79cbbfe4d0affa215a0b69bec8c Mon Sep 17 00:00:00 2001 From: t895 Date: Wed, 24 Jan 2024 12:38:12 -0500 Subject: [PATCH 3/4] android: Add option to make MessageDialogFragments non-dismissible Additionally fixes an issue where its viewmodel could hold onto a stale positive action --- .../fragments/MessageDialogFragment.kt | 34 ++++++++++++++----- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt index 620d8db7c..22b084b9a 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt @@ -26,9 +26,15 @@ class MessageDialogFragment : DialogFragment() { val descriptionId = requireArguments().getInt(DESCRIPTION_ID) val descriptionString = requireArguments().getString(DESCRIPTION_STRING)!! val helpLinkId = requireArguments().getInt(HELP_LINK) + val dismissible = requireArguments().getBoolean(DISMISSIBLE) + val clearPositiveAction = requireArguments().getBoolean(CLEAR_POSITIVE_ACTION) val builder = MaterialAlertDialogBuilder(requireContext()) + if (clearPositiveAction) { + messageDialogViewModel.positiveAction = null + } + if (messageDialogViewModel.positiveAction == null) { builder.setPositiveButton(R.string.close, null) } else { @@ -51,6 +57,8 @@ class MessageDialogFragment : DialogFragment() { } } + isCancelable = dismissible + return builder.show() } @@ -67,6 +75,8 @@ class MessageDialogFragment : DialogFragment() { private const val DESCRIPTION_ID = "DescriptionId" private const val DESCRIPTION_STRING = "DescriptionString" private const val HELP_LINK = "Link" + private const val DISMISSIBLE = "Dismissible" + private const val CLEAR_POSITIVE_ACTION = "ClearPositiveAction" fun newInstance( activity: FragmentActivity? = null, @@ -75,22 +85,28 @@ class MessageDialogFragment : DialogFragment() { descriptionId: Int = 0, descriptionString: String = "", helpLinkId: Int = 0, + dismissible: Boolean = true, positiveAction: (() -> Unit)? = null ): MessageDialogFragment { - val dialog = MessageDialogFragment() - val bundle = Bundle() - bundle.apply { - putInt(TITLE_ID, titleId) - putString(TITLE_STRING, titleString) - putInt(DESCRIPTION_ID, descriptionId) - putString(DESCRIPTION_STRING, descriptionString) - putInt(HELP_LINK, helpLinkId) - } + var clearPositiveAction = false if (activity != null) { ViewModelProvider(activity)[MessageDialogViewModel::class.java].apply { clear() this.positiveAction = positiveAction } + } else { + clearPositiveAction = true + } + + val dialog = MessageDialogFragment() + val bundle = Bundle().apply { + putInt(TITLE_ID, titleId) + putString(TITLE_STRING, titleString) + putInt(DESCRIPTION_ID, descriptionId) + putString(DESCRIPTION_STRING, descriptionString) + putInt(HELP_LINK, helpLinkId) + putBoolean(DISMISSIBLE, dismissible) + putBoolean(CLEAR_POSITIVE_ACTION, clearPositiveAction) } dialog.arguments = bundle return dialog From b8be8dff69bf174e4ed41a398018e5cad8128d17 Mon Sep 17 00:00:00 2001 From: t895 Date: Wed, 24 Jan 2024 12:37:26 -0500 Subject: [PATCH 4/4] android: Add key check --- .../java/org/yuzu/yuzu_emu/NativeLibrary.kt | 5 +++ .../yuzu/yuzu_emu/fragments/SetupFragment.kt | 6 ++- .../org/yuzu/yuzu_emu/model/HomeViewModel.kt | 7 ++++ .../org/yuzu/yuzu_emu/ui/main/MainActivity.kt | 42 +++++++++++++++++++ src/android/app/src/main/jni/native.cpp | 6 +++ .../app/src/main/res/values/strings.xml | 3 ++ 6 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt index c408485c6..0fb35bf98 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt @@ -614,6 +614,11 @@ object NativeLibrary { */ external fun clearFilesystemProvider() + /** + * Checks if all necessary keys are present for decryption + */ + external fun areKeysPresent(): Boolean + /** * Button type for use in onTouchEvent */ diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt index 064342cdd..ebf41a639 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt @@ -31,6 +31,7 @@ import androidx.preference.PreferenceManager import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback import com.google.android.material.transition.MaterialFadeThrough import kotlinx.coroutines.launch +import org.yuzu.yuzu_emu.NativeLibrary import java.io.File import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.YuzuApplication @@ -162,7 +163,7 @@ class SetupFragment : Fragment() { R.string.install_prod_keys_warning_help, { val file = File(DirectoryInitialization.userDirectory + "/keys/prod.keys") - if (file.exists()) { + if (file.exists() && NativeLibrary.areKeysPresent()) { StepState.COMPLETE } else { StepState.INCOMPLETE @@ -347,7 +348,8 @@ class SetupFragment : Fragment() { val getProdKey = registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> if (result != null) { - if (mainActivity.processKey(result)) { + mainActivity.processKey(result) + if (NativeLibrary.areKeysPresent()) { keyCallback.onStepCompleted() } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt index 513ac2fc5..cfc777b81 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt @@ -31,6 +31,9 @@ class HomeViewModel : ViewModel() { private val _reloadPropertiesList = MutableStateFlow(false) val reloadPropertiesList get() = _reloadPropertiesList.asStateFlow() + private val _checkKeys = MutableStateFlow(false) + val checkKeys = _checkKeys.asStateFlow() + var navigatedToSetup = false fun setNavigationVisibility(visible: Boolean, animated: Boolean) { @@ -66,4 +69,8 @@ class HomeViewModel : ViewModel() { fun reloadPropertiesList(reload: Boolean) { _reloadPropertiesList.value = reload } + + fun setCheckKeys(value: Boolean) { + _checkKeys.value = value + } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt index c2cc29961..b3967d294 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt @@ -64,6 +64,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider { override var themeId: Int = 0 + private val CHECKED_DECRYPTION = "CheckedDecryption" + private var checkedDecryption = false + override fun onCreate(savedInstanceState: Bundle?) { val splashScreen = installSplashScreen() splashScreen.setKeepOnScreenCondition { !DirectoryInitialization.areDirectoriesReady } @@ -75,6 +78,18 @@ class MainActivity : AppCompatActivity(), ThemeProvider { binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) + if (savedInstanceState != null) { + checkedDecryption = savedInstanceState.getBoolean(CHECKED_DECRYPTION) + } + if (!checkedDecryption) { + val firstTimeSetup = PreferenceManager.getDefaultSharedPreferences(applicationContext) + .getBoolean(Settings.PREF_FIRST_APP_LAUNCH, true) + if (!firstTimeSetup) { + checkKeys() + } + checkedDecryption = true + } + WindowCompat.setDecorFitsSystemWindows(window, false) window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING) @@ -150,6 +165,16 @@ class MainActivity : AppCompatActivity(), ThemeProvider { } } } + launch { + repeatOnLifecycle(Lifecycle.State.CREATED) { + homeViewModel.checkKeys.collect { + if (it) { + checkKeys() + homeViewModel.setCheckKeys(false) + } + } + } + } } // Dismiss previous notifications (should not happen unless a crash occurred) @@ -158,6 +183,21 @@ class MainActivity : AppCompatActivity(), ThemeProvider { setInsets() } + private fun checkKeys() { + if (!NativeLibrary.areKeysPresent()) { + MessageDialogFragment.newInstance( + titleId = R.string.keys_missing, + descriptionId = R.string.keys_missing_description, + helpLinkId = R.string.keys_missing_help + ).show(supportFragmentManager, MessageDialogFragment.TAG) + } + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putBoolean(CHECKED_DECRYPTION, checkedDecryption) + } + fun finishSetup(navController: NavController) { navController.navigate(R.id.action_firstTimeSetupFragment_to_gamesFragment) (binding.navigationView as NavigationBarView).setupWithNavController(navController) @@ -349,6 +389,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { R.string.install_keys_success, Toast.LENGTH_SHORT ).show() + homeViewModel.setCheckKeys(true) gamesViewModel.reloadGames(true) return true } else { @@ -399,6 +440,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider { firmwarePath.deleteRecursively() cacheFirmwareDir.copyRecursively(firmwarePath, true) NativeLibrary.initializeSystem(true) + homeViewModel.setCheckKeys(true) getString(R.string.save_file_imported_success) } } catch (e: Exception) { diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 4c3644cc5..e51453eca 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -913,4 +913,10 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_clearFilesystemProvider(JNIEnv* env, EmulationSession::GetInstance().GetContentProvider()->ClearAllEntries(); } +jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_areKeysPresent(JNIEnv* env, jobject jobj) { + auto& system = EmulationSession::GetInstance().System(); + system.GetFileSystemController().CreateFactories(*system.GetFilesystem()); + return ContentManager::AreKeysPresent(); +} + } // extern "C" diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 779eb36a8..3cd1586fd 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -144,6 +144,9 @@ No save data found Verify installed content Checks all installed content for corruption + Encryption keys are missing + Firmware and retail games cannot be decrypted + https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys Applet launcher