Compare commits
5 Commits
android-21
...
android-20
Author | SHA1 | Date | |
---|---|---|---|
486c3db222 | |||
f286c2fde3 | |||
34fb9081e7 | |||
1fc82b84c7 | |||
3ab0600904 |
@ -1,9 +1,9 @@
|
|||||||
| Pull Request | Commit | Title | Author | Merged? |
|
| Pull Request | Commit | Title | Author | Merged? |
|
||||||
|----|----|----|----|----|
|
|----|----|----|----|----|
|
||||||
|
| [12499](https://github.com/yuzu-emu/yuzu-android//pull/12499) | [`e4915fb7d`](https://github.com/yuzu-emu/yuzu-android//pull/12499/files) | Rework time services | [Kelebek1](https://github.com/Kelebek1/) | Yes |
|
||||||
| [12749](https://github.com/yuzu-emu/yuzu-android//pull/12749) | [`e3171486d`](https://github.com/yuzu-emu/yuzu-android//pull/12749/files) | general: workarounds for SMMU syncing issues | [liamwhite](https://github.com/liamwhite/) | Yes |
|
| [12749](https://github.com/yuzu-emu/yuzu-android//pull/12749) | [`e3171486d`](https://github.com/yuzu-emu/yuzu-android//pull/12749/files) | general: workarounds for SMMU syncing issues | [liamwhite](https://github.com/liamwhite/) | Yes |
|
||||||
| [12759](https://github.com/yuzu-emu/yuzu-android//pull/12759) | [`a120f8ff4`](https://github.com/yuzu-emu/yuzu-android//pull/12759/files) | core: miscellaneous fixes | [liamwhite](https://github.com/liamwhite/) | Yes |
|
| [12759](https://github.com/yuzu-emu/yuzu-android//pull/12759) | [`a120f8ff4`](https://github.com/yuzu-emu/yuzu-android//pull/12759/files) | core: miscellaneous fixes | [liamwhite](https://github.com/liamwhite/) | Yes |
|
||||||
| [12769](https://github.com/yuzu-emu/yuzu-android//pull/12769) | [`ad4622da2`](https://github.com/yuzu-emu/yuzu-android//pull/12769/files) | core: hid: Reduce controller requests | [german77](https://github.com/german77/) | Yes |
|
| [12769](https://github.com/yuzu-emu/yuzu-android//pull/12769) | [`ad4622da2`](https://github.com/yuzu-emu/yuzu-android//pull/12769/files) | core: hid: Reduce controller requests | [german77](https://github.com/german77/) | Yes |
|
||||||
| [12777](https://github.com/yuzu-emu/yuzu-android//pull/12777) | [`b8be8dff6`](https://github.com/yuzu-emu/yuzu-android//pull/12777/files) | android: Add key warning | [t895](https://github.com/t895/) | Yes |
|
|
||||||
|
|
||||||
|
|
||||||
End of merge log. You can find the original README.md below the break.
|
End of merge log. You can find the original README.md below the break.
|
||||||
|
@ -303,11 +303,6 @@ object NativeLibrary {
|
|||||||
*/
|
*/
|
||||||
external fun getCpuBackend(): String
|
external fun getCpuBackend(): String
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current GPU Driver.
|
|
||||||
*/
|
|
||||||
external fun getGpuDriver(): String
|
|
||||||
|
|
||||||
external fun applySettings()
|
external fun applySettings()
|
||||||
|
|
||||||
external fun logSettings()
|
external fun logSettings()
|
||||||
@ -619,11 +614,6 @@ 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
|
||||||
*/
|
*/
|
||||||
|
@ -14,20 +14,15 @@ import androidx.recyclerview.widget.RecyclerView
|
|||||||
* Generic adapter that implements an [AsyncDifferConfig] and covers some of the basic boilerplate
|
* Generic adapter that implements an [AsyncDifferConfig] and covers some of the basic boilerplate
|
||||||
* code used in every [RecyclerView].
|
* code used in every [RecyclerView].
|
||||||
* Type assigned to [Model] must inherit from [Object] in order to be compared properly.
|
* Type assigned to [Model] must inherit from [Object] in order to be compared properly.
|
||||||
* @param exact Decides whether each item will be compared by reference or by their contents
|
|
||||||
*/
|
*/
|
||||||
abstract class AbstractDiffAdapter<Model : Any, Holder : AbstractViewHolder<Model>>(
|
abstract class AbstractDiffAdapter<Model : Any, Holder : AbstractViewHolder<Model>> :
|
||||||
exact: Boolean = true
|
ListAdapter<Model, Holder>(AsyncDifferConfig.Builder(DiffCallback<Model>()).build()) {
|
||||||
) : ListAdapter<Model, Holder>(AsyncDifferConfig.Builder(DiffCallback<Model>(exact)).build()) {
|
|
||||||
override fun onBindViewHolder(holder: Holder, position: Int) =
|
override fun onBindViewHolder(holder: Holder, position: Int) =
|
||||||
holder.bind(currentList[position])
|
holder.bind(currentList[position])
|
||||||
|
|
||||||
private class DiffCallback<Model>(val exact: Boolean) : DiffUtil.ItemCallback<Model>() {
|
private class DiffCallback<Model> : DiffUtil.ItemCallback<Model>() {
|
||||||
override fun areItemsTheSame(oldItem: Model & Any, newItem: Model & Any): Boolean {
|
override fun areItemsTheSame(oldItem: Model & Any, newItem: Model & Any): Boolean {
|
||||||
if (exact) {
|
return oldItem === newItem
|
||||||
return oldItem === newItem
|
|
||||||
}
|
|
||||||
return oldItem == newItem
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("DiffUtilEquals")
|
@SuppressLint("DiffUtilEquals")
|
||||||
|
@ -30,7 +30,7 @@ import org.yuzu.yuzu_emu.utils.GameIconUtils
|
|||||||
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
|
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
|
||||||
|
|
||||||
class GameAdapter(private val activity: AppCompatActivity) :
|
class GameAdapter(private val activity: AppCompatActivity) :
|
||||||
AbstractDiffAdapter<Game, GameAdapter.GameViewHolder>(exact = false) {
|
AbstractDiffAdapter<Game, GameAdapter.GameViewHolder>() {
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GameViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GameViewHolder {
|
||||||
CardGameBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
CardGameBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||||
.also { return GameViewHolder(it) }
|
.also { return GameViewHolder(it) }
|
||||||
|
@ -38,6 +38,7 @@ import androidx.window.layout.WindowLayoutInfo
|
|||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.google.android.material.slider.Slider
|
import com.google.android.material.slider.Slider
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.collect
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.yuzu.yuzu_emu.HomeNavigationDirections
|
import org.yuzu.yuzu_emu.HomeNavigationDirections
|
||||||
@ -140,9 +141,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
|
|
||||||
// So this fragment doesn't restart on configuration changes; i.e. rotation.
|
// So this fragment doesn't restart on configuration changes; i.e. rotation.
|
||||||
retainInstance = true
|
retainInstance = true
|
||||||
emulationState = EmulationState(game.path) {
|
emulationState = EmulationState(game.path)
|
||||||
return@EmulationState driverViewModel.isInteractionAllowed.value
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -371,15 +370,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
launch {
|
|
||||||
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
|
||||||
driverViewModel.isInteractionAllowed.collect {
|
|
||||||
if (it) {
|
|
||||||
startEmulation()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
launch {
|
launch {
|
||||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
||||||
emulationViewModel.emulationStarted.collectLatest {
|
emulationViewModel.emulationStarted.collectLatest {
|
||||||
@ -408,10 +398,19 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
launch {
|
||||||
|
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||||
|
driverViewModel.isInteractionAllowed.collect {
|
||||||
|
if (it) {
|
||||||
|
onEmulationStart()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startEmulation() {
|
private fun onEmulationStart() {
|
||||||
if (!NativeLibrary.isRunning() && !NativeLibrary.isPaused()) {
|
if (!NativeLibrary.isRunning() && !NativeLibrary.isPaused()) {
|
||||||
if (!DirectoryInitialization.areDirectoriesReady) {
|
if (!DirectoryInitialization.areDirectoriesReady) {
|
||||||
DirectoryInitialization.start()
|
DirectoryInitialization.start()
|
||||||
@ -486,15 +485,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
val FRAMETIME = 2
|
val FRAMETIME = 2
|
||||||
val SPEED = 3
|
val SPEED = 3
|
||||||
perfStatsUpdater = {
|
perfStatsUpdater = {
|
||||||
if (emulationViewModel.emulationStarted.value &&
|
if (emulationViewModel.emulationStarted.value) {
|
||||||
!emulationViewModel.isEmulationStopping.value
|
|
||||||
) {
|
|
||||||
val perfStats = NativeLibrary.getPerfStats()
|
val perfStats = NativeLibrary.getPerfStats()
|
||||||
val cpuBackend = NativeLibrary.getCpuBackend()
|
val cpuBackend = NativeLibrary.getCpuBackend()
|
||||||
val gpuDriver = NativeLibrary.getGpuDriver()
|
|
||||||
if (_binding != null) {
|
if (_binding != null) {
|
||||||
binding.showFpsText.text =
|
binding.showFpsText.text =
|
||||||
String.format("FPS: %.1f\n%s/%s", perfStats[FPS], cpuBackend, gpuDriver)
|
String.format("FPS: %.1f\n%s", perfStats[FPS], cpuBackend)
|
||||||
}
|
}
|
||||||
perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 800)
|
perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 800)
|
||||||
}
|
}
|
||||||
@ -811,10 +807,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class EmulationState(
|
private class EmulationState(private val gamePath: String) {
|
||||||
private val gamePath: String,
|
|
||||||
private val emulationCanStart: () -> Boolean
|
|
||||||
) {
|
|
||||||
private var state: State
|
private var state: State
|
||||||
private var surface: Surface? = null
|
private var surface: Surface? = null
|
||||||
|
|
||||||
@ -908,7 +901,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
State.PAUSED -> Log.warning(
|
State.PAUSED -> Log.warning(
|
||||||
"[EmulationFragment] Surface cleared while emulation paused."
|
"[EmulationFragment] Surface cleared while emulation paused."
|
||||||
)
|
)
|
||||||
|
|
||||||
else -> Log.warning(
|
else -> Log.warning(
|
||||||
"[EmulationFragment] Surface cleared while emulation stopped."
|
"[EmulationFragment] Surface cleared while emulation stopped."
|
||||||
)
|
)
|
||||||
@ -918,10 +910,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
|
|
||||||
private fun runWithValidSurface() {
|
private fun runWithValidSurface() {
|
||||||
NativeLibrary.surfaceChanged(surface)
|
NativeLibrary.surfaceChanged(surface)
|
||||||
if (!emulationCanStart.invoke()) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
when (state) {
|
when (state) {
|
||||||
State.STOPPED -> {
|
State.STOPPED -> {
|
||||||
val emulationThread = Thread({
|
val emulationThread = Thread({
|
||||||
|
@ -26,15 +26,9 @@ 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 {
|
||||||
@ -57,8 +51,6 @@ class MessageDialogFragment : DialogFragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
isCancelable = dismissible
|
|
||||||
|
|
||||||
return builder.show()
|
return builder.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,8 +67,6 @@ 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,
|
||||||
@ -85,28 +75,22 @@ 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 {
|
||||||
var clearPositiveAction = false
|
|
||||||
if (activity != null) {
|
|
||||||
ViewModelProvider(activity)[MessageDialogViewModel::class.java].apply {
|
|
||||||
clear()
|
|
||||||
this.positiveAction = positiveAction
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
clearPositiveAction = true
|
|
||||||
}
|
|
||||||
|
|
||||||
val dialog = MessageDialogFragment()
|
val dialog = MessageDialogFragment()
|
||||||
val bundle = Bundle().apply {
|
val bundle = Bundle()
|
||||||
|
bundle.apply {
|
||||||
putInt(TITLE_ID, titleId)
|
putInt(TITLE_ID, titleId)
|
||||||
putString(TITLE_STRING, titleString)
|
putString(TITLE_STRING, titleString)
|
||||||
putInt(DESCRIPTION_ID, descriptionId)
|
putInt(DESCRIPTION_ID, descriptionId)
|
||||||
putString(DESCRIPTION_STRING, descriptionString)
|
putString(DESCRIPTION_STRING, descriptionString)
|
||||||
putInt(HELP_LINK, helpLinkId)
|
putInt(HELP_LINK, helpLinkId)
|
||||||
putBoolean(DISMISSIBLE, dismissible)
|
}
|
||||||
putBoolean(CLEAR_POSITIVE_ACTION, clearPositiveAction)
|
if (activity != null) {
|
||||||
|
ViewModelProvider(activity)[MessageDialogViewModel::class.java].apply {
|
||||||
|
clear()
|
||||||
|
this.positiveAction = positiveAction
|
||||||
|
}
|
||||||
}
|
}
|
||||||
dialog.arguments = bundle
|
dialog.arguments = bundle
|
||||||
return dialog
|
return dialog
|
||||||
|
@ -31,7 +31,6 @@ 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
|
||||||
@ -163,7 +162,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() && NativeLibrary.areKeysPresent()) {
|
if (file.exists()) {
|
||||||
StepState.COMPLETE
|
StepState.COMPLETE
|
||||||
} else {
|
} else {
|
||||||
StepState.INCOMPLETE
|
StepState.INCOMPLETE
|
||||||
@ -348,8 +347,7 @@ class SetupFragment : Fragment() {
|
|||||||
val getProdKey =
|
val getProdKey =
|
||||||
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
|
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
mainActivity.processKey(result)
|
if (mainActivity.processKey(result)) {
|
||||||
if (NativeLibrary.areKeysPresent()) {
|
|
||||||
keyCallback.onStepCompleted()
|
keyCallback.onStepCompleted()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -144,7 +144,6 @@ class DriverViewModel : ViewModel() {
|
|||||||
val selectedDriverFile = File(StringSetting.DRIVER_PATH.getString())
|
val selectedDriverFile = File(StringSetting.DRIVER_PATH.getString())
|
||||||
val selectedDriverMetadata = GpuDriverHelper.customDriverSettingData
|
val selectedDriverMetadata = GpuDriverHelper.customDriverSettingData
|
||||||
if (GpuDriverHelper.installedCustomDriverData == selectedDriverMetadata) {
|
if (GpuDriverHelper.installedCustomDriverData == selectedDriverMetadata) {
|
||||||
setDriverReady()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,19 +70,11 @@ class Game(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (this === other) return true
|
if (other !is Game) {
|
||||||
if (javaClass != other?.javaClass) return false
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
other as Game
|
return hashCode() == other.hashCode()
|
||||||
|
|
||||||
if (title != other.title) return false
|
|
||||||
if (path != other.path) return false
|
|
||||||
if (programId != other.programId) return false
|
|
||||||
if (developer != other.developer) return false
|
|
||||||
if (version != other.version) return false
|
|
||||||
if (isHomebrew != other.isHomebrew) return false
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
|
@ -31,9 +31,6 @@ 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) {
|
||||||
@ -69,8 +66,4 @@ 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,9 +64,6 @@ 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 }
|
||||||
@ -78,18 +75,6 @@ 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)
|
||||||
|
|
||||||
@ -165,16 +150,6 @@ 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)
|
||||||
@ -183,21 +158,6 @@ 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)
|
||||||
@ -389,7 +349,6 @@ 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 {
|
||||||
@ -440,7 +399,6 @@ 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) {
|
||||||
|
@ -247,7 +247,6 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string
|
|||||||
m_system.GetCpuManager().OnGpuReady();
|
m_system.GetCpuManager().OnGpuReady();
|
||||||
m_system.RegisterExitCallback([&] { HaltEmulation(); });
|
m_system.RegisterExitCallback([&] { HaltEmulation(); });
|
||||||
|
|
||||||
OnEmulationStarted();
|
|
||||||
return Core::SystemResultStatus::Success;
|
return Core::SystemResultStatus::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -464,8 +463,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(),
|
EmulationSession::GetInstance().System().GetFilesystem().get(),
|
||||||
GetJString(env, j_file), callback));
|
GetJString(env, j_file), callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -675,11 +674,6 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCpuBackend(JNIEnv* env, jclass
|
|||||||
return ToJString(env, "JIT");
|
return ToJString(env, "JIT");
|
||||||
}
|
}
|
||||||
|
|
||||||
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGpuDriver(JNIEnv* env, jobject jobj) {
|
|
||||||
return ToJString(env,
|
|
||||||
EmulationSession::GetInstance().System().GPU().Renderer().GetDeviceVendor());
|
|
||||||
}
|
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_applySettings(JNIEnv* env, jobject jobj) {
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_applySettings(JNIEnv* env, jobject jobj) {
|
||||||
EmulationSession::GetInstance().System().ApplySettings();
|
EmulationSession::GetInstance().System().ApplySettings();
|
||||||
}
|
}
|
||||||
@ -825,7 +819,7 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeUpdate(JNIEnv* env, jobject job
|
|||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeDLC(JNIEnv* env, jobject jobj,
|
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,9 +829,8 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeMod(JNIEnv* env, jobject jobj,
|
|||||||
program_id, GetJString(env, jname));
|
program_id, GetJString(env, jname));
|
||||||
}
|
}
|
||||||
|
|
||||||
jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEnv* env,
|
jobject Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEnv* env, jobject jobj,
|
||||||
jobject jobj,
|
jobject jcallback) {
|
||||||
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;");
|
||||||
@ -849,7 +842,7 @@ jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEn
|
|||||||
|
|
||||||
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) {
|
||||||
@ -870,7 +863,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,
|
||||||
@ -919,10 +912,4 @@ 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,9 +144,6 @@
|
|||||||
<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 Reference to the system instance
|
* \param system Raw pointer 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 Reference to the system instance
|
* \param system Raw pointer to the system instance
|
||||||
* \param vfs Reference to the VfsFilesystem instance in Core::System
|
* \param vfs Raw pointer 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 Reference to the VfsFilesystem instance in Core::System
|
* \param vfs Raw pointer to the VfsFilesystem instance in Core::System
|
||||||
* \param filename Path to the NCA file
|
* \param filename Path to the NCA file
|
||||||
* \param registered_cache Reference to the registered cache that the NCA will be installed to
|
* \param registered_cache Raw pointer 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 Reference to the system instance
|
* \param system Raw pointer to the system instance
|
||||||
* \param provider Reference to the content provider that's tracking indexed games
|
* \param provider Raw pointer 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 Reference to the system instance
|
* \param system Raw pointer 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 =
|
const auto loader = Loader::GetLoader(
|
||||||
Loader::GetLoader(system, system.GetFilesystem()->OpenFile(game_path, FileSys::Mode::Read));
|
*system, system->GetFilesystem()->OpenFile(game_path, FileSys::Mode::Read));
|
||||||
if (loader == nullptr) {
|
if (loader == nullptr) {
|
||||||
return GameVerificationResult::NotImplemented;
|
return GameVerificationResult::NotImplemented;
|
||||||
}
|
}
|
||||||
@ -368,11 +368,4 @@ 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
|
||||||
|
@ -39,10 +39,6 @@ void SortPhysicalDevicesPerVendor(std::vector<VkPhysicalDevice>& devices,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsMicrosoftDozen(const char* device_name) {
|
|
||||||
return std::strstr(device_name, "Microsoft") != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SortPhysicalDevices(std::vector<VkPhysicalDevice>& devices, const InstanceDispatch& dld) {
|
void SortPhysicalDevices(std::vector<VkPhysicalDevice>& devices, const InstanceDispatch& dld) {
|
||||||
// Sort by name, this will set a base and make GPUs with higher numbers appear first
|
// Sort by name, this will set a base and make GPUs with higher numbers appear first
|
||||||
// (e.g. GTX 1650 will intentionally be listed before a GTX 1080).
|
// (e.g. GTX 1650 will intentionally be listed before a GTX 1080).
|
||||||
@ -56,12 +52,6 @@ void SortPhysicalDevices(std::vector<VkPhysicalDevice>& devices, const InstanceD
|
|||||||
});
|
});
|
||||||
// Prefer Nvidia over AMD, AMD over Intel, Intel over the rest.
|
// Prefer Nvidia over AMD, AMD over Intel, Intel over the rest.
|
||||||
SortPhysicalDevicesPerVendor(devices, dld, {0x10DE, 0x1002, 0x8086});
|
SortPhysicalDevicesPerVendor(devices, dld, {0x10DE, 0x1002, 0x8086});
|
||||||
// Demote Microsoft's Dozen devices to the bottom.
|
|
||||||
SortPhysicalDevices(
|
|
||||||
devices, dld,
|
|
||||||
[](const VkPhysicalDeviceProperties& lhs, const VkPhysicalDeviceProperties& rhs) {
|
|
||||||
return IsMicrosoftDozen(rhs.deviceName) && !IsMicrosoftDozen(lhs.deviceName);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
@ -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
|
||||||
OnCheckFirmwareDecryption();
|
OnReinitializeKeys(ReinitializeKeyBehavior::NoWarning);
|
||||||
|
|
||||||
game_list->LoadCompatibilityList();
|
game_list->LoadCompatibilityList();
|
||||||
game_list->PopulateAsync(UISettings::values.game_dirs);
|
game_list->PopulateAsync(UISettings::values.game_dirs);
|
||||||
@ -1574,6 +1574,8 @@ 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); });
|
||||||
@ -2499,7 +2501,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, program_id);
|
const size_t count = ContentManager::RemoveAllDLC(system.get(), 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."));
|
||||||
@ -2796,7 +2798,8 @@ void GMainWindow::OnGameListVerifyIntegrity(const std::string& game_path) {
|
|||||||
return progress.wasCanceled();
|
return progress.wasCanceled();
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto result = ContentManager::VerifyGameContents(*system, game_path, QtProgressCallback);
|
const auto result =
|
||||||
|
ContentManager::VerifyGameContents(system.get(), game_path, QtProgressCallback);
|
||||||
progress.close();
|
progress.close();
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case ContentManager::GameVerificationResult::Success:
|
case ContentManager::GameVerificationResult::Success:
|
||||||
@ -3265,7 +3268,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, *vfs, file.toStdString(),
|
return ContentManager::InstallNSP(system.get(), vfs.get(), file.toStdString(),
|
||||||
progress_callback);
|
progress_callback);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -3368,7 +3371,7 @@ ContentManager::InstallResult GMainWindow::InstallNCA(const QString& filename) {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
return ContentManager::InstallNCA(*vfs, filename.toStdString(), *registered_cache,
|
return ContentManager::InstallNCA(vfs.get(), filename.toStdString(), registered_cache,
|
||||||
static_cast<FileSys::TitleType>(index), progress_callback);
|
static_cast<FileSys::TitleType>(index), progress_callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4118,7 +4121,7 @@ void GMainWindow::OnVerifyInstalledContents() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const std::vector<std::string> result =
|
const std::vector<std::string> result =
|
||||||
ContentManager::VerifyInstalledContents(*system, *provider, QtProgressCallback);
|
ContentManager::VerifyInstalledContents(system.get(), provider.get(), QtProgressCallback);
|
||||||
progress.close();
|
progress.close();
|
||||||
|
|
||||||
if (result.empty()) {
|
if (result.empty()) {
|
||||||
@ -4548,20 +4551,122 @@ void GMainWindow::OnMouseActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GMainWindow::OnCheckFirmwareDecryption() {
|
void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
|
||||||
|
if (behavior == ReinitializeKeyBehavior::Warning) {
|
||||||
|
const auto res = QMessageBox::information(
|
||||||
|
this, tr("Confirm Key Rederivation"),
|
||||||
|
tr("You are about to force rederive all of your keys. \nIf you do not know what "
|
||||||
|
"this "
|
||||||
|
"means or what you are doing, \nthis is a potentially destructive action. "
|
||||||
|
"\nPlease "
|
||||||
|
"make sure this is what you want \nand optionally make backups.\n\nThis will "
|
||||||
|
"delete "
|
||||||
|
"your autogenerated key files and re-run the key derivation module."),
|
||||||
|
QMessageBox::StandardButtons{QMessageBox::Ok, QMessageBox::Cancel});
|
||||||
|
|
||||||
|
if (res == QMessageBox::Cancel)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto keys_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::KeysDir);
|
||||||
|
|
||||||
|
Common::FS::RemoveFile(keys_dir / "prod.keys_autogenerated");
|
||||||
|
Common::FS::RemoveFile(keys_dir / "console.keys_autogenerated");
|
||||||
|
Common::FS::RemoveFile(keys_dir / "title.keys_autogenerated");
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
|
||||||
|
bool all_keys_present{true};
|
||||||
|
|
||||||
|
if (keys.BaseDeriveNecessary()) {
|
||||||
|
Core::Crypto::PartitionDataManager pdm{vfs->OpenDirectory("", FileSys::Mode::Read)};
|
||||||
|
|
||||||
|
const auto function = [this, &keys, &pdm] {
|
||||||
|
keys.PopulateFromPartitionData(pdm);
|
||||||
|
|
||||||
|
system->GetFileSystemController().CreateFactories(*vfs);
|
||||||
|
keys.DeriveETicket(pdm, system->GetContentProvider());
|
||||||
|
};
|
||||||
|
|
||||||
|
QString errors;
|
||||||
|
if (!pdm.HasFuses()) {
|
||||||
|
errors += tr("Missing fuses");
|
||||||
|
}
|
||||||
|
if (!pdm.HasBoot0()) {
|
||||||
|
errors += tr(" - Missing BOOT0");
|
||||||
|
}
|
||||||
|
if (!pdm.HasPackage2()) {
|
||||||
|
errors += tr(" - Missing BCPKG2-1-Normal-Main");
|
||||||
|
}
|
||||||
|
if (!pdm.HasProdInfo()) {
|
||||||
|
errors += tr(" - Missing PRODINFO");
|
||||||
|
}
|
||||||
|
if (!errors.isEmpty()) {
|
||||||
|
all_keys_present = false;
|
||||||
|
QMessageBox::warning(
|
||||||
|
this, tr("Derivation Components Missing"),
|
||||||
|
tr("Encryption keys are missing. "
|
||||||
|
"<br>Please follow <a href='https://yuzu-emu.org/help/quickstart/'>the yuzu "
|
||||||
|
"quickstart guide</a> to get all your keys, firmware and "
|
||||||
|
"games.<br><br><small>(%1)</small>")
|
||||||
|
.arg(errors));
|
||||||
|
}
|
||||||
|
|
||||||
|
QProgressDialog prog(this);
|
||||||
|
prog.setRange(0, 0);
|
||||||
|
prog.setLabelText(tr("Deriving keys...\nThis may take up to a minute depending \non your "
|
||||||
|
"system's performance."));
|
||||||
|
prog.setWindowTitle(tr("Deriving Keys"));
|
||||||
|
|
||||||
|
prog.show();
|
||||||
|
|
||||||
|
auto future = QtConcurrent::run(function);
|
||||||
|
while (!future.isFinished()) {
|
||||||
|
QCoreApplication::processEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
prog.close();
|
||||||
|
}
|
||||||
|
|
||||||
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("Derivation Components Missing"),
|
this, tr("System Archive Decryption Failed"),
|
||||||
tr("Encryption keys are missing. "
|
tr("Encryption keys failed to decrypt firmware. "
|
||||||
"<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,6 +125,11 @@ enum class EmulatedDirectoryTarget {
|
|||||||
SDMC,
|
SDMC,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class ReinitializeKeyBehavior {
|
||||||
|
NoWarning,
|
||||||
|
Warning,
|
||||||
|
};
|
||||||
|
|
||||||
namespace VkDeviceInfo {
|
namespace VkDeviceInfo {
|
||||||
class Record;
|
class Record;
|
||||||
}
|
}
|
||||||
@ -395,7 +400,7 @@ private slots:
|
|||||||
void OnMiiEdit();
|
void OnMiiEdit();
|
||||||
void OnOpenControllerMenu();
|
void OnOpenControllerMenu();
|
||||||
void OnCaptureScreenshot();
|
void OnCaptureScreenshot();
|
||||||
void OnCheckFirmwareDecryption();
|
void OnReinitializeKeys(ReinitializeKeyBehavior behavior);
|
||||||
void OnLanguageChanged(const QString& locale);
|
void OnLanguageChanged(const QString& locale);
|
||||||
void OnMouseActivity();
|
void OnMouseActivity();
|
||||||
bool OnShutdownBegin();
|
bool OnShutdownBegin();
|
||||||
@ -436,6 +441,7 @@ 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,6 +224,11 @@
|
|||||||
<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