Merge pull request #12777 from t895/firmware-warning
android: Add key warning
This commit is contained in:
		@@ -619,6 +619,11 @@ object NativeLibrary {
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    external fun clearFilesystemProvider()
 | 
					    external fun clearFilesystemProvider()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Checks if all necessary keys are present for decryption
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    external fun areKeysPresent(): Boolean
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Button type for use in onTouchEvent
 | 
					     * Button type for use in onTouchEvent
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -26,9 +26,15 @@ class MessageDialogFragment : DialogFragment() {
 | 
				
			|||||||
        val descriptionId = requireArguments().getInt(DESCRIPTION_ID)
 | 
					        val descriptionId = requireArguments().getInt(DESCRIPTION_ID)
 | 
				
			||||||
        val descriptionString = requireArguments().getString(DESCRIPTION_STRING)!!
 | 
					        val descriptionString = requireArguments().getString(DESCRIPTION_STRING)!!
 | 
				
			||||||
        val helpLinkId = requireArguments().getInt(HELP_LINK)
 | 
					        val helpLinkId = requireArguments().getInt(HELP_LINK)
 | 
				
			||||||
 | 
					        val dismissible = requireArguments().getBoolean(DISMISSIBLE)
 | 
				
			||||||
 | 
					        val clearPositiveAction = requireArguments().getBoolean(CLEAR_POSITIVE_ACTION)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        val builder = MaterialAlertDialogBuilder(requireContext())
 | 
					        val builder = MaterialAlertDialogBuilder(requireContext())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (clearPositiveAction) {
 | 
				
			||||||
 | 
					            messageDialogViewModel.positiveAction = null
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (messageDialogViewModel.positiveAction == null) {
 | 
					        if (messageDialogViewModel.positiveAction == null) {
 | 
				
			||||||
            builder.setPositiveButton(R.string.close, null)
 | 
					            builder.setPositiveButton(R.string.close, null)
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
@@ -51,6 +57,8 @@ class MessageDialogFragment : DialogFragment() {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        isCancelable = dismissible
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return builder.show()
 | 
					        return builder.show()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -67,6 +75,8 @@ class MessageDialogFragment : DialogFragment() {
 | 
				
			|||||||
        private const val DESCRIPTION_ID = "DescriptionId"
 | 
					        private const val DESCRIPTION_ID = "DescriptionId"
 | 
				
			||||||
        private const val DESCRIPTION_STRING = "DescriptionString"
 | 
					        private const val DESCRIPTION_STRING = "DescriptionString"
 | 
				
			||||||
        private const val HELP_LINK = "Link"
 | 
					        private const val HELP_LINK = "Link"
 | 
				
			||||||
 | 
					        private const val DISMISSIBLE = "Dismissible"
 | 
				
			||||||
 | 
					        private const val CLEAR_POSITIVE_ACTION = "ClearPositiveAction"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        fun newInstance(
 | 
					        fun newInstance(
 | 
				
			||||||
            activity: FragmentActivity? = null,
 | 
					            activity: FragmentActivity? = null,
 | 
				
			||||||
@@ -75,22 +85,28 @@ class MessageDialogFragment : DialogFragment() {
 | 
				
			|||||||
            descriptionId: Int = 0,
 | 
					            descriptionId: Int = 0,
 | 
				
			||||||
            descriptionString: String = "",
 | 
					            descriptionString: String = "",
 | 
				
			||||||
            helpLinkId: Int = 0,
 | 
					            helpLinkId: Int = 0,
 | 
				
			||||||
 | 
					            dismissible: Boolean = true,
 | 
				
			||||||
            positiveAction: (() -> Unit)? = null
 | 
					            positiveAction: (() -> Unit)? = null
 | 
				
			||||||
        ): MessageDialogFragment {
 | 
					        ): MessageDialogFragment {
 | 
				
			||||||
            val dialog = MessageDialogFragment()
 | 
					            var clearPositiveAction = false
 | 
				
			||||||
            val bundle = Bundle()
 | 
					 | 
				
			||||||
            bundle.apply {
 | 
					 | 
				
			||||||
                putInt(TITLE_ID, titleId)
 | 
					 | 
				
			||||||
                putString(TITLE_STRING, titleString)
 | 
					 | 
				
			||||||
                putInt(DESCRIPTION_ID, descriptionId)
 | 
					 | 
				
			||||||
                putString(DESCRIPTION_STRING, descriptionString)
 | 
					 | 
				
			||||||
                putInt(HELP_LINK, helpLinkId)
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            if (activity != null) {
 | 
					            if (activity != null) {
 | 
				
			||||||
                ViewModelProvider(activity)[MessageDialogViewModel::class.java].apply {
 | 
					                ViewModelProvider(activity)[MessageDialogViewModel::class.java].apply {
 | 
				
			||||||
                    clear()
 | 
					                    clear()
 | 
				
			||||||
                    this.positiveAction = positiveAction
 | 
					                    this.positiveAction = positiveAction
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                clearPositiveAction = true
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            val dialog = MessageDialogFragment()
 | 
				
			||||||
 | 
					            val bundle = Bundle().apply {
 | 
				
			||||||
 | 
					                putInt(TITLE_ID, titleId)
 | 
				
			||||||
 | 
					                putString(TITLE_STRING, titleString)
 | 
				
			||||||
 | 
					                putInt(DESCRIPTION_ID, descriptionId)
 | 
				
			||||||
 | 
					                putString(DESCRIPTION_STRING, descriptionString)
 | 
				
			||||||
 | 
					                putInt(HELP_LINK, helpLinkId)
 | 
				
			||||||
 | 
					                putBoolean(DISMISSIBLE, dismissible)
 | 
				
			||||||
 | 
					                putBoolean(CLEAR_POSITIVE_ACTION, clearPositiveAction)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            dialog.arguments = bundle
 | 
					            dialog.arguments = bundle
 | 
				
			||||||
            return dialog
 | 
					            return dialog
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,6 +31,7 @@ import androidx.preference.PreferenceManager
 | 
				
			|||||||
import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
 | 
					import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
 | 
				
			||||||
import com.google.android.material.transition.MaterialFadeThrough
 | 
					import com.google.android.material.transition.MaterialFadeThrough
 | 
				
			||||||
import kotlinx.coroutines.launch
 | 
					import kotlinx.coroutines.launch
 | 
				
			||||||
 | 
					import org.yuzu.yuzu_emu.NativeLibrary
 | 
				
			||||||
import java.io.File
 | 
					import java.io.File
 | 
				
			||||||
import org.yuzu.yuzu_emu.R
 | 
					import org.yuzu.yuzu_emu.R
 | 
				
			||||||
import org.yuzu.yuzu_emu.YuzuApplication
 | 
					import org.yuzu.yuzu_emu.YuzuApplication
 | 
				
			||||||
@@ -162,7 +163,7 @@ class SetupFragment : Fragment() {
 | 
				
			|||||||
                    R.string.install_prod_keys_warning_help,
 | 
					                    R.string.install_prod_keys_warning_help,
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
                        val file = File(DirectoryInitialization.userDirectory + "/keys/prod.keys")
 | 
					                        val file = File(DirectoryInitialization.userDirectory + "/keys/prod.keys")
 | 
				
			||||||
                        if (file.exists()) {
 | 
					                        if (file.exists() && NativeLibrary.areKeysPresent()) {
 | 
				
			||||||
                            StepState.COMPLETE
 | 
					                            StepState.COMPLETE
 | 
				
			||||||
                        } else {
 | 
					                        } else {
 | 
				
			||||||
                            StepState.INCOMPLETE
 | 
					                            StepState.INCOMPLETE
 | 
				
			||||||
@@ -347,7 +348,8 @@ class SetupFragment : Fragment() {
 | 
				
			|||||||
    val getProdKey =
 | 
					    val getProdKey =
 | 
				
			||||||
        registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
 | 
					        registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
 | 
				
			||||||
            if (result != null) {
 | 
					            if (result != null) {
 | 
				
			||||||
                if (mainActivity.processKey(result)) {
 | 
					                mainActivity.processKey(result)
 | 
				
			||||||
 | 
					                if (NativeLibrary.areKeysPresent()) {
 | 
				
			||||||
                    keyCallback.onStepCompleted()
 | 
					                    keyCallback.onStepCompleted()
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,6 +31,9 @@ class HomeViewModel : ViewModel() {
 | 
				
			|||||||
    private val _reloadPropertiesList = MutableStateFlow(false)
 | 
					    private val _reloadPropertiesList = MutableStateFlow(false)
 | 
				
			||||||
    val reloadPropertiesList get() = _reloadPropertiesList.asStateFlow()
 | 
					    val reloadPropertiesList get() = _reloadPropertiesList.asStateFlow()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private val _checkKeys = MutableStateFlow(false)
 | 
				
			||||||
 | 
					    val checkKeys = _checkKeys.asStateFlow()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var navigatedToSetup = false
 | 
					    var navigatedToSetup = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fun setNavigationVisibility(visible: Boolean, animated: Boolean) {
 | 
					    fun setNavigationVisibility(visible: Boolean, animated: Boolean) {
 | 
				
			||||||
@@ -66,4 +69,8 @@ class HomeViewModel : ViewModel() {
 | 
				
			|||||||
    fun reloadPropertiesList(reload: Boolean) {
 | 
					    fun reloadPropertiesList(reload: Boolean) {
 | 
				
			||||||
        _reloadPropertiesList.value = reload
 | 
					        _reloadPropertiesList.value = reload
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fun setCheckKeys(value: Boolean) {
 | 
				
			||||||
 | 
					        _checkKeys.value = value
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -64,6 +64,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    override var themeId: Int = 0
 | 
					    override var themeId: Int = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private val CHECKED_DECRYPTION = "CheckedDecryption"
 | 
				
			||||||
 | 
					    private var checkedDecryption = false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override fun onCreate(savedInstanceState: Bundle?) {
 | 
					    override fun onCreate(savedInstanceState: Bundle?) {
 | 
				
			||||||
        val splashScreen = installSplashScreen()
 | 
					        val splashScreen = installSplashScreen()
 | 
				
			||||||
        splashScreen.setKeepOnScreenCondition { !DirectoryInitialization.areDirectoriesReady }
 | 
					        splashScreen.setKeepOnScreenCondition { !DirectoryInitialization.areDirectoriesReady }
 | 
				
			||||||
@@ -75,6 +78,18 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
 | 
				
			|||||||
        binding = ActivityMainBinding.inflate(layoutInflater)
 | 
					        binding = ActivityMainBinding.inflate(layoutInflater)
 | 
				
			||||||
        setContentView(binding.root)
 | 
					        setContentView(binding.root)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (savedInstanceState != null) {
 | 
				
			||||||
 | 
					            checkedDecryption = savedInstanceState.getBoolean(CHECKED_DECRYPTION)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (!checkedDecryption) {
 | 
				
			||||||
 | 
					            val firstTimeSetup = PreferenceManager.getDefaultSharedPreferences(applicationContext)
 | 
				
			||||||
 | 
					                .getBoolean(Settings.PREF_FIRST_APP_LAUNCH, true)
 | 
				
			||||||
 | 
					            if (!firstTimeSetup) {
 | 
				
			||||||
 | 
					                checkKeys()
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            checkedDecryption = true
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        WindowCompat.setDecorFitsSystemWindows(window, false)
 | 
					        WindowCompat.setDecorFitsSystemWindows(window, false)
 | 
				
			||||||
        window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING)
 | 
					        window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -150,6 +165,16 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
 | 
				
			|||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            launch {
 | 
				
			||||||
 | 
					                repeatOnLifecycle(Lifecycle.State.CREATED) {
 | 
				
			||||||
 | 
					                    homeViewModel.checkKeys.collect {
 | 
				
			||||||
 | 
					                        if (it) {
 | 
				
			||||||
 | 
					                            checkKeys()
 | 
				
			||||||
 | 
					                            homeViewModel.setCheckKeys(false)
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Dismiss previous notifications (should not happen unless a crash occurred)
 | 
					        // Dismiss previous notifications (should not happen unless a crash occurred)
 | 
				
			||||||
@@ -158,6 +183,21 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
 | 
				
			|||||||
        setInsets()
 | 
					        setInsets()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private fun checkKeys() {
 | 
				
			||||||
 | 
					        if (!NativeLibrary.areKeysPresent()) {
 | 
				
			||||||
 | 
					            MessageDialogFragment.newInstance(
 | 
				
			||||||
 | 
					                titleId = R.string.keys_missing,
 | 
				
			||||||
 | 
					                descriptionId = R.string.keys_missing_description,
 | 
				
			||||||
 | 
					                helpLinkId = R.string.keys_missing_help
 | 
				
			||||||
 | 
					            ).show(supportFragmentManager, MessageDialogFragment.TAG)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    override fun onSaveInstanceState(outState: Bundle) {
 | 
				
			||||||
 | 
					        super.onSaveInstanceState(outState)
 | 
				
			||||||
 | 
					        outState.putBoolean(CHECKED_DECRYPTION, checkedDecryption)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fun finishSetup(navController: NavController) {
 | 
					    fun finishSetup(navController: NavController) {
 | 
				
			||||||
        navController.navigate(R.id.action_firstTimeSetupFragment_to_gamesFragment)
 | 
					        navController.navigate(R.id.action_firstTimeSetupFragment_to_gamesFragment)
 | 
				
			||||||
        (binding.navigationView as NavigationBarView).setupWithNavController(navController)
 | 
					        (binding.navigationView as NavigationBarView).setupWithNavController(navController)
 | 
				
			||||||
@@ -349,6 +389,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
 | 
				
			|||||||
                    R.string.install_keys_success,
 | 
					                    R.string.install_keys_success,
 | 
				
			||||||
                    Toast.LENGTH_SHORT
 | 
					                    Toast.LENGTH_SHORT
 | 
				
			||||||
                ).show()
 | 
					                ).show()
 | 
				
			||||||
 | 
					                homeViewModel.setCheckKeys(true)
 | 
				
			||||||
                gamesViewModel.reloadGames(true)
 | 
					                gamesViewModel.reloadGames(true)
 | 
				
			||||||
                return true
 | 
					                return true
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
@@ -399,6 +440,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
 | 
				
			|||||||
                        firmwarePath.deleteRecursively()
 | 
					                        firmwarePath.deleteRecursively()
 | 
				
			||||||
                        cacheFirmwareDir.copyRecursively(firmwarePath, true)
 | 
					                        cacheFirmwareDir.copyRecursively(firmwarePath, true)
 | 
				
			||||||
                        NativeLibrary.initializeSystem(true)
 | 
					                        NativeLibrary.initializeSystem(true)
 | 
				
			||||||
 | 
					                        homeViewModel.setCheckKeys(true)
 | 
				
			||||||
                        getString(R.string.save_file_imported_success)
 | 
					                        getString(R.string.save_file_imported_success)
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                } catch (e: Exception) {
 | 
					                } catch (e: Exception) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -464,8 +464,8 @@ int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject
 | 
				
			|||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return static_cast<int>(
 | 
					    return static_cast<int>(
 | 
				
			||||||
        ContentManager::InstallNSP(&EmulationSession::GetInstance().System(),
 | 
					        ContentManager::InstallNSP(EmulationSession::GetInstance().System(),
 | 
				
			||||||
                                   EmulationSession::GetInstance().System().GetFilesystem().get(),
 | 
					                                   *EmulationSession::GetInstance().System().GetFilesystem(),
 | 
				
			||||||
                                   GetJString(env, j_file), callback));
 | 
					                                   GetJString(env, j_file), callback));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -825,7 +825,7 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeUpdate(JNIEnv* env, jobject job
 | 
				
			|||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeDLC(JNIEnv* env, jobject jobj,
 | 
					void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeDLC(JNIEnv* env, jobject jobj,
 | 
				
			||||||
                                                     jstring jprogramId) {
 | 
					                                                     jstring jprogramId) {
 | 
				
			||||||
    auto program_id = EmulationSession::GetProgramId(env, jprogramId);
 | 
					    auto program_id = EmulationSession::GetProgramId(env, jprogramId);
 | 
				
			||||||
    ContentManager::RemoveAllDLC(&EmulationSession::GetInstance().System(), program_id);
 | 
					    ContentManager::RemoveAllDLC(EmulationSession::GetInstance().System(), program_id);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeMod(JNIEnv* env, jobject jobj, jstring jprogramId,
 | 
					void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeMod(JNIEnv* env, jobject jobj, jstring jprogramId,
 | 
				
			||||||
@@ -835,8 +835,9 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeMod(JNIEnv* env, jobject jobj,
 | 
				
			|||||||
                              program_id, GetJString(env, jname));
 | 
					                              program_id, GetJString(env, jname));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
jobject Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEnv* env, jobject jobj,
 | 
					jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEnv* env,
 | 
				
			||||||
                                                                      jobject jcallback) {
 | 
					                                                                           jobject jobj,
 | 
				
			||||||
 | 
					                                                                           jobject jcallback) {
 | 
				
			||||||
    auto jlambdaClass = env->GetObjectClass(jcallback);
 | 
					    auto jlambdaClass = env->GetObjectClass(jcallback);
 | 
				
			||||||
    auto jlambdaInvokeMethod = env->GetMethodID(
 | 
					    auto jlambdaInvokeMethod = env->GetMethodID(
 | 
				
			||||||
        jlambdaClass, "invoke", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
 | 
					        jlambdaClass, "invoke", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
 | 
				
			||||||
@@ -848,7 +849,7 @@ jobject Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEnv* en
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    auto& session = EmulationSession::GetInstance();
 | 
					    auto& session = EmulationSession::GetInstance();
 | 
				
			||||||
    std::vector<std::string> result = ContentManager::VerifyInstalledContents(
 | 
					    std::vector<std::string> result = ContentManager::VerifyInstalledContents(
 | 
				
			||||||
        &session.System(), session.GetContentProvider(), callback);
 | 
					        session.System(), *session.GetContentProvider(), callback);
 | 
				
			||||||
    jobjectArray jresult =
 | 
					    jobjectArray jresult =
 | 
				
			||||||
        env->NewObjectArray(result.size(), IDCache::GetStringClass(), ToJString(env, ""));
 | 
					        env->NewObjectArray(result.size(), IDCache::GetStringClass(), ToJString(env, ""));
 | 
				
			||||||
    for (size_t i = 0; i < result.size(); ++i) {
 | 
					    for (size_t i = 0; i < result.size(); ++i) {
 | 
				
			||||||
@@ -869,7 +870,7 @@ jint Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyGameContents(JNIEnv* env, jobje
 | 
				
			|||||||
    };
 | 
					    };
 | 
				
			||||||
    auto& session = EmulationSession::GetInstance();
 | 
					    auto& session = EmulationSession::GetInstance();
 | 
				
			||||||
    return static_cast<jint>(
 | 
					    return static_cast<jint>(
 | 
				
			||||||
        ContentManager::VerifyGameContents(&session.System(), GetJString(env, jpath), callback));
 | 
					        ContentManager::VerifyGameContents(session.System(), GetJString(env, jpath), callback));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject jobj,
 | 
					jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject jobj,
 | 
				
			||||||
@@ -918,4 +919,10 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_clearFilesystemProvider(JNIEnv* env,
 | 
				
			|||||||
    EmulationSession::GetInstance().GetContentProvider()->ClearAllEntries();
 | 
					    EmulationSession::GetInstance().GetContentProvider()->ClearAllEntries();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_areKeysPresent(JNIEnv* env, jobject jobj) {
 | 
				
			||||||
 | 
					    auto& system = EmulationSession::GetInstance().System();
 | 
				
			||||||
 | 
					    system.GetFileSystemController().CreateFactories(*system.GetFilesystem());
 | 
				
			||||||
 | 
					    return ContentManager::AreKeysPresent();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} // extern "C"
 | 
					} // extern "C"
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -144,6 +144,9 @@
 | 
				
			|||||||
    <string name="no_save_data_found">No save data found</string>
 | 
					    <string name="no_save_data_found">No save data found</string>
 | 
				
			||||||
    <string name="verify_installed_content">Verify installed content</string>
 | 
					    <string name="verify_installed_content">Verify installed content</string>
 | 
				
			||||||
    <string name="verify_installed_content_description">Checks all installed content for corruption</string>
 | 
					    <string name="verify_installed_content_description">Checks all installed content for corruption</string>
 | 
				
			||||||
 | 
					    <string name="keys_missing">Encryption keys are missing</string>
 | 
				
			||||||
 | 
					    <string name="keys_missing_description">Firmware and retail games cannot be decrypted</string>
 | 
				
			||||||
 | 
					    <string name="keys_missing_help">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <!-- Applet launcher strings -->
 | 
					    <!-- Applet launcher strings -->
 | 
				
			||||||
    <string name="applets">Applet launcher</string>
 | 
					    <string name="applets">Applet launcher</string>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -47,14 +47,14 @@ inline bool RemoveDLC(const Service::FileSystem::FileSystemController& fs_contro
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * \brief Removes all DLC for a game
 | 
					 * \brief Removes all DLC for a game
 | 
				
			||||||
 * \param system Raw pointer to the system instance
 | 
					 * \param system Reference to the system instance
 | 
				
			||||||
 * \param program_id Program ID for the game that will have all of its DLC removed
 | 
					 * \param program_id Program ID for the game that will have all of its DLC removed
 | 
				
			||||||
 * \return Number of DLC removed
 | 
					 * \return Number of DLC removed
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
inline size_t RemoveAllDLC(Core::System* system, const u64 program_id) {
 | 
					inline size_t RemoveAllDLC(Core::System& system, const u64 program_id) {
 | 
				
			||||||
    size_t count{};
 | 
					    size_t count{};
 | 
				
			||||||
    const auto& fs_controller = system->GetFileSystemController();
 | 
					    const auto& fs_controller = system.GetFileSystemController();
 | 
				
			||||||
    const auto dlc_entries = system->GetContentProvider().ListEntriesFilter(
 | 
					    const auto dlc_entries = system.GetContentProvider().ListEntriesFilter(
 | 
				
			||||||
        FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
 | 
					        FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
 | 
				
			||||||
    std::vector<u64> program_dlc_entries;
 | 
					    std::vector<u64> program_dlc_entries;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -124,15 +124,15 @@ inline bool RemoveMod(const Service::FileSystem::FileSystemController& fs_contro
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * \brief Installs an NSP
 | 
					 * \brief Installs an NSP
 | 
				
			||||||
 * \param system Raw pointer to the system instance
 | 
					 * \param system Reference to the system instance
 | 
				
			||||||
 * \param vfs Raw pointer to the VfsFilesystem instance in Core::System
 | 
					 * \param vfs Reference to the VfsFilesystem instance in Core::System
 | 
				
			||||||
 * \param filename Path to the NSP file
 | 
					 * \param filename Path to the NSP file
 | 
				
			||||||
 * \param callback Callback to report the progress of the installation. The first size_t
 | 
					 * \param callback Callback to report the progress of the installation. The first size_t
 | 
				
			||||||
 * parameter is the total size of the virtual file and the second is the current progress. If you
 | 
					 * parameter is the total size of the virtual file and the second is the current progress. If you
 | 
				
			||||||
 * return true to the callback, it will cancel the installation as soon as possible.
 | 
					 * return true to the callback, it will cancel the installation as soon as possible.
 | 
				
			||||||
 * \return [InstallResult] representing how the installation finished
 | 
					 * \return [InstallResult] representing how the installation finished
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
inline InstallResult InstallNSP(Core::System* system, FileSys::VfsFilesystem* vfs,
 | 
					inline InstallResult InstallNSP(Core::System& system, FileSys::VfsFilesystem& vfs,
 | 
				
			||||||
                                const std::string& filename,
 | 
					                                const std::string& filename,
 | 
				
			||||||
                                const std::function<bool(size_t, size_t)>& callback) {
 | 
					                                const std::function<bool(size_t, size_t)>& callback) {
 | 
				
			||||||
    const auto copy = [callback](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest,
 | 
					    const auto copy = [callback](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest,
 | 
				
			||||||
@@ -159,7 +159,7 @@ inline InstallResult InstallNSP(Core::System* system, FileSys::VfsFilesystem* vf
 | 
				
			|||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::shared_ptr<FileSys::NSP> nsp;
 | 
					    std::shared_ptr<FileSys::NSP> nsp;
 | 
				
			||||||
    FileSys::VirtualFile file = vfs->OpenFile(filename, FileSys::Mode::Read);
 | 
					    FileSys::VirtualFile file = vfs.OpenFile(filename, FileSys::Mode::Read);
 | 
				
			||||||
    if (boost::to_lower_copy(file->GetName()).ends_with(std::string("nsp"))) {
 | 
					    if (boost::to_lower_copy(file->GetName()).ends_with(std::string("nsp"))) {
 | 
				
			||||||
        nsp = std::make_shared<FileSys::NSP>(file);
 | 
					        nsp = std::make_shared<FileSys::NSP>(file);
 | 
				
			||||||
        if (nsp->IsExtractedType()) {
 | 
					        if (nsp->IsExtractedType()) {
 | 
				
			||||||
@@ -173,7 +173,7 @@ inline InstallResult InstallNSP(Core::System* system, FileSys::VfsFilesystem* vf
 | 
				
			|||||||
        return InstallResult::Failure;
 | 
					        return InstallResult::Failure;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    const auto res =
 | 
					    const auto res =
 | 
				
			||||||
        system->GetFileSystemController().GetUserNANDContents()->InstallEntry(*nsp, true, copy);
 | 
					        system.GetFileSystemController().GetUserNANDContents()->InstallEntry(*nsp, true, copy);
 | 
				
			||||||
    switch (res) {
 | 
					    switch (res) {
 | 
				
			||||||
    case FileSys::InstallResult::Success:
 | 
					    case FileSys::InstallResult::Success:
 | 
				
			||||||
        return InstallResult::Success;
 | 
					        return InstallResult::Success;
 | 
				
			||||||
@@ -188,17 +188,17 @@ inline InstallResult InstallNSP(Core::System* system, FileSys::VfsFilesystem* vf
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * \brief Installs an NCA
 | 
					 * \brief Installs an NCA
 | 
				
			||||||
 * \param vfs Raw pointer to the VfsFilesystem instance in Core::System
 | 
					 * \param vfs Reference to the VfsFilesystem instance in Core::System
 | 
				
			||||||
 * \param filename Path to the NCA file
 | 
					 * \param filename Path to the NCA file
 | 
				
			||||||
 * \param registered_cache Raw pointer to the registered cache that the NCA will be installed to
 | 
					 * \param registered_cache Reference to the registered cache that the NCA will be installed to
 | 
				
			||||||
 * \param title_type Type of NCA package to install
 | 
					 * \param title_type Type of NCA package to install
 | 
				
			||||||
 * \param callback Callback to report the progress of the installation. The first size_t
 | 
					 * \param callback Callback to report the progress of the installation. The first size_t
 | 
				
			||||||
 * parameter is the total size of the virtual file and the second is the current progress. If you
 | 
					 * parameter is the total size of the virtual file and the second is the current progress. If you
 | 
				
			||||||
 * return true to the callback, it will cancel the installation as soon as possible.
 | 
					 * return true to the callback, it will cancel the installation as soon as possible.
 | 
				
			||||||
 * \return [InstallResult] representing how the installation finished
 | 
					 * \return [InstallResult] representing how the installation finished
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
inline InstallResult InstallNCA(FileSys::VfsFilesystem* vfs, const std::string& filename,
 | 
					inline InstallResult InstallNCA(FileSys::VfsFilesystem& vfs, const std::string& filename,
 | 
				
			||||||
                                FileSys::RegisteredCache* registered_cache,
 | 
					                                FileSys::RegisteredCache& registered_cache,
 | 
				
			||||||
                                const FileSys::TitleType title_type,
 | 
					                                const FileSys::TitleType title_type,
 | 
				
			||||||
                                const std::function<bool(size_t, size_t)>& callback) {
 | 
					                                const std::function<bool(size_t, size_t)>& callback) {
 | 
				
			||||||
    const auto copy = [callback](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest,
 | 
					    const auto copy = [callback](const FileSys::VirtualFile& src, const FileSys::VirtualFile& dest,
 | 
				
			||||||
@@ -224,7 +224,7 @@ inline InstallResult InstallNCA(FileSys::VfsFilesystem* vfs, const std::string&
 | 
				
			|||||||
        return true;
 | 
					        return true;
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const auto nca = std::make_shared<FileSys::NCA>(vfs->OpenFile(filename, FileSys::Mode::Read));
 | 
					    const auto nca = std::make_shared<FileSys::NCA>(vfs.OpenFile(filename, FileSys::Mode::Read));
 | 
				
			||||||
    const auto id = nca->GetStatus();
 | 
					    const auto id = nca->GetStatus();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Game updates necessary are missing base RomFS
 | 
					    // Game updates necessary are missing base RomFS
 | 
				
			||||||
@@ -233,7 +233,7 @@ inline InstallResult InstallNCA(FileSys::VfsFilesystem* vfs, const std::string&
 | 
				
			|||||||
        return InstallResult::Failure;
 | 
					        return InstallResult::Failure;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const auto res = registered_cache->InstallEntry(*nca, title_type, true, copy);
 | 
					    const auto res = registered_cache.InstallEntry(*nca, title_type, true, copy);
 | 
				
			||||||
    if (res == FileSys::InstallResult::Success) {
 | 
					    if (res == FileSys::InstallResult::Success) {
 | 
				
			||||||
        return InstallResult::Success;
 | 
					        return InstallResult::Success;
 | 
				
			||||||
    } else if (res == FileSys::InstallResult::OverwriteExisting) {
 | 
					    } else if (res == FileSys::InstallResult::OverwriteExisting) {
 | 
				
			||||||
@@ -245,19 +245,19 @@ inline InstallResult InstallNCA(FileSys::VfsFilesystem* vfs, const std::string&
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * \brief Verifies the installed contents for a given ManualContentProvider
 | 
					 * \brief Verifies the installed contents for a given ManualContentProvider
 | 
				
			||||||
 * \param system Raw pointer to the system instance
 | 
					 * \param system Reference to the system instance
 | 
				
			||||||
 * \param provider Raw pointer to the content provider that's tracking indexed games
 | 
					 * \param provider Reference to the content provider that's tracking indexed games
 | 
				
			||||||
 * \param callback Callback to report the progress of the installation. The first size_t
 | 
					 * \param callback Callback to report the progress of the installation. The first size_t
 | 
				
			||||||
 * parameter is the total size of the installed contents and the second is the current progress. If
 | 
					 * parameter is the total size of the installed contents and the second is the current progress. If
 | 
				
			||||||
 * you return true to the callback, it will cancel the installation as soon as possible.
 | 
					 * you return true to the callback, it will cancel the installation as soon as possible.
 | 
				
			||||||
 * \return A list of entries that failed to install. Returns an empty vector if successful.
 | 
					 * \return A list of entries that failed to install. Returns an empty vector if successful.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
inline std::vector<std::string> VerifyInstalledContents(
 | 
					inline std::vector<std::string> VerifyInstalledContents(
 | 
				
			||||||
    Core::System* system, FileSys::ManualContentProvider* provider,
 | 
					    Core::System& system, FileSys::ManualContentProvider& provider,
 | 
				
			||||||
    const std::function<bool(size_t, size_t)>& callback) {
 | 
					    const std::function<bool(size_t, size_t)>& callback) {
 | 
				
			||||||
    // Get content registries.
 | 
					    // Get content registries.
 | 
				
			||||||
    auto bis_contents = system->GetFileSystemController().GetSystemNANDContents();
 | 
					    auto bis_contents = system.GetFileSystemController().GetSystemNANDContents();
 | 
				
			||||||
    auto user_contents = system->GetFileSystemController().GetUserNANDContents();
 | 
					    auto user_contents = system.GetFileSystemController().GetUserNANDContents();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::vector<FileSys::RegisteredCache*> content_providers;
 | 
					    std::vector<FileSys::RegisteredCache*> content_providers;
 | 
				
			||||||
    if (bis_contents) {
 | 
					    if (bis_contents) {
 | 
				
			||||||
@@ -309,11 +309,11 @@ inline std::vector<std::string> VerifyInstalledContents(
 | 
				
			|||||||
            const auto title_id = nca.GetTitleId();
 | 
					            const auto title_id = nca.GetTitleId();
 | 
				
			||||||
            std::string title_name = "unknown";
 | 
					            std::string title_name = "unknown";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            const auto control = provider->GetEntry(FileSys::GetBaseTitleID(title_id),
 | 
					            const auto control = provider.GetEntry(FileSys::GetBaseTitleID(title_id),
 | 
				
			||||||
                                                    FileSys::ContentRecordType::Control);
 | 
					                                                   FileSys::ContentRecordType::Control);
 | 
				
			||||||
            if (control && control->GetStatus() == Loader::ResultStatus::Success) {
 | 
					            if (control && control->GetStatus() == Loader::ResultStatus::Success) {
 | 
				
			||||||
                const FileSys::PatchManager pm{title_id, system->GetFileSystemController(),
 | 
					                const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
 | 
				
			||||||
                                               *provider};
 | 
					                                               provider};
 | 
				
			||||||
                const auto [nacp, logo] = pm.ParseControlNCA(*control);
 | 
					                const auto [nacp, logo] = pm.ParseControlNCA(*control);
 | 
				
			||||||
                if (nacp) {
 | 
					                if (nacp) {
 | 
				
			||||||
                    title_name = nacp->GetApplicationName();
 | 
					                    title_name = nacp->GetApplicationName();
 | 
				
			||||||
@@ -335,7 +335,7 @@ inline std::vector<std::string> VerifyInstalledContents(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * \brief Verifies the contents of a given game
 | 
					 * \brief Verifies the contents of a given game
 | 
				
			||||||
 * \param system Raw pointer to the system instance
 | 
					 * \param system Reference to the system instance
 | 
				
			||||||
 * \param game_path Patch to the game file
 | 
					 * \param game_path Patch to the game file
 | 
				
			||||||
 * \param callback Callback to report the progress of the installation. The first size_t
 | 
					 * \param callback Callback to report the progress of the installation. The first size_t
 | 
				
			||||||
 * parameter is the total size of the installed contents and the second is the current progress. If
 | 
					 * parameter is the total size of the installed contents and the second is the current progress. If
 | 
				
			||||||
@@ -343,10 +343,10 @@ inline std::vector<std::string> VerifyInstalledContents(
 | 
				
			|||||||
 * \return GameVerificationResult representing how the verification process finished
 | 
					 * \return GameVerificationResult representing how the verification process finished
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
inline GameVerificationResult VerifyGameContents(
 | 
					inline GameVerificationResult VerifyGameContents(
 | 
				
			||||||
    Core::System* system, const std::string& game_path,
 | 
					    Core::System& system, const std::string& game_path,
 | 
				
			||||||
    const std::function<bool(size_t, size_t)>& callback) {
 | 
					    const std::function<bool(size_t, size_t)>& callback) {
 | 
				
			||||||
    const auto loader = Loader::GetLoader(
 | 
					    const auto loader =
 | 
				
			||||||
        *system, system->GetFilesystem()->OpenFile(game_path, FileSys::Mode::Read));
 | 
					        Loader::GetLoader(system, system.GetFilesystem()->OpenFile(game_path, FileSys::Mode::Read));
 | 
				
			||||||
    if (loader == nullptr) {
 | 
					    if (loader == nullptr) {
 | 
				
			||||||
        return GameVerificationResult::NotImplemented;
 | 
					        return GameVerificationResult::NotImplemented;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -368,4 +368,11 @@ inline GameVerificationResult VerifyGameContents(
 | 
				
			|||||||
    return GameVerificationResult::Success;
 | 
					    return GameVerificationResult::Success;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Checks if the keys required for decrypting firmware and games are available
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					inline bool AreKeysPresent() {
 | 
				
			||||||
 | 
					    return !Core::Crypto::KeyManager::Instance().BaseDeriveNecessary();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
} // namespace ContentManager
 | 
					} // namespace ContentManager
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -423,7 +423,7 @@ GMainWindow::GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulk
 | 
				
			|||||||
    RemoveCachedContents();
 | 
					    RemoveCachedContents();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Gen keys if necessary
 | 
					    // Gen keys if necessary
 | 
				
			||||||
    OnReinitializeKeys(ReinitializeKeyBehavior::NoWarning);
 | 
					    OnCheckFirmwareDecryption();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    game_list->LoadCompatibilityList();
 | 
					    game_list->LoadCompatibilityList();
 | 
				
			||||||
    game_list->PopulateAsync(UISettings::values.game_dirs);
 | 
					    game_list->PopulateAsync(UISettings::values.game_dirs);
 | 
				
			||||||
@@ -1574,8 +1574,6 @@ void GMainWindow::ConnectMenuEvents() {
 | 
				
			|||||||
    connect(multiplayer_state, &MultiplayerState::SaveConfig, this, &GMainWindow::OnSaveConfig);
 | 
					    connect(multiplayer_state, &MultiplayerState::SaveConfig, this, &GMainWindow::OnSaveConfig);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Tools
 | 
					    // Tools
 | 
				
			||||||
    connect_menu(ui->action_Rederive, std::bind(&GMainWindow::OnReinitializeKeys, this,
 | 
					 | 
				
			||||||
                                                ReinitializeKeyBehavior::Warning));
 | 
					 | 
				
			||||||
    connect_menu(ui->action_Load_Album, &GMainWindow::OnAlbum);
 | 
					    connect_menu(ui->action_Load_Album, &GMainWindow::OnAlbum);
 | 
				
			||||||
    connect_menu(ui->action_Load_Cabinet_Nickname_Owner,
 | 
					    connect_menu(ui->action_Load_Cabinet_Nickname_Owner,
 | 
				
			||||||
                 [this]() { OnCabinet(Service::NFP::CabinetMode::StartNicknameAndOwnerSettings); });
 | 
					                 [this]() { OnCabinet(Service::NFP::CabinetMode::StartNicknameAndOwnerSettings); });
 | 
				
			||||||
@@ -2501,7 +2499,7 @@ void GMainWindow::RemoveUpdateContent(u64 program_id, InstalledEntryType type) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void GMainWindow::RemoveAddOnContent(u64 program_id, InstalledEntryType type) {
 | 
					void GMainWindow::RemoveAddOnContent(u64 program_id, InstalledEntryType type) {
 | 
				
			||||||
    const size_t count = ContentManager::RemoveAllDLC(system.get(), program_id);
 | 
					    const size_t count = ContentManager::RemoveAllDLC(*system, program_id);
 | 
				
			||||||
    if (count == 0) {
 | 
					    if (count == 0) {
 | 
				
			||||||
        QMessageBox::warning(this, GetGameListErrorRemoving(type),
 | 
					        QMessageBox::warning(this, GetGameListErrorRemoving(type),
 | 
				
			||||||
                             tr("There are no DLC installed for this title."));
 | 
					                             tr("There are no DLC installed for this title."));
 | 
				
			||||||
@@ -2798,8 +2796,7 @@ void GMainWindow::OnGameListVerifyIntegrity(const std::string& game_path) {
 | 
				
			|||||||
        return progress.wasCanceled();
 | 
					        return progress.wasCanceled();
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const auto result =
 | 
					    const auto result = ContentManager::VerifyGameContents(*system, game_path, QtProgressCallback);
 | 
				
			||||||
        ContentManager::VerifyGameContents(system.get(), game_path, QtProgressCallback);
 | 
					 | 
				
			||||||
    progress.close();
 | 
					    progress.close();
 | 
				
			||||||
    switch (result) {
 | 
					    switch (result) {
 | 
				
			||||||
    case ContentManager::GameVerificationResult::Success:
 | 
					    case ContentManager::GameVerificationResult::Success:
 | 
				
			||||||
@@ -3268,7 +3265,7 @@ void GMainWindow::OnMenuInstallToNAND() {
 | 
				
			|||||||
                return false;
 | 
					                return false;
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
            future = QtConcurrent::run([this, &file, progress_callback] {
 | 
					            future = QtConcurrent::run([this, &file, progress_callback] {
 | 
				
			||||||
                return ContentManager::InstallNSP(system.get(), vfs.get(), file.toStdString(),
 | 
					                return ContentManager::InstallNSP(*system, *vfs, file.toStdString(),
 | 
				
			||||||
                                                  progress_callback);
 | 
					                                                  progress_callback);
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -3371,7 +3368,7 @@ ContentManager::InstallResult GMainWindow::InstallNCA(const QString& filename) {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        return false;
 | 
					        return false;
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    return ContentManager::InstallNCA(vfs.get(), filename.toStdString(), registered_cache,
 | 
					    return ContentManager::InstallNCA(*vfs, filename.toStdString(), *registered_cache,
 | 
				
			||||||
                                      static_cast<FileSys::TitleType>(index), progress_callback);
 | 
					                                      static_cast<FileSys::TitleType>(index), progress_callback);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -4121,7 +4118,7 @@ void GMainWindow::OnVerifyInstalledContents() {
 | 
				
			|||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const std::vector<std::string> result =
 | 
					    const std::vector<std::string> result =
 | 
				
			||||||
        ContentManager::VerifyInstalledContents(system.get(), provider.get(), QtProgressCallback);
 | 
					        ContentManager::VerifyInstalledContents(*system, *provider, QtProgressCallback);
 | 
				
			||||||
    progress.close();
 | 
					    progress.close();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (result.empty()) {
 | 
					    if (result.empty()) {
 | 
				
			||||||
@@ -4551,122 +4548,20 @@ void GMainWindow::OnMouseActivity() {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
 | 
					void GMainWindow::OnCheckFirmwareDecryption() {
 | 
				
			||||||
    if (behavior == ReinitializeKeyBehavior::Warning) {
 | 
					 | 
				
			||||||
        const auto res = QMessageBox::information(
 | 
					 | 
				
			||||||
            this, tr("Confirm Key Rederivation"),
 | 
					 | 
				
			||||||
            tr("You are about to force rederive all of your keys. \nIf you do not know what "
 | 
					 | 
				
			||||||
               "this "
 | 
					 | 
				
			||||||
               "means or what you are doing, \nthis is a potentially destructive action. "
 | 
					 | 
				
			||||||
               "\nPlease "
 | 
					 | 
				
			||||||
               "make sure this is what you want \nand optionally make backups.\n\nThis will "
 | 
					 | 
				
			||||||
               "delete "
 | 
					 | 
				
			||||||
               "your autogenerated key files and re-run the key derivation module."),
 | 
					 | 
				
			||||||
            QMessageBox::StandardButtons{QMessageBox::Ok, QMessageBox::Cancel});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (res == QMessageBox::Cancel)
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const auto keys_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::KeysDir);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Common::FS::RemoveFile(keys_dir / "prod.keys_autogenerated");
 | 
					 | 
				
			||||||
        Common::FS::RemoveFile(keys_dir / "console.keys_autogenerated");
 | 
					 | 
				
			||||||
        Common::FS::RemoveFile(keys_dir / "title.keys_autogenerated");
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
 | 
					 | 
				
			||||||
    bool all_keys_present{true};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (keys.BaseDeriveNecessary()) {
 | 
					 | 
				
			||||||
        Core::Crypto::PartitionDataManager pdm{vfs->OpenDirectory("", FileSys::Mode::Read)};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const auto function = [this, &keys, &pdm] {
 | 
					 | 
				
			||||||
            keys.PopulateFromPartitionData(pdm);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            system->GetFileSystemController().CreateFactories(*vfs);
 | 
					 | 
				
			||||||
            keys.DeriveETicket(pdm, system->GetContentProvider());
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        QString errors;
 | 
					 | 
				
			||||||
        if (!pdm.HasFuses()) {
 | 
					 | 
				
			||||||
            errors += tr("Missing fuses");
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (!pdm.HasBoot0()) {
 | 
					 | 
				
			||||||
            errors += tr(" - Missing BOOT0");
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (!pdm.HasPackage2()) {
 | 
					 | 
				
			||||||
            errors += tr(" - Missing BCPKG2-1-Normal-Main");
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (!pdm.HasProdInfo()) {
 | 
					 | 
				
			||||||
            errors += tr(" - Missing PRODINFO");
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (!errors.isEmpty()) {
 | 
					 | 
				
			||||||
            all_keys_present = false;
 | 
					 | 
				
			||||||
            QMessageBox::warning(
 | 
					 | 
				
			||||||
                this, tr("Derivation Components Missing"),
 | 
					 | 
				
			||||||
                tr("Encryption keys are missing. "
 | 
					 | 
				
			||||||
                   "<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu "
 | 
					 | 
				
			||||||
                   "quickstart guide</a> to get all your keys, firmware and "
 | 
					 | 
				
			||||||
                   "games.<br><br><small>(%1)</small>")
 | 
					 | 
				
			||||||
                    .arg(errors));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        QProgressDialog prog(this);
 | 
					 | 
				
			||||||
        prog.setRange(0, 0);
 | 
					 | 
				
			||||||
        prog.setLabelText(tr("Deriving keys...\nThis may take up to a minute depending \non your "
 | 
					 | 
				
			||||||
                             "system's performance."));
 | 
					 | 
				
			||||||
        prog.setWindowTitle(tr("Deriving Keys"));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        prog.show();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        auto future = QtConcurrent::run(function);
 | 
					 | 
				
			||||||
        while (!future.isFinished()) {
 | 
					 | 
				
			||||||
            QCoreApplication::processEvents();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        prog.close();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    system->GetFileSystemController().CreateFactories(*vfs);
 | 
					    system->GetFileSystemController().CreateFactories(*vfs);
 | 
				
			||||||
 | 
					    if (!ContentManager::AreKeysPresent()) {
 | 
				
			||||||
    if (all_keys_present && !this->CheckSystemArchiveDecryption()) {
 | 
					 | 
				
			||||||
        LOG_WARNING(Frontend, "Mii model decryption failed");
 | 
					 | 
				
			||||||
        QMessageBox::warning(
 | 
					        QMessageBox::warning(
 | 
				
			||||||
            this, tr("System Archive Decryption Failed"),
 | 
					            this, tr("Derivation Components Missing"),
 | 
				
			||||||
            tr("Encryption keys failed to decrypt firmware. "
 | 
					            tr("Encryption keys are missing. "
 | 
				
			||||||
               "<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu "
 | 
					               "<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu "
 | 
				
			||||||
               "quickstart guide</a> to get all your keys, firmware and "
 | 
					               "quickstart guide</a> to get all your keys, firmware and "
 | 
				
			||||||
               "games."));
 | 
					               "games."));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    SetFirmwareVersion();
 | 
					    SetFirmwareVersion();
 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (behavior == ReinitializeKeyBehavior::Warning) {
 | 
					 | 
				
			||||||
        game_list->PopulateAsync(UISettings::values.game_dirs);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    UpdateMenuState();
 | 
					    UpdateMenuState();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool GMainWindow::CheckSystemArchiveDecryption() {
 | 
					 | 
				
			||||||
    constexpr u64 MiiModelId = 0x0100000000000802;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    auto bis_system = system->GetFileSystemController().GetSystemNANDContents();
 | 
					 | 
				
			||||||
    if (!bis_system) {
 | 
					 | 
				
			||||||
        // Not having system BIS files is not an error.
 | 
					 | 
				
			||||||
        return true;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    auto mii_nca = bis_system->GetEntry(MiiModelId, FileSys::ContentRecordType::Data);
 | 
					 | 
				
			||||||
    if (!mii_nca) {
 | 
					 | 
				
			||||||
        // Not having the Mii model is not an error.
 | 
					 | 
				
			||||||
        return true;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Return whether we are able to decrypt the RomFS of the Mii model.
 | 
					 | 
				
			||||||
    return mii_nca->GetRomFS().get() != nullptr;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bool GMainWindow::CheckFirmwarePresence() {
 | 
					bool GMainWindow::CheckFirmwarePresence() {
 | 
				
			||||||
    constexpr u64 MiiEditId = static_cast<u64>(Service::AM::Applets::AppletProgramId::MiiEdit);
 | 
					    constexpr u64 MiiEditId = static_cast<u64>(Service::AM::Applets::AppletProgramId::MiiEdit);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -125,11 +125,6 @@ enum class EmulatedDirectoryTarget {
 | 
				
			|||||||
    SDMC,
 | 
					    SDMC,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum class ReinitializeKeyBehavior {
 | 
					 | 
				
			||||||
    NoWarning,
 | 
					 | 
				
			||||||
    Warning,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace VkDeviceInfo {
 | 
					namespace VkDeviceInfo {
 | 
				
			||||||
class Record;
 | 
					class Record;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -400,7 +395,7 @@ private slots:
 | 
				
			|||||||
    void OnMiiEdit();
 | 
					    void OnMiiEdit();
 | 
				
			||||||
    void OnOpenControllerMenu();
 | 
					    void OnOpenControllerMenu();
 | 
				
			||||||
    void OnCaptureScreenshot();
 | 
					    void OnCaptureScreenshot();
 | 
				
			||||||
    void OnReinitializeKeys(ReinitializeKeyBehavior behavior);
 | 
					    void OnCheckFirmwareDecryption();
 | 
				
			||||||
    void OnLanguageChanged(const QString& locale);
 | 
					    void OnLanguageChanged(const QString& locale);
 | 
				
			||||||
    void OnMouseActivity();
 | 
					    void OnMouseActivity();
 | 
				
			||||||
    bool OnShutdownBegin();
 | 
					    bool OnShutdownBegin();
 | 
				
			||||||
@@ -441,7 +436,6 @@ private:
 | 
				
			|||||||
    void LoadTranslation();
 | 
					    void LoadTranslation();
 | 
				
			||||||
    void OpenPerGameConfiguration(u64 title_id, const std::string& file_name);
 | 
					    void OpenPerGameConfiguration(u64 title_id, const std::string& file_name);
 | 
				
			||||||
    bool CheckDarkMode();
 | 
					    bool CheckDarkMode();
 | 
				
			||||||
    bool CheckSystemArchiveDecryption();
 | 
					 | 
				
			||||||
    bool CheckFirmwarePresence();
 | 
					    bool CheckFirmwarePresence();
 | 
				
			||||||
    void SetFirmwareVersion();
 | 
					    void SetFirmwareVersion();
 | 
				
			||||||
    void ConfigureFilesystemProvider(const std::string& filepath);
 | 
					    void ConfigureFilesystemProvider(const std::string& filepath);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -224,11 +224,6 @@
 | 
				
			|||||||
    <string>&Stop</string>
 | 
					    <string>&Stop</string>
 | 
				
			||||||
   </property>
 | 
					   </property>
 | 
				
			||||||
  </action>
 | 
					  </action>
 | 
				
			||||||
  <action name="action_Rederive">
 | 
					 | 
				
			||||||
   <property name="text">
 | 
					 | 
				
			||||||
    <string>&Reinitialize keys...</string>
 | 
					 | 
				
			||||||
   </property>
 | 
					 | 
				
			||||||
  </action>
 | 
					 | 
				
			||||||
  <action name="action_Verify_installed_contents">
 | 
					  <action name="action_Verify_installed_contents">
 | 
				
			||||||
   <property name="text">
 | 
					   <property name="text">
 | 
				
			||||||
    <string>&Verify Installed Contents</string>
 | 
					    <string>&Verify Installed Contents</string>
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user