diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt | 5 | ||||
| -rw-r--r-- | src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/MessageDialogFragment.kt | 32 | ||||
| -rw-r--r-- | src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt | 6 | ||||
| -rw-r--r-- | src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeViewModel.kt | 7 | ||||
| -rw-r--r-- | src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt | 42 | ||||
| -rw-r--r-- | src/android/app/src/main/jni/native.cpp | 21 | ||||
| -rw-r--r-- | src/android/app/src/main/res/values/strings.xml | 3 | ||||
| -rw-r--r-- | src/frontend_common/content_manager.h | 63 | ||||
| -rw-r--r-- | src/yuzu/main.cpp | 125 | ||||
| -rw-r--r-- | src/yuzu/main.h | 8 | ||||
| -rw-r--r-- | src/yuzu/main.ui | 5 |
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 | |||
| 31 | import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback | 31 | import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback |
| 32 | import com.google.android.material.transition.MaterialFadeThrough | 32 | import com.google.android.material.transition.MaterialFadeThrough |
| 33 | import kotlinx.coroutines.launch | 33 | import kotlinx.coroutines.launch |
| 34 | import org.yuzu.yuzu_emu.NativeLibrary | ||
| 34 | import java.io.File | 35 | import java.io.File |
| 35 | import org.yuzu.yuzu_emu.R | 36 | import org.yuzu.yuzu_emu.R |
| 36 | import org.yuzu.yuzu_emu.YuzuApplication | 37 | import 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 | |||
| 825 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeDLC(JNIEnv* env, jobject jobj, | 825 | void 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 | ||
| 831 | void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeMod(JNIEnv* env, jobject jobj, jstring jprogramId, | 831 | 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, | |||
| 835 | program_id, GetJString(env, jname)); | 835 | program_id, GetJString(env, jname)); |
| 836 | } | 836 | } |
| 837 | 837 | ||
| 838 | jobject Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEnv* env, jobject jobj, | 838 | jobjectArray 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 | ||
| 875 | jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject jobj, | 876 | 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, | |||
| 918 | EmulationSession::GetInstance().GetContentProvider()->ClearAllEntries(); | 919 | EmulationSession::GetInstance().GetContentProvider()->ClearAllEntries(); |
| 919 | } | 920 | } |
| 920 | 921 | ||
| 922 | jboolean 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 | */ |
| 54 | inline size_t RemoveAllDLC(Core::System* system, const u64 program_id) { | 54 | inline 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 | */ |
| 135 | inline InstallResult InstallNSP(Core::System* system, FileSys::VfsFilesystem* vfs, | 135 | inline 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 | */ |
| 200 | inline InstallResult InstallNCA(FileSys::VfsFilesystem* vfs, const std::string& filename, | 200 | inline 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 | */ |
| 255 | inline std::vector<std::string> VerifyInstalledContents( | 255 | inline 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 | */ |
| 345 | inline GameVerificationResult VerifyGameContents( | 345 | inline 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 | */ | ||
| 374 | inline 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 | ||
| 2503 | void GMainWindow::RemoveAddOnContent(u64 program_id, InstalledEntryType type) { | 2501 | void 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 | ||
| 4554 | void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { | 4551 | void 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 | ||
| 4651 | bool 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 | |||
| 4670 | bool GMainWindow::CheckFirmwarePresence() { | 4565 | bool 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 | ||
| 128 | enum class ReinitializeKeyBehavior { | ||
| 129 | NoWarning, | ||
| 130 | Warning, | ||
| 131 | }; | ||
| 132 | |||
| 133 | namespace VkDeviceInfo { | 128 | namespace VkDeviceInfo { |
| 134 | class Record; | 129 | class 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>&Stop</string> | 224 | <string>&Stop</string> |
| 225 | </property> | 225 | </property> |
| 226 | </action> | 226 | </action> |
| 227 | <action name="action_Rederive"> | ||
| 228 | <property name="text"> | ||
| 229 | <string>&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>&Verify Installed Contents</string> | 229 | <string>&Verify Installed Contents</string> |