summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar liamwhite2024-01-25 16:21:29 -0500
committerGravatar GitHub2024-01-25 16:21:29 -0500
commit3e2d3548f2bf90f823fb9c565ca2b97be3d048b7 (patch)
tree74137247efc83f233a4d81260de41eeb8aae1d8d /src
parentMerge pull request #12783 from liamwhite/cmif-generation (diff)
parentandroid: Add key check (diff)
downloadyuzu-3e2d3548f2bf90f823fb9c565ca2b97be3d048b7.tar.gz
yuzu-3e2d3548f2bf90f823fb9c565ca2b97be3d048b7.tar.xz
yuzu-3e2d3548f2bf90f823fb9c565ca2b97be3d048b7.zip
Merge pull request #12777 from t895/firmware-warning
android: Add key warning
Diffstat (limited to 'src')
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt5
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt32
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt6
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt7
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt42
-rw-r--r--src/android/app/src/main/jni/native.cpp21
-rw-r--r--src/android/app/src/main/res/values/strings.xml3
-rw-r--r--src/frontend_common/content_manager.h63
-rw-r--r--src/yuzu/main.cpp125
-rw-r--r--src/yuzu/main.h8
-rw-r--r--src/yuzu/main.ui5
11 files changed, 145 insertions, 172 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 5b9f553f7..55abba093 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
@@ -620,6 +620,11 @@ object NativeLibrary {
620 external fun clearFilesystemProvider() 620 external fun clearFilesystemProvider()
621 621
622 /** 622 /**
623 * Checks if all necessary keys are present for decryption
624 */
625 external fun areKeysPresent(): Boolean
626
627 /**
623 * Button type for use in onTouchEvent 628 * Button type for use in onTouchEvent
624 */ 629 */
625 object ButtonType { 630 object ButtonType {
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() {
26 val descriptionId = requireArguments().getInt(DESCRIPTION_ID) 26 val descriptionId = requireArguments().getInt(DESCRIPTION_ID)
27 val descriptionString = requireArguments().getString(DESCRIPTION_STRING)!! 27 val descriptionString = requireArguments().getString(DESCRIPTION_STRING)!!
28 val helpLinkId = requireArguments().getInt(HELP_LINK) 28 val helpLinkId = requireArguments().getInt(HELP_LINK)
29 val dismissible = requireArguments().getBoolean(DISMISSIBLE)
30 val clearPositiveAction = requireArguments().getBoolean(CLEAR_POSITIVE_ACTION)
29 31
30 val builder = MaterialAlertDialogBuilder(requireContext()) 32 val builder = MaterialAlertDialogBuilder(requireContext())
31 33
34 if (clearPositiveAction) {
35 messageDialogViewModel.positiveAction = null
36 }
37
32 if (messageDialogViewModel.positiveAction == null) { 38 if (messageDialogViewModel.positiveAction == null) {
33 builder.setPositiveButton(R.string.close, null) 39 builder.setPositiveButton(R.string.close, null)
34 } else { 40 } else {
@@ -51,6 +57,8 @@ class MessageDialogFragment : DialogFragment() {
51 } 57 }
52 } 58 }
53 59
60 isCancelable = dismissible
61
54 return builder.show() 62 return builder.show()
55 } 63 }
56 64
@@ -67,6 +75,8 @@ class MessageDialogFragment : DialogFragment() {
67 private const val DESCRIPTION_ID = "DescriptionId" 75 private const val DESCRIPTION_ID = "DescriptionId"
68 private const val DESCRIPTION_STRING = "DescriptionString" 76 private const val DESCRIPTION_STRING = "DescriptionString"
69 private const val HELP_LINK = "Link" 77 private const val HELP_LINK = "Link"
78 private const val DISMISSIBLE = "Dismissible"
79 private const val CLEAR_POSITIVE_ACTION = "ClearPositiveAction"
70 80
71 fun newInstance( 81 fun newInstance(
72 activity: FragmentActivity? = null, 82 activity: FragmentActivity? = null,
@@ -75,22 +85,28 @@ class MessageDialogFragment : DialogFragment() {
75 descriptionId: Int = 0, 85 descriptionId: Int = 0,
76 descriptionString: String = "", 86 descriptionString: String = "",
77 helpLinkId: Int = 0, 87 helpLinkId: Int = 0,
88 dismissible: Boolean = true,
78 positiveAction: (() -> Unit)? = null 89 positiveAction: (() -> Unit)? = null
79 ): MessageDialogFragment { 90 ): MessageDialogFragment {
91 var clearPositiveAction = false
92 if (activity != null) {
93 ViewModelProvider(activity)[MessageDialogViewModel::class.java].apply {
94 clear()
95 this.positiveAction = positiveAction
96 }
97 } else {
98 clearPositiveAction = true
99 }
100
80 val dialog = MessageDialogFragment() 101 val dialog = MessageDialogFragment()
81 val bundle = Bundle() 102 val bundle = Bundle().apply {
82 bundle.apply {
83 putInt(TITLE_ID, titleId) 103 putInt(TITLE_ID, titleId)
84 putString(TITLE_STRING, titleString) 104 putString(TITLE_STRING, titleString)
85 putInt(DESCRIPTION_ID, descriptionId) 105 putInt(DESCRIPTION_ID, descriptionId)
86 putString(DESCRIPTION_STRING, descriptionString) 106 putString(DESCRIPTION_STRING, descriptionString)
87 putInt(HELP_LINK, helpLinkId) 107 putInt(HELP_LINK, helpLinkId)
88 } 108 putBoolean(DISMISSIBLE, dismissible)
89 if (activity != null) { 109 putBoolean(CLEAR_POSITIVE_ACTION, clearPositiveAction)
90 ViewModelProvider(activity)[MessageDialogViewModel::class.java].apply {
91 clear()
92 this.positiveAction = positiveAction
93 }
94 } 110 }
95 dialog.arguments = bundle 111 dialog.arguments = bundle
96 return dialog 112 return dialog
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
31import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback 31import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
32import com.google.android.material.transition.MaterialFadeThrough 32import com.google.android.material.transition.MaterialFadeThrough
33import kotlinx.coroutines.launch 33import kotlinx.coroutines.launch
34import org.yuzu.yuzu_emu.NativeLibrary
34import java.io.File 35import java.io.File
35import org.yuzu.yuzu_emu.R 36import org.yuzu.yuzu_emu.R
36import org.yuzu.yuzu_emu.YuzuApplication 37import org.yuzu.yuzu_emu.YuzuApplication
@@ -162,7 +163,7 @@ class SetupFragment : Fragment() {
162 R.string.install_prod_keys_warning_help, 163 R.string.install_prod_keys_warning_help,
163 { 164 {
164 val file = File(DirectoryInitialization.userDirectory + "/keys/prod.keys") 165 val file = File(DirectoryInitialization.userDirectory + "/keys/prod.keys")
165 if (file.exists()) { 166 if (file.exists() && NativeLibrary.areKeysPresent()) {
166 StepState.COMPLETE 167 StepState.COMPLETE
167 } else { 168 } else {
168 StepState.INCOMPLETE 169 StepState.INCOMPLETE
@@ -347,7 +348,8 @@ class SetupFragment : Fragment() {
347 val getProdKey = 348 val getProdKey =
348 registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> 349 registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
349 if (result != null) { 350 if (result != null) {
350 if (mainActivity.processKey(result)) { 351 mainActivity.processKey(result)
352 if (NativeLibrary.areKeysPresent()) {
351 keyCallback.onStepCompleted() 353 keyCallback.onStepCompleted()
352 } 354 }
353 } 355 }
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() {
31 private val _reloadPropertiesList = MutableStateFlow(false) 31 private val _reloadPropertiesList = MutableStateFlow(false)
32 val reloadPropertiesList get() = _reloadPropertiesList.asStateFlow() 32 val reloadPropertiesList get() = _reloadPropertiesList.asStateFlow()
33 33
34 private val _checkKeys = MutableStateFlow(false)
35 val checkKeys = _checkKeys.asStateFlow()
36
34 var navigatedToSetup = false 37 var navigatedToSetup = false
35 38
36 fun setNavigationVisibility(visible: Boolean, animated: Boolean) { 39 fun setNavigationVisibility(visible: Boolean, animated: Boolean) {
@@ -66,4 +69,8 @@ class HomeViewModel : ViewModel() {
66 fun reloadPropertiesList(reload: Boolean) { 69 fun reloadPropertiesList(reload: Boolean) {
67 _reloadPropertiesList.value = reload 70 _reloadPropertiesList.value = reload
68 } 71 }
72
73 fun setCheckKeys(value: Boolean) {
74 _checkKeys.value = value
75 }
69} 76}
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 {
64 64
65 override var themeId: Int = 0 65 override var themeId: Int = 0
66 66
67 private val CHECKED_DECRYPTION = "CheckedDecryption"
68 private var checkedDecryption = false
69
67 override fun onCreate(savedInstanceState: Bundle?) { 70 override fun onCreate(savedInstanceState: Bundle?) {
68 val splashScreen = installSplashScreen() 71 val splashScreen = installSplashScreen()
69 splashScreen.setKeepOnScreenCondition { !DirectoryInitialization.areDirectoriesReady } 72 splashScreen.setKeepOnScreenCondition { !DirectoryInitialization.areDirectoriesReady }
@@ -75,6 +78,18 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
75 binding = ActivityMainBinding.inflate(layoutInflater) 78 binding = ActivityMainBinding.inflate(layoutInflater)
76 setContentView(binding.root) 79 setContentView(binding.root)
77 80
81 if (savedInstanceState != null) {
82 checkedDecryption = savedInstanceState.getBoolean(CHECKED_DECRYPTION)
83 }
84 if (!checkedDecryption) {
85 val firstTimeSetup = PreferenceManager.getDefaultSharedPreferences(applicationContext)
86 .getBoolean(Settings.PREF_FIRST_APP_LAUNCH, true)
87 if (!firstTimeSetup) {
88 checkKeys()
89 }
90 checkedDecryption = true
91 }
92
78 WindowCompat.setDecorFitsSystemWindows(window, false) 93 WindowCompat.setDecorFitsSystemWindows(window, false)
79 window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING) 94 window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING)
80 95
@@ -150,6 +165,16 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
150 } 165 }
151 } 166 }
152 } 167 }
168 launch {
169 repeatOnLifecycle(Lifecycle.State.CREATED) {
170 homeViewModel.checkKeys.collect {
171 if (it) {
172 checkKeys()
173 homeViewModel.setCheckKeys(false)
174 }
175 }
176 }
177 }
153 } 178 }
154 179
155 // Dismiss previous notifications (should not happen unless a crash occurred) 180 // Dismiss previous notifications (should not happen unless a crash occurred)
@@ -158,6 +183,21 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
158 setInsets() 183 setInsets()
159 } 184 }
160 185
186 private fun checkKeys() {
187 if (!NativeLibrary.areKeysPresent()) {
188 MessageDialogFragment.newInstance(
189 titleId = R.string.keys_missing,
190 descriptionId = R.string.keys_missing_description,
191 helpLinkId = R.string.keys_missing_help
192 ).show(supportFragmentManager, MessageDialogFragment.TAG)
193 }
194 }
195
196 override fun onSaveInstanceState(outState: Bundle) {
197 super.onSaveInstanceState(outState)
198 outState.putBoolean(CHECKED_DECRYPTION, checkedDecryption)
199 }
200
161 fun finishSetup(navController: NavController) { 201 fun finishSetup(navController: NavController) {
162 navController.navigate(R.id.action_firstTimeSetupFragment_to_gamesFragment) 202 navController.navigate(R.id.action_firstTimeSetupFragment_to_gamesFragment)
163 (binding.navigationView as NavigationBarView).setupWithNavController(navController) 203 (binding.navigationView as NavigationBarView).setupWithNavController(navController)
@@ -349,6 +389,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
349 R.string.install_keys_success, 389 R.string.install_keys_success,
350 Toast.LENGTH_SHORT 390 Toast.LENGTH_SHORT
351 ).show() 391 ).show()
392 homeViewModel.setCheckKeys(true)
352 gamesViewModel.reloadGames(true) 393 gamesViewModel.reloadGames(true)
353 return true 394 return true
354 } else { 395 } else {
@@ -399,6 +440,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
399 firmwarePath.deleteRecursively() 440 firmwarePath.deleteRecursively()
400 cacheFirmwareDir.copyRecursively(firmwarePath, true) 441 cacheFirmwareDir.copyRecursively(firmwarePath, true)
401 NativeLibrary.initializeSystem(true) 442 NativeLibrary.initializeSystem(true)
443 homeViewModel.setCheckKeys(true)
402 getString(R.string.save_file_imported_success) 444 getString(R.string.save_file_imported_success)
403 } 445 }
404 } catch (e: Exception) { 446 } catch (e: Exception) {
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp
index c20c2d2b8..247f2c2b3 100644
--- a/src/android/app/src/main/jni/native.cpp
+++ b/src/android/app/src/main/jni/native.cpp
@@ -464,8 +464,8 @@ int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject
464 }; 464 };
465 465
466 return static_cast<int>( 466 return static_cast<int>(
467 ContentManager::InstallNSP(&EmulationSession::GetInstance().System(), 467 ContentManager::InstallNSP(EmulationSession::GetInstance().System(),
468 EmulationSession::GetInstance().System().GetFilesystem().get(), 468 *EmulationSession::GetInstance().System().GetFilesystem(),
469 GetJString(env, j_file), callback)); 469 GetJString(env, j_file), callback));
470} 470}
471 471
@@ -825,7 +825,7 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeUpdate(JNIEnv* env, jobject job
825void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeDLC(JNIEnv* env, jobject jobj, 825void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeDLC(JNIEnv* env, jobject jobj,
826 jstring jprogramId) { 826 jstring jprogramId) {
827 auto program_id = EmulationSession::GetProgramId(env, jprogramId); 827 auto program_id = EmulationSession::GetProgramId(env, jprogramId);
828 ContentManager::RemoveAllDLC(&EmulationSession::GetInstance().System(), program_id); 828 ContentManager::RemoveAllDLC(EmulationSession::GetInstance().System(), program_id);
829} 829}
830 830
831void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeMod(JNIEnv* env, jobject jobj, jstring jprogramId, 831void 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,
835 program_id, GetJString(env, jname)); 835 program_id, GetJString(env, jname));
836} 836}
837 837
838jobject Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEnv* env, jobject jobj, 838jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEnv* env,
839 jobject jcallback) { 839 jobject jobj,
840 jobject jcallback) {
840 auto jlambdaClass = env->GetObjectClass(jcallback); 841 auto jlambdaClass = env->GetObjectClass(jcallback);
841 auto jlambdaInvokeMethod = env->GetMethodID( 842 auto jlambdaInvokeMethod = env->GetMethodID(
842 jlambdaClass, "invoke", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); 843 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
848 849
849 auto& session = EmulationSession::GetInstance(); 850 auto& session = EmulationSession::GetInstance();
850 std::vector<std::string> result = ContentManager::VerifyInstalledContents( 851 std::vector<std::string> result = ContentManager::VerifyInstalledContents(
851 &session.System(), session.GetContentProvider(), callback); 852 session.System(), *session.GetContentProvider(), callback);
852 jobjectArray jresult = 853 jobjectArray jresult =
853 env->NewObjectArray(result.size(), IDCache::GetStringClass(), ToJString(env, "")); 854 env->NewObjectArray(result.size(), IDCache::GetStringClass(), ToJString(env, ""));
854 for (size_t i = 0; i < result.size(); ++i) { 855 for (size_t i = 0; i < result.size(); ++i) {
@@ -869,7 +870,7 @@ jint Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyGameContents(JNIEnv* env, jobje
869 }; 870 };
870 auto& session = EmulationSession::GetInstance(); 871 auto& session = EmulationSession::GetInstance();
871 return static_cast<jint>( 872 return static_cast<jint>(
872 ContentManager::VerifyGameContents(&session.System(), GetJString(env, jpath), callback)); 873 ContentManager::VerifyGameContents(session.System(), GetJString(env, jpath), callback));
873} 874}
874 875
875jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject jobj, 876jstring 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,
918 EmulationSession::GetInstance().GetContentProvider()->ClearAllEntries(); 919 EmulationSession::GetInstance().GetContentProvider()->ClearAllEntries();
919} 920}
920 921
922jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_areKeysPresent(JNIEnv* env, jobject jobj) {
923 auto& system = EmulationSession::GetInstance().System();
924 system.GetFileSystemController().CreateFactories(*system.GetFilesystem());
925 return ContentManager::AreKeysPresent();
926}
927
921} // extern "C" 928} // 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 @@
144 <string name="no_save_data_found">No save data found</string> 144 <string name="no_save_data_found">No save data found</string>
145 <string name="verify_installed_content">Verify installed content</string> 145 <string name="verify_installed_content">Verify installed content</string>
146 <string name="verify_installed_content_description">Checks all installed content for corruption</string> 146 <string name="verify_installed_content_description">Checks all installed content for corruption</string>
147 <string name="keys_missing">Encryption keys are missing</string>
148 <string name="keys_missing_description">Firmware and retail games cannot be decrypted</string>
149 <string name="keys_missing_help">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string>
147 150
148 <!-- Applet launcher strings --> 151 <!-- Applet launcher strings -->
149 <string name="applets">Applet launcher</string> 152 <string name="applets">Applet launcher</string>
diff --git a/src/frontend_common/content_manager.h b/src/frontend_common/content_manager.h
index 0b0fee73e..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
47 47
48/** 48/**
49 * \brief Removes all DLC for a game 49 * \brief Removes all DLC for a game
50 * \param system Raw pointer to the system instance 50 * \param system Reference to the system instance
51 * \param program_id Program ID for the game that will have all of its DLC removed 51 * \param program_id Program ID for the game that will have all of its DLC removed
52 * \return Number of DLC removed 52 * \return Number of DLC removed
53 */ 53 */
54inline size_t RemoveAllDLC(Core::System* system, const u64 program_id) { 54inline size_t RemoveAllDLC(Core::System& system, const u64 program_id) {
55 size_t count{}; 55 size_t count{};
56 const auto& fs_controller = system->GetFileSystemController(); 56 const auto& fs_controller = system.GetFileSystemController();
57 const auto dlc_entries = system->GetContentProvider().ListEntriesFilter( 57 const auto dlc_entries = system.GetContentProvider().ListEntriesFilter(
58 FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); 58 FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
59 std::vector<u64> program_dlc_entries; 59 std::vector<u64> program_dlc_entries;
60 60
@@ -124,15 +124,15 @@ inline bool RemoveMod(const Service::FileSystem::FileSystemController& fs_contro
124 124
125/** 125/**
126 * \brief Installs an NSP 126 * \brief Installs an NSP
127 * \param system Raw pointer to the system instance 127 * \param system Reference to the system instance
128 * \param vfs Raw pointer to the VfsFilesystem instance in Core::System 128 * \param vfs Reference to the VfsFilesystem instance in Core::System
129 * \param filename Path to the NSP file 129 * \param filename Path to the NSP file
130 * \param callback Callback to report the progress of the installation. The first size_t 130 * \param callback Callback to report the progress of the installation. The first size_t
131 * parameter is the total size of the virtual file and the second is the current progress. If you 131 * parameter is the total size of the virtual file and the second is the current progress. If you
132 * return true to the callback, it will cancel the installation as soon as possible. 132 * return true to the callback, it will cancel the installation as soon as possible.
133 * \return [InstallResult] representing how the installation finished 133 * \return [InstallResult] representing how the installation finished
134 */ 134 */
135inline InstallResult InstallNSP(Core::System* system, FileSys::VfsFilesystem* vfs, 135inline InstallResult InstallNSP(Core::System& system, FileSys::VfsFilesystem& vfs,
136 const std::string& filename, 136 const std::string& filename,
137 const std::function<bool(size_t, size_t)>& callback) { 137 const std::function<bool(size_t, size_t)>& callback) {
138 const auto copy = [callback](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest, 138 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
159 }; 159 };
160 160
161 std::shared_ptr<FileSys::NSP> nsp; 161 std::shared_ptr<FileSys::NSP> nsp;
162 FileSys::VirtualFile file = vfs->OpenFile(filename, FileSys::Mode::Read); 162 FileSys::VirtualFile file = vfs.OpenFile(filename, FileSys::Mode::Read);
163 if (boost::to_lower_copy(file->GetName()).ends_with(std::string("nsp"))) { 163 if (boost::to_lower_copy(file->GetName()).ends_with(std::string("nsp"))) {
164 nsp = std::make_shared<FileSys::NSP>(file); 164 nsp = std::make_shared<FileSys::NSP>(file);
165 if (nsp->IsExtractedType()) { 165 if (nsp->IsExtractedType()) {
@@ -173,7 +173,7 @@ inline InstallResult InstallNSP(Core::System* system, FileSys::VfsFilesystem* vf
173 return InstallResult::Failure; 173 return InstallResult::Failure;
174 } 174 }
175 const auto res = 175 const auto res =
176 system->GetFileSystemController().GetUserNANDContents()->InstallEntry(*nsp, true, copy); 176 system.GetFileSystemController().GetUserNANDContents()->InstallEntry(*nsp, true, copy);
177 switch (res) { 177 switch (res) {
178 case FileSys::InstallResult::Success: 178 case FileSys::InstallResult::Success:
179 return InstallResult::Success; 179 return InstallResult::Success;
@@ -188,17 +188,17 @@ inline InstallResult InstallNSP(Core::System* system, FileSys::VfsFilesystem* vf
188 188
189/** 189/**
190 * \brief Installs an NCA 190 * \brief Installs an NCA
191 * \param vfs Raw pointer to the VfsFilesystem instance in Core::System 191 * \param vfs Reference to the VfsFilesystem instance in Core::System
192 * \param filename Path to the NCA file 192 * \param filename Path to the NCA file
193 * \param registered_cache Raw pointer to the registered cache that the NCA will be installed to 193 * \param registered_cache Reference to the registered cache that the NCA will be installed to
194 * \param title_type Type of NCA package to install 194 * \param title_type Type of NCA package to install
195 * \param callback Callback to report the progress of the installation. The first size_t 195 * \param callback Callback to report the progress of the installation. The first size_t
196 * parameter is the total size of the virtual file and the second is the current progress. If you 196 * parameter is the total size of the virtual file and the second is the current progress. If you
197 * return true to the callback, it will cancel the installation as soon as possible. 197 * return true to the callback, it will cancel the installation as soon as possible.
198 * \return [InstallResult] representing how the installation finished 198 * \return [InstallResult] representing how the installation finished
199 */ 199 */
200inline InstallResult InstallNCA(FileSys::VfsFilesystem* vfs, const std::string& filename, 200inline InstallResult InstallNCA(FileSys::VfsFilesystem& vfs, const std::string& filename,
201 FileSys::RegisteredCache* registered_cache, 201 FileSys::RegisteredCache& registered_cache,
202 const FileSys::TitleType title_type, 202 const FileSys::TitleType title_type,
203 const std::function<bool(size_t, size_t)>& callback) { 203 const std::function<bool(size_t, size_t)>& callback) {
204 const auto copy = [callback](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest, 204 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&
224 return true; 224 return true;
225 }; 225 };
226 226
227 const auto nca = std::make_shared<FileSys::NCA>(vfs->OpenFile(filename, FileSys::Mode::Read)); 227 const auto nca = std::make_shared<FileSys::NCA>(vfs.OpenFile(filename, FileSys::Mode::Read));
228 const auto id = nca->GetStatus(); 228 const auto id = nca->GetStatus();
229 229
230 // Game updates necessary are missing base RomFS 230 // Game updates necessary are missing base RomFS
@@ -233,7 +233,7 @@ inline InstallResult InstallNCA(FileSys::VfsFilesystem* vfs, const std::string&
233 return InstallResult::Failure; 233 return InstallResult::Failure;
234 } 234 }
235 235
236 const auto res = registered_cache->InstallEntry(*nca, title_type, true, copy); 236 const auto res = registered_cache.InstallEntry(*nca, title_type, true, copy);
237 if (res == FileSys::InstallResult::Success) { 237 if (res == FileSys::InstallResult::Success) {
238 return InstallResult::Success; 238 return InstallResult::Success;
239 } else if (res == FileSys::InstallResult::OverwriteExisting) { 239 } else if (res == FileSys::InstallResult::OverwriteExisting) {
@@ -245,19 +245,19 @@ inline InstallResult InstallNCA(FileSys::VfsFilesystem* vfs, const std::string&
245 245
246/** 246/**
247 * \brief Verifies the installed contents for a given ManualContentProvider 247 * \brief Verifies the installed contents for a given ManualContentProvider
248 * \param system Raw pointer to the system instance 248 * \param system Reference to the system instance
249 * \param provider Raw pointer to the content provider that's tracking indexed games 249 * \param provider Reference to the content provider that's tracking indexed games
250 * \param callback Callback to report the progress of the installation. The first size_t 250 * \param callback Callback to report the progress of the installation. The first size_t
251 * parameter is the total size of the installed contents and the second is the current progress. If 251 * parameter is the total size of the installed contents and the second is the current progress. If
252 * you return true to the callback, it will cancel the installation as soon as possible. 252 * you return true to the callback, it will cancel the installation as soon as possible.
253 * \return A list of entries that failed to install. Returns an empty vector if successful. 253 * \return A list of entries that failed to install. Returns an empty vector if successful.
254 */ 254 */
255inline std::vector<std::string> VerifyInstalledContents( 255inline std::vector<std::string> VerifyInstalledContents(
256 Core::System* system, FileSys::ManualContentProvider* provider, 256 Core::System& system, FileSys::ManualContentProvider& provider,
257 const std::function<bool(size_t, size_t)>& callback) { 257 const std::function<bool(size_t, size_t)>& callback) {
258 // Get content registries. 258 // Get content registries.
259 auto bis_contents = system->GetFileSystemController().GetSystemNANDContents(); 259 auto bis_contents = system.GetFileSystemController().GetSystemNANDContents();
260 auto user_contents = system->GetFileSystemController().GetUserNANDContents(); 260 auto user_contents = system.GetFileSystemController().GetUserNANDContents();
261 261
262 std::vector<FileSys::RegisteredCache*> content_providers; 262 std::vector<FileSys::RegisteredCache*> content_providers;
263 if (bis_contents) { 263 if (bis_contents) {
@@ -309,11 +309,11 @@ inline std::vector<std::string> VerifyInstalledContents(
309 const auto title_id = nca.GetTitleId(); 309 const auto title_id = nca.GetTitleId();
310 std::string title_name = "unknown"; 310 std::string title_name = "unknown";
311 311
312 const auto control = provider->GetEntry(FileSys::GetBaseTitleID(title_id), 312 const auto control = provider.GetEntry(FileSys::GetBaseTitleID(title_id),
313 FileSys::ContentRecordType::Control); 313 FileSys::ContentRecordType::Control);
314 if (control && control->GetStatus() == Loader::ResultStatus::Success) { 314 if (control && control->GetStatus() == Loader::ResultStatus::Success) {
315 const FileSys::PatchManager pm{title_id, system->GetFileSystemController(), 315 const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
316 *provider}; 316 provider};
317 const auto [nacp, logo] = pm.ParseControlNCA(*control); 317 const auto [nacp, logo] = pm.ParseControlNCA(*control);
318 if (nacp) { 318 if (nacp) {
319 title_name = nacp->GetApplicationName(); 319 title_name = nacp->GetApplicationName();
@@ -335,7 +335,7 @@ inline std::vector<std::string> VerifyInstalledContents(
335 335
336/** 336/**
337 * \brief Verifies the contents of a given game 337 * \brief Verifies the contents of a given game
338 * \param system Raw pointer to the system instance 338 * \param system Reference to the system instance
339 * \param game_path Patch to the game file 339 * \param game_path Patch to the game file
340 * \param callback Callback to report the progress of the installation. The first size_t 340 * \param callback Callback to report the progress of the installation. The first size_t
341 * parameter is the total size of the installed contents and the second is the current progress. If 341 * 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(
343 * \return GameVerificationResult representing how the verification process finished 343 * \return GameVerificationResult representing how the verification process finished
344 */ 344 */
345inline GameVerificationResult VerifyGameContents( 345inline GameVerificationResult VerifyGameContents(
346 Core::System* system, const std::string& game_path, 346 Core::System& system, const std::string& game_path,
347 const std::function<bool(size_t, size_t)>& callback) { 347 const std::function<bool(size_t, size_t)>& callback) {
348 const auto loader = Loader::GetLoader( 348 const auto loader =
349 *system, system->GetFilesystem()->OpenFile(game_path, FileSys::Mode::Read)); 349 Loader::GetLoader(system, system.GetFilesystem()->OpenFile(game_path, FileSys::Mode::Read));
350 if (loader == nullptr) { 350 if (loader == nullptr) {
351 return GameVerificationResult::NotImplemented; 351 return GameVerificationResult::NotImplemented;
352 } 352 }
@@ -368,4 +368,11 @@ inline GameVerificationResult VerifyGameContents(
368 return GameVerificationResult::Success; 368 return GameVerificationResult::Success;
369} 369}
370 370
371/**
372 * Checks if the keys required for decrypting firmware and games are available
373 */
374inline bool AreKeysPresent() {
375 return !Core::Crypto::KeyManager::Instance().BaseDeriveNecessary();
376}
377
371} // namespace ContentManager 378} // namespace ContentManager
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index d8b0beadf..e14410f7d 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -423,7 +423,7 @@ GMainWindow::GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulk
423 RemoveCachedContents(); 423 RemoveCachedContents();
424 424
425 // Gen keys if necessary 425 // Gen keys if necessary
426 OnReinitializeKeys(ReinitializeKeyBehavior::NoWarning); 426 OnCheckFirmwareDecryption();
427 427
428 game_list->LoadCompatibilityList(); 428 game_list->LoadCompatibilityList();
429 game_list->PopulateAsync(UISettings::values.game_dirs); 429 game_list->PopulateAsync(UISettings::values.game_dirs);
@@ -1574,8 +1574,6 @@ void GMainWindow::ConnectMenuEvents() {
1574 connect(multiplayer_state, &MultiplayerState::SaveConfig, this, &GMainWindow::OnSaveConfig); 1574 connect(multiplayer_state, &MultiplayerState::SaveConfig, this, &GMainWindow::OnSaveConfig);
1575 1575
1576 // Tools 1576 // Tools
1577 connect_menu(ui->action_Rederive, std::bind(&GMainWindow::OnReinitializeKeys, this,
1578 ReinitializeKeyBehavior::Warning));
1579 connect_menu(ui->action_Load_Album, &GMainWindow::OnAlbum); 1577 connect_menu(ui->action_Load_Album, &GMainWindow::OnAlbum);
1580 connect_menu(ui->action_Load_Cabinet_Nickname_Owner, 1578 connect_menu(ui->action_Load_Cabinet_Nickname_Owner,
1581 [this]() { OnCabinet(Service::NFP::CabinetMode::StartNicknameAndOwnerSettings); }); 1579 [this]() { OnCabinet(Service::NFP::CabinetMode::StartNicknameAndOwnerSettings); });
@@ -2501,7 +2499,7 @@ void GMainWindow::RemoveUpdateContent(u64 program_id, InstalledEntryType type) {
2501} 2499}
2502 2500
2503void GMainWindow::RemoveAddOnContent(u64 program_id, InstalledEntryType type) { 2501void GMainWindow::RemoveAddOnContent(u64 program_id, InstalledEntryType type) {
2504 const size_t count = ContentManager::RemoveAllDLC(system.get(), program_id); 2502 const size_t count = ContentManager::RemoveAllDLC(*system, program_id);
2505 if (count == 0) { 2503 if (count == 0) {
2506 QMessageBox::warning(this, GetGameListErrorRemoving(type), 2504 QMessageBox::warning(this, GetGameListErrorRemoving(type),
2507 tr("There are no DLC installed for this title.")); 2505 tr("There are no DLC installed for this title."));
@@ -2798,8 +2796,7 @@ void GMainWindow::OnGameListVerifyIntegrity(const std::string& game_path) {
2798 return progress.wasCanceled(); 2796 return progress.wasCanceled();
2799 }; 2797 };
2800 2798
2801 const auto result = 2799 const auto result = ContentManager::VerifyGameContents(*system, game_path, QtProgressCallback);
2802 ContentManager::VerifyGameContents(system.get(), game_path, QtProgressCallback);
2803 progress.close(); 2800 progress.close();
2804 switch (result) { 2801 switch (result) {
2805 case ContentManager::GameVerificationResult::Success: 2802 case ContentManager::GameVerificationResult::Success:
@@ -3268,7 +3265,7 @@ void GMainWindow::OnMenuInstallToNAND() {
3268 return false; 3265 return false;
3269 }; 3266 };
3270 future = QtConcurrent::run([this, &file, progress_callback] { 3267 future = QtConcurrent::run([this, &file, progress_callback] {
3271 return ContentManager::InstallNSP(system.get(), vfs.get(), file.toStdString(), 3268 return ContentManager::InstallNSP(*system, *vfs, file.toStdString(),
3272 progress_callback); 3269 progress_callback);
3273 }); 3270 });
3274 3271
@@ -3371,7 +3368,7 @@ ContentManager::InstallResult GMainWindow::InstallNCA(const QString& filename) {
3371 } 3368 }
3372 return false; 3369 return false;
3373 }; 3370 };
3374 return ContentManager::InstallNCA(vfs.get(), filename.toStdString(), registered_cache, 3371 return ContentManager::InstallNCA(*vfs, filename.toStdString(), *registered_cache,
3375 static_cast<FileSys::TitleType>(index), progress_callback); 3372 static_cast<FileSys::TitleType>(index), progress_callback);
3376} 3373}
3377 3374
@@ -4121,7 +4118,7 @@ void GMainWindow::OnVerifyInstalledContents() {
4121 }; 4118 };
4122 4119
4123 const std::vector<std::string> result = 4120 const std::vector<std::string> result =
4124 ContentManager::VerifyInstalledContents(system.get(), provider.get(), QtProgressCallback); 4121 ContentManager::VerifyInstalledContents(*system, *provider, QtProgressCallback);
4125 progress.close(); 4122 progress.close();
4126 4123
4127 if (result.empty()) { 4124 if (result.empty()) {
@@ -4551,122 +4548,20 @@ void GMainWindow::OnMouseActivity() {
4551 } 4548 }
4552} 4549}
4553 4550
4554void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { 4551void GMainWindow::OnCheckFirmwareDecryption() {
4555 if (behavior == ReinitializeKeyBehavior::Warning) {
4556 const auto res = QMessageBox::information(
4557 this, tr("Confirm Key Rederivation"),
4558 tr("You are about to force rederive all of your keys. \nIf you do not know what "
4559 "this "
4560 "means or what you are doing, \nthis is a potentially destructive action. "
4561 "\nPlease "
4562 "make sure this is what you want \nand optionally make backups.\n\nThis will "
4563 "delete "
4564 "your autogenerated key files and re-run the key derivation module."),
4565 QMessageBox::StandardButtons{QMessageBox::Ok, QMessageBox::Cancel});
4566
4567 if (res == QMessageBox::Cancel)
4568 return;
4569
4570 const auto keys_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::KeysDir);
4571
4572 Common::FS::RemoveFile(keys_dir / "prod.keys_autogenerated");
4573 Common::FS::RemoveFile(keys_dir / "console.keys_autogenerated");
4574 Common::FS::RemoveFile(keys_dir / "title.keys_autogenerated");
4575 }
4576
4577 Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
4578 bool all_keys_present{true};
4579
4580 if (keys.BaseDeriveNecessary()) {
4581 Core::Crypto::PartitionDataManager pdm{vfs->OpenDirectory("", FileSys::Mode::Read)};
4582
4583 const auto function = [this, &keys, &pdm] {
4584 keys.PopulateFromPartitionData(pdm);
4585
4586 system->GetFileSystemController().CreateFactories(*vfs);
4587 keys.DeriveETicket(pdm, system->GetContentProvider());
4588 };
4589
4590 QString errors;
4591 if (!pdm.HasFuses()) {
4592 errors += tr("Missing fuses");
4593 }
4594 if (!pdm.HasBoot0()) {
4595 errors += tr(" - Missing BOOT0");
4596 }
4597 if (!pdm.HasPackage2()) {
4598 errors += tr(" - Missing BCPKG2-1-Normal-Main");
4599 }
4600 if (!pdm.HasProdInfo()) {
4601 errors += tr(" - Missing PRODINFO");
4602 }
4603 if (!errors.isEmpty()) {
4604 all_keys_present = false;
4605 QMessageBox::warning(
4606 this, tr("Derivation Components Missing"),
4607 tr("Encryption keys are missing. "
4608 "<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu "
4609 "quickstart guide</a> to get all your keys, firmware and "
4610 "games.<br><br><small>(%1)</small>")
4611 .arg(errors));
4612 }
4613
4614 QProgressDialog prog(this);
4615 prog.setRange(0, 0);
4616 prog.setLabelText(tr("Deriving keys...\nThis may take up to a minute depending \non your "
4617 "system's performance."));
4618 prog.setWindowTitle(tr("Deriving Keys"));
4619
4620 prog.show();
4621
4622 auto future = QtConcurrent::run(function);
4623 while (!future.isFinished()) {
4624 QCoreApplication::processEvents();
4625 }
4626
4627 prog.close();
4628 }
4629
4630 system->GetFileSystemController().CreateFactories(*vfs); 4552 system->GetFileSystemController().CreateFactories(*vfs);
4631 4553 if (!ContentManager::AreKeysPresent()) {
4632 if (all_keys_present && !this->CheckSystemArchiveDecryption()) {
4633 LOG_WARNING(Frontend, "Mii model decryption failed");
4634 QMessageBox::warning( 4554 QMessageBox::warning(
4635 this, tr("System Archive Decryption Failed"), 4555 this, tr("Derivation Components Missing"),
4636 tr("Encryption keys failed to decrypt firmware. " 4556 tr("Encryption keys are missing. "
4637 "<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu " 4557 "<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu "
4638 "quickstart guide</a> to get all your keys, firmware and " 4558 "quickstart guide</a> to get all your keys, firmware and "
4639 "games.")); 4559 "games."));
4640 } 4560 }
4641
4642 SetFirmwareVersion(); 4561 SetFirmwareVersion();
4643
4644 if (behavior == ReinitializeKeyBehavior::Warning) {
4645 game_list->PopulateAsync(UISettings::values.game_dirs);
4646 }
4647
4648 UpdateMenuState(); 4562 UpdateMenuState();
4649} 4563}
4650 4564
4651bool GMainWindow::CheckSystemArchiveDecryption() {
4652 constexpr u64 MiiModelId = 0x0100000000000802;
4653
4654 auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
4655 if (!bis_system) {
4656 // Not having system BIS files is not an error.
4657 return true;
4658 }
4659
4660 auto mii_nca = bis_system->GetEntry(MiiModelId, FileSys::ContentRecordType::Data);
4661 if (!mii_nca) {
4662 // Not having the Mii model is not an error.
4663 return true;
4664 }
4665
4666 // Return whether we are able to decrypt the RomFS of the Mii model.
4667 return mii_nca->GetRomFS().get() != nullptr;
4668}
4669
4670bool GMainWindow::CheckFirmwarePresence() { 4565bool GMainWindow::CheckFirmwarePresence() {
4671 constexpr u64 MiiEditId = static_cast<u64>(Service::AM::Applets::AppletProgramId::MiiEdit); 4566 constexpr u64 MiiEditId = static_cast<u64>(Service::AM::Applets::AppletProgramId::MiiEdit);
4672 4567
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 {
125 SDMC, 125 SDMC,
126}; 126};
127 127
128enum class ReinitializeKeyBehavior {
129 NoWarning,
130 Warning,
131};
132
133namespace VkDeviceInfo { 128namespace VkDeviceInfo {
134class Record; 129class Record;
135} 130}
@@ -400,7 +395,7 @@ private slots:
400 void OnMiiEdit(); 395 void OnMiiEdit();
401 void OnOpenControllerMenu(); 396 void OnOpenControllerMenu();
402 void OnCaptureScreenshot(); 397 void OnCaptureScreenshot();
403 void OnReinitializeKeys(ReinitializeKeyBehavior behavior); 398 void OnCheckFirmwareDecryption();
404 void OnLanguageChanged(const QString& locale); 399 void OnLanguageChanged(const QString& locale);
405 void OnMouseActivity(); 400 void OnMouseActivity();
406 bool OnShutdownBegin(); 401 bool OnShutdownBegin();
@@ -441,7 +436,6 @@ private:
441 void LoadTranslation(); 436 void LoadTranslation();
442 void OpenPerGameConfiguration(u64 title_id, const std::string& file_name); 437 void OpenPerGameConfiguration(u64 title_id, const std::string& file_name);
443 bool CheckDarkMode(); 438 bool CheckDarkMode();
444 bool CheckSystemArchiveDecryption();
445 bool CheckFirmwarePresence(); 439 bool CheckFirmwarePresence();
446 void SetFirmwareVersion(); 440 void SetFirmwareVersion();
447 void ConfigureFilesystemProvider(const std::string& filepath); 441 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 @@
224 <string>&amp;Stop</string> 224 <string>&amp;Stop</string>
225 </property> 225 </property>
226 </action> 226 </action>
227 <action name="action_Rederive">
228 <property name="text">
229 <string>&amp;Reinitialize keys...</string>
230 </property>
231 </action>
232 <action name="action_Verify_installed_contents"> 227 <action name="action_Verify_installed_contents">
233 <property name="text"> 228 <property name="text">
234 <string>&amp;Verify Installed Contents</string> 229 <string>&amp;Verify Installed Contents</string>