Merge pull request #12777 from t895/firmware-warning
android: Add key warning
This commit is contained in:
		| @@ -619,6 +619,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 | ||||
|      */ | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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() | ||||
|                 } | ||||
|             } | ||||
|   | ||||
| @@ -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 | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -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) { | ||||
|   | ||||
| @@ -464,8 +464,8 @@ int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject | ||||
|     }; | ||||
|  | ||||
|     return static_cast<int>( | ||||
|         ContentManager::InstallNSP(&EmulationSession::GetInstance().System(), | ||||
|                                    EmulationSession::GetInstance().System().GetFilesystem().get(), | ||||
|         ContentManager::InstallNSP(EmulationSession::GetInstance().System(), | ||||
|                                    *EmulationSession::GetInstance().System().GetFilesystem(), | ||||
|                                    GetJString(env, j_file), callback)); | ||||
| } | ||||
|  | ||||
| @@ -825,7 +825,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, | ||||
| @@ -835,8 +835,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;"); | ||||
| @@ -848,7 +849,7 @@ jobject Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEnv* en | ||||
|  | ||||
|     auto& session = EmulationSession::GetInstance(); | ||||
|     std::vector<std::string> 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) { | ||||
| @@ -869,7 +870,7 @@ jint Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyGameContents(JNIEnv* env, jobje | ||||
|     }; | ||||
|     auto& session = EmulationSession::GetInstance(); | ||||
|     return static_cast<jint>( | ||||
|         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, | ||||
| @@ -918,4 +919,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" | ||||
|   | ||||
| @@ -144,6 +144,9 @@ | ||||
|     <string name="no_save_data_found">No save data found</string> | ||||
|     <string name="verify_installed_content">Verify installed content</string> | ||||
|     <string name="verify_installed_content_description">Checks all installed content for corruption</string> | ||||
|     <string name="keys_missing">Encryption keys are missing</string> | ||||
|     <string name="keys_missing_description">Firmware and retail games cannot be decrypted</string> | ||||
|     <string name="keys_missing_help">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> | ||||
|  | ||||
|     <!-- Applet launcher strings --> | ||||
|     <string name="applets">Applet launcher</string> | ||||
|   | ||||
| @@ -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<u64> 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<bool(size_t, size_t)>& 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<FileSys::NSP> 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<FileSys::NSP>(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<bool(size_t, size_t)>& 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<FileSys::NCA>(vfs->OpenFile(filename, FileSys::Mode::Read)); | ||||
|     const auto nca = std::make_shared<FileSys::NCA>(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<std::string> VerifyInstalledContents( | ||||
|     Core::System* system, FileSys::ManualContentProvider* provider, | ||||
|     Core::System& system, FileSys::ManualContentProvider& provider, | ||||
|     const std::function<bool(size_t, size_t)>& 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<FileSys::RegisteredCache*> content_providers; | ||||
|     if (bis_contents) { | ||||
| @@ -309,11 +309,11 @@ inline std::vector<std::string> 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<std::string> 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<std::string> 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<bool(size_t, size_t)>& 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; | ||||
|     } | ||||
| @@ -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 | ||||
|   | ||||
| @@ -423,7 +423,7 @@ GMainWindow::GMainWindow(std::unique_ptr<QtConfig> 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); }); | ||||
| @@ -2501,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.")); | ||||
| @@ -2798,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: | ||||
| @@ -3268,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); | ||||
|             }); | ||||
|  | ||||
| @@ -3371,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<FileSys::TitleType>(index), progress_callback); | ||||
| } | ||||
|  | ||||
| @@ -4121,7 +4118,7 @@ void GMainWindow::OnVerifyInstalledContents() { | ||||
|     }; | ||||
|  | ||||
|     const std::vector<std::string> result = | ||||
|         ContentManager::VerifyInstalledContents(system.get(), provider.get(), QtProgressCallback); | ||||
|         ContentManager::VerifyInstalledContents(*system, *provider, QtProgressCallback); | ||||
|     progress.close(); | ||||
|  | ||||
|     if (result.empty()) { | ||||
| @@ -4551,122 +4548,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. " | ||||
|                    "<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu " | ||||
|                    "quickstart guide</a> to get all your keys, firmware and " | ||||
|                    "games.<br><br><small>(%1)</small>") | ||||
|                     .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. " | ||||
|                "<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu " | ||||
|                "quickstart guide</a> 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<u64>(Service::AM::Applets::AppletProgramId::MiiEdit); | ||||
|  | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -224,11 +224,6 @@ | ||||
|     <string>&Stop</string> | ||||
|    </property> | ||||
|   </action> | ||||
|   <action name="action_Rederive"> | ||||
|    <property name="text"> | ||||
|     <string>&Reinitialize keys...</string> | ||||
|    </property> | ||||
|   </action> | ||||
|   <action name="action_Verify_installed_contents"> | ||||
|    <property name="text"> | ||||
|     <string>&Verify Installed Contents</string> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user