forked from etc/pineapple-src
early-access version 3930
This commit is contained in:
parent
db958cf156
commit
72b22f183c
55 changed files with 1482 additions and 528 deletions
|
@ -1,7 +1,7 @@
|
||||||
yuzu emulator early access
|
yuzu emulator early access
|
||||||
=============
|
=============
|
||||||
|
|
||||||
This is the source code for early-access 3929.
|
This is the source code for early-access 3930.
|
||||||
|
|
||||||
## Legal Notice
|
## Legal Notice
|
||||||
|
|
||||||
|
|
|
@ -15,13 +15,9 @@ import androidx.annotation.Keep
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
import org.yuzu.yuzu_emu.YuzuApplication.Companion.appContext
|
|
||||||
import org.yuzu.yuzu_emu.activities.EmulationActivity
|
import org.yuzu.yuzu_emu.activities.EmulationActivity
|
||||||
import org.yuzu.yuzu_emu.utils.DocumentsTree.Companion.isNativePath
|
import org.yuzu.yuzu_emu.utils.DocumentsTree.Companion.isNativePath
|
||||||
import org.yuzu.yuzu_emu.utils.FileUtil.exists
|
import org.yuzu.yuzu_emu.utils.FileUtil
|
||||||
import org.yuzu.yuzu_emu.utils.FileUtil.getFileSize
|
|
||||||
import org.yuzu.yuzu_emu.utils.FileUtil.isDirectory
|
|
||||||
import org.yuzu.yuzu_emu.utils.FileUtil.openContentUri
|
|
||||||
import org.yuzu.yuzu_emu.utils.Log
|
import org.yuzu.yuzu_emu.utils.Log
|
||||||
import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable
|
import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable
|
||||||
|
|
||||||
|
@ -75,7 +71,7 @@ object NativeLibrary {
|
||||||
return if (isNativePath(path!!)) {
|
return if (isNativePath(path!!)) {
|
||||||
YuzuApplication.documentsTree!!.openContentUri(path, openmode)
|
YuzuApplication.documentsTree!!.openContentUri(path, openmode)
|
||||||
} else {
|
} else {
|
||||||
openContentUri(appContext, path, openmode)
|
FileUtil.openContentUri(path, openmode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,7 +81,7 @@ object NativeLibrary {
|
||||||
return if (isNativePath(path!!)) {
|
return if (isNativePath(path!!)) {
|
||||||
YuzuApplication.documentsTree!!.getFileSize(path)
|
YuzuApplication.documentsTree!!.getFileSize(path)
|
||||||
} else {
|
} else {
|
||||||
getFileSize(appContext, path)
|
FileUtil.getFileSize(path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,7 +91,7 @@ object NativeLibrary {
|
||||||
return if (isNativePath(path!!)) {
|
return if (isNativePath(path!!)) {
|
||||||
YuzuApplication.documentsTree!!.exists(path)
|
YuzuApplication.documentsTree!!.exists(path)
|
||||||
} else {
|
} else {
|
||||||
exists(appContext, path)
|
FileUtil.exists(path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,7 +101,7 @@ object NativeLibrary {
|
||||||
return if (isNativePath(path!!)) {
|
return if (isNativePath(path!!)) {
|
||||||
YuzuApplication.documentsTree!!.isDirectory(path)
|
YuzuApplication.documentsTree!!.isDirectory(path)
|
||||||
} else {
|
} else {
|
||||||
isDirectory(appContext, path)
|
FileUtil.isDirectory(path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,7 @@ class YuzuApplication : Application() {
|
||||||
application = this
|
application = this
|
||||||
documentsTree = DocumentsTree()
|
documentsTree = DocumentsTree()
|
||||||
DirectoryInitialization.start()
|
DirectoryInitialization.start()
|
||||||
GpuDriverHelper.initializeDriverParameters(applicationContext)
|
GpuDriverHelper.initializeDriverParameters()
|
||||||
NativeLibrary.logDeviceInfo()
|
NativeLibrary.logDeviceInfo()
|
||||||
|
|
||||||
createNotificationChannels()
|
createNotificationChannels()
|
||||||
|
|
117
src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/DriverAdapter.kt
Executable file
117
src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/DriverAdapter.kt
Executable file
|
@ -0,0 +1,117 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
package org.yuzu.yuzu_emu.adapters
|
||||||
|
|
||||||
|
import android.text.TextUtils
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.recyclerview.widget.AsyncDifferConfig
|
||||||
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
|
import androidx.recyclerview.widget.ListAdapter
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import org.yuzu.yuzu_emu.R
|
||||||
|
import org.yuzu.yuzu_emu.databinding.CardDriverOptionBinding
|
||||||
|
import org.yuzu.yuzu_emu.model.DriverViewModel
|
||||||
|
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
|
||||||
|
import org.yuzu.yuzu_emu.utils.GpuDriverMetadata
|
||||||
|
|
||||||
|
class DriverAdapter(private val driverViewModel: DriverViewModel) :
|
||||||
|
ListAdapter<Pair<String, GpuDriverMetadata>, DriverAdapter.DriverViewHolder>(
|
||||||
|
AsyncDifferConfig.Builder(DiffCallback()).build()
|
||||||
|
) {
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DriverViewHolder {
|
||||||
|
val binding =
|
||||||
|
CardDriverOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||||
|
return DriverViewHolder(binding)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int = currentList.size
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: DriverViewHolder, position: Int) =
|
||||||
|
holder.bind(currentList[position])
|
||||||
|
|
||||||
|
private fun onSelectDriver(position: Int) {
|
||||||
|
driverViewModel.setSelectedDriverIndex(position)
|
||||||
|
notifyItemChanged(driverViewModel.previouslySelectedDriver)
|
||||||
|
notifyItemChanged(driverViewModel.selectedDriver)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onDeleteDriver(driverData: Pair<String, GpuDriverMetadata>, position: Int) {
|
||||||
|
if (driverViewModel.selectedDriver > position) {
|
||||||
|
driverViewModel.setSelectedDriverIndex(driverViewModel.selectedDriver - 1)
|
||||||
|
}
|
||||||
|
if (GpuDriverHelper.customDriverData == driverData.second) {
|
||||||
|
driverViewModel.setSelectedDriverIndex(0)
|
||||||
|
}
|
||||||
|
driverViewModel.driversToDelete.add(driverData.first)
|
||||||
|
driverViewModel.removeDriver(driverData)
|
||||||
|
notifyItemRemoved(position)
|
||||||
|
notifyItemChanged(driverViewModel.selectedDriver)
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class DriverViewHolder(val binding: CardDriverOptionBinding) :
|
||||||
|
RecyclerView.ViewHolder(binding.root) {
|
||||||
|
private lateinit var driverData: Pair<String, GpuDriverMetadata>
|
||||||
|
|
||||||
|
fun bind(driverData: Pair<String, GpuDriverMetadata>) {
|
||||||
|
this.driverData = driverData
|
||||||
|
val driver = driverData.second
|
||||||
|
|
||||||
|
binding.apply {
|
||||||
|
radioButton.isChecked = driverViewModel.selectedDriver == bindingAdapterPosition
|
||||||
|
root.setOnClickListener {
|
||||||
|
onSelectDriver(bindingAdapterPosition)
|
||||||
|
}
|
||||||
|
buttonDelete.setOnClickListener {
|
||||||
|
onDeleteDriver(driverData, bindingAdapterPosition)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delay marquee by 3s
|
||||||
|
title.postDelayed(
|
||||||
|
{
|
||||||
|
title.isSelected = true
|
||||||
|
title.ellipsize = TextUtils.TruncateAt.MARQUEE
|
||||||
|
version.isSelected = true
|
||||||
|
version.ellipsize = TextUtils.TruncateAt.MARQUEE
|
||||||
|
description.isSelected = true
|
||||||
|
description.ellipsize = TextUtils.TruncateAt.MARQUEE
|
||||||
|
},
|
||||||
|
3000
|
||||||
|
)
|
||||||
|
if (driver.name == null) {
|
||||||
|
title.setText(R.string.system_gpu_driver)
|
||||||
|
description.text = ""
|
||||||
|
version.text = ""
|
||||||
|
version.visibility = View.GONE
|
||||||
|
description.visibility = View.GONE
|
||||||
|
buttonDelete.visibility = View.GONE
|
||||||
|
} else {
|
||||||
|
title.text = driver.name
|
||||||
|
version.text = driver.version
|
||||||
|
description.text = driver.description
|
||||||
|
version.visibility = View.VISIBLE
|
||||||
|
description.visibility = View.VISIBLE
|
||||||
|
buttonDelete.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DiffCallback : DiffUtil.ItemCallback<Pair<String, GpuDriverMetadata>>() {
|
||||||
|
override fun areItemsTheSame(
|
||||||
|
oldItem: Pair<String, GpuDriverMetadata>,
|
||||||
|
newItem: Pair<String, GpuDriverMetadata>
|
||||||
|
): Boolean {
|
||||||
|
return oldItem.first == newItem.first
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun areContentsTheSame(
|
||||||
|
oldItem: Pair<String, GpuDriverMetadata>,
|
||||||
|
newItem: Pair<String, GpuDriverMetadata>
|
||||||
|
): Boolean {
|
||||||
|
return oldItem.second == newItem.second
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,185 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
package org.yuzu.yuzu_emu.fragments
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import androidx.core.view.ViewCompat
|
||||||
|
import androidx.core.view.WindowInsetsCompat
|
||||||
|
import androidx.core.view.updatePadding
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.fragment.app.activityViewModels
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.navigation.findNavController
|
||||||
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
|
import com.google.android.material.transition.MaterialSharedAxis
|
||||||
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.yuzu.yuzu_emu.R
|
||||||
|
import org.yuzu.yuzu_emu.adapters.DriverAdapter
|
||||||
|
import org.yuzu.yuzu_emu.databinding.FragmentDriverManagerBinding
|
||||||
|
import org.yuzu.yuzu_emu.model.DriverViewModel
|
||||||
|
import org.yuzu.yuzu_emu.model.HomeViewModel
|
||||||
|
import org.yuzu.yuzu_emu.utils.FileUtil
|
||||||
|
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
class DriverManagerFragment : Fragment() {
|
||||||
|
private var _binding: FragmentDriverManagerBinding? = null
|
||||||
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
|
private val homeViewModel: HomeViewModel by activityViewModels()
|
||||||
|
private val driverViewModel: DriverViewModel by activityViewModels()
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)
|
||||||
|
returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
|
||||||
|
reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View {
|
||||||
|
_binding = FragmentDriverManagerBinding.inflate(inflater)
|
||||||
|
return binding.root
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
homeViewModel.setNavigationVisibility(visible = false, animated = true)
|
||||||
|
homeViewModel.setStatusBarShadeVisibility(visible = false)
|
||||||
|
|
||||||
|
if (!driverViewModel.isInteractionAllowed) {
|
||||||
|
DriversLoadingDialogFragment().show(
|
||||||
|
childFragmentManager,
|
||||||
|
DriversLoadingDialogFragment.TAG
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.toolbarDrivers.setNavigationOnClickListener {
|
||||||
|
binding.root.findNavController().popBackStack()
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.buttonInstall.setOnClickListener {
|
||||||
|
getDriver.launch(arrayOf("application/zip"))
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.listDrivers.apply {
|
||||||
|
layoutManager = GridLayoutManager(
|
||||||
|
requireContext(),
|
||||||
|
resources.getInteger(R.integer.grid_columns)
|
||||||
|
)
|
||||||
|
adapter = DriverAdapter(driverViewModel)
|
||||||
|
}
|
||||||
|
|
||||||
|
viewLifecycleOwner.lifecycleScope.apply {
|
||||||
|
launch {
|
||||||
|
driverViewModel.driverList.collectLatest {
|
||||||
|
(binding.listDrivers.adapter as DriverAdapter).submitList(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
launch {
|
||||||
|
driverViewModel.newDriverInstalled.collect {
|
||||||
|
if (_binding != null && it) {
|
||||||
|
(binding.listDrivers.adapter as DriverAdapter).apply {
|
||||||
|
notifyItemChanged(driverViewModel.previouslySelectedDriver)
|
||||||
|
notifyItemChanged(driverViewModel.selectedDriver)
|
||||||
|
driverViewModel.setNewDriverInstalled(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setInsets()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start installing requested driver
|
||||||
|
override fun onStop() {
|
||||||
|
super.onStop()
|
||||||
|
driverViewModel.onCloseDriverManager()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setInsets() =
|
||||||
|
ViewCompat.setOnApplyWindowInsetsListener(
|
||||||
|
binding.root
|
||||||
|
) { _: View, windowInsets: WindowInsetsCompat ->
|
||||||
|
val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||||
|
val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout())
|
||||||
|
|
||||||
|
val leftInsets = barInsets.left + cutoutInsets.left
|
||||||
|
val rightInsets = barInsets.right + cutoutInsets.right
|
||||||
|
|
||||||
|
val mlpAppBar = binding.toolbarDrivers.layoutParams as ViewGroup.MarginLayoutParams
|
||||||
|
mlpAppBar.leftMargin = leftInsets
|
||||||
|
mlpAppBar.rightMargin = rightInsets
|
||||||
|
binding.toolbarDrivers.layoutParams = mlpAppBar
|
||||||
|
|
||||||
|
val mlplistDrivers = binding.listDrivers.layoutParams as ViewGroup.MarginLayoutParams
|
||||||
|
mlplistDrivers.leftMargin = leftInsets
|
||||||
|
mlplistDrivers.rightMargin = rightInsets
|
||||||
|
binding.listDrivers.layoutParams = mlplistDrivers
|
||||||
|
|
||||||
|
val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab)
|
||||||
|
val mlpFab =
|
||||||
|
binding.buttonInstall.layoutParams as ViewGroup.MarginLayoutParams
|
||||||
|
mlpFab.leftMargin = leftInsets + fabSpacing
|
||||||
|
mlpFab.rightMargin = rightInsets + fabSpacing
|
||||||
|
mlpFab.bottomMargin = barInsets.bottom + fabSpacing
|
||||||
|
binding.buttonInstall.layoutParams = mlpFab
|
||||||
|
|
||||||
|
binding.listDrivers.updatePadding(
|
||||||
|
bottom = barInsets.bottom +
|
||||||
|
resources.getDimensionPixelSize(R.dimen.spacing_bottom_list_fab)
|
||||||
|
)
|
||||||
|
|
||||||
|
windowInsets
|
||||||
|
}
|
||||||
|
|
||||||
|
private val getDriver =
|
||||||
|
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
|
||||||
|
if (result == null) {
|
||||||
|
return@registerForActivityResult
|
||||||
|
}
|
||||||
|
|
||||||
|
IndeterminateProgressDialogFragment.newInstance(
|
||||||
|
requireActivity(),
|
||||||
|
R.string.installing_driver,
|
||||||
|
false
|
||||||
|
) {
|
||||||
|
// Ignore file exceptions when a user selects an invalid zip
|
||||||
|
try {
|
||||||
|
GpuDriverHelper.copyDriverToInternalStorage(result)
|
||||||
|
} catch (_: IOException) {
|
||||||
|
return@newInstance getString(R.string.select_gpu_driver_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
val driverData = GpuDriverHelper.customDriverData
|
||||||
|
if (driverData.name == null) {
|
||||||
|
return@newInstance getString(R.string.select_gpu_driver_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
val driverInList =
|
||||||
|
driverViewModel.driverList.value.firstOrNull { it.second == driverData }
|
||||||
|
if (driverInList != null) {
|
||||||
|
return@newInstance getString(R.string.driver_already_installed)
|
||||||
|
} else {
|
||||||
|
driverViewModel.addDriver(
|
||||||
|
Pair(
|
||||||
|
"${GpuDriverHelper.driverStoragePath}/${FileUtil.getFilename(result)}",
|
||||||
|
driverData
|
||||||
|
)
|
||||||
|
)
|
||||||
|
driverViewModel.setNewDriverInstalled(true)
|
||||||
|
}
|
||||||
|
return@newInstance Any()
|
||||||
|
}.show(childFragmentManager, IndeterminateProgressDialogFragment.TAG)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,75 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
package org.yuzu.yuzu_emu.fragments
|
||||||
|
|
||||||
|
import android.app.Dialog
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import androidx.fragment.app.activityViewModels
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.yuzu.yuzu_emu.R
|
||||||
|
import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
|
||||||
|
import org.yuzu.yuzu_emu.model.DriverViewModel
|
||||||
|
|
||||||
|
class DriversLoadingDialogFragment : DialogFragment() {
|
||||||
|
private val driverViewModel: DriverViewModel by activityViewModels()
|
||||||
|
|
||||||
|
private lateinit var binding: DialogProgressBarBinding
|
||||||
|
|
||||||
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
|
binding = DialogProgressBarBinding.inflate(layoutInflater)
|
||||||
|
binding.progressBar.isIndeterminate = true
|
||||||
|
|
||||||
|
isCancelable = false
|
||||||
|
|
||||||
|
return MaterialAlertDialogBuilder(requireContext())
|
||||||
|
.setTitle(R.string.loading)
|
||||||
|
.setView(binding.root)
|
||||||
|
.create()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View = binding.root
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
viewLifecycleOwner.lifecycleScope.apply {
|
||||||
|
launch {
|
||||||
|
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||||
|
driverViewModel.areDriversLoading.collect { checkForDismiss() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
launch {
|
||||||
|
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||||
|
driverViewModel.isDriverReady.collect { checkForDismiss() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
launch {
|
||||||
|
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||||
|
driverViewModel.isDeletingDrivers.collect { checkForDismiss() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkForDismiss() {
|
||||||
|
if (driverViewModel.isInteractionAllowed) {
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val TAG = "DriversLoadingDialogFragment"
|
||||||
|
}
|
||||||
|
}
|
|
@ -39,6 +39,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
|
||||||
|
@ -50,6 +51,7 @@ import org.yuzu.yuzu_emu.databinding.DialogOverlayAdjustBinding
|
||||||
import org.yuzu.yuzu_emu.databinding.FragmentEmulationBinding
|
import org.yuzu.yuzu_emu.databinding.FragmentEmulationBinding
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
|
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
||||||
|
import org.yuzu.yuzu_emu.model.DriverViewModel
|
||||||
import org.yuzu.yuzu_emu.model.Game
|
import org.yuzu.yuzu_emu.model.Game
|
||||||
import org.yuzu.yuzu_emu.model.EmulationViewModel
|
import org.yuzu.yuzu_emu.model.EmulationViewModel
|
||||||
import org.yuzu.yuzu_emu.overlay.InputOverlay
|
import org.yuzu.yuzu_emu.overlay.InputOverlay
|
||||||
|
@ -70,6 +72,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||||
private lateinit var game: Game
|
private lateinit var game: Game
|
||||||
|
|
||||||
private val emulationViewModel: EmulationViewModel by activityViewModels()
|
private val emulationViewModel: EmulationViewModel by activityViewModels()
|
||||||
|
private val driverViewModel: DriverViewModel by activityViewModels()
|
||||||
|
|
||||||
private var isInFoldableLayout = false
|
private var isInFoldableLayout = false
|
||||||
|
|
||||||
|
@ -299,6 +302,21 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
launch {
|
||||||
|
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||||
|
driverViewModel.isDriverReady.collect {
|
||||||
|
if (it && !emulationState.isRunning) {
|
||||||
|
if (!DirectoryInitialization.areDirectoriesReady) {
|
||||||
|
DirectoryInitialization.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
updateScreenLayout()
|
||||||
|
|
||||||
|
emulationState.run(emulationActivity!!.isActivityRecreated)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -332,17 +350,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
|
||||||
super.onResume()
|
|
||||||
if (!DirectoryInitialization.areDirectoriesReady) {
|
|
||||||
DirectoryInitialization.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
updateScreenLayout()
|
|
||||||
|
|
||||||
emulationState.run(emulationActivity!!.isActivityRecreated)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPause() {
|
override fun onPause() {
|
||||||
if (emulationState.isRunning && emulationActivity?.isInPictureInPictureMode != true) {
|
if (emulationState.isRunning && emulationActivity?.isInPictureInPictureMode != true) {
|
||||||
emulationState.pause()
|
emulationState.pause()
|
||||||
|
|
|
@ -5,7 +5,6 @@ package org.yuzu.yuzu_emu.fragments
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.content.ActivityNotFoundException
|
import android.content.ActivityNotFoundException
|
||||||
import android.content.DialogInterface
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
@ -28,7 +27,6 @@ import androidx.fragment.app.activityViewModels
|
||||||
import androidx.navigation.findNavController
|
import androidx.navigation.findNavController
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|
||||||
import com.google.android.material.transition.MaterialSharedAxis
|
import com.google.android.material.transition.MaterialSharedAxis
|
||||||
import org.yuzu.yuzu_emu.BuildConfig
|
import org.yuzu.yuzu_emu.BuildConfig
|
||||||
import org.yuzu.yuzu_emu.HomeNavigationDirections
|
import org.yuzu.yuzu_emu.HomeNavigationDirections
|
||||||
|
@ -37,6 +35,7 @@ import org.yuzu.yuzu_emu.adapters.HomeSettingAdapter
|
||||||
import org.yuzu.yuzu_emu.databinding.FragmentHomeSettingsBinding
|
import org.yuzu.yuzu_emu.databinding.FragmentHomeSettingsBinding
|
||||||
import org.yuzu.yuzu_emu.features.DocumentProvider
|
import org.yuzu.yuzu_emu.features.DocumentProvider
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
||||||
|
import org.yuzu.yuzu_emu.model.DriverViewModel
|
||||||
import org.yuzu.yuzu_emu.model.HomeSetting
|
import org.yuzu.yuzu_emu.model.HomeSetting
|
||||||
import org.yuzu.yuzu_emu.model.HomeViewModel
|
import org.yuzu.yuzu_emu.model.HomeViewModel
|
||||||
import org.yuzu.yuzu_emu.ui.main.MainActivity
|
import org.yuzu.yuzu_emu.ui.main.MainActivity
|
||||||
|
@ -50,6 +49,7 @@ class HomeSettingsFragment : Fragment() {
|
||||||
private lateinit var mainActivity: MainActivity
|
private lateinit var mainActivity: MainActivity
|
||||||
|
|
||||||
private val homeViewModel: HomeViewModel by activityViewModels()
|
private val homeViewModel: HomeViewModel by activityViewModels()
|
||||||
|
private val driverViewModel: DriverViewModel by activityViewModels()
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
@ -107,13 +107,17 @@ class HomeSettingsFragment : Fragment() {
|
||||||
)
|
)
|
||||||
add(
|
add(
|
||||||
HomeSetting(
|
HomeSetting(
|
||||||
R.string.install_gpu_driver,
|
R.string.gpu_driver_manager,
|
||||||
R.string.install_gpu_driver_description,
|
R.string.install_gpu_driver_description,
|
||||||
R.drawable.ic_exit,
|
R.drawable.ic_build,
|
||||||
{ driverInstaller() },
|
{
|
||||||
|
binding.root.findNavController()
|
||||||
|
.navigate(R.id.action_homeSettingsFragment_to_driverManagerFragment)
|
||||||
|
},
|
||||||
{ GpuDriverHelper.supportsCustomDriverLoading() },
|
{ GpuDriverHelper.supportsCustomDriverLoading() },
|
||||||
R.string.custom_driver_not_supported,
|
R.string.custom_driver_not_supported,
|
||||||
R.string.custom_driver_not_supported_description
|
R.string.custom_driver_not_supported_description,
|
||||||
|
driverViewModel.selectedDriverMetadata
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
add(
|
add(
|
||||||
|
@ -292,31 +296,6 @@ class HomeSettingsFragment : Fragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun driverInstaller() {
|
|
||||||
// Get the driver name for the dialog message.
|
|
||||||
var driverName = GpuDriverHelper.customDriverName
|
|
||||||
if (driverName == null) {
|
|
||||||
driverName = getString(R.string.system_gpu_driver)
|
|
||||||
}
|
|
||||||
|
|
||||||
MaterialAlertDialogBuilder(requireContext())
|
|
||||||
.setTitle(getString(R.string.select_gpu_driver_title))
|
|
||||||
.setMessage(driverName)
|
|
||||||
.setNegativeButton(android.R.string.cancel, null)
|
|
||||||
.setNeutralButton(R.string.select_gpu_driver_default) { _: DialogInterface?, _: Int ->
|
|
||||||
GpuDriverHelper.installDefaultDriver(requireContext())
|
|
||||||
Toast.makeText(
|
|
||||||
requireContext(),
|
|
||||||
R.string.select_gpu_driver_use_default,
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
.setPositiveButton(R.string.select_gpu_driver_install) { _: DialogInterface?, _: Int ->
|
|
||||||
mainActivity.getDriver.launch(arrayOf("application/zip"))
|
|
||||||
}
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun shareLog() {
|
private fun shareLog() {
|
||||||
val file = DocumentFile.fromSingleUri(
|
val file = DocumentFile.fromSingleUri(
|
||||||
mainActivity,
|
mainActivity,
|
||||||
|
|
|
@ -10,8 +10,8 @@ import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import androidx.fragment.app.FragmentActivity
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.ViewModelProvider
|
import androidx.lifecycle.ViewModelProvider
|
||||||
|
@ -78,6 +78,10 @@ class IndeterminateProgressDialogFragment : DialogFragment() {
|
||||||
requireActivity().supportFragmentManager,
|
requireActivity().supportFragmentManager,
|
||||||
MessageDialogFragment.TAG
|
MessageDialogFragment.TAG
|
||||||
)
|
)
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
}
|
}
|
||||||
taskViewModel.clear()
|
taskViewModel.clear()
|
||||||
}
|
}
|
||||||
|
@ -115,7 +119,7 @@ class IndeterminateProgressDialogFragment : DialogFragment() {
|
||||||
private const val CANCELLABLE = "Cancellable"
|
private const val CANCELLABLE = "Cancellable"
|
||||||
|
|
||||||
fun newInstance(
|
fun newInstance(
|
||||||
activity: AppCompatActivity,
|
activity: FragmentActivity,
|
||||||
titleId: Int,
|
titleId: Int,
|
||||||
cancellable: Boolean = false,
|
cancellable: Boolean = false,
|
||||||
task: () -> Any
|
task: () -> Any
|
||||||
|
|
158
src/android/app/src/main/java/org/yuzu/yuzu_emu/model/DriverViewModel.kt
Executable file
158
src/android/app/src/main/java/org/yuzu/yuzu_emu/model/DriverViewModel.kt
Executable file
|
@ -0,0 +1,158 @@
|
||||||
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
package org.yuzu.yuzu_emu.model
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.yuzu.yuzu_emu.R
|
||||||
|
import org.yuzu.yuzu_emu.YuzuApplication
|
||||||
|
import org.yuzu.yuzu_emu.utils.FileUtil
|
||||||
|
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
|
||||||
|
import org.yuzu.yuzu_emu.utils.GpuDriverMetadata
|
||||||
|
import java.io.BufferedOutputStream
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class DriverViewModel : ViewModel() {
|
||||||
|
private val _areDriversLoading = MutableStateFlow(false)
|
||||||
|
val areDriversLoading: StateFlow<Boolean> get() = _areDriversLoading
|
||||||
|
|
||||||
|
private val _isDriverReady = MutableStateFlow(true)
|
||||||
|
val isDriverReady: StateFlow<Boolean> get() = _isDriverReady
|
||||||
|
|
||||||
|
private val _isDeletingDrivers = MutableStateFlow(false)
|
||||||
|
val isDeletingDrivers: StateFlow<Boolean> get() = _isDeletingDrivers
|
||||||
|
|
||||||
|
private val _driverList = MutableStateFlow(mutableListOf<Pair<String, GpuDriverMetadata>>())
|
||||||
|
val driverList: StateFlow<MutableList<Pair<String, GpuDriverMetadata>>> get() = _driverList
|
||||||
|
|
||||||
|
var previouslySelectedDriver = 0
|
||||||
|
var selectedDriver = -1
|
||||||
|
|
||||||
|
private val _selectedDriverMetadata =
|
||||||
|
MutableStateFlow(
|
||||||
|
GpuDriverHelper.customDriverData.name
|
||||||
|
?: YuzuApplication.appContext.getString(R.string.system_gpu_driver)
|
||||||
|
)
|
||||||
|
val selectedDriverMetadata: StateFlow<String> get() = _selectedDriverMetadata
|
||||||
|
|
||||||
|
private val _newDriverInstalled = MutableStateFlow(false)
|
||||||
|
val newDriverInstalled: StateFlow<Boolean> get() = _newDriverInstalled
|
||||||
|
|
||||||
|
val driversToDelete = mutableListOf<String>()
|
||||||
|
|
||||||
|
val isInteractionAllowed
|
||||||
|
get() = !areDriversLoading.value && isDriverReady.value && !isDeletingDrivers.value
|
||||||
|
|
||||||
|
init {
|
||||||
|
_areDriversLoading.value = true
|
||||||
|
viewModelScope.launch {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
val drivers = GpuDriverHelper.getDrivers()
|
||||||
|
val currentDriverMetadata = GpuDriverHelper.customDriverData
|
||||||
|
for (i in drivers.indices) {
|
||||||
|
if (drivers[i].second == currentDriverMetadata) {
|
||||||
|
setSelectedDriverIndex(i)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a user had installed a driver before the manager was implemented, this zips
|
||||||
|
// the installed driver to UserData/gpu_drivers/CustomDriver.zip so that it can
|
||||||
|
// be indexed and exported as expected.
|
||||||
|
if (selectedDriver == -1) {
|
||||||
|
val driverToSave =
|
||||||
|
File(GpuDriverHelper.driverStoragePath, "CustomDriver.zip")
|
||||||
|
driverToSave.createNewFile()
|
||||||
|
FileUtil.zipFromInternalStorage(
|
||||||
|
File(GpuDriverHelper.driverInstallationPath!!),
|
||||||
|
GpuDriverHelper.driverInstallationPath!!,
|
||||||
|
BufferedOutputStream(driverToSave.outputStream())
|
||||||
|
)
|
||||||
|
drivers.add(Pair(driverToSave.path, currentDriverMetadata))
|
||||||
|
setSelectedDriverIndex(drivers.size - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
_driverList.value = drivers
|
||||||
|
_areDriversLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setSelectedDriverIndex(value: Int) {
|
||||||
|
if (selectedDriver != -1) {
|
||||||
|
previouslySelectedDriver = selectedDriver
|
||||||
|
}
|
||||||
|
selectedDriver = value
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setNewDriverInstalled(value: Boolean) {
|
||||||
|
_newDriverInstalled.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addDriver(driverData: Pair<String, GpuDriverMetadata>) {
|
||||||
|
val driverIndex = _driverList.value.indexOfFirst { it == driverData }
|
||||||
|
if (driverIndex == -1) {
|
||||||
|
setSelectedDriverIndex(_driverList.value.size)
|
||||||
|
_driverList.value.add(driverData)
|
||||||
|
_selectedDriverMetadata.value = driverData.second.name
|
||||||
|
?: YuzuApplication.appContext.getString(R.string.system_gpu_driver)
|
||||||
|
} else {
|
||||||
|
setSelectedDriverIndex(driverIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeDriver(driverData: Pair<String, GpuDriverMetadata>) {
|
||||||
|
_driverList.value.remove(driverData)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onCloseDriverManager() {
|
||||||
|
_isDeletingDrivers.value = true
|
||||||
|
viewModelScope.launch {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
driversToDelete.forEach {
|
||||||
|
val driver = File(it)
|
||||||
|
if (driver.exists()) {
|
||||||
|
driver.delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
driversToDelete.clear()
|
||||||
|
_isDeletingDrivers.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GpuDriverHelper.customDriverData == driverList.value[selectedDriver].second) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_isDriverReady.value = false
|
||||||
|
viewModelScope.launch {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
if (selectedDriver == 0) {
|
||||||
|
GpuDriverHelper.installDefaultDriver()
|
||||||
|
setDriverReady()
|
||||||
|
return@withContext
|
||||||
|
}
|
||||||
|
|
||||||
|
val driverToInstall = File(driverList.value[selectedDriver].first)
|
||||||
|
if (driverToInstall.exists()) {
|
||||||
|
GpuDriverHelper.installCustomDriver(driverToInstall)
|
||||||
|
} else {
|
||||||
|
GpuDriverHelper.installDefaultDriver()
|
||||||
|
}
|
||||||
|
setDriverReady()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setDriverReady() {
|
||||||
|
_isDriverReady.value = true
|
||||||
|
_selectedDriverMetadata.value = GpuDriverHelper.customDriverData.name
|
||||||
|
?: YuzuApplication.appContext.getString(R.string.system_gpu_driver)
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,12 +29,10 @@ import androidx.navigation.fragment.NavHostFragment
|
||||||
import androidx.navigation.ui.setupWithNavController
|
import androidx.navigation.ui.setupWithNavController
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import com.google.android.material.color.MaterialColors
|
import com.google.android.material.color.MaterialColors
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|
||||||
import com.google.android.material.navigation.NavigationBarView
|
import com.google.android.material.navigation.NavigationBarView
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FilenameFilter
|
import java.io.FilenameFilter
|
||||||
import java.io.IOException
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
@ -43,7 +41,6 @@ import org.yuzu.yuzu_emu.NativeLibrary
|
||||||
import org.yuzu.yuzu_emu.R
|
import org.yuzu.yuzu_emu.R
|
||||||
import org.yuzu.yuzu_emu.activities.EmulationActivity
|
import org.yuzu.yuzu_emu.activities.EmulationActivity
|
||||||
import org.yuzu.yuzu_emu.databinding.ActivityMainBinding
|
import org.yuzu.yuzu_emu.databinding.ActivityMainBinding
|
||||||
import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
|
|
||||||
import org.yuzu.yuzu_emu.features.DocumentProvider
|
import org.yuzu.yuzu_emu.features.DocumentProvider
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
import org.yuzu.yuzu_emu.features.settings.model.Settings
|
||||||
import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
|
import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
|
||||||
|
@ -343,11 +340,10 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||||
|
|
||||||
val dstPath = DirectoryInitialization.userDirectory + "/keys/"
|
val dstPath = DirectoryInitialization.userDirectory + "/keys/"
|
||||||
if (FileUtil.copyUriToInternalStorage(
|
if (FileUtil.copyUriToInternalStorage(
|
||||||
applicationContext,
|
|
||||||
result,
|
result,
|
||||||
dstPath,
|
dstPath,
|
||||||
"prod.keys"
|
"prod.keys"
|
||||||
)
|
) != null
|
||||||
) {
|
) {
|
||||||
if (NativeLibrary.reloadKeys()) {
|
if (NativeLibrary.reloadKeys()) {
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
|
@ -446,11 +442,10 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||||
|
|
||||||
val dstPath = DirectoryInitialization.userDirectory + "/keys/"
|
val dstPath = DirectoryInitialization.userDirectory + "/keys/"
|
||||||
if (FileUtil.copyUriToInternalStorage(
|
if (FileUtil.copyUriToInternalStorage(
|
||||||
applicationContext,
|
|
||||||
result,
|
result,
|
||||||
dstPath,
|
dstPath,
|
||||||
"key_retail.bin"
|
"key_retail.bin"
|
||||||
)
|
) != null
|
||||||
) {
|
) {
|
||||||
if (NativeLibrary.reloadKeys()) {
|
if (NativeLibrary.reloadKeys()) {
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
|
@ -469,59 +464,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val getDriver =
|
|
||||||
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
|
|
||||||
if (result == null) {
|
|
||||||
return@registerForActivityResult
|
|
||||||
}
|
|
||||||
|
|
||||||
val takeFlags =
|
|
||||||
Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION
|
|
||||||
contentResolver.takePersistableUriPermission(
|
|
||||||
result,
|
|
||||||
takeFlags
|
|
||||||
)
|
|
||||||
|
|
||||||
val progressBinding = DialogProgressBarBinding.inflate(layoutInflater)
|
|
||||||
progressBinding.progressBar.isIndeterminate = true
|
|
||||||
val installationDialog = MaterialAlertDialogBuilder(this)
|
|
||||||
.setTitle(R.string.installing_driver)
|
|
||||||
.setView(progressBinding.root)
|
|
||||||
.show()
|
|
||||||
|
|
||||||
lifecycleScope.launch {
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
// Ignore file exceptions when a user selects an invalid zip
|
|
||||||
try {
|
|
||||||
GpuDriverHelper.installCustomDriver(applicationContext, result)
|
|
||||||
} catch (_: IOException) {
|
|
||||||
}
|
|
||||||
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
installationDialog.dismiss()
|
|
||||||
|
|
||||||
val driverName = GpuDriverHelper.customDriverName
|
|
||||||
if (driverName != null) {
|
|
||||||
Toast.makeText(
|
|
||||||
applicationContext,
|
|
||||||
getString(
|
|
||||||
R.string.select_gpu_driver_install_success,
|
|
||||||
driverName
|
|
||||||
),
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
} else {
|
|
||||||
Toast.makeText(
|
|
||||||
applicationContext,
|
|
||||||
R.string.select_gpu_driver_error,
|
|
||||||
Toast.LENGTH_LONG
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val installGameUpdate = registerForActivityResult(
|
val installGameUpdate = registerForActivityResult(
|
||||||
ActivityResultContracts.OpenMultipleDocuments()
|
ActivityResultContracts.OpenMultipleDocuments()
|
||||||
) { documents: List<Uri> ->
|
) { documents: List<Uri> ->
|
||||||
|
|
|
@ -7,7 +7,6 @@ import android.net.Uri
|
||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import org.yuzu.yuzu_emu.YuzuApplication
|
|
||||||
import org.yuzu.yuzu_emu.model.MinimalDocumentFile
|
import org.yuzu.yuzu_emu.model.MinimalDocumentFile
|
||||||
|
|
||||||
class DocumentsTree {
|
class DocumentsTree {
|
||||||
|
@ -22,7 +21,7 @@ class DocumentsTree {
|
||||||
|
|
||||||
fun openContentUri(filepath: String, openMode: String?): Int {
|
fun openContentUri(filepath: String, openMode: String?): Int {
|
||||||
val node = resolvePath(filepath) ?: return -1
|
val node = resolvePath(filepath) ?: return -1
|
||||||
return FileUtil.openContentUri(YuzuApplication.appContext, node.uri.toString(), openMode)
|
return FileUtil.openContentUri(node.uri.toString(), openMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getFileSize(filepath: String): Long {
|
fun getFileSize(filepath: String): Long {
|
||||||
|
@ -30,7 +29,7 @@ class DocumentsTree {
|
||||||
return if (node == null || node.isDirectory) {
|
return if (node == null || node.isDirectory) {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
FileUtil.getFileSize(YuzuApplication.appContext, node.uri.toString())
|
FileUtil.getFileSize(node.uri.toString())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,7 +66,7 @@ class DocumentsTree {
|
||||||
* @param parent parent node of this level
|
* @param parent parent node of this level
|
||||||
*/
|
*/
|
||||||
private fun structTree(parent: DocumentsNode) {
|
private fun structTree(parent: DocumentsNode) {
|
||||||
val documents = FileUtil.listFiles(YuzuApplication.appContext, parent.uri!!)
|
val documents = FileUtil.listFiles(parent.uri!!)
|
||||||
for (document in documents) {
|
for (document in documents) {
|
||||||
val node = DocumentsNode(document)
|
val node = DocumentsNode(document)
|
||||||
node.parent = parent
|
node.parent = parent
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
package org.yuzu.yuzu_emu.utils
|
package org.yuzu.yuzu_emu.utils
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.database.Cursor
|
import android.database.Cursor
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.provider.DocumentsContract
|
import android.provider.DocumentsContract
|
||||||
|
@ -11,7 +10,6 @@ import androidx.documentfile.provider.DocumentFile
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import java.io.BufferedInputStream
|
import java.io.BufferedInputStream
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileOutputStream
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.net.URLDecoder
|
import java.net.URLDecoder
|
||||||
|
@ -21,6 +19,8 @@ import org.yuzu.yuzu_emu.YuzuApplication
|
||||||
import org.yuzu.yuzu_emu.model.MinimalDocumentFile
|
import org.yuzu.yuzu_emu.model.MinimalDocumentFile
|
||||||
import org.yuzu.yuzu_emu.model.TaskState
|
import org.yuzu.yuzu_emu.model.TaskState
|
||||||
import java.io.BufferedOutputStream
|
import java.io.BufferedOutputStream
|
||||||
|
import java.lang.NullPointerException
|
||||||
|
import java.nio.charset.StandardCharsets
|
||||||
import java.util.zip.ZipOutputStream
|
import java.util.zip.ZipOutputStream
|
||||||
|
|
||||||
object FileUtil {
|
object FileUtil {
|
||||||
|
@ -29,6 +29,8 @@ object FileUtil {
|
||||||
const val APPLICATION_OCTET_STREAM = "application/octet-stream"
|
const val APPLICATION_OCTET_STREAM = "application/octet-stream"
|
||||||
const val TEXT_PLAIN = "text/plain"
|
const val TEXT_PLAIN = "text/plain"
|
||||||
|
|
||||||
|
private val context get() = YuzuApplication.appContext
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a file from directory with filename.
|
* Create a file from directory with filename.
|
||||||
* @param context Application context
|
* @param context Application context
|
||||||
|
@ -36,11 +38,11 @@ object FileUtil {
|
||||||
* @param filename file display name.
|
* @param filename file display name.
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
fun createFile(context: Context?, directory: String?, filename: String): DocumentFile? {
|
fun createFile(directory: String?, filename: String): DocumentFile? {
|
||||||
var decodedFilename = filename
|
var decodedFilename = filename
|
||||||
try {
|
try {
|
||||||
val directoryUri = Uri.parse(directory)
|
val directoryUri = Uri.parse(directory)
|
||||||
val parent = DocumentFile.fromTreeUri(context!!, directoryUri) ?: return null
|
val parent = DocumentFile.fromTreeUri(context, directoryUri) ?: return null
|
||||||
decodedFilename = URLDecoder.decode(decodedFilename, DECODE_METHOD)
|
decodedFilename = URLDecoder.decode(decodedFilename, DECODE_METHOD)
|
||||||
var mimeType = APPLICATION_OCTET_STREAM
|
var mimeType = APPLICATION_OCTET_STREAM
|
||||||
if (decodedFilename.endsWith(".txt")) {
|
if (decodedFilename.endsWith(".txt")) {
|
||||||
|
@ -56,16 +58,15 @@ object FileUtil {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a directory from directory with filename.
|
* Create a directory from directory with filename.
|
||||||
* @param context Application context
|
|
||||||
* @param directory parent path for directory.
|
* @param directory parent path for directory.
|
||||||
* @param directoryName directory display name.
|
* @param directoryName directory display name.
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
fun createDir(context: Context?, directory: String?, directoryName: String?): DocumentFile? {
|
fun createDir(directory: String?, directoryName: String?): DocumentFile? {
|
||||||
var decodedDirectoryName = directoryName
|
var decodedDirectoryName = directoryName
|
||||||
try {
|
try {
|
||||||
val directoryUri = Uri.parse(directory)
|
val directoryUri = Uri.parse(directory)
|
||||||
val parent = DocumentFile.fromTreeUri(context!!, directoryUri) ?: return null
|
val parent = DocumentFile.fromTreeUri(context, directoryUri) ?: return null
|
||||||
decodedDirectoryName = URLDecoder.decode(decodedDirectoryName, DECODE_METHOD)
|
decodedDirectoryName = URLDecoder.decode(decodedDirectoryName, DECODE_METHOD)
|
||||||
val isExist = parent.findFile(decodedDirectoryName)
|
val isExist = parent.findFile(decodedDirectoryName)
|
||||||
return isExist ?: parent.createDirectory(decodedDirectoryName)
|
return isExist ?: parent.createDirectory(decodedDirectoryName)
|
||||||
|
@ -77,13 +78,12 @@ object FileUtil {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open content uri and return file descriptor to JNI.
|
* Open content uri and return file descriptor to JNI.
|
||||||
* @param context Application context
|
|
||||||
* @param path Native content uri path
|
* @param path Native content uri path
|
||||||
* @param openMode will be one of "r", "r", "rw", "wa", "rwa"
|
* @param openMode will be one of "r", "r", "rw", "wa", "rwa"
|
||||||
* @return file descriptor
|
* @return file descriptor
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun openContentUri(context: Context, path: String, openMode: String?): Int {
|
fun openContentUri(path: String, openMode: String?): Int {
|
||||||
try {
|
try {
|
||||||
val uri = Uri.parse(path)
|
val uri = Uri.parse(path)
|
||||||
val parcelFileDescriptor = context.contentResolver.openFileDescriptor(uri, openMode!!)
|
val parcelFileDescriptor = context.contentResolver.openFileDescriptor(uri, openMode!!)
|
||||||
|
@ -103,11 +103,10 @@ object FileUtil {
|
||||||
/**
|
/**
|
||||||
* Reference: https://stackoverflow.com/questions/42186820/documentfile-is-very-slow
|
* Reference: https://stackoverflow.com/questions/42186820/documentfile-is-very-slow
|
||||||
* This function will be faster than DoucmentFile.listFiles
|
* This function will be faster than DoucmentFile.listFiles
|
||||||
* @param context Application context
|
|
||||||
* @param uri Directory uri.
|
* @param uri Directory uri.
|
||||||
* @return CheapDocument lists.
|
* @return CheapDocument lists.
|
||||||
*/
|
*/
|
||||||
fun listFiles(context: Context, uri: Uri): Array<MinimalDocumentFile> {
|
fun listFiles(uri: Uri): Array<MinimalDocumentFile> {
|
||||||
val resolver = context.contentResolver
|
val resolver = context.contentResolver
|
||||||
val columns = arrayOf(
|
val columns = arrayOf(
|
||||||
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
|
DocumentsContract.Document.COLUMN_DOCUMENT_ID,
|
||||||
|
@ -145,7 +144,7 @@ object FileUtil {
|
||||||
* @param path Native content uri path
|
* @param path Native content uri path
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
fun exists(context: Context, path: String?): Boolean {
|
fun exists(path: String?): Boolean {
|
||||||
var c: Cursor? = null
|
var c: Cursor? = null
|
||||||
try {
|
try {
|
||||||
val mUri = Uri.parse(path)
|
val mUri = Uri.parse(path)
|
||||||
|
@ -165,7 +164,7 @@ object FileUtil {
|
||||||
* @param path content uri path
|
* @param path content uri path
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
fun isDirectory(context: Context, path: String): Boolean {
|
fun isDirectory(path: String): Boolean {
|
||||||
val resolver = context.contentResolver
|
val resolver = context.contentResolver
|
||||||
val columns = arrayOf(
|
val columns = arrayOf(
|
||||||
DocumentsContract.Document.COLUMN_MIME_TYPE
|
DocumentsContract.Document.COLUMN_MIME_TYPE
|
||||||
|
@ -210,10 +209,10 @@ object FileUtil {
|
||||||
return filename
|
return filename
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getFilesName(context: Context, path: String): Array<String> {
|
fun getFilesName(path: String): Array<String> {
|
||||||
val uri = Uri.parse(path)
|
val uri = Uri.parse(path)
|
||||||
val files: MutableList<String> = ArrayList()
|
val files: MutableList<String> = ArrayList()
|
||||||
for (file in listFiles(context, uri)) {
|
for (file in listFiles(uri)) {
|
||||||
files.add(file.filename)
|
files.add(file.filename)
|
||||||
}
|
}
|
||||||
return files.toTypedArray()
|
return files.toTypedArray()
|
||||||
|
@ -225,7 +224,7 @@ object FileUtil {
|
||||||
* @return long file size
|
* @return long file size
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun getFileSize(context: Context, path: String): Long {
|
fun getFileSize(path: String): Long {
|
||||||
val resolver = context.contentResolver
|
val resolver = context.contentResolver
|
||||||
val columns = arrayOf(
|
val columns = arrayOf(
|
||||||
DocumentsContract.Document.COLUMN_SIZE
|
DocumentsContract.Document.COLUMN_SIZE
|
||||||
|
@ -245,43 +244,37 @@ object FileUtil {
|
||||||
return size
|
return size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an input stream with a given [Uri] and copies its data to the given path. This will
|
||||||
|
* overwrite any pre-existing files.
|
||||||
|
*
|
||||||
|
* @param sourceUri The [Uri] to copy data from
|
||||||
|
* @param destinationParentPath Destination directory
|
||||||
|
* @param destinationFilename Optionally renames the file once copied
|
||||||
|
*/
|
||||||
fun copyUriToInternalStorage(
|
fun copyUriToInternalStorage(
|
||||||
context: Context,
|
sourceUri: Uri,
|
||||||
sourceUri: Uri?,
|
|
||||||
destinationParentPath: String,
|
destinationParentPath: String,
|
||||||
destinationFilename: String
|
destinationFilename: String = ""
|
||||||
): Boolean {
|
): File? =
|
||||||
var input: InputStream? = null
|
|
||||||
var output: FileOutputStream? = null
|
|
||||||
try {
|
try {
|
||||||
input = context.contentResolver.openInputStream(sourceUri!!)
|
val fileName =
|
||||||
output = FileOutputStream("$destinationParentPath/$destinationFilename")
|
if (destinationFilename == "") getFilename(sourceUri) else "/$destinationFilename"
|
||||||
val buffer = ByteArray(1024)
|
val inputStream = context.contentResolver.openInputStream(sourceUri)!!
|
||||||
var len: Int
|
|
||||||
while (input!!.read(buffer).also { len = it } != -1) {
|
val destinationFile = File("$destinationParentPath$fileName")
|
||||||
output.write(buffer, 0, len)
|
if (destinationFile.exists()) {
|
||||||
|
destinationFile.delete()
|
||||||
}
|
}
|
||||||
output.flush()
|
|
||||||
return true
|
destinationFile.outputStream().use { fos ->
|
||||||
} catch (e: Exception) {
|
inputStream.use { it.copyTo(fos) }
|
||||||
Log.error("[FileUtil]: Cannot copy file, error: " + e.message)
|
}
|
||||||
} finally {
|
destinationFile
|
||||||
if (input != null) {
|
|
||||||
try {
|
|
||||||
input.close()
|
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
Log.error("[FileUtil]: Cannot close input file, error: " + e.message)
|
null
|
||||||
}
|
} catch (e: NullPointerException) {
|
||||||
}
|
null
|
||||||
if (output != null) {
|
|
||||||
try {
|
|
||||||
output.close()
|
|
||||||
} catch (e: IOException) {
|
|
||||||
Log.error("[FileUtil]: Cannot close output file, error: " + e.message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -368,4 +361,12 @@ object FileUtil {
|
||||||
return fileName.substring(fileName.lastIndexOf(".") + 1)
|
return fileName.substring(fileName.lastIndexOf(".") + 1)
|
||||||
.lowercase()
|
.lowercase()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
|
fun getStringFromFile(file: File): String =
|
||||||
|
String(file.readBytes(), StandardCharsets.UTF_8)
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
|
fun getStringFromInputStream(stream: InputStream): String =
|
||||||
|
String(stream.readBytes(), StandardCharsets.UTF_8)
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ object GameHelper {
|
||||||
// Ensure keys are loaded so that ROM metadata can be decrypted.
|
// Ensure keys are loaded so that ROM metadata can be decrypted.
|
||||||
NativeLibrary.reloadKeys()
|
NativeLibrary.reloadKeys()
|
||||||
|
|
||||||
addGamesRecursive(games, FileUtil.listFiles(context, gamesUri), 3)
|
addGamesRecursive(games, FileUtil.listFiles(gamesUri), 3)
|
||||||
|
|
||||||
// Cache list of games found on disk
|
// Cache list of games found on disk
|
||||||
val serializedGames = mutableSetOf<String>()
|
val serializedGames = mutableSetOf<String>()
|
||||||
|
@ -58,7 +58,7 @@ object GameHelper {
|
||||||
if (it.isDirectory) {
|
if (it.isDirectory) {
|
||||||
addGamesRecursive(
|
addGamesRecursive(
|
||||||
games,
|
games,
|
||||||
FileUtil.listFiles(YuzuApplication.appContext, it.uri),
|
FileUtil.listFiles(it.uri),
|
||||||
depth - 1
|
depth - 1
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -3,64 +3,33 @@
|
||||||
|
|
||||||
package org.yuzu.yuzu_emu.utils
|
package org.yuzu.yuzu_emu.utils
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
import java.io.BufferedInputStream
|
import java.io.BufferedInputStream
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileInputStream
|
|
||||||
import java.io.FileOutputStream
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.util.zip.ZipInputStream
|
|
||||||
import org.yuzu.yuzu_emu.NativeLibrary
|
import org.yuzu.yuzu_emu.NativeLibrary
|
||||||
import org.yuzu.yuzu_emu.utils.FileUtil.copyUriToInternalStorage
|
import org.yuzu.yuzu_emu.YuzuApplication
|
||||||
|
import java.util.zip.ZipException
|
||||||
|
import java.util.zip.ZipFile
|
||||||
|
|
||||||
object GpuDriverHelper {
|
object GpuDriverHelper {
|
||||||
private const val META_JSON_FILENAME = "meta.json"
|
private const val META_JSON_FILENAME = "meta.json"
|
||||||
private const val DRIVER_INTERNAL_FILENAME = "gpu_driver.zip"
|
|
||||||
private var fileRedirectionPath: String? = null
|
private var fileRedirectionPath: String? = null
|
||||||
private var driverInstallationPath: String? = null
|
var driverInstallationPath: String? = null
|
||||||
private var hookLibPath: String? = null
|
private var hookLibPath: String? = null
|
||||||
|
|
||||||
@Throws(IOException::class)
|
val driverStoragePath get() = DirectoryInitialization.userDirectory!! + "/gpu_drivers/"
|
||||||
private fun unzip(zipFilePath: String, destDir: String) {
|
|
||||||
val dir = File(destDir)
|
|
||||||
|
|
||||||
// Create output directory if it doesn't exist
|
fun initializeDriverParameters() {
|
||||||
if (!dir.exists()) dir.mkdirs()
|
|
||||||
|
|
||||||
// Unpack the files.
|
|
||||||
val inputStream = FileInputStream(zipFilePath)
|
|
||||||
val zis = ZipInputStream(BufferedInputStream(inputStream))
|
|
||||||
val buffer = ByteArray(1024)
|
|
||||||
var ze = zis.nextEntry
|
|
||||||
while (ze != null) {
|
|
||||||
val newFile = File(destDir, ze.name)
|
|
||||||
val canonicalPath = newFile.canonicalPath
|
|
||||||
if (!canonicalPath.startsWith(destDir + ze.name)) {
|
|
||||||
throw SecurityException("Zip file attempted path traversal! " + ze.name)
|
|
||||||
}
|
|
||||||
|
|
||||||
newFile.parentFile!!.mkdirs()
|
|
||||||
val fos = FileOutputStream(newFile)
|
|
||||||
var len: Int
|
|
||||||
while (zis.read(buffer).also { len = it } > 0) {
|
|
||||||
fos.write(buffer, 0, len)
|
|
||||||
}
|
|
||||||
fos.close()
|
|
||||||
zis.closeEntry()
|
|
||||||
ze = zis.nextEntry
|
|
||||||
}
|
|
||||||
zis.closeEntry()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun initializeDriverParameters(context: Context) {
|
|
||||||
try {
|
try {
|
||||||
// Initialize the file redirection directory.
|
// Initialize the file redirection directory.
|
||||||
fileRedirectionPath =
|
fileRedirectionPath = YuzuApplication.appContext
|
||||||
context.getExternalFilesDir(null)!!.canonicalPath + "/gpu/vk_file_redirect/"
|
.getExternalFilesDir(null)!!.canonicalPath + "/gpu/vk_file_redirect/"
|
||||||
|
|
||||||
// Initialize the driver installation directory.
|
// Initialize the driver installation directory.
|
||||||
driverInstallationPath = context.filesDir.canonicalPath + "/gpu_driver/"
|
driverInstallationPath = YuzuApplication.appContext
|
||||||
|
.filesDir.canonicalPath + "/gpu_driver/"
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
throw RuntimeException(e)
|
throw RuntimeException(e)
|
||||||
}
|
}
|
||||||
|
@ -69,68 +38,169 @@ object GpuDriverHelper {
|
||||||
initializeDirectories()
|
initializeDirectories()
|
||||||
|
|
||||||
// Initialize hook libraries directory.
|
// Initialize hook libraries directory.
|
||||||
hookLibPath = context.applicationInfo.nativeLibraryDir + "/"
|
hookLibPath = YuzuApplication.appContext.applicationInfo.nativeLibraryDir + "/"
|
||||||
|
|
||||||
// Initialize GPU driver.
|
// Initialize GPU driver.
|
||||||
NativeLibrary.initializeGpuDriver(
|
NativeLibrary.initializeGpuDriver(
|
||||||
hookLibPath,
|
hookLibPath,
|
||||||
driverInstallationPath,
|
driverInstallationPath,
|
||||||
customDriverLibraryName,
|
customDriverData.libraryName,
|
||||||
fileRedirectionPath
|
fileRedirectionPath
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun installDefaultDriver(context: Context) {
|
fun getDrivers(): MutableList<Pair<String, GpuDriverMetadata>> {
|
||||||
// Removing the installed driver will result in the backend using the default system driver.
|
val driverZips = File(driverStoragePath).listFiles()
|
||||||
val driverInstallationDir = File(driverInstallationPath!!)
|
val drivers: MutableList<Pair<String, GpuDriverMetadata>> =
|
||||||
deleteRecursive(driverInstallationDir)
|
driverZips
|
||||||
initializeDriverParameters(context)
|
?.mapNotNull {
|
||||||
|
val metadata = getMetadataFromZip(it)
|
||||||
|
metadata.name?.let { _ -> Pair(it.path, metadata) }
|
||||||
|
}
|
||||||
|
?.sortedByDescending { it: Pair<String, GpuDriverMetadata> -> it.second.name }
|
||||||
|
?.distinct()
|
||||||
|
?.toMutableList() ?: mutableListOf()
|
||||||
|
|
||||||
|
// TODO: Get system driver information
|
||||||
|
drivers.add(0, Pair("", GpuDriverMetadata()))
|
||||||
|
return drivers
|
||||||
}
|
}
|
||||||
|
|
||||||
fun installCustomDriver(context: Context, driverPathUri: Uri?) {
|
fun installDefaultDriver() {
|
||||||
|
// Removing the installed driver will result in the backend using the default system driver.
|
||||||
|
File(driverInstallationPath!!).deleteRecursively()
|
||||||
|
initializeDriverParameters()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun copyDriverToInternalStorage(driverUri: Uri): Boolean {
|
||||||
|
// Ensure we have directories.
|
||||||
|
initializeDirectories()
|
||||||
|
|
||||||
|
// Copy the zip file URI to user data
|
||||||
|
val copiedFile =
|
||||||
|
FileUtil.copyUriToInternalStorage(driverUri, driverStoragePath) ?: return false
|
||||||
|
|
||||||
|
// Validate driver
|
||||||
|
val metadata = getMetadataFromZip(copiedFile)
|
||||||
|
if (metadata.name == null) {
|
||||||
|
copiedFile.delete()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (metadata.minApi > Build.VERSION.SDK_INT) {
|
||||||
|
copiedFile.delete()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies driver zip into user data directory so that it can be exported along with
|
||||||
|
* other user data and also unzipped into the installation directory
|
||||||
|
*/
|
||||||
|
fun installCustomDriver(driverUri: Uri): Boolean {
|
||||||
// Revert to system default in the event the specified driver is bad.
|
// Revert to system default in the event the specified driver is bad.
|
||||||
installDefaultDriver(context)
|
installDefaultDriver()
|
||||||
|
|
||||||
// Ensure we have directories.
|
// Ensure we have directories.
|
||||||
initializeDirectories()
|
initializeDirectories()
|
||||||
|
|
||||||
// Copy the zip file URI into our private storage.
|
// Copy the zip file URI to user data
|
||||||
copyUriToInternalStorage(
|
val copiedFile =
|
||||||
context,
|
FileUtil.copyUriToInternalStorage(driverUri, driverStoragePath) ?: return false
|
||||||
driverPathUri,
|
|
||||||
driverInstallationPath!!,
|
// Validate driver
|
||||||
DRIVER_INTERNAL_FILENAME
|
val metadata = getMetadataFromZip(copiedFile)
|
||||||
)
|
if (metadata.name == null) {
|
||||||
|
copiedFile.delete()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (metadata.minApi > Build.VERSION.SDK_INT) {
|
||||||
|
copiedFile.delete()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// Unzip the driver.
|
// Unzip the driver.
|
||||||
try {
|
try {
|
||||||
unzip(driverInstallationPath + DRIVER_INTERNAL_FILENAME, driverInstallationPath!!)
|
FileUtil.unzipToInternalStorage(
|
||||||
|
BufferedInputStream(copiedFile.inputStream()),
|
||||||
|
File(driverInstallationPath!!)
|
||||||
|
)
|
||||||
} catch (e: SecurityException) {
|
} catch (e: SecurityException) {
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the driver parameters.
|
// Initialize the driver parameters.
|
||||||
initializeDriverParameters(context)
|
initializeDriverParameters()
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unzips driver into installation directory
|
||||||
|
*/
|
||||||
|
fun installCustomDriver(driver: File): Boolean {
|
||||||
|
// Revert to system default in the event the specified driver is bad.
|
||||||
|
installDefaultDriver()
|
||||||
|
|
||||||
|
// Ensure we have directories.
|
||||||
|
initializeDirectories()
|
||||||
|
|
||||||
|
// Validate driver
|
||||||
|
val metadata = getMetadataFromZip(driver)
|
||||||
|
if (metadata.name == null) {
|
||||||
|
driver.delete()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unzip the driver to the private installation directory
|
||||||
|
try {
|
||||||
|
FileUtil.unzipToInternalStorage(
|
||||||
|
BufferedInputStream(driver.inputStream()),
|
||||||
|
File(driverInstallationPath!!)
|
||||||
|
)
|
||||||
|
} catch (e: SecurityException) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize the driver parameters.
|
||||||
|
initializeDriverParameters()
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes in a zip file and reads the meta.json file for presentation to the UI
|
||||||
|
*
|
||||||
|
* @param driver Zip containing driver and meta.json file
|
||||||
|
* @return A non-null [GpuDriverMetadata] instance that may have null members
|
||||||
|
*/
|
||||||
|
fun getMetadataFromZip(driver: File): GpuDriverMetadata {
|
||||||
|
try {
|
||||||
|
ZipFile(driver).use { zf ->
|
||||||
|
val entries = zf.entries()
|
||||||
|
while (entries.hasMoreElements()) {
|
||||||
|
val entry = entries.nextElement()
|
||||||
|
if (!entry.isDirectory && entry.name.lowercase().contains(".json")) {
|
||||||
|
zf.getInputStream(entry).use {
|
||||||
|
return GpuDriverMetadata(it, entry.size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (_: ZipException) {
|
||||||
|
}
|
||||||
|
return GpuDriverMetadata()
|
||||||
}
|
}
|
||||||
|
|
||||||
external fun supportsCustomDriverLoading(): Boolean
|
external fun supportsCustomDriverLoading(): Boolean
|
||||||
|
|
||||||
// Parse the custom driver metadata to retrieve the name.
|
// Parse the custom driver metadata to retrieve the name.
|
||||||
val customDriverName: String?
|
val customDriverData: GpuDriverMetadata
|
||||||
get() {
|
get() = GpuDriverMetadata(File(driverInstallationPath + META_JSON_FILENAME))
|
||||||
val metadata = GpuDriverMetadata(driverInstallationPath + META_JSON_FILENAME)
|
|
||||||
return metadata.name
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the custom driver metadata to retrieve the library name.
|
fun initializeDirectories() {
|
||||||
private val customDriverLibraryName: String?
|
|
||||||
get() {
|
|
||||||
// Parse the custom driver metadata to retrieve the library name.
|
|
||||||
val metadata = GpuDriverMetadata(driverInstallationPath + META_JSON_FILENAME)
|
|
||||||
return metadata.libraryName
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun initializeDirectories() {
|
|
||||||
// Ensure the file redirection directory exists.
|
// Ensure the file redirection directory exists.
|
||||||
val fileRedirectionDir = File(fileRedirectionPath!!)
|
val fileRedirectionDir = File(fileRedirectionPath!!)
|
||||||
if (!fileRedirectionDir.exists()) {
|
if (!fileRedirectionDir.exists()) {
|
||||||
|
@ -141,14 +211,10 @@ object GpuDriverHelper {
|
||||||
if (!driverInstallationDir.exists()) {
|
if (!driverInstallationDir.exists()) {
|
||||||
driverInstallationDir.mkdirs()
|
driverInstallationDir.mkdirs()
|
||||||
}
|
}
|
||||||
|
// Ensure the driver storage directory exists
|
||||||
|
val driverStorageDirectory = File(driverStoragePath)
|
||||||
|
if (!driverStorageDirectory.exists()) {
|
||||||
|
driverStorageDirectory.mkdirs()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun deleteRecursive(fileOrDirectory: File) {
|
|
||||||
if (fileOrDirectory.isDirectory) {
|
|
||||||
for (child in fileOrDirectory.listFiles()!!) {
|
|
||||||
deleteRecursive(child)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fileOrDirectory.delete()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,29 +4,29 @@
|
||||||
package org.yuzu.yuzu_emu.utils
|
package org.yuzu.yuzu_emu.utils
|
||||||
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.nio.charset.StandardCharsets
|
|
||||||
import java.nio.file.Files
|
|
||||||
import java.nio.file.Paths
|
|
||||||
import org.json.JSONException
|
import org.json.JSONException
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
import java.io.File
|
||||||
|
import java.io.InputStream
|
||||||
|
|
||||||
class GpuDriverMetadata(metadataFilePath: String) {
|
class GpuDriverMetadata {
|
||||||
var name: String? = null
|
/**
|
||||||
var description: String? = null
|
* Tries to get driver metadata information from a meta.json [File]
|
||||||
var author: String? = null
|
*
|
||||||
var vendor: String? = null
|
* @param metadataFile meta.json file provided with a GPU driver
|
||||||
var driverVersion: String? = null
|
*/
|
||||||
var minApi = 0
|
constructor(metadataFile: File) {
|
||||||
var libraryName: String? = null
|
if (metadataFile.length() > MAX_META_SIZE_BYTES) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
|
||||||
try {
|
try {
|
||||||
val json = JSONObject(getStringFromFile(metadataFilePath))
|
val json = JSONObject(FileUtil.getStringFromFile(metadataFile))
|
||||||
name = json.getString("name")
|
name = json.getString("name")
|
||||||
description = json.getString("description")
|
description = json.getString("description")
|
||||||
author = json.getString("author")
|
author = json.getString("author")
|
||||||
vendor = json.getString("vendor")
|
vendor = json.getString("vendor")
|
||||||
driverVersion = json.getString("driverVersion")
|
version = json.getString("driverVersion")
|
||||||
minApi = json.getInt("minApi")
|
minApi = json.getInt("minApi")
|
||||||
libraryName = json.getString("libraryName")
|
libraryName = json.getString("libraryName")
|
||||||
} catch (e: JSONException) {
|
} catch (e: JSONException) {
|
||||||
|
@ -36,12 +36,84 @@ class GpuDriverMetadata(metadataFilePath: String) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
/**
|
||||||
@Throws(IOException::class)
|
* Tries to get driver metadata information from an input stream that's intended to be
|
||||||
private fun getStringFromFile(filePath: String): String {
|
* from a zip file
|
||||||
val path = Paths.get(filePath)
|
*
|
||||||
val bytes = Files.readAllBytes(path)
|
* @param metadataStream ZipEntry input stream
|
||||||
return String(bytes, StandardCharsets.UTF_8)
|
* @param size Size of the file in bytes
|
||||||
|
*/
|
||||||
|
constructor(metadataStream: InputStream, size: Long) {
|
||||||
|
if (size > MAX_META_SIZE_BYTES) {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
val json = JSONObject(FileUtil.getStringFromInputStream(metadataStream))
|
||||||
|
name = json.getString("name")
|
||||||
|
description = json.getString("description")
|
||||||
|
author = json.getString("author")
|
||||||
|
vendor = json.getString("vendor")
|
||||||
|
version = json.getString("driverVersion")
|
||||||
|
minApi = json.getInt("minApi")
|
||||||
|
libraryName = json.getString("libraryName")
|
||||||
|
} catch (e: JSONException) {
|
||||||
|
// JSON is malformed, ignore and treat as unsupported metadata.
|
||||||
|
} catch (e: IOException) {
|
||||||
|
// File is inaccessible, ignore and treat as unsupported metadata.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an empty metadata instance
|
||||||
|
*/
|
||||||
|
constructor()
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (other !is GpuDriverMetadata) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return other.name == name &&
|
||||||
|
other.description == description &&
|
||||||
|
other.author == author &&
|
||||||
|
other.vendor == vendor &&
|
||||||
|
other.version == version &&
|
||||||
|
other.minApi == minApi &&
|
||||||
|
other.libraryName == libraryName
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = name?.hashCode() ?: 0
|
||||||
|
result = 31 * result + (description?.hashCode() ?: 0)
|
||||||
|
result = 31 * result + (author?.hashCode() ?: 0)
|
||||||
|
result = 31 * result + (vendor?.hashCode() ?: 0)
|
||||||
|
result = 31 * result + (version?.hashCode() ?: 0)
|
||||||
|
result = 31 * result + minApi
|
||||||
|
result = 31 * result + (libraryName?.hashCode() ?: 0)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String =
|
||||||
|
"""
|
||||||
|
Name - $name
|
||||||
|
Description - $description
|
||||||
|
Author - $author
|
||||||
|
Vendor - $vendor
|
||||||
|
Version - $version
|
||||||
|
Min API - $minApi
|
||||||
|
Library Name - $libraryName
|
||||||
|
""".trimMargin().trimIndent()
|
||||||
|
|
||||||
|
var name: String? = null
|
||||||
|
var description: String? = null
|
||||||
|
var author: String? = null
|
||||||
|
var vendor: String? = null
|
||||||
|
var version: String? = null
|
||||||
|
var minApi = 0
|
||||||
|
var libraryName: String? = null
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val MAX_META_SIZE_BYTES = 500000
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
9
src/android/app/src/main/res/drawable/ic_build.xml
Executable file
9
src/android/app/src/main/res/drawable/ic_build.xml
Executable file
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="?attr/colorControlNormal"
|
||||||
|
android:pathData="M22.7,19l-9.1,-9.1c0.9,-2.3 0.4,-5 -1.5,-6.9 -2,-2 -5,-2.4 -7.4,-1.3L9,6 6,9 1.6,4.7C0.4,7.1 0.9,10.1 2.9,12.1c1.9,1.9 4.6,2.4 6.9,1.5l9.1,9.1c0.4,0.4 1,0.4 1.4,0l2.3,-2.3c0.5,-0.4 0.5,-1.1 0.1,-1.4z" />
|
||||||
|
</vector>
|
9
src/android/app/src/main/res/drawable/ic_delete.xml
Executable file
9
src/android/app/src/main/res/drawable/ic_delete.xml
Executable file
|
@ -0,0 +1,9 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="?attr/colorControlNormal"
|
||||||
|
android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z" />
|
||||||
|
</vector>
|
89
src/android/app/src/main/res/layout/card_driver_option.xml
Executable file
89
src/android/app/src/main/res/layout/card_driver_option.xml
Executable file
|
@ -0,0 +1,89 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
style="?attr/materialCardViewOutlinedStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="16dp"
|
||||||
|
android:layout_marginVertical="12dp"
|
||||||
|
android:background="?attr/selectableItemBackground"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
|
<RadioButton
|
||||||
|
android:id="@+id/radio_button"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:clickable="false"
|
||||||
|
android:checked="false" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_gravity="center_vertical">
|
||||||
|
|
||||||
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
android:id="@+id/title"
|
||||||
|
style="@style/TextAppearance.Material3.TitleMedium"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ellipsize="none"
|
||||||
|
android:marqueeRepeatLimit="marquee_forever"
|
||||||
|
android:requiresFadingEdge="horizontal"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textAlignment="viewStart"
|
||||||
|
tools:text="@string/select_gpu_driver_default" />
|
||||||
|
|
||||||
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
android:id="@+id/version"
|
||||||
|
style="@style/TextAppearance.Material3.BodyMedium"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="6dp"
|
||||||
|
android:ellipsize="none"
|
||||||
|
android:marqueeRepeatLimit="marquee_forever"
|
||||||
|
android:requiresFadingEdge="horizontal"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textAlignment="viewStart"
|
||||||
|
tools:text="@string/install_gpu_driver_description" />
|
||||||
|
|
||||||
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
android:id="@+id/description"
|
||||||
|
style="@style/TextAppearance.Material3.BodyMedium"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="6dp"
|
||||||
|
android:ellipsize="none"
|
||||||
|
android:marqueeRepeatLimit="marquee_forever"
|
||||||
|
android:requiresFadingEdge="horizontal"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textAlignment="viewStart"
|
||||||
|
tools:text="@string/install_gpu_driver_description" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/button_delete"
|
||||||
|
style="@style/Widget.Material3.Button.IconButton"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:contentDescription="@string/delete"
|
||||||
|
android:tooltipText="@string/delete"
|
||||||
|
app:icon="@drawable/ic_delete"
|
||||||
|
app:iconTint="?attr/colorControlNormal" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
48
src/android/app/src/main/res/layout/fragment_driver_manager.xml
Executable file
48
src/android/app/src/main/res/layout/fragment_driver_manager.xml
Executable file
|
@ -0,0 +1,48 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:id="@+id/coordinator_licenses"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="?attr/colorSurface">
|
||||||
|
|
||||||
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:id="@+id/appbar_drivers"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
|
app:liftOnScrollTargetViewId="@id/list_drivers">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
|
android:id="@+id/toolbar_drivers"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
app:navigationIcon="@drawable/ic_back"
|
||||||
|
app:title="@string/gpu_driver_manager" />
|
||||||
|
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/list_drivers"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||||
|
android:id="@+id/button_install"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="bottom|end"
|
||||||
|
android:text="@string/install"
|
||||||
|
app:icon="@drawable/ic_add"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -22,6 +22,9 @@
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_homeSettingsFragment_to_installableFragment"
|
android:id="@+id/action_homeSettingsFragment_to_installableFragment"
|
||||||
app:destination="@id/installableFragment" />
|
app:destination="@id/installableFragment" />
|
||||||
|
<action
|
||||||
|
android:id="@+id/action_homeSettingsFragment_to_driverManagerFragment"
|
||||||
|
app:destination="@id/driverManagerFragment" />
|
||||||
</fragment>
|
</fragment>
|
||||||
|
|
||||||
<fragment
|
<fragment
|
||||||
|
@ -95,5 +98,9 @@
|
||||||
android:id="@+id/installableFragment"
|
android:id="@+id/installableFragment"
|
||||||
android:name="org.yuzu.yuzu_emu.fragments.InstallableFragment"
|
android:name="org.yuzu.yuzu_emu.fragments.InstallableFragment"
|
||||||
android:label="InstallableFragment" />
|
android:label="InstallableFragment" />
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/driverManagerFragment"
|
||||||
|
android:name="org.yuzu.yuzu_emu.fragments.DriverManagerFragment"
|
||||||
|
android:label="DriverManagerFragment" />
|
||||||
|
|
||||||
</navigation>
|
</navigation>
|
||||||
|
|
|
@ -168,9 +168,7 @@
|
||||||
<string name="select_gpu_driver_title">Möchtest du deinen aktuellen GPU-Treiber ersetzen?</string>
|
<string name="select_gpu_driver_title">Möchtest du deinen aktuellen GPU-Treiber ersetzen?</string>
|
||||||
<string name="select_gpu_driver_install">Installieren</string>
|
<string name="select_gpu_driver_install">Installieren</string>
|
||||||
<string name="select_gpu_driver_default">Standard</string>
|
<string name="select_gpu_driver_default">Standard</string>
|
||||||
<string name="select_gpu_driver_install_success">%s wurde installiert</string>
|
|
||||||
<string name="select_gpu_driver_use_default">Standard GPU-Treiber wird verwendet</string>
|
<string name="select_gpu_driver_use_default">Standard GPU-Treiber wird verwendet</string>
|
||||||
<string name="select_gpu_driver_error">Ungültiger Treiber ausgewählt, Standard-Treiber wird verwendet!</string>
|
|
||||||
<string name="system_gpu_driver">System GPU-Treiber</string>
|
<string name="system_gpu_driver">System GPU-Treiber</string>
|
||||||
<string name="installing_driver">Treiber wird installiert...</string>
|
<string name="installing_driver">Treiber wird installiert...</string>
|
||||||
|
|
||||||
|
|
|
@ -171,9 +171,7 @@
|
||||||
<string name="select_gpu_driver_title">¿Quiere reemplazar el driver de GPU actual?</string>
|
<string name="select_gpu_driver_title">¿Quiere reemplazar el driver de GPU actual?</string>
|
||||||
<string name="select_gpu_driver_install">Instalar</string>
|
<string name="select_gpu_driver_install">Instalar</string>
|
||||||
<string name="select_gpu_driver_default">Predeterminado</string>
|
<string name="select_gpu_driver_default">Predeterminado</string>
|
||||||
<string name="select_gpu_driver_install_success">Instalado %s</string>
|
|
||||||
<string name="select_gpu_driver_use_default">Usando el driver de GPU por defecto </string>
|
<string name="select_gpu_driver_use_default">Usando el driver de GPU por defecto </string>
|
||||||
<string name="select_gpu_driver_error">¡Driver no válido, utilizando el predeterminado del sistema!</string>
|
|
||||||
<string name="system_gpu_driver">Driver GPU del sistema</string>
|
<string name="system_gpu_driver">Driver GPU del sistema</string>
|
||||||
<string name="installing_driver">Instalando driver...</string>
|
<string name="installing_driver">Instalando driver...</string>
|
||||||
|
|
||||||
|
|
|
@ -171,9 +171,7 @@
|
||||||
<string name="select_gpu_driver_title">Souhaitez vous remplacer votre pilote actuel ?</string>
|
<string name="select_gpu_driver_title">Souhaitez vous remplacer votre pilote actuel ?</string>
|
||||||
<string name="select_gpu_driver_install">Installer</string>
|
<string name="select_gpu_driver_install">Installer</string>
|
||||||
<string name="select_gpu_driver_default">Défaut</string>
|
<string name="select_gpu_driver_default">Défaut</string>
|
||||||
<string name="select_gpu_driver_install_success">%s Installé</string>
|
|
||||||
<string name="select_gpu_driver_use_default">Utilisation du pilote de GPU par défaut</string>
|
<string name="select_gpu_driver_use_default">Utilisation du pilote de GPU par défaut</string>
|
||||||
<string name="select_gpu_driver_error">Pilote non valide sélectionné, utilisation du paramètre par défaut du système !</string>
|
|
||||||
<string name="system_gpu_driver">Pilote du GPU du système</string>
|
<string name="system_gpu_driver">Pilote du GPU du système</string>
|
||||||
<string name="installing_driver">Installation du pilote...</string>
|
<string name="installing_driver">Installation du pilote...</string>
|
||||||
|
|
||||||
|
|
|
@ -171,9 +171,7 @@
|
||||||
<string name="select_gpu_driver_title">Vuoi sostituire il driver della tua GPU attuale?</string>
|
<string name="select_gpu_driver_title">Vuoi sostituire il driver della tua GPU attuale?</string>
|
||||||
<string name="select_gpu_driver_install">Installa</string>
|
<string name="select_gpu_driver_install">Installa</string>
|
||||||
<string name="select_gpu_driver_default">Predefinito</string>
|
<string name="select_gpu_driver_default">Predefinito</string>
|
||||||
<string name="select_gpu_driver_install_success">Installato%s</string>
|
|
||||||
<string name="select_gpu_driver_use_default">Utilizza il driver predefinito della GPU.</string>
|
<string name="select_gpu_driver_use_default">Utilizza il driver predefinito della GPU.</string>
|
||||||
<string name="select_gpu_driver_error">Il driver selezionato è invalido, è in utilizzo quello predefinito di sistema!</string>
|
|
||||||
<string name="system_gpu_driver">Driver GPU del sistema</string>
|
<string name="system_gpu_driver">Driver GPU del sistema</string>
|
||||||
<string name="installing_driver">Installando i driver...</string>
|
<string name="installing_driver">Installando i driver...</string>
|
||||||
|
|
||||||
|
|
|
@ -170,9 +170,7 @@
|
||||||
<string name="select_gpu_driver_title">現在のGPUドライバーを置き換えますか?</string>
|
<string name="select_gpu_driver_title">現在のGPUドライバーを置き換えますか?</string>
|
||||||
<string name="select_gpu_driver_install">インストール</string>
|
<string name="select_gpu_driver_install">インストール</string>
|
||||||
<string name="select_gpu_driver_default">デフォルト</string>
|
<string name="select_gpu_driver_default">デフォルト</string>
|
||||||
<string name="select_gpu_driver_install_success">%s をインストールしました</string>
|
|
||||||
<string name="select_gpu_driver_use_default">デフォルトのGPUドライバーを使用します</string>
|
<string name="select_gpu_driver_use_default">デフォルトのGPUドライバーを使用します</string>
|
||||||
<string name="select_gpu_driver_error">選択されたドライバが無効なため、システムのデフォルトを使用します!</string>
|
|
||||||
<string name="system_gpu_driver">システムのGPUドライバ</string>
|
<string name="system_gpu_driver">システムのGPUドライバ</string>
|
||||||
<string name="installing_driver">インストール中…</string>
|
<string name="installing_driver">インストール中…</string>
|
||||||
|
|
||||||
|
|
|
@ -171,9 +171,7 @@
|
||||||
<string name="select_gpu_driver_title">현재 사용 중인 GPU 드라이버를 교체하겠습니까?</string>
|
<string name="select_gpu_driver_title">현재 사용 중인 GPU 드라이버를 교체하겠습니까?</string>
|
||||||
<string name="select_gpu_driver_install">설치</string>
|
<string name="select_gpu_driver_install">설치</string>
|
||||||
<string name="select_gpu_driver_default">기본값</string>
|
<string name="select_gpu_driver_default">기본값</string>
|
||||||
<string name="select_gpu_driver_install_success">설치된 %s</string>
|
|
||||||
<string name="select_gpu_driver_use_default">기본 GPU 드라이버 사용</string>
|
<string name="select_gpu_driver_use_default">기본 GPU 드라이버 사용</string>
|
||||||
<string name="select_gpu_driver_error">시스템 기본값을 사용하여 잘못된 드라이버를 선택했습니다!</string>
|
|
||||||
<string name="system_gpu_driver">시스템 GPU 드라이버</string>
|
<string name="system_gpu_driver">시스템 GPU 드라이버</string>
|
||||||
<string name="installing_driver">드라이버 설치 중...</string>
|
<string name="installing_driver">드라이버 설치 중...</string>
|
||||||
|
|
||||||
|
|
|
@ -171,9 +171,7 @@
|
||||||
<string name="select_gpu_driver_title">Ønsker du å bytte ut din nåværende GPU-driver?</string>
|
<string name="select_gpu_driver_title">Ønsker du å bytte ut din nåværende GPU-driver?</string>
|
||||||
<string name="select_gpu_driver_install">Installer</string>
|
<string name="select_gpu_driver_install">Installer</string>
|
||||||
<string name="select_gpu_driver_default">Standard</string>
|
<string name="select_gpu_driver_default">Standard</string>
|
||||||
<string name="select_gpu_driver_install_success">Installert %s</string>
|
|
||||||
<string name="select_gpu_driver_use_default">Bruk av standard GPU-driver</string>
|
<string name="select_gpu_driver_use_default">Bruk av standard GPU-driver</string>
|
||||||
<string name="select_gpu_driver_error">Ugyldig driver valgt, bruker systemstandard!</string>
|
|
||||||
<string name="system_gpu_driver">Systemets GPU-driver</string>
|
<string name="system_gpu_driver">Systemets GPU-driver</string>
|
||||||
<string name="installing_driver">Installerer driver...</string>
|
<string name="installing_driver">Installerer driver...</string>
|
||||||
|
|
||||||
|
|
|
@ -171,9 +171,7 @@
|
||||||
<string name="select_gpu_driver_title">Chcesz zastąpić obecny sterownik układu graficznego?</string>
|
<string name="select_gpu_driver_title">Chcesz zastąpić obecny sterownik układu graficznego?</string>
|
||||||
<string name="select_gpu_driver_install">Zainstaluj</string>
|
<string name="select_gpu_driver_install">Zainstaluj</string>
|
||||||
<string name="select_gpu_driver_default">Domyślne</string>
|
<string name="select_gpu_driver_default">Domyślne</string>
|
||||||
<string name="select_gpu_driver_install_success">Zainstalowano %s</string>
|
|
||||||
<string name="select_gpu_driver_use_default">Aktywny domyślny sterownik GPU</string>
|
<string name="select_gpu_driver_use_default">Aktywny domyślny sterownik GPU</string>
|
||||||
<string name="select_gpu_driver_error">Wybrano błędny sterownik, powrót do domyślnego. </string>
|
|
||||||
<string name="system_gpu_driver">Systemowy sterownik GPU</string>
|
<string name="system_gpu_driver">Systemowy sterownik GPU</string>
|
||||||
<string name="installing_driver">Instalowanie sterownika...</string>
|
<string name="installing_driver">Instalowanie sterownika...</string>
|
||||||
|
|
||||||
|
|
|
@ -171,9 +171,7 @@
|
||||||
<string name="select_gpu_driver_title">Queres substituir o driver do GPU atual? </string>
|
<string name="select_gpu_driver_title">Queres substituir o driver do GPU atual? </string>
|
||||||
<string name="select_gpu_driver_install">Instalar</string>
|
<string name="select_gpu_driver_install">Instalar</string>
|
||||||
<string name="select_gpu_driver_default">Padrão</string>
|
<string name="select_gpu_driver_default">Padrão</string>
|
||||||
<string name="select_gpu_driver_install_success">Instalado%s</string>
|
|
||||||
<string name="select_gpu_driver_use_default">Usar o driver padrão do GPU</string>
|
<string name="select_gpu_driver_use_default">Usar o driver padrão do GPU</string>
|
||||||
<string name="select_gpu_driver_error">Driver selecionado inválido, a usar o padrão do sistema!</string>
|
|
||||||
<string name="system_gpu_driver">Driver do GPU padrão</string>
|
<string name="system_gpu_driver">Driver do GPU padrão</string>
|
||||||
<string name="installing_driver">A instalar o Driver...</string>
|
<string name="installing_driver">A instalar o Driver...</string>
|
||||||
|
|
||||||
|
|
|
@ -171,9 +171,7 @@
|
||||||
<string name="select_gpu_driver_title">Queres substituir o driver do GPU atual? </string>
|
<string name="select_gpu_driver_title">Queres substituir o driver do GPU atual? </string>
|
||||||
<string name="select_gpu_driver_install">Instalar</string>
|
<string name="select_gpu_driver_install">Instalar</string>
|
||||||
<string name="select_gpu_driver_default">Padrão</string>
|
<string name="select_gpu_driver_default">Padrão</string>
|
||||||
<string name="select_gpu_driver_install_success">Instalado%s</string>
|
|
||||||
<string name="select_gpu_driver_use_default">Usar o driver padrão do GPU</string>
|
<string name="select_gpu_driver_use_default">Usar o driver padrão do GPU</string>
|
||||||
<string name="select_gpu_driver_error">Driver selecionado inválido, a usar o padrão do sistema!</string>
|
|
||||||
<string name="system_gpu_driver">Driver do GPU padrão</string>
|
<string name="system_gpu_driver">Driver do GPU padrão</string>
|
||||||
<string name="installing_driver">A instalar o Driver...</string>
|
<string name="installing_driver">A instalar o Driver...</string>
|
||||||
|
|
||||||
|
|
|
@ -171,9 +171,7 @@
|
||||||
<string name="select_gpu_driver_title">Хотите заменить текущий драйвер ГП?</string>
|
<string name="select_gpu_driver_title">Хотите заменить текущий драйвер ГП?</string>
|
||||||
<string name="select_gpu_driver_install">Установить</string>
|
<string name="select_gpu_driver_install">Установить</string>
|
||||||
<string name="select_gpu_driver_default">По умолчанию</string>
|
<string name="select_gpu_driver_default">По умолчанию</string>
|
||||||
<string name="select_gpu_driver_install_success">Установлено %s</string>
|
|
||||||
<string name="select_gpu_driver_use_default">Используется стандартный драйвер ГП </string>
|
<string name="select_gpu_driver_use_default">Используется стандартный драйвер ГП </string>
|
||||||
<string name="select_gpu_driver_error">Выбран неверный драйвер, используется стандартный системный!</string>
|
|
||||||
<string name="system_gpu_driver">Системный драйвер ГП</string>
|
<string name="system_gpu_driver">Системный драйвер ГП</string>
|
||||||
<string name="installing_driver">Установка драйвера...</string>
|
<string name="installing_driver">Установка драйвера...</string>
|
||||||
|
|
||||||
|
|
|
@ -171,9 +171,7 @@
|
||||||
<string name="select_gpu_driver_title">Хочете замінити поточний драйвер ГП?</string>
|
<string name="select_gpu_driver_title">Хочете замінити поточний драйвер ГП?</string>
|
||||||
<string name="select_gpu_driver_install">Встановити</string>
|
<string name="select_gpu_driver_install">Встановити</string>
|
||||||
<string name="select_gpu_driver_default">За замовчуванням</string>
|
<string name="select_gpu_driver_default">За замовчуванням</string>
|
||||||
<string name="select_gpu_driver_install_success">Встановлено %s</string>
|
|
||||||
<string name="select_gpu_driver_use_default">Використовується стандартний драйвер ГП</string>
|
<string name="select_gpu_driver_use_default">Використовується стандартний драйвер ГП</string>
|
||||||
<string name="select_gpu_driver_error">Обрано неправильний драйвер, використовується стандартний системний!</string>
|
|
||||||
<string name="system_gpu_driver">Системний драйвер ГП</string>
|
<string name="system_gpu_driver">Системний драйвер ГП</string>
|
||||||
<string name="installing_driver">Встановлення драйвера...</string>
|
<string name="installing_driver">Встановлення драйвера...</string>
|
||||||
|
|
||||||
|
|
|
@ -171,9 +171,7 @@
|
||||||
<string name="select_gpu_driver_title">要取代您当前的 GPU 驱动程序吗?</string>
|
<string name="select_gpu_driver_title">要取代您当前的 GPU 驱动程序吗?</string>
|
||||||
<string name="select_gpu_driver_install">安装</string>
|
<string name="select_gpu_driver_install">安装</string>
|
||||||
<string name="select_gpu_driver_default">系统默认</string>
|
<string name="select_gpu_driver_default">系统默认</string>
|
||||||
<string name="select_gpu_driver_install_success">已安装 %s</string>
|
|
||||||
<string name="select_gpu_driver_use_default">使用默认 GPU 驱动程序</string>
|
<string name="select_gpu_driver_use_default">使用默认 GPU 驱动程序</string>
|
||||||
<string name="select_gpu_driver_error">选择的驱动程序无效,将使用系统默认的驱动程序!</string>
|
|
||||||
<string name="system_gpu_driver">系统 GPU 驱动程序</string>
|
<string name="system_gpu_driver">系统 GPU 驱动程序</string>
|
||||||
<string name="installing_driver">正在安装驱动程序…</string>
|
<string name="installing_driver">正在安装驱动程序…</string>
|
||||||
|
|
||||||
|
|
|
@ -171,9 +171,7 @@
|
||||||
<string name="select_gpu_driver_title">要取代您目前的 GPU 驅動程式嗎?</string>
|
<string name="select_gpu_driver_title">要取代您目前的 GPU 驅動程式嗎?</string>
|
||||||
<string name="select_gpu_driver_install">安裝</string>
|
<string name="select_gpu_driver_install">安裝</string>
|
||||||
<string name="select_gpu_driver_default">預設</string>
|
<string name="select_gpu_driver_default">預設</string>
|
||||||
<string name="select_gpu_driver_install_success">已安裝 %s</string>
|
|
||||||
<string name="select_gpu_driver_use_default">使用預設 GPU 驅動程式</string>
|
<string name="select_gpu_driver_use_default">使用預設 GPU 驅動程式</string>
|
||||||
<string name="select_gpu_driver_error">選取的驅動程式無效,將使用系統預設驅動程式!</string>
|
|
||||||
<string name="system_gpu_driver">系統 GPU 驅動程式</string>
|
<string name="system_gpu_driver">系統 GPU 驅動程式</string>
|
||||||
<string name="installing_driver">正在安裝驅動程式…</string>
|
<string name="installing_driver">正在安裝驅動程式…</string>
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
<dimen name="menu_width">256dp</dimen>
|
<dimen name="menu_width">256dp</dimen>
|
||||||
<dimen name="card_width">165dp</dimen>
|
<dimen name="card_width">165dp</dimen>
|
||||||
<dimen name="icon_inset">24dp</dimen>
|
<dimen name="icon_inset">24dp</dimen>
|
||||||
|
<dimen name="spacing_bottom_list_fab">72dp</dimen>
|
||||||
|
<dimen name="spacing_fab">24dp</dimen>
|
||||||
|
|
||||||
<dimen name="dialog_margin">20dp</dimen>
|
<dimen name="dialog_margin">20dp</dimen>
|
||||||
<dimen name="elevated_app_bar">3dp</dimen>
|
<dimen name="elevated_app_bar">3dp</dimen>
|
||||||
|
|
|
@ -72,6 +72,7 @@
|
||||||
<string name="invalid_keys_error">Invalid encryption keys</string>
|
<string name="invalid_keys_error">Invalid encryption keys</string>
|
||||||
<string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string>
|
<string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string>
|
||||||
<string name="install_keys_failure_description">The selected file is incorrect or corrupt. Please redump your keys.</string>
|
<string name="install_keys_failure_description">The selected file is incorrect or corrupt. Please redump your keys.</string>
|
||||||
|
<string name="gpu_driver_manager">GPU Driver Manager</string>
|
||||||
<string name="install_gpu_driver">Install GPU driver</string>
|
<string name="install_gpu_driver">Install GPU driver</string>
|
||||||
<string name="install_gpu_driver_description">Install alternative drivers for potentially better performance or accuracy</string>
|
<string name="install_gpu_driver_description">Install alternative drivers for potentially better performance or accuracy</string>
|
||||||
<string name="advanced_settings">Advanced settings</string>
|
<string name="advanced_settings">Advanced settings</string>
|
||||||
|
@ -234,15 +235,17 @@
|
||||||
<string name="export_failed">Export failed</string>
|
<string name="export_failed">Export failed</string>
|
||||||
<string name="import_failed">Import failed</string>
|
<string name="import_failed">Import failed</string>
|
||||||
<string name="cancelling">Cancelling</string>
|
<string name="cancelling">Cancelling</string>
|
||||||
|
<string name="install">Install</string>
|
||||||
|
<string name="delete">Delete</string>
|
||||||
|
|
||||||
<!-- GPU driver installation -->
|
<!-- GPU driver installation -->
|
||||||
<string name="select_gpu_driver">Select GPU driver</string>
|
<string name="select_gpu_driver">Select GPU driver</string>
|
||||||
<string name="select_gpu_driver_title">Would you like to replace your current GPU driver?</string>
|
<string name="select_gpu_driver_title">Would you like to replace your current GPU driver?</string>
|
||||||
<string name="select_gpu_driver_install">Install</string>
|
<string name="select_gpu_driver_install">Install</string>
|
||||||
<string name="select_gpu_driver_default">Default</string>
|
<string name="select_gpu_driver_default">Default</string>
|
||||||
<string name="select_gpu_driver_install_success">Installed %s</string>
|
|
||||||
<string name="select_gpu_driver_use_default">Using default GPU driver</string>
|
<string name="select_gpu_driver_use_default">Using default GPU driver</string>
|
||||||
<string name="select_gpu_driver_error">Invalid driver selected, using system default!</string>
|
<string name="select_gpu_driver_error">Invalid driver selected</string>
|
||||||
|
<string name="driver_already_installed">Driver already installed</string>
|
||||||
<string name="system_gpu_driver">System GPU driver</string>
|
<string name="system_gpu_driver">System GPU driver</string>
|
||||||
<string name="installing_driver">Installing driver…</string>
|
<string name="installing_driver">Installing driver…</string>
|
||||||
|
|
||||||
|
|
|
@ -106,7 +106,7 @@ static_assert(KernelPageBufferAdditionalSize ==
|
||||||
/// memory.
|
/// memory.
|
||||||
static KPhysicalAddress TranslateSlabAddrToPhysical(KMemoryLayout& memory_layout,
|
static KPhysicalAddress TranslateSlabAddrToPhysical(KMemoryLayout& memory_layout,
|
||||||
KVirtualAddress slab_addr) {
|
KVirtualAddress slab_addr) {
|
||||||
slab_addr -= GetInteger(memory_layout.GetSlabRegionAddress());
|
slab_addr -= memory_layout.GetSlabRegion().GetAddress();
|
||||||
return GetInteger(slab_addr) + Core::DramMemoryMap::SlabHeapBase;
|
return GetInteger(slab_addr) + Core::DramMemoryMap::SlabHeapBase;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,7 +196,12 @@ void InitializeSlabHeaps(Core::System& system, KMemoryLayout& memory_layout) {
|
||||||
auto& kernel = system.Kernel();
|
auto& kernel = system.Kernel();
|
||||||
|
|
||||||
// Get the start of the slab region, since that's where we'll be working.
|
// Get the start of the slab region, since that's where we'll be working.
|
||||||
KVirtualAddress address = memory_layout.GetSlabRegionAddress();
|
const KMemoryRegion& slab_region = memory_layout.GetSlabRegion();
|
||||||
|
KVirtualAddress address = slab_region.GetAddress();
|
||||||
|
|
||||||
|
// Clear the slab region.
|
||||||
|
// TODO: implement access to kernel VAs.
|
||||||
|
// std::memset(device_ptr, 0, slab_region.GetSize());
|
||||||
|
|
||||||
// Initialize slab type array to be in sorted order.
|
// Initialize slab type array to be in sorted order.
|
||||||
std::array<KSlabType, KSlabType_Count> slab_types;
|
std::array<KSlabType, KSlabType_Count> slab_types;
|
||||||
|
|
|
@ -19,4 +19,8 @@ static inline KPhysicalAddress GetInitialProcessBinaryPhysicalAddress() {
|
||||||
MainMemoryAddress);
|
MainMemoryAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline size_t GetInitialProcessBinarySize() {
|
||||||
|
return InitialProcessBinarySizeMax;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Kernel
|
} // namespace Kernel
|
||||||
|
|
|
@ -36,6 +36,7 @@ enum class KMemoryState : u32 {
|
||||||
FlagCanChangeAttribute = (1 << 24),
|
FlagCanChangeAttribute = (1 << 24),
|
||||||
FlagCanCodeMemory = (1 << 25),
|
FlagCanCodeMemory = (1 << 25),
|
||||||
FlagLinearMapped = (1 << 26),
|
FlagLinearMapped = (1 << 26),
|
||||||
|
FlagCanPermissionLock = (1 << 27),
|
||||||
|
|
||||||
FlagsData = FlagCanReprotect | FlagCanUseIpc | FlagCanUseNonDeviceIpc | FlagCanUseNonSecureIpc |
|
FlagsData = FlagCanReprotect | FlagCanUseIpc | FlagCanUseNonDeviceIpc | FlagCanUseNonSecureIpc |
|
||||||
FlagMapped | FlagCanAlias | FlagCanTransfer | FlagCanQueryPhysical |
|
FlagMapped | FlagCanAlias | FlagCanTransfer | FlagCanQueryPhysical |
|
||||||
|
@ -50,12 +51,16 @@ enum class KMemoryState : u32 {
|
||||||
FlagLinearMapped,
|
FlagLinearMapped,
|
||||||
|
|
||||||
Free = static_cast<u32>(Svc::MemoryState::Free),
|
Free = static_cast<u32>(Svc::MemoryState::Free),
|
||||||
Io = static_cast<u32>(Svc::MemoryState::Io) | FlagMapped | FlagCanDeviceMap |
|
|
||||||
|
IoMemory = static_cast<u32>(Svc::MemoryState::Io) | FlagMapped | FlagCanDeviceMap |
|
||||||
FlagCanAlignedDeviceMap,
|
FlagCanAlignedDeviceMap,
|
||||||
|
IoRegister =
|
||||||
|
static_cast<u32>(Svc::MemoryState::Io) | FlagCanDeviceMap | FlagCanAlignedDeviceMap,
|
||||||
|
|
||||||
Static = static_cast<u32>(Svc::MemoryState::Static) | FlagMapped | FlagCanQueryPhysical,
|
Static = static_cast<u32>(Svc::MemoryState::Static) | FlagMapped | FlagCanQueryPhysical,
|
||||||
Code = static_cast<u32>(Svc::MemoryState::Code) | FlagsCode | FlagCanMapProcess,
|
Code = static_cast<u32>(Svc::MemoryState::Code) | FlagsCode | FlagCanMapProcess,
|
||||||
CodeData = static_cast<u32>(Svc::MemoryState::CodeData) | FlagsData | FlagCanMapProcess |
|
CodeData = static_cast<u32>(Svc::MemoryState::CodeData) | FlagsData | FlagCanMapProcess |
|
||||||
FlagCanCodeMemory,
|
FlagCanCodeMemory | FlagCanPermissionLock,
|
||||||
Normal = static_cast<u32>(Svc::MemoryState::Normal) | FlagsData | FlagCanCodeMemory,
|
Normal = static_cast<u32>(Svc::MemoryState::Normal) | FlagsData | FlagCanCodeMemory,
|
||||||
Shared = static_cast<u32>(Svc::MemoryState::Shared) | FlagMapped | FlagReferenceCounted |
|
Shared = static_cast<u32>(Svc::MemoryState::Shared) | FlagMapped | FlagReferenceCounted |
|
||||||
FlagLinearMapped,
|
FlagLinearMapped,
|
||||||
|
@ -65,7 +70,8 @@ enum class KMemoryState : u32 {
|
||||||
AliasCode = static_cast<u32>(Svc::MemoryState::AliasCode) | FlagsCode | FlagCanMapProcess |
|
AliasCode = static_cast<u32>(Svc::MemoryState::AliasCode) | FlagsCode | FlagCanMapProcess |
|
||||||
FlagCanCodeAlias,
|
FlagCanCodeAlias,
|
||||||
AliasCodeData = static_cast<u32>(Svc::MemoryState::AliasCodeData) | FlagsData |
|
AliasCodeData = static_cast<u32>(Svc::MemoryState::AliasCodeData) | FlagsData |
|
||||||
FlagCanMapProcess | FlagCanCodeAlias | FlagCanCodeMemory,
|
FlagCanMapProcess | FlagCanCodeAlias | FlagCanCodeMemory |
|
||||||
|
FlagCanPermissionLock,
|
||||||
|
|
||||||
Ipc = static_cast<u32>(Svc::MemoryState::Ipc) | FlagsMisc | FlagCanAlignedDeviceMap |
|
Ipc = static_cast<u32>(Svc::MemoryState::Ipc) | FlagsMisc | FlagCanAlignedDeviceMap |
|
||||||
FlagCanUseIpc | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
|
FlagCanUseIpc | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
|
||||||
|
@ -73,7 +79,7 @@ enum class KMemoryState : u32 {
|
||||||
Stack = static_cast<u32>(Svc::MemoryState::Stack) | FlagsMisc | FlagCanAlignedDeviceMap |
|
Stack = static_cast<u32>(Svc::MemoryState::Stack) | FlagsMisc | FlagCanAlignedDeviceMap |
|
||||||
FlagCanUseIpc | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
|
FlagCanUseIpc | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
|
||||||
|
|
||||||
ThreadLocal = static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagMapped | FlagLinearMapped,
|
ThreadLocal = static_cast<u32>(Svc::MemoryState::ThreadLocal) | FlagLinearMapped,
|
||||||
|
|
||||||
Transfered = static_cast<u32>(Svc::MemoryState::Transfered) | FlagsMisc |
|
Transfered = static_cast<u32>(Svc::MemoryState::Transfered) | FlagsMisc |
|
||||||
FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc |
|
FlagCanAlignedDeviceMap | FlagCanChangeAttribute | FlagCanUseIpc |
|
||||||
|
@ -94,7 +100,7 @@ enum class KMemoryState : u32 {
|
||||||
NonDeviceIpc =
|
NonDeviceIpc =
|
||||||
static_cast<u32>(Svc::MemoryState::NonDeviceIpc) | FlagsMisc | FlagCanUseNonDeviceIpc,
|
static_cast<u32>(Svc::MemoryState::NonDeviceIpc) | FlagsMisc | FlagCanUseNonDeviceIpc,
|
||||||
|
|
||||||
Kernel = static_cast<u32>(Svc::MemoryState::Kernel) | FlagMapped,
|
Kernel = static_cast<u32>(Svc::MemoryState::Kernel),
|
||||||
|
|
||||||
GeneratedCode = static_cast<u32>(Svc::MemoryState::GeneratedCode) | FlagMapped |
|
GeneratedCode = static_cast<u32>(Svc::MemoryState::GeneratedCode) | FlagMapped |
|
||||||
FlagReferenceCounted | FlagCanDebug | FlagLinearMapped,
|
FlagReferenceCounted | FlagCanDebug | FlagLinearMapped,
|
||||||
|
@ -105,34 +111,36 @@ enum class KMemoryState : u32 {
|
||||||
|
|
||||||
Insecure = static_cast<u32>(Svc::MemoryState::Insecure) | FlagMapped | FlagReferenceCounted |
|
Insecure = static_cast<u32>(Svc::MemoryState::Insecure) | FlagMapped | FlagReferenceCounted |
|
||||||
FlagLinearMapped | FlagCanChangeAttribute | FlagCanDeviceMap |
|
FlagLinearMapped | FlagCanChangeAttribute | FlagCanDeviceMap |
|
||||||
FlagCanAlignedDeviceMap | FlagCanUseNonSecureIpc | FlagCanUseNonDeviceIpc,
|
FlagCanAlignedDeviceMap | FlagCanQueryPhysical | FlagCanUseNonSecureIpc |
|
||||||
|
FlagCanUseNonDeviceIpc,
|
||||||
};
|
};
|
||||||
DECLARE_ENUM_FLAG_OPERATORS(KMemoryState);
|
DECLARE_ENUM_FLAG_OPERATORS(KMemoryState);
|
||||||
|
|
||||||
static_assert(static_cast<u32>(KMemoryState::Free) == 0x00000000);
|
static_assert(static_cast<u32>(KMemoryState::Free) == 0x00000000);
|
||||||
static_assert(static_cast<u32>(KMemoryState::Io) == 0x00182001);
|
static_assert(static_cast<u32>(KMemoryState::IoMemory) == 0x00182001);
|
||||||
|
static_assert(static_cast<u32>(KMemoryState::IoRegister) == 0x00180001);
|
||||||
static_assert(static_cast<u32>(KMemoryState::Static) == 0x00042002);
|
static_assert(static_cast<u32>(KMemoryState::Static) == 0x00042002);
|
||||||
static_assert(static_cast<u32>(KMemoryState::Code) == 0x04DC7E03);
|
static_assert(static_cast<u32>(KMemoryState::Code) == 0x04DC7E03);
|
||||||
static_assert(static_cast<u32>(KMemoryState::CodeData) == 0x07FEBD04);
|
static_assert(static_cast<u32>(KMemoryState::CodeData) == 0x0FFEBD04);
|
||||||
static_assert(static_cast<u32>(KMemoryState::Normal) == 0x077EBD05);
|
static_assert(static_cast<u32>(KMemoryState::Normal) == 0x077EBD05);
|
||||||
static_assert(static_cast<u32>(KMemoryState::Shared) == 0x04402006);
|
static_assert(static_cast<u32>(KMemoryState::Shared) == 0x04402006);
|
||||||
|
|
||||||
static_assert(static_cast<u32>(KMemoryState::AliasCode) == 0x04DD7E08);
|
static_assert(static_cast<u32>(KMemoryState::AliasCode) == 0x04DD7E08);
|
||||||
static_assert(static_cast<u32>(KMemoryState::AliasCodeData) == 0x07FFBD09);
|
static_assert(static_cast<u32>(KMemoryState::AliasCodeData) == 0x0FFFBD09);
|
||||||
static_assert(static_cast<u32>(KMemoryState::Ipc) == 0x045C3C0A);
|
static_assert(static_cast<u32>(KMemoryState::Ipc) == 0x045C3C0A);
|
||||||
static_assert(static_cast<u32>(KMemoryState::Stack) == 0x045C3C0B);
|
static_assert(static_cast<u32>(KMemoryState::Stack) == 0x045C3C0B);
|
||||||
static_assert(static_cast<u32>(KMemoryState::ThreadLocal) == 0x0400200C);
|
static_assert(static_cast<u32>(KMemoryState::ThreadLocal) == 0x0400000C);
|
||||||
static_assert(static_cast<u32>(KMemoryState::Transfered) == 0x055C3C0D);
|
static_assert(static_cast<u32>(KMemoryState::Transfered) == 0x055C3C0D);
|
||||||
static_assert(static_cast<u32>(KMemoryState::SharedTransfered) == 0x045C380E);
|
static_assert(static_cast<u32>(KMemoryState::SharedTransfered) == 0x045C380E);
|
||||||
static_assert(static_cast<u32>(KMemoryState::SharedCode) == 0x0440380F);
|
static_assert(static_cast<u32>(KMemoryState::SharedCode) == 0x0440380F);
|
||||||
static_assert(static_cast<u32>(KMemoryState::Inaccessible) == 0x00000010);
|
static_assert(static_cast<u32>(KMemoryState::Inaccessible) == 0x00000010);
|
||||||
static_assert(static_cast<u32>(KMemoryState::NonSecureIpc) == 0x045C3811);
|
static_assert(static_cast<u32>(KMemoryState::NonSecureIpc) == 0x045C3811);
|
||||||
static_assert(static_cast<u32>(KMemoryState::NonDeviceIpc) == 0x044C2812);
|
static_assert(static_cast<u32>(KMemoryState::NonDeviceIpc) == 0x044C2812);
|
||||||
static_assert(static_cast<u32>(KMemoryState::Kernel) == 0x00002013);
|
static_assert(static_cast<u32>(KMemoryState::Kernel) == 0x00000013);
|
||||||
static_assert(static_cast<u32>(KMemoryState::GeneratedCode) == 0x04402214);
|
static_assert(static_cast<u32>(KMemoryState::GeneratedCode) == 0x04402214);
|
||||||
static_assert(static_cast<u32>(KMemoryState::CodeOut) == 0x04402015);
|
static_assert(static_cast<u32>(KMemoryState::CodeOut) == 0x04402015);
|
||||||
static_assert(static_cast<u32>(KMemoryState::Coverage) == 0x00002016);
|
static_assert(static_cast<u32>(KMemoryState::Coverage) == 0x00002016);
|
||||||
static_assert(static_cast<u32>(KMemoryState::Insecure) == 0x05583817);
|
static_assert(static_cast<u32>(KMemoryState::Insecure) == 0x055C3817);
|
||||||
|
|
||||||
enum class KMemoryPermission : u8 {
|
enum class KMemoryPermission : u8 {
|
||||||
None = 0,
|
None = 0,
|
||||||
|
@ -182,8 +190,9 @@ enum class KMemoryAttribute : u8 {
|
||||||
IpcLocked = static_cast<u8>(Svc::MemoryAttribute::IpcLocked),
|
IpcLocked = static_cast<u8>(Svc::MemoryAttribute::IpcLocked),
|
||||||
DeviceShared = static_cast<u8>(Svc::MemoryAttribute::DeviceShared),
|
DeviceShared = static_cast<u8>(Svc::MemoryAttribute::DeviceShared),
|
||||||
Uncached = static_cast<u8>(Svc::MemoryAttribute::Uncached),
|
Uncached = static_cast<u8>(Svc::MemoryAttribute::Uncached),
|
||||||
|
PermissionLocked = static_cast<u8>(Svc::MemoryAttribute::PermissionLocked),
|
||||||
|
|
||||||
SetMask = Uncached,
|
SetMask = Uncached | PermissionLocked,
|
||||||
};
|
};
|
||||||
DECLARE_ENUM_FLAG_OPERATORS(KMemoryAttribute);
|
DECLARE_ENUM_FLAG_OPERATORS(KMemoryAttribute);
|
||||||
|
|
||||||
|
@ -261,6 +270,10 @@ struct KMemoryInfo {
|
||||||
return m_state;
|
return m_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr Svc::MemoryState GetSvcState() const {
|
||||||
|
return static_cast<Svc::MemoryState>(m_state & KMemoryState::Mask);
|
||||||
|
}
|
||||||
|
|
||||||
constexpr KMemoryPermission GetPermission() const {
|
constexpr KMemoryPermission GetPermission() const {
|
||||||
return m_permission;
|
return m_permission;
|
||||||
}
|
}
|
||||||
|
@ -326,6 +339,10 @@ public:
|
||||||
return this->GetEndAddress() - 1;
|
return this->GetEndAddress() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr KMemoryState GetState() const {
|
||||||
|
return m_memory_state;
|
||||||
|
}
|
||||||
|
|
||||||
constexpr u16 GetIpcLockCount() const {
|
constexpr u16 GetIpcLockCount() const {
|
||||||
return m_ipc_lock_count;
|
return m_ipc_lock_count;
|
||||||
}
|
}
|
||||||
|
@ -443,6 +460,13 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr void UpdateAttribute(KMemoryAttribute mask, KMemoryAttribute attr) {
|
||||||
|
ASSERT(False(mask & KMemoryAttribute::IpcLocked));
|
||||||
|
ASSERT(False(mask & KMemoryAttribute::DeviceShared));
|
||||||
|
|
||||||
|
m_attribute = (m_attribute & ~mask) | attr;
|
||||||
|
}
|
||||||
|
|
||||||
constexpr void Split(KMemoryBlock* block, KProcessAddress addr) {
|
constexpr void Split(KMemoryBlock* block, KProcessAddress addr) {
|
||||||
ASSERT(this->GetAddress() < addr);
|
ASSERT(this->GetAddress() < addr);
|
||||||
ASSERT(this->Contains(addr));
|
ASSERT(this->Contains(addr));
|
||||||
|
|
|
@ -160,8 +160,8 @@ void KMemoryBlockManager::Update(KMemoryBlockManagerUpdateAllocator* allocator,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update block state.
|
// Update block state.
|
||||||
it->Update(state, perm, attr, cur_address == address, static_cast<u8>(set_disable_attr),
|
it->Update(state, perm, attr, it->GetAddress() == address,
|
||||||
static_cast<u8>(clear_disable_attr));
|
static_cast<u8>(set_disable_attr), static_cast<u8>(clear_disable_attr));
|
||||||
cur_address += cur_info.GetSize();
|
cur_address += cur_info.GetSize();
|
||||||
remaining_pages -= cur_info.GetNumPages();
|
remaining_pages -= cur_info.GetNumPages();
|
||||||
}
|
}
|
||||||
|
@ -175,7 +175,9 @@ void KMemoryBlockManager::UpdateIfMatch(KMemoryBlockManagerUpdateAllocator* allo
|
||||||
KProcessAddress address, size_t num_pages,
|
KProcessAddress address, size_t num_pages,
|
||||||
KMemoryState test_state, KMemoryPermission test_perm,
|
KMemoryState test_state, KMemoryPermission test_perm,
|
||||||
KMemoryAttribute test_attr, KMemoryState state,
|
KMemoryAttribute test_attr, KMemoryState state,
|
||||||
KMemoryPermission perm, KMemoryAttribute attr) {
|
KMemoryPermission perm, KMemoryAttribute attr,
|
||||||
|
KMemoryBlockDisableMergeAttribute set_disable_attr,
|
||||||
|
KMemoryBlockDisableMergeAttribute clear_disable_attr) {
|
||||||
// Ensure for auditing that we never end up with an invalid tree.
|
// Ensure for auditing that we never end up with an invalid tree.
|
||||||
KScopedMemoryBlockManagerAuditor auditor(this);
|
KScopedMemoryBlockManagerAuditor auditor(this);
|
||||||
ASSERT(Common::IsAligned(GetInteger(address), PageSize));
|
ASSERT(Common::IsAligned(GetInteger(address), PageSize));
|
||||||
|
@ -214,7 +216,8 @@ void KMemoryBlockManager::UpdateIfMatch(KMemoryBlockManagerUpdateAllocator* allo
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update block state.
|
// Update block state.
|
||||||
it->Update(state, perm, attr, false, 0, 0);
|
it->Update(state, perm, attr, false, static_cast<u8>(set_disable_attr),
|
||||||
|
static_cast<u8>(clear_disable_attr));
|
||||||
cur_address += cur_info.GetSize();
|
cur_address += cur_info.GetSize();
|
||||||
remaining_pages -= cur_info.GetNumPages();
|
remaining_pages -= cur_info.GetNumPages();
|
||||||
} else {
|
} else {
|
||||||
|
@ -284,6 +287,65 @@ void KMemoryBlockManager::UpdateLock(KMemoryBlockManagerUpdateAllocator* allocat
|
||||||
this->CoalesceForUpdate(allocator, address, num_pages);
|
this->CoalesceForUpdate(allocator, address, num_pages);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void KMemoryBlockManager::UpdateAttribute(KMemoryBlockManagerUpdateAllocator* allocator,
|
||||||
|
KProcessAddress address, size_t num_pages,
|
||||||
|
KMemoryAttribute mask, KMemoryAttribute attr) {
|
||||||
|
// Ensure for auditing that we never end up with an invalid tree.
|
||||||
|
KScopedMemoryBlockManagerAuditor auditor(this);
|
||||||
|
ASSERT(Common::IsAligned(GetInteger(address), PageSize));
|
||||||
|
|
||||||
|
KProcessAddress cur_address = address;
|
||||||
|
size_t remaining_pages = num_pages;
|
||||||
|
iterator it = this->FindIterator(address);
|
||||||
|
|
||||||
|
while (remaining_pages > 0) {
|
||||||
|
const size_t remaining_size = remaining_pages * PageSize;
|
||||||
|
KMemoryInfo cur_info = it->GetMemoryInfo();
|
||||||
|
|
||||||
|
if ((it->GetAttribute() & mask) != attr) {
|
||||||
|
// If we need to, create a new block before and insert it.
|
||||||
|
if (cur_info.GetAddress() != GetInteger(cur_address)) {
|
||||||
|
KMemoryBlock* new_block = allocator->Allocate();
|
||||||
|
|
||||||
|
it->Split(new_block, cur_address);
|
||||||
|
it = m_memory_block_tree.insert(*new_block);
|
||||||
|
it++;
|
||||||
|
|
||||||
|
cur_info = it->GetMemoryInfo();
|
||||||
|
cur_address = cur_info.GetAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we need to, create a new block after and insert it.
|
||||||
|
if (cur_info.GetSize() > remaining_size) {
|
||||||
|
KMemoryBlock* new_block = allocator->Allocate();
|
||||||
|
|
||||||
|
it->Split(new_block, cur_address + remaining_size);
|
||||||
|
it = m_memory_block_tree.insert(*new_block);
|
||||||
|
|
||||||
|
cur_info = it->GetMemoryInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update block state.
|
||||||
|
it->UpdateAttribute(mask, attr);
|
||||||
|
cur_address += cur_info.GetSize();
|
||||||
|
remaining_pages -= cur_info.GetNumPages();
|
||||||
|
} else {
|
||||||
|
// If we already have the right attributes, just advance.
|
||||||
|
if (cur_address + remaining_size < cur_info.GetEndAddress()) {
|
||||||
|
remaining_pages = 0;
|
||||||
|
cur_address += remaining_size;
|
||||||
|
} else {
|
||||||
|
remaining_pages =
|
||||||
|
(cur_address + remaining_size - cur_info.GetEndAddress()) / PageSize;
|
||||||
|
cur_address = cur_info.GetEndAddress();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->CoalesceForUpdate(allocator, address, num_pages);
|
||||||
|
}
|
||||||
|
|
||||||
// Debug.
|
// Debug.
|
||||||
bool KMemoryBlockManager::CheckState() const {
|
bool KMemoryBlockManager::CheckState() const {
|
||||||
// Loop over every block, ensuring that we are sorted and coalesced.
|
// Loop over every block, ensuring that we are sorted and coalesced.
|
||||||
|
|
|
@ -115,7 +115,11 @@ public:
|
||||||
void UpdateIfMatch(KMemoryBlockManagerUpdateAllocator* allocator, KProcessAddress address,
|
void UpdateIfMatch(KMemoryBlockManagerUpdateAllocator* allocator, KProcessAddress address,
|
||||||
size_t num_pages, KMemoryState test_state, KMemoryPermission test_perm,
|
size_t num_pages, KMemoryState test_state, KMemoryPermission test_perm,
|
||||||
KMemoryAttribute test_attr, KMemoryState state, KMemoryPermission perm,
|
KMemoryAttribute test_attr, KMemoryState state, KMemoryPermission perm,
|
||||||
KMemoryAttribute attr);
|
KMemoryAttribute attr, KMemoryBlockDisableMergeAttribute set_disable_attr,
|
||||||
|
KMemoryBlockDisableMergeAttribute clear_disable_attr);
|
||||||
|
|
||||||
|
void UpdateAttribute(KMemoryBlockManagerUpdateAllocator* allocator, KProcessAddress address,
|
||||||
|
size_t num_pages, KMemoryAttribute mask, KMemoryAttribute attr);
|
||||||
|
|
||||||
iterator FindIterator(KProcessAddress address) const {
|
iterator FindIterator(KProcessAddress address) const {
|
||||||
return m_memory_block_tree.find(KMemoryBlock(
|
return m_memory_block_tree.find(KMemoryBlock(
|
||||||
|
|
|
@ -137,11 +137,9 @@ public:
|
||||||
return GetStackTopAddress(core_id, KMemoryRegionType_KernelMiscExceptionStack);
|
return GetStackTopAddress(core_id, KMemoryRegionType_KernelMiscExceptionStack);
|
||||||
}
|
}
|
||||||
|
|
||||||
KVirtualAddress GetSlabRegionAddress() const {
|
const KMemoryRegion& GetSlabRegion() const {
|
||||||
return Dereference(GetVirtualMemoryRegionTree().FindByType(KMemoryRegionType_KernelSlab))
|
return Dereference(GetVirtualMemoryRegionTree().FindByType(KMemoryRegionType_KernelSlab));
|
||||||
.GetAddress();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const KMemoryRegion& GetDeviceRegion(KMemoryRegionType type) const {
|
const KMemoryRegion& GetDeviceRegion(KMemoryRegionType type) const {
|
||||||
return Dereference(GetPhysicalMemoryRegionTree().FindFirstDerived(type));
|
return Dereference(GetPhysicalMemoryRegionTree().FindFirstDerived(type));
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,7 +119,8 @@ void KMemoryManager::Initialize(KVirtualAddress management_region, size_t manage
|
||||||
// Free each region to its corresponding heap.
|
// Free each region to its corresponding heap.
|
||||||
size_t reserved_sizes[MaxManagerCount] = {};
|
size_t reserved_sizes[MaxManagerCount] = {};
|
||||||
const KPhysicalAddress ini_start = GetInitialProcessBinaryPhysicalAddress();
|
const KPhysicalAddress ini_start = GetInitialProcessBinaryPhysicalAddress();
|
||||||
const KPhysicalAddress ini_end = ini_start + InitialProcessBinarySizeMax;
|
const size_t ini_size = GetInitialProcessBinarySize();
|
||||||
|
const KPhysicalAddress ini_end = ini_start + ini_size;
|
||||||
const KPhysicalAddress ini_last = ini_end - 1;
|
const KPhysicalAddress ini_last = ini_end - 1;
|
||||||
for (const auto& it : m_system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree()) {
|
for (const auto& it : m_system.Kernel().MemoryLayout().GetPhysicalMemoryRegionTree()) {
|
||||||
if (it.IsDerivedFrom(KMemoryRegionType_DramUserPool)) {
|
if (it.IsDerivedFrom(KMemoryRegionType_DramUserPool)) {
|
||||||
|
@ -137,13 +138,13 @@ void KMemoryManager::Initialize(KVirtualAddress management_region, size_t manage
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open/reserve the ini memory.
|
// Open/reserve the ini memory.
|
||||||
manager.OpenFirst(ini_start, InitialProcessBinarySizeMax / PageSize);
|
manager.OpenFirst(ini_start, ini_size / PageSize);
|
||||||
reserved_sizes[it.GetAttributes()] += InitialProcessBinarySizeMax;
|
reserved_sizes[it.GetAttributes()] += ini_size;
|
||||||
|
|
||||||
// Free memory after the ini to the heap.
|
// Free memory after the ini to the heap.
|
||||||
if (ini_last != cur_last) {
|
if (ini_last != cur_last) {
|
||||||
ASSERT(cur_end != 0);
|
ASSERT(cur_end != 0);
|
||||||
manager.Free(ini_end, cur_end - ini_end);
|
manager.Free(ini_end, (cur_end - ini_end) / PageSize);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Ensure there's no partial overlap with the ini image.
|
// Ensure there's no partial overlap with the ini image.
|
||||||
|
|
|
@ -190,9 +190,15 @@ static_assert(KMemoryRegionType_DramKernelInitPt.GetValue() ==
|
||||||
constexpr inline auto KMemoryRegionType_DramKernelSecureAppletMemory =
|
constexpr inline auto KMemoryRegionType_DramKernelSecureAppletMemory =
|
||||||
KMemoryRegionType_DramKernelBase.DeriveSparse(1, 3, 0).SetAttribute(
|
KMemoryRegionType_DramKernelBase.DeriveSparse(1, 3, 0).SetAttribute(
|
||||||
KMemoryRegionAttr_LinearMapped);
|
KMemoryRegionAttr_LinearMapped);
|
||||||
|
constexpr inline const auto KMemoryRegionType_DramKernelSecureUnknown =
|
||||||
|
KMemoryRegionType_DramKernelBase.DeriveSparse(1, 3, 1).SetAttribute(
|
||||||
|
KMemoryRegionAttr_LinearMapped);
|
||||||
static_assert(KMemoryRegionType_DramKernelSecureAppletMemory.GetValue() ==
|
static_assert(KMemoryRegionType_DramKernelSecureAppletMemory.GetValue() ==
|
||||||
(0x18E | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap |
|
(0x18E | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap |
|
||||||
KMemoryRegionAttr_LinearMapped));
|
KMemoryRegionAttr_LinearMapped));
|
||||||
|
static_assert(KMemoryRegionType_DramKernelSecureUnknown.GetValue() ==
|
||||||
|
(0x28E | KMemoryRegionAttr_CarveoutProtected | KMemoryRegionAttr_NoUserMap |
|
||||||
|
KMemoryRegionAttr_LinearMapped));
|
||||||
|
|
||||||
constexpr inline auto KMemoryRegionType_DramReservedEarly =
|
constexpr inline auto KMemoryRegionType_DramReservedEarly =
|
||||||
KMemoryRegionType_DramReservedBase.DeriveAttribute(KMemoryRegionAttr_NoUserMap);
|
KMemoryRegionType_DramReservedBase.DeriveAttribute(KMemoryRegionAttr_NoUserMap);
|
||||||
|
@ -217,16 +223,18 @@ constexpr inline auto KMemoryRegionType_DramPoolPartition =
|
||||||
static_assert(KMemoryRegionType_DramPoolPartition.GetValue() ==
|
static_assert(KMemoryRegionType_DramPoolPartition.GetValue() ==
|
||||||
(0x26 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
|
(0x26 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
|
||||||
|
|
||||||
constexpr inline auto KMemoryRegionType_DramPoolManagement =
|
// UNUSED: .Derive(4, 1);
|
||||||
KMemoryRegionType_DramPoolPartition.DeriveTransition(0, 2).DeriveTransition().SetAttribute(
|
// UNUSED: .Derive(4, 2);
|
||||||
|
constexpr inline const auto KMemoryRegionType_DramPoolManagement =
|
||||||
|
KMemoryRegionType_DramPoolPartition.Derive(4, 0).SetAttribute(
|
||||||
KMemoryRegionAttr_CarveoutProtected);
|
KMemoryRegionAttr_CarveoutProtected);
|
||||||
constexpr inline auto KMemoryRegionType_DramUserPool =
|
constexpr inline const auto KMemoryRegionType_DramUserPool =
|
||||||
KMemoryRegionType_DramPoolPartition.DeriveTransition(1, 2).DeriveTransition();
|
KMemoryRegionType_DramPoolPartition.Derive(4, 3);
|
||||||
static_assert(KMemoryRegionType_DramPoolManagement.GetValue() ==
|
static_assert(KMemoryRegionType_DramPoolManagement.GetValue() ==
|
||||||
(0x166 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap |
|
(0xE6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap |
|
||||||
KMemoryRegionAttr_CarveoutProtected));
|
KMemoryRegionAttr_CarveoutProtected));
|
||||||
static_assert(KMemoryRegionType_DramUserPool.GetValue() ==
|
static_assert(KMemoryRegionType_DramUserPool.GetValue() ==
|
||||||
(0x1A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
|
(0x266 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
|
||||||
|
|
||||||
constexpr inline auto KMemoryRegionType_DramApplicationPool =
|
constexpr inline auto KMemoryRegionType_DramApplicationPool =
|
||||||
KMemoryRegionType_DramUserPool.Derive(4, 0);
|
KMemoryRegionType_DramUserPool.Derive(4, 0);
|
||||||
|
@ -237,60 +245,63 @@ constexpr inline auto KMemoryRegionType_DramSystemNonSecurePool =
|
||||||
constexpr inline auto KMemoryRegionType_DramSystemPool =
|
constexpr inline auto KMemoryRegionType_DramSystemPool =
|
||||||
KMemoryRegionType_DramUserPool.Derive(4, 3).SetAttribute(KMemoryRegionAttr_CarveoutProtected);
|
KMemoryRegionType_DramUserPool.Derive(4, 3).SetAttribute(KMemoryRegionAttr_CarveoutProtected);
|
||||||
static_assert(KMemoryRegionType_DramApplicationPool.GetValue() ==
|
static_assert(KMemoryRegionType_DramApplicationPool.GetValue() ==
|
||||||
(0x7A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
|
(0xE66 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
|
||||||
static_assert(KMemoryRegionType_DramAppletPool.GetValue() ==
|
static_assert(KMemoryRegionType_DramAppletPool.GetValue() ==
|
||||||
(0xBA6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
|
(0x1666 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
|
||||||
static_assert(KMemoryRegionType_DramSystemNonSecurePool.GetValue() ==
|
static_assert(KMemoryRegionType_DramSystemNonSecurePool.GetValue() ==
|
||||||
(0xDA6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
|
(0x1A66 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap));
|
||||||
static_assert(KMemoryRegionType_DramSystemPool.GetValue() ==
|
static_assert(KMemoryRegionType_DramSystemPool.GetValue() ==
|
||||||
(0x13A6 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap |
|
(0x2666 | KMemoryRegionAttr_LinearMapped | KMemoryRegionAttr_NoUserMap |
|
||||||
KMemoryRegionAttr_CarveoutProtected));
|
KMemoryRegionAttr_CarveoutProtected));
|
||||||
|
|
||||||
constexpr inline auto KMemoryRegionType_VirtualDramHeapBase =
|
constexpr inline auto KMemoryRegionType_VirtualDramHeapBase =
|
||||||
KMemoryRegionType_Dram.DeriveSparse(1, 3, 0);
|
KMemoryRegionType_Dram.DeriveSparse(1, 4, 0);
|
||||||
constexpr inline auto KMemoryRegionType_VirtualDramKernelPtHeap =
|
constexpr inline auto KMemoryRegionType_VirtualDramKernelPtHeap =
|
||||||
KMemoryRegionType_Dram.DeriveSparse(1, 3, 1);
|
KMemoryRegionType_Dram.DeriveSparse(1, 4, 1);
|
||||||
constexpr inline auto KMemoryRegionType_VirtualDramKernelTraceBuffer =
|
constexpr inline auto KMemoryRegionType_VirtualDramKernelTraceBuffer =
|
||||||
KMemoryRegionType_Dram.DeriveSparse(1, 3, 2);
|
KMemoryRegionType_Dram.DeriveSparse(1, 4, 2);
|
||||||
static_assert(KMemoryRegionType_VirtualDramHeapBase.GetValue() == 0x1A);
|
static_assert(KMemoryRegionType_VirtualDramHeapBase.GetValue() == 0x1A);
|
||||||
static_assert(KMemoryRegionType_VirtualDramKernelPtHeap.GetValue() == 0x2A);
|
static_assert(KMemoryRegionType_VirtualDramKernelPtHeap.GetValue() == 0x2A);
|
||||||
static_assert(KMemoryRegionType_VirtualDramKernelTraceBuffer.GetValue() == 0x4A);
|
static_assert(KMemoryRegionType_VirtualDramKernelTraceBuffer.GetValue() == 0x4A);
|
||||||
|
|
||||||
// UNUSED: .DeriveSparse(2, 2, 0);
|
// UNUSED: .Derive(4, 2);
|
||||||
constexpr inline auto KMemoryRegionType_VirtualDramUnknownDebug =
|
constexpr inline const auto KMemoryRegionType_VirtualDramUnknownDebug =
|
||||||
KMemoryRegionType_Dram.DeriveSparse(2, 2, 1);
|
KMemoryRegionType_Dram.Advance(2).Derive(4, 0);
|
||||||
static_assert(KMemoryRegionType_VirtualDramUnknownDebug.GetValue() == (0x52));
|
constexpr inline const auto KMemoryRegionType_VirtualDramKernelSecureAppletMemory =
|
||||||
|
KMemoryRegionType_Dram.Advance(2).Derive(4, 1);
|
||||||
|
constexpr inline const auto KMemoryRegionType_VirtualDramKernelSecureUnknown =
|
||||||
|
KMemoryRegionType_Dram.Advance(2).Derive(4, 3);
|
||||||
|
static_assert(KMemoryRegionType_VirtualDramUnknownDebug.GetValue() == (0x32));
|
||||||
|
static_assert(KMemoryRegionType_VirtualDramKernelSecureAppletMemory.GetValue() == (0x52));
|
||||||
|
static_assert(KMemoryRegionType_VirtualDramKernelSecureUnknown.GetValue() == (0x92));
|
||||||
|
|
||||||
constexpr inline auto KMemoryRegionType_VirtualDramKernelSecureAppletMemory =
|
// UNUSED: .Derive(4, 3);
|
||||||
KMemoryRegionType_Dram.DeriveSparse(3, 1, 0);
|
constexpr inline const auto KMemoryRegionType_VirtualDramKernelInitPt =
|
||||||
static_assert(KMemoryRegionType_VirtualDramKernelSecureAppletMemory.GetValue() == (0x62));
|
KMemoryRegionType_VirtualDramHeapBase.Derive(4, 0);
|
||||||
|
constexpr inline const auto KMemoryRegionType_VirtualDramPoolManagement =
|
||||||
constexpr inline auto KMemoryRegionType_VirtualDramKernelInitPt =
|
KMemoryRegionType_VirtualDramHeapBase.Derive(4, 1);
|
||||||
KMemoryRegionType_VirtualDramHeapBase.Derive(3, 0);
|
constexpr inline const auto KMemoryRegionType_VirtualDramUserPool =
|
||||||
constexpr inline auto KMemoryRegionType_VirtualDramPoolManagement =
|
KMemoryRegionType_VirtualDramHeapBase.Derive(4, 2);
|
||||||
KMemoryRegionType_VirtualDramHeapBase.Derive(3, 1);
|
static_assert(KMemoryRegionType_VirtualDramKernelInitPt.GetValue() == 0x31A);
|
||||||
constexpr inline auto KMemoryRegionType_VirtualDramUserPool =
|
static_assert(KMemoryRegionType_VirtualDramPoolManagement.GetValue() == 0x51A);
|
||||||
KMemoryRegionType_VirtualDramHeapBase.Derive(3, 2);
|
static_assert(KMemoryRegionType_VirtualDramUserPool.GetValue() == 0x61A);
|
||||||
static_assert(KMemoryRegionType_VirtualDramKernelInitPt.GetValue() == 0x19A);
|
|
||||||
static_assert(KMemoryRegionType_VirtualDramPoolManagement.GetValue() == 0x29A);
|
|
||||||
static_assert(KMemoryRegionType_VirtualDramUserPool.GetValue() == 0x31A);
|
|
||||||
|
|
||||||
// NOTE: For unknown reason, the pools are derived out-of-order here.
|
// NOTE: For unknown reason, the pools are derived out-of-order here.
|
||||||
// It's worth eventually trying to understand why Nintendo made this choice.
|
// It's worth eventually trying to understand why Nintendo made this choice.
|
||||||
// UNUSED: .Derive(6, 0);
|
// UNUSED: .Derive(6, 0);
|
||||||
// UNUSED: .Derive(6, 1);
|
// UNUSED: .Derive(6, 1);
|
||||||
constexpr inline auto KMemoryRegionType_VirtualDramAppletPool =
|
constexpr inline const auto KMemoryRegionType_VirtualDramApplicationPool =
|
||||||
KMemoryRegionType_VirtualDramUserPool.Derive(6, 2);
|
KMemoryRegionType_VirtualDramUserPool.Derive(4, 0);
|
||||||
constexpr inline auto KMemoryRegionType_VirtualDramApplicationPool =
|
constexpr inline const auto KMemoryRegionType_VirtualDramAppletPool =
|
||||||
KMemoryRegionType_VirtualDramUserPool.Derive(6, 3);
|
KMemoryRegionType_VirtualDramUserPool.Derive(4, 1);
|
||||||
constexpr inline auto KMemoryRegionType_VirtualDramSystemNonSecurePool =
|
constexpr inline const auto KMemoryRegionType_VirtualDramSystemNonSecurePool =
|
||||||
KMemoryRegionType_VirtualDramUserPool.Derive(6, 4);
|
KMemoryRegionType_VirtualDramUserPool.Derive(4, 2);
|
||||||
constexpr inline auto KMemoryRegionType_VirtualDramSystemPool =
|
constexpr inline const auto KMemoryRegionType_VirtualDramSystemPool =
|
||||||
KMemoryRegionType_VirtualDramUserPool.Derive(6, 5);
|
KMemoryRegionType_VirtualDramUserPool.Derive(4, 3);
|
||||||
static_assert(KMemoryRegionType_VirtualDramAppletPool.GetValue() == 0x1B1A);
|
static_assert(KMemoryRegionType_VirtualDramApplicationPool.GetValue() == 0x361A);
|
||||||
static_assert(KMemoryRegionType_VirtualDramApplicationPool.GetValue() == 0x271A);
|
static_assert(KMemoryRegionType_VirtualDramAppletPool.GetValue() == 0x561A);
|
||||||
static_assert(KMemoryRegionType_VirtualDramSystemNonSecurePool.GetValue() == 0x2B1A);
|
static_assert(KMemoryRegionType_VirtualDramSystemNonSecurePool.GetValue() == 0x661A);
|
||||||
static_assert(KMemoryRegionType_VirtualDramSystemPool.GetValue() == 0x331A);
|
static_assert(KMemoryRegionType_VirtualDramSystemPool.GetValue() == 0x961A);
|
||||||
|
|
||||||
constexpr inline auto KMemoryRegionType_ArchDeviceBase =
|
constexpr inline auto KMemoryRegionType_ArchDeviceBase =
|
||||||
KMemoryRegionType_Kernel.DeriveTransition(0, 1).SetSparseOnly();
|
KMemoryRegionType_Kernel.DeriveTransition(0, 1).SetSparseOnly();
|
||||||
|
@ -354,12 +365,14 @@ constexpr inline auto KMemoryRegionType_KernelTemp =
|
||||||
static_assert(KMemoryRegionType_KernelTemp.GetValue() == 0x31);
|
static_assert(KMemoryRegionType_KernelTemp.GetValue() == 0x31);
|
||||||
|
|
||||||
constexpr KMemoryRegionType GetTypeForVirtualLinearMapping(u32 type_id) {
|
constexpr KMemoryRegionType GetTypeForVirtualLinearMapping(u32 type_id) {
|
||||||
if (KMemoryRegionType_KernelTraceBuffer.IsAncestorOf(type_id)) {
|
if (KMemoryRegionType_DramKernelPtHeap.IsAncestorOf(type_id)) {
|
||||||
return KMemoryRegionType_VirtualDramKernelTraceBuffer;
|
|
||||||
} else if (KMemoryRegionType_DramKernelPtHeap.IsAncestorOf(type_id)) {
|
|
||||||
return KMemoryRegionType_VirtualDramKernelPtHeap;
|
return KMemoryRegionType_VirtualDramKernelPtHeap;
|
||||||
} else if (KMemoryRegionType_DramKernelSecureAppletMemory.IsAncestorOf(type_id)) {
|
} else if (KMemoryRegionType_DramKernelSecureAppletMemory.IsAncestorOf(type_id)) {
|
||||||
return KMemoryRegionType_VirtualDramKernelSecureAppletMemory;
|
return KMemoryRegionType_VirtualDramKernelSecureAppletMemory;
|
||||||
|
} else if (KMemoryRegionType_DramKernelSecureUnknown.IsAncestorOf(type_id)) {
|
||||||
|
return KMemoryRegionType_VirtualDramKernelSecureUnknown;
|
||||||
|
} else if (KMemoryRegionType_KernelTraceBuffer.IsAncestorOf(type_id)) {
|
||||||
|
return KMemoryRegionType_VirtualDramKernelTraceBuffer;
|
||||||
} else if ((type_id | KMemoryRegionAttr_ShouldKernelMap) == type_id) {
|
} else if ((type_id | KMemoryRegionAttr_ShouldKernelMap) == type_id) {
|
||||||
return KMemoryRegionType_VirtualDramUnknownDebug;
|
return KMemoryRegionType_VirtualDramUnknownDebug;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -183,12 +183,17 @@ private:
|
||||||
|
|
||||||
class KScopedPageGroup {
|
class KScopedPageGroup {
|
||||||
public:
|
public:
|
||||||
explicit KScopedPageGroup(const KPageGroup* gp) : m_pg(gp) {
|
explicit KScopedPageGroup(const KPageGroup* gp, bool not_first = true) : m_pg(gp) {
|
||||||
if (m_pg) {
|
if (m_pg) {
|
||||||
|
if (not_first) {
|
||||||
m_pg->Open();
|
m_pg->Open();
|
||||||
|
} else {
|
||||||
|
m_pg->OpenFirst();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
explicit KScopedPageGroup(const KPageGroup& gp) : KScopedPageGroup(std::addressof(gp)) {}
|
}
|
||||||
|
explicit KScopedPageGroup(const KPageGroup& gp, bool not_first = true)
|
||||||
|
: KScopedPageGroup(std::addressof(gp), not_first) {}
|
||||||
~KScopedPageGroup() {
|
~KScopedPageGroup() {
|
||||||
if (m_pg) {
|
if (m_pg) {
|
||||||
m_pg->Close();
|
m_pg->Close();
|
||||||
|
|
|
@ -505,7 +505,7 @@ Result KPageTable::UnmapCodeMemory(KProcessAddress dst_address, KProcessAddress
|
||||||
R_TRY(this->CheckMemoryStateContiguous(
|
R_TRY(this->CheckMemoryStateContiguous(
|
||||||
std::addressof(num_dst_allocator_blocks), dst_address, size, KMemoryState::FlagCanCodeAlias,
|
std::addressof(num_dst_allocator_blocks), dst_address, size, KMemoryState::FlagCanCodeAlias,
|
||||||
KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None,
|
KMemoryState::FlagCanCodeAlias, KMemoryPermission::None, KMemoryPermission::None,
|
||||||
KMemoryAttribute::All, KMemoryAttribute::None));
|
KMemoryAttribute::All & ~KMemoryAttribute::PermissionLocked, KMemoryAttribute::None));
|
||||||
|
|
||||||
// Determine whether any pages being unmapped are code.
|
// Determine whether any pages being unmapped are code.
|
||||||
bool any_code_pages = false;
|
bool any_code_pages = false;
|
||||||
|
@ -1724,7 +1724,17 @@ Result KPageTable::MapPhysicalMemory(KProcessAddress address, size_t size) {
|
||||||
PageSize;
|
PageSize;
|
||||||
|
|
||||||
// While we have pages to map, map them.
|
// While we have pages to map, map them.
|
||||||
while (map_pages > 0) {
|
{
|
||||||
|
// Create a page group for the current mapping range.
|
||||||
|
KPageGroup cur_pg(m_kernel, m_block_info_manager);
|
||||||
|
{
|
||||||
|
ON_RESULT_FAILURE_2 {
|
||||||
|
cur_pg.OpenFirst();
|
||||||
|
cur_pg.Close();
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t remain_pages = map_pages;
|
||||||
|
while (remain_pages > 0) {
|
||||||
// Check if we're at the end of the physical block.
|
// Check if we're at the end of the physical block.
|
||||||
if (pg_pages == 0) {
|
if (pg_pages == 0) {
|
||||||
// Ensure there are more pages to map.
|
// Ensure there are more pages to map.
|
||||||
|
@ -1736,20 +1746,24 @@ Result KPageTable::MapPhysicalMemory(KProcessAddress address, size_t size) {
|
||||||
pg_pages = pg_it->GetNumPages();
|
pg_pages = pg_it->GetNumPages();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map whatever we can.
|
// Add whatever we can to the current block.
|
||||||
const size_t cur_pages = std::min(pg_pages, map_pages);
|
const size_t cur_pages = std::min(pg_pages, remain_pages);
|
||||||
R_TRY(Operate(cur_address, cur_pages, KMemoryPermission::UserReadWrite,
|
R_TRY(cur_pg.AddBlock(pg_phys_addr +
|
||||||
OperationType::MapFirst, pg_phys_addr));
|
((pg_pages - cur_pages) * PageSize),
|
||||||
|
cur_pages));
|
||||||
|
|
||||||
// Advance.
|
// Advance.
|
||||||
cur_address += cur_pages * PageSize;
|
remain_pages -= cur_pages;
|
||||||
map_pages -= cur_pages;
|
|
||||||
|
|
||||||
pg_phys_addr += cur_pages * PageSize;
|
|
||||||
pg_pages -= cur_pages;
|
pg_pages -= cur_pages;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Map the pages.
|
||||||
|
R_TRY(this->Operate(cur_address, map_pages, cur_pg,
|
||||||
|
OperationType::MapFirstGroup));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check if we're done.
|
// Check if we're done.
|
||||||
if (last_address <= info.GetLastAddress()) {
|
if (last_address <= info.GetLastAddress()) {
|
||||||
break;
|
break;
|
||||||
|
@ -1770,7 +1784,11 @@ Result KPageTable::MapPhysicalMemory(KProcessAddress address, size_t size) {
|
||||||
m_memory_block_manager.UpdateIfMatch(
|
m_memory_block_manager.UpdateIfMatch(
|
||||||
std::addressof(allocator), address, size / PageSize, KMemoryState::Free,
|
std::addressof(allocator), address, size / PageSize, KMemoryState::Free,
|
||||||
KMemoryPermission::None, KMemoryAttribute::None, KMemoryState::Normal,
|
KMemoryPermission::None, KMemoryAttribute::None, KMemoryState::Normal,
|
||||||
KMemoryPermission::UserReadWrite, KMemoryAttribute::None);
|
KMemoryPermission::UserReadWrite, KMemoryAttribute::None,
|
||||||
|
address == this->GetAliasRegionStart()
|
||||||
|
? KMemoryBlockDisableMergeAttribute::Normal
|
||||||
|
: KMemoryBlockDisableMergeAttribute::None,
|
||||||
|
KMemoryBlockDisableMergeAttribute::None);
|
||||||
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
@ -1868,6 +1886,13 @@ Result KPageTable::UnmapPhysicalMemory(KProcessAddress address, size_t size) {
|
||||||
|
|
||||||
// Iterate over the memory, unmapping as we go.
|
// Iterate over the memory, unmapping as we go.
|
||||||
auto it = m_memory_block_manager.FindIterator(cur_address);
|
auto it = m_memory_block_manager.FindIterator(cur_address);
|
||||||
|
|
||||||
|
const auto clear_merge_attr =
|
||||||
|
(it->GetState() == KMemoryState::Normal &&
|
||||||
|
it->GetAddress() == this->GetAliasRegionStart() && it->GetAddress() == address)
|
||||||
|
? KMemoryBlockDisableMergeAttribute::Normal
|
||||||
|
: KMemoryBlockDisableMergeAttribute::None;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
// Check that the iterator is valid.
|
// Check that the iterator is valid.
|
||||||
ASSERT(it != m_memory_block_manager.end());
|
ASSERT(it != m_memory_block_manager.end());
|
||||||
|
@ -1905,7 +1930,7 @@ Result KPageTable::UnmapPhysicalMemory(KProcessAddress address, size_t size) {
|
||||||
m_memory_block_manager.Update(std::addressof(allocator), address, size / PageSize,
|
m_memory_block_manager.Update(std::addressof(allocator), address, size / PageSize,
|
||||||
KMemoryState::Free, KMemoryPermission::None,
|
KMemoryState::Free, KMemoryPermission::None,
|
||||||
KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None,
|
KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None,
|
||||||
KMemoryBlockDisableMergeAttribute::None);
|
clear_merge_attr);
|
||||||
|
|
||||||
// We succeeded.
|
// We succeeded.
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
|
@ -2379,8 +2404,7 @@ Result KPageTable::MapPageGroup(KProcessAddress* out_addr, const KPageGroup& pg,
|
||||||
KScopedPageTableUpdater updater(this);
|
KScopedPageTableUpdater updater(this);
|
||||||
|
|
||||||
// Perform mapping operation.
|
// Perform mapping operation.
|
||||||
const KPageProperties properties = {perm, state == KMemoryState::Io, false,
|
const KPageProperties properties = {perm, false, false, DisableMergeAttribute::DisableHead};
|
||||||
DisableMergeAttribute::DisableHead};
|
|
||||||
R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false));
|
R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false));
|
||||||
|
|
||||||
// Update the blocks.
|
// Update the blocks.
|
||||||
|
@ -2422,8 +2446,7 @@ Result KPageTable::MapPageGroup(KProcessAddress addr, const KPageGroup& pg, KMem
|
||||||
KScopedPageTableUpdater updater(this);
|
KScopedPageTableUpdater updater(this);
|
||||||
|
|
||||||
// Perform mapping operation.
|
// Perform mapping operation.
|
||||||
const KPageProperties properties = {perm, state == KMemoryState::Io, false,
|
const KPageProperties properties = {perm, false, false, DisableMergeAttribute::DisableHead};
|
||||||
DisableMergeAttribute::DisableHead};
|
|
||||||
R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false));
|
R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false));
|
||||||
|
|
||||||
// Update the blocks.
|
// Update the blocks.
|
||||||
|
@ -2652,10 +2675,17 @@ Result KPageTable::SetMemoryAttribute(KProcessAddress addr, size_t size, u32 mas
|
||||||
size_t num_allocator_blocks;
|
size_t num_allocator_blocks;
|
||||||
constexpr auto AttributeTestMask =
|
constexpr auto AttributeTestMask =
|
||||||
~(KMemoryAttribute::SetMask | KMemoryAttribute::DeviceShared);
|
~(KMemoryAttribute::SetMask | KMemoryAttribute::DeviceShared);
|
||||||
R_TRY(this->CheckMemoryState(
|
const KMemoryState state_test_mask =
|
||||||
std::addressof(old_state), std::addressof(old_perm), std::addressof(old_attr),
|
static_cast<KMemoryState>(((mask & static_cast<u32>(KMemoryAttribute::Uncached))
|
||||||
std::addressof(num_allocator_blocks), addr, size, KMemoryState::FlagCanChangeAttribute,
|
? static_cast<u32>(KMemoryState::FlagCanChangeAttribute)
|
||||||
KMemoryState::FlagCanChangeAttribute, KMemoryPermission::None, KMemoryPermission::None,
|
: 0) |
|
||||||
|
((mask & static_cast<u32>(KMemoryAttribute::PermissionLocked))
|
||||||
|
? static_cast<u32>(KMemoryState::FlagCanPermissionLock)
|
||||||
|
: 0));
|
||||||
|
R_TRY(this->CheckMemoryState(std::addressof(old_state), std::addressof(old_perm),
|
||||||
|
std::addressof(old_attr), std::addressof(num_allocator_blocks),
|
||||||
|
addr, size, state_test_mask, state_test_mask,
|
||||||
|
KMemoryPermission::None, KMemoryPermission::None,
|
||||||
AttributeTestMask, KMemoryAttribute::None, ~AttributeTestMask));
|
AttributeTestMask, KMemoryAttribute::None, ~AttributeTestMask));
|
||||||
|
|
||||||
// Create an update allocator.
|
// Create an update allocator.
|
||||||
|
@ -2664,18 +2694,17 @@ Result KPageTable::SetMemoryAttribute(KProcessAddress addr, size_t size, u32 mas
|
||||||
m_memory_block_slab_manager, num_allocator_blocks);
|
m_memory_block_slab_manager, num_allocator_blocks);
|
||||||
R_TRY(allocator_result);
|
R_TRY(allocator_result);
|
||||||
|
|
||||||
// Determine the new attribute.
|
// If we need to, perform a change attribute operation.
|
||||||
const KMemoryAttribute new_attr =
|
if (True(KMemoryAttribute::Uncached & static_cast<KMemoryAttribute>(mask))) {
|
||||||
static_cast<KMemoryAttribute>(((old_attr & static_cast<KMemoryAttribute>(~mask)) |
|
|
||||||
static_cast<KMemoryAttribute>(attr & mask)));
|
|
||||||
|
|
||||||
// Perform operation.
|
// Perform operation.
|
||||||
this->Operate(addr, num_pages, old_perm, OperationType::ChangePermissionsAndRefresh);
|
R_TRY(this->Operate(addr, num_pages, old_perm,
|
||||||
|
OperationType::ChangePermissionsAndRefreshAndFlush, 0));
|
||||||
|
}
|
||||||
|
|
||||||
// Update the blocks.
|
// Update the blocks.
|
||||||
m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, old_perm,
|
m_memory_block_manager.UpdateAttribute(std::addressof(allocator), addr, num_pages,
|
||||||
new_attr, KMemoryBlockDisableMergeAttribute::None,
|
static_cast<KMemoryAttribute>(mask),
|
||||||
KMemoryBlockDisableMergeAttribute::None);
|
static_cast<KMemoryAttribute>(attr));
|
||||||
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
@ -2863,7 +2892,8 @@ Result KPageTable::LockForMapDeviceAddressSpace(bool* out_is_io, KProcessAddress
|
||||||
&KMemoryBlock::ShareToDevice, KMemoryPermission::None);
|
&KMemoryBlock::ShareToDevice, KMemoryPermission::None);
|
||||||
|
|
||||||
// Set whether the locked memory was io.
|
// Set whether the locked memory was io.
|
||||||
*out_is_io = old_state == KMemoryState::Io;
|
*out_is_io =
|
||||||
|
static_cast<Svc::MemoryState>(old_state & KMemoryState::Mask) == Svc::MemoryState::Io;
|
||||||
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
@ -3021,9 +3051,10 @@ Result KPageTable::Operate(KProcessAddress addr, size_t num_pages, const KPageGr
|
||||||
ASSERT(num_pages == page_group.GetNumPages());
|
ASSERT(num_pages == page_group.GetNumPages());
|
||||||
|
|
||||||
switch (operation) {
|
switch (operation) {
|
||||||
case OperationType::MapGroup: {
|
case OperationType::MapGroup:
|
||||||
|
case OperationType::MapFirstGroup: {
|
||||||
// We want to maintain a new reference to every page in the group.
|
// We want to maintain a new reference to every page in the group.
|
||||||
KScopedPageGroup spg(page_group);
|
KScopedPageGroup spg(page_group, operation != OperationType::MapFirstGroup);
|
||||||
|
|
||||||
for (const auto& node : page_group) {
|
for (const auto& node : page_group) {
|
||||||
const size_t size{node.GetNumPages() * PageSize};
|
const size_t size{node.GetNumPages() * PageSize};
|
||||||
|
@ -3065,7 +3096,6 @@ Result KPageTable::Operate(KProcessAddress addr, size_t num_pages, KMemoryPermis
|
||||||
m_memory->UnmapRegion(*m_page_table_impl, addr, num_pages * PageSize);
|
m_memory->UnmapRegion(*m_page_table_impl, addr, num_pages * PageSize);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OperationType::MapFirst:
|
|
||||||
case OperationType::Map: {
|
case OperationType::Map: {
|
||||||
ASSERT(map_addr);
|
ASSERT(map_addr);
|
||||||
ASSERT(Common::IsAligned(GetInteger(map_addr), PageSize));
|
ASSERT(Common::IsAligned(GetInteger(map_addr), PageSize));
|
||||||
|
@ -3073,12 +3103,8 @@ Result KPageTable::Operate(KProcessAddress addr, size_t num_pages, KMemoryPermis
|
||||||
|
|
||||||
// Open references to pages, if we should.
|
// Open references to pages, if we should.
|
||||||
if (IsHeapPhysicalAddress(m_kernel.MemoryLayout(), map_addr)) {
|
if (IsHeapPhysicalAddress(m_kernel.MemoryLayout(), map_addr)) {
|
||||||
if (operation == OperationType::MapFirst) {
|
|
||||||
m_kernel.MemoryManager().OpenFirst(map_addr, num_pages);
|
|
||||||
} else {
|
|
||||||
m_kernel.MemoryManager().Open(map_addr, num_pages);
|
m_kernel.MemoryManager().Open(map_addr, num_pages);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OperationType::Separate: {
|
case OperationType::Separate: {
|
||||||
|
@ -3087,6 +3113,7 @@ Result KPageTable::Operate(KProcessAddress addr, size_t num_pages, KMemoryPermis
|
||||||
}
|
}
|
||||||
case OperationType::ChangePermissions:
|
case OperationType::ChangePermissions:
|
||||||
case OperationType::ChangePermissionsAndRefresh:
|
case OperationType::ChangePermissionsAndRefresh:
|
||||||
|
case OperationType::ChangePermissionsAndRefreshAndFlush:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ASSERT(false);
|
ASSERT(false);
|
||||||
|
@ -3106,79 +3133,79 @@ void KPageTable::FinalizeUpdate(PageLinkedList* page_list) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
KProcessAddress KPageTable::GetRegionAddress(KMemoryState state) const {
|
KProcessAddress KPageTable::GetRegionAddress(Svc::MemoryState state) const {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case KMemoryState::Free:
|
case Svc::MemoryState::Free:
|
||||||
case KMemoryState::Kernel:
|
case Svc::MemoryState::Kernel:
|
||||||
return m_address_space_start;
|
return m_address_space_start;
|
||||||
case KMemoryState::Normal:
|
case Svc::MemoryState::Normal:
|
||||||
return m_heap_region_start;
|
return m_heap_region_start;
|
||||||
case KMemoryState::Ipc:
|
case Svc::MemoryState::Ipc:
|
||||||
case KMemoryState::NonSecureIpc:
|
case Svc::MemoryState::NonSecureIpc:
|
||||||
case KMemoryState::NonDeviceIpc:
|
case Svc::MemoryState::NonDeviceIpc:
|
||||||
return m_alias_region_start;
|
return m_alias_region_start;
|
||||||
case KMemoryState::Stack:
|
case Svc::MemoryState::Stack:
|
||||||
return m_stack_region_start;
|
return m_stack_region_start;
|
||||||
case KMemoryState::Static:
|
case Svc::MemoryState::Static:
|
||||||
case KMemoryState::ThreadLocal:
|
case Svc::MemoryState::ThreadLocal:
|
||||||
return m_kernel_map_region_start;
|
return m_kernel_map_region_start;
|
||||||
case KMemoryState::Io:
|
case Svc::MemoryState::Io:
|
||||||
case KMemoryState::Shared:
|
case Svc::MemoryState::Shared:
|
||||||
case KMemoryState::AliasCode:
|
case Svc::MemoryState::AliasCode:
|
||||||
case KMemoryState::AliasCodeData:
|
case Svc::MemoryState::AliasCodeData:
|
||||||
case KMemoryState::Transfered:
|
case Svc::MemoryState::Transfered:
|
||||||
case KMemoryState::SharedTransfered:
|
case Svc::MemoryState::SharedTransfered:
|
||||||
case KMemoryState::SharedCode:
|
case Svc::MemoryState::SharedCode:
|
||||||
case KMemoryState::GeneratedCode:
|
case Svc::MemoryState::GeneratedCode:
|
||||||
case KMemoryState::CodeOut:
|
case Svc::MemoryState::CodeOut:
|
||||||
case KMemoryState::Coverage:
|
case Svc::MemoryState::Coverage:
|
||||||
case KMemoryState::Insecure:
|
case Svc::MemoryState::Insecure:
|
||||||
return m_alias_code_region_start;
|
return m_alias_code_region_start;
|
||||||
case KMemoryState::Code:
|
case Svc::MemoryState::Code:
|
||||||
case KMemoryState::CodeData:
|
case Svc::MemoryState::CodeData:
|
||||||
return m_code_region_start;
|
return m_code_region_start;
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t KPageTable::GetRegionSize(KMemoryState state) const {
|
size_t KPageTable::GetRegionSize(Svc::MemoryState state) const {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case KMemoryState::Free:
|
case Svc::MemoryState::Free:
|
||||||
case KMemoryState::Kernel:
|
case Svc::MemoryState::Kernel:
|
||||||
return m_address_space_end - m_address_space_start;
|
return m_address_space_end - m_address_space_start;
|
||||||
case KMemoryState::Normal:
|
case Svc::MemoryState::Normal:
|
||||||
return m_heap_region_end - m_heap_region_start;
|
return m_heap_region_end - m_heap_region_start;
|
||||||
case KMemoryState::Ipc:
|
case Svc::MemoryState::Ipc:
|
||||||
case KMemoryState::NonSecureIpc:
|
case Svc::MemoryState::NonSecureIpc:
|
||||||
case KMemoryState::NonDeviceIpc:
|
case Svc::MemoryState::NonDeviceIpc:
|
||||||
return m_alias_region_end - m_alias_region_start;
|
return m_alias_region_end - m_alias_region_start;
|
||||||
case KMemoryState::Stack:
|
case Svc::MemoryState::Stack:
|
||||||
return m_stack_region_end - m_stack_region_start;
|
return m_stack_region_end - m_stack_region_start;
|
||||||
case KMemoryState::Static:
|
case Svc::MemoryState::Static:
|
||||||
case KMemoryState::ThreadLocal:
|
case Svc::MemoryState::ThreadLocal:
|
||||||
return m_kernel_map_region_end - m_kernel_map_region_start;
|
return m_kernel_map_region_end - m_kernel_map_region_start;
|
||||||
case KMemoryState::Io:
|
case Svc::MemoryState::Io:
|
||||||
case KMemoryState::Shared:
|
case Svc::MemoryState::Shared:
|
||||||
case KMemoryState::AliasCode:
|
case Svc::MemoryState::AliasCode:
|
||||||
case KMemoryState::AliasCodeData:
|
case Svc::MemoryState::AliasCodeData:
|
||||||
case KMemoryState::Transfered:
|
case Svc::MemoryState::Transfered:
|
||||||
case KMemoryState::SharedTransfered:
|
case Svc::MemoryState::SharedTransfered:
|
||||||
case KMemoryState::SharedCode:
|
case Svc::MemoryState::SharedCode:
|
||||||
case KMemoryState::GeneratedCode:
|
case Svc::MemoryState::GeneratedCode:
|
||||||
case KMemoryState::CodeOut:
|
case Svc::MemoryState::CodeOut:
|
||||||
case KMemoryState::Coverage:
|
case Svc::MemoryState::Coverage:
|
||||||
case KMemoryState::Insecure:
|
case Svc::MemoryState::Insecure:
|
||||||
return m_alias_code_region_end - m_alias_code_region_start;
|
return m_alias_code_region_end - m_alias_code_region_start;
|
||||||
case KMemoryState::Code:
|
case Svc::MemoryState::Code:
|
||||||
case KMemoryState::CodeData:
|
case Svc::MemoryState::CodeData:
|
||||||
return m_code_region_end - m_code_region_start;
|
return m_code_region_end - m_code_region_start;
|
||||||
default:
|
default:
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool KPageTable::CanContain(KProcessAddress addr, size_t size, KMemoryState state) const {
|
bool KPageTable::CanContain(KProcessAddress addr, size_t size, Svc::MemoryState state) const {
|
||||||
const KProcessAddress end = addr + size;
|
const KProcessAddress end = addr + size;
|
||||||
const KProcessAddress last = end - 1;
|
const KProcessAddress last = end - 1;
|
||||||
|
|
||||||
|
@ -3192,32 +3219,32 @@ bool KPageTable::CanContain(KProcessAddress addr, size_t size, KMemoryState stat
|
||||||
const bool is_in_alias = !(end <= m_alias_region_start || m_alias_region_end <= addr ||
|
const bool is_in_alias = !(end <= m_alias_region_start || m_alias_region_end <= addr ||
|
||||||
m_alias_region_start == m_alias_region_end);
|
m_alias_region_start == m_alias_region_end);
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case KMemoryState::Free:
|
case Svc::MemoryState::Free:
|
||||||
case KMemoryState::Kernel:
|
case Svc::MemoryState::Kernel:
|
||||||
return is_in_region;
|
return is_in_region;
|
||||||
case KMemoryState::Io:
|
case Svc::MemoryState::Io:
|
||||||
case KMemoryState::Static:
|
case Svc::MemoryState::Static:
|
||||||
case KMemoryState::Code:
|
case Svc::MemoryState::Code:
|
||||||
case KMemoryState::CodeData:
|
case Svc::MemoryState::CodeData:
|
||||||
case KMemoryState::Shared:
|
case Svc::MemoryState::Shared:
|
||||||
case KMemoryState::AliasCode:
|
case Svc::MemoryState::AliasCode:
|
||||||
case KMemoryState::AliasCodeData:
|
case Svc::MemoryState::AliasCodeData:
|
||||||
case KMemoryState::Stack:
|
case Svc::MemoryState::Stack:
|
||||||
case KMemoryState::ThreadLocal:
|
case Svc::MemoryState::ThreadLocal:
|
||||||
case KMemoryState::Transfered:
|
case Svc::MemoryState::Transfered:
|
||||||
case KMemoryState::SharedTransfered:
|
case Svc::MemoryState::SharedTransfered:
|
||||||
case KMemoryState::SharedCode:
|
case Svc::MemoryState::SharedCode:
|
||||||
case KMemoryState::GeneratedCode:
|
case Svc::MemoryState::GeneratedCode:
|
||||||
case KMemoryState::CodeOut:
|
case Svc::MemoryState::CodeOut:
|
||||||
case KMemoryState::Coverage:
|
case Svc::MemoryState::Coverage:
|
||||||
case KMemoryState::Insecure:
|
case Svc::MemoryState::Insecure:
|
||||||
return is_in_region && !is_in_heap && !is_in_alias;
|
return is_in_region && !is_in_heap && !is_in_alias;
|
||||||
case KMemoryState::Normal:
|
case Svc::MemoryState::Normal:
|
||||||
ASSERT(is_in_heap);
|
ASSERT(is_in_heap);
|
||||||
return is_in_region && !is_in_alias;
|
return is_in_region && !is_in_alias;
|
||||||
case KMemoryState::Ipc:
|
case Svc::MemoryState::Ipc:
|
||||||
case KMemoryState::NonSecureIpc:
|
case Svc::MemoryState::NonSecureIpc:
|
||||||
case KMemoryState::NonDeviceIpc:
|
case Svc::MemoryState::NonDeviceIpc:
|
||||||
ASSERT(is_in_alias);
|
ASSERT(is_in_alias);
|
||||||
return is_in_region && !is_in_heap;
|
return is_in_region && !is_in_heap;
|
||||||
default:
|
default:
|
||||||
|
@ -3281,21 +3308,16 @@ Result KPageTable::CheckMemoryStateContiguous(size_t* out_blocks_needed, KProces
|
||||||
|
|
||||||
Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
|
Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
|
||||||
KMemoryAttribute* out_attr, size_t* out_blocks_needed,
|
KMemoryAttribute* out_attr, size_t* out_blocks_needed,
|
||||||
KProcessAddress addr, size_t size, KMemoryState state_mask,
|
KMemoryBlockManager::const_iterator it,
|
||||||
|
KProcessAddress last_addr, KMemoryState state_mask,
|
||||||
KMemoryState state, KMemoryPermission perm_mask,
|
KMemoryState state, KMemoryPermission perm_mask,
|
||||||
KMemoryPermission perm, KMemoryAttribute attr_mask,
|
KMemoryPermission perm, KMemoryAttribute attr_mask,
|
||||||
KMemoryAttribute attr, KMemoryAttribute ignore_attr) const {
|
KMemoryAttribute attr, KMemoryAttribute ignore_attr) const {
|
||||||
ASSERT(this->IsLockedByCurrentThread());
|
ASSERT(this->IsLockedByCurrentThread());
|
||||||
|
|
||||||
// Get information about the first block.
|
// Get information about the first block.
|
||||||
const KProcessAddress last_addr = addr + size - 1;
|
|
||||||
KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(addr);
|
|
||||||
KMemoryInfo info = it->GetMemoryInfo();
|
KMemoryInfo info = it->GetMemoryInfo();
|
||||||
|
|
||||||
// If the start address isn't aligned, we need a block.
|
|
||||||
const size_t blocks_for_start_align =
|
|
||||||
(Common::AlignDown(GetInteger(addr), PageSize) != info.GetAddress()) ? 1 : 0;
|
|
||||||
|
|
||||||
// Validate all blocks in the range have correct state.
|
// Validate all blocks in the range have correct state.
|
||||||
const KMemoryState first_state = info.m_state;
|
const KMemoryState first_state = info.m_state;
|
||||||
const KMemoryPermission first_perm = info.m_permission;
|
const KMemoryPermission first_perm = info.m_permission;
|
||||||
|
@ -3321,10 +3343,6 @@ Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission*
|
||||||
info = it->GetMemoryInfo();
|
info = it->GetMemoryInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the end address isn't aligned, we need a block.
|
|
||||||
const size_t blocks_for_end_align =
|
|
||||||
(Common::AlignUp(GetInteger(addr) + size, PageSize) != info.GetEndAddress()) ? 1 : 0;
|
|
||||||
|
|
||||||
// Write output state.
|
// Write output state.
|
||||||
if (out_state != nullptr) {
|
if (out_state != nullptr) {
|
||||||
*out_state = first_state;
|
*out_state = first_state;
|
||||||
|
@ -3335,9 +3353,39 @@ Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission*
|
||||||
if (out_attr != nullptr) {
|
if (out_attr != nullptr) {
|
||||||
*out_attr = static_cast<KMemoryAttribute>(first_attr & ~ignore_attr);
|
*out_attr = static_cast<KMemoryAttribute>(first_attr & ~ignore_attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the end address isn't aligned, we need a block.
|
||||||
if (out_blocks_needed != nullptr) {
|
if (out_blocks_needed != nullptr) {
|
||||||
*out_blocks_needed = blocks_for_start_align + blocks_for_end_align;
|
const size_t blocks_for_end_align =
|
||||||
|
(Common::AlignDown(GetInteger(last_addr), PageSize) + PageSize != info.GetEndAddress())
|
||||||
|
? 1
|
||||||
|
: 0;
|
||||||
|
*out_blocks_needed = blocks_for_end_align;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
|
||||||
|
KMemoryAttribute* out_attr, size_t* out_blocks_needed,
|
||||||
|
KProcessAddress addr, size_t size, KMemoryState state_mask,
|
||||||
|
KMemoryState state, KMemoryPermission perm_mask,
|
||||||
|
KMemoryPermission perm, KMemoryAttribute attr_mask,
|
||||||
|
KMemoryAttribute attr, KMemoryAttribute ignore_attr) const {
|
||||||
|
ASSERT(this->IsLockedByCurrentThread());
|
||||||
|
|
||||||
|
// Check memory state.
|
||||||
|
const KProcessAddress last_addr = addr + size - 1;
|
||||||
|
KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(addr);
|
||||||
|
R_TRY(this->CheckMemoryState(out_state, out_perm, out_attr, out_blocks_needed, it, last_addr,
|
||||||
|
state_mask, state, perm_mask, perm, attr_mask, attr, ignore_attr));
|
||||||
|
|
||||||
|
// If the start address isn't aligned, we need a block.
|
||||||
|
if (out_blocks_needed != nullptr &&
|
||||||
|
Common::AlignDown(GetInteger(addr), PageSize) != it->GetAddress()) {
|
||||||
|
++(*out_blocks_needed);
|
||||||
|
}
|
||||||
|
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -126,8 +126,6 @@ public:
|
||||||
return m_block_info_manager;
|
return m_block_info_manager;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CanContain(KProcessAddress addr, size_t size, KMemoryState state) const;
|
|
||||||
|
|
||||||
Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
|
Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment,
|
||||||
KPhysicalAddress phys_addr, KProcessAddress region_start,
|
KPhysicalAddress phys_addr, KProcessAddress region_start,
|
||||||
size_t region_num_pages, KMemoryState state, KMemoryPermission perm) {
|
size_t region_num_pages, KMemoryState state, KMemoryPermission perm) {
|
||||||
|
@ -162,6 +160,21 @@ public:
|
||||||
void RemapPageGroup(PageLinkedList* page_list, KProcessAddress address, size_t size,
|
void RemapPageGroup(PageLinkedList* page_list, KProcessAddress address, size_t size,
|
||||||
const KPageGroup& pg);
|
const KPageGroup& pg);
|
||||||
|
|
||||||
|
KProcessAddress GetRegionAddress(Svc::MemoryState state) const;
|
||||||
|
size_t GetRegionSize(Svc::MemoryState state) const;
|
||||||
|
bool CanContain(KProcessAddress addr, size_t size, Svc::MemoryState state) const;
|
||||||
|
|
||||||
|
KProcessAddress GetRegionAddress(KMemoryState state) const {
|
||||||
|
return this->GetRegionAddress(static_cast<Svc::MemoryState>(state & KMemoryState::Mask));
|
||||||
|
}
|
||||||
|
size_t GetRegionSize(KMemoryState state) const {
|
||||||
|
return this->GetRegionSize(static_cast<Svc::MemoryState>(state & KMemoryState::Mask));
|
||||||
|
}
|
||||||
|
bool CanContain(KProcessAddress addr, size_t size, KMemoryState state) const {
|
||||||
|
return this->CanContain(addr, size,
|
||||||
|
static_cast<Svc::MemoryState>(state & KMemoryState::Mask));
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
struct PageLinkedList {
|
struct PageLinkedList {
|
||||||
private:
|
private:
|
||||||
|
@ -204,12 +217,13 @@ protected:
|
||||||
private:
|
private:
|
||||||
enum class OperationType : u32 {
|
enum class OperationType : u32 {
|
||||||
Map = 0,
|
Map = 0,
|
||||||
MapFirst = 1,
|
MapGroup = 1,
|
||||||
MapGroup = 2,
|
MapFirstGroup = 2,
|
||||||
Unmap = 3,
|
Unmap = 3,
|
||||||
ChangePermissions = 4,
|
ChangePermissions = 4,
|
||||||
ChangePermissionsAndRefresh = 5,
|
ChangePermissionsAndRefresh = 5,
|
||||||
Separate = 6,
|
ChangePermissionsAndRefreshAndFlush = 6,
|
||||||
|
Separate = 7,
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr KMemoryAttribute DefaultMemoryIgnoreAttr =
|
static constexpr KMemoryAttribute DefaultMemoryIgnoreAttr =
|
||||||
|
@ -228,8 +242,6 @@ private:
|
||||||
Result Operate(KProcessAddress addr, size_t num_pages, KMemoryPermission perm,
|
Result Operate(KProcessAddress addr, size_t num_pages, KMemoryPermission perm,
|
||||||
OperationType operation, KPhysicalAddress map_addr = 0);
|
OperationType operation, KPhysicalAddress map_addr = 0);
|
||||||
void FinalizeUpdate(PageLinkedList* page_list);
|
void FinalizeUpdate(PageLinkedList* page_list);
|
||||||
KProcessAddress GetRegionAddress(KMemoryState state) const;
|
|
||||||
size_t GetRegionSize(KMemoryState state) const;
|
|
||||||
|
|
||||||
KProcessAddress FindFreeArea(KProcessAddress region_start, size_t region_num_pages,
|
KProcessAddress FindFreeArea(KProcessAddress region_start, size_t region_num_pages,
|
||||||
size_t num_pages, size_t alignment, size_t offset,
|
size_t num_pages, size_t alignment, size_t offset,
|
||||||
|
@ -250,6 +262,13 @@ private:
|
||||||
Result CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask, KMemoryState state,
|
Result CheckMemoryState(const KMemoryInfo& info, KMemoryState state_mask, KMemoryState state,
|
||||||
KMemoryPermission perm_mask, KMemoryPermission perm,
|
KMemoryPermission perm_mask, KMemoryPermission perm,
|
||||||
KMemoryAttribute attr_mask, KMemoryAttribute attr) const;
|
KMemoryAttribute attr_mask, KMemoryAttribute attr) const;
|
||||||
|
Result CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
|
||||||
|
KMemoryAttribute* out_attr, size_t* out_blocks_needed,
|
||||||
|
KMemoryBlockManager::const_iterator it, KProcessAddress last_addr,
|
||||||
|
KMemoryState state_mask, KMemoryState state,
|
||||||
|
KMemoryPermission perm_mask, KMemoryPermission perm,
|
||||||
|
KMemoryAttribute attr_mask, KMemoryAttribute attr,
|
||||||
|
KMemoryAttribute ignore_attr = DefaultMemoryIgnoreAttr) const;
|
||||||
Result CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
|
Result CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm,
|
||||||
KMemoryAttribute* out_attr, size_t* out_blocks_needed,
|
KMemoryAttribute* out_attr, size_t* out_blocks_needed,
|
||||||
KProcessAddress addr, size_t size, KMemoryState state_mask,
|
KProcessAddress addr, size_t size, KMemoryState state_mask,
|
||||||
|
|
|
@ -623,14 +623,33 @@ struct KernelCore::Impl {
|
||||||
ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert(
|
ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert(
|
||||||
GetInteger(slab_start_phys_addr), slab_region_size, KMemoryRegionType_DramKernelSlab));
|
GetInteger(slab_start_phys_addr), slab_region_size, KMemoryRegionType_DramKernelSlab));
|
||||||
|
|
||||||
|
// Insert a physical region for the secure applet memory.
|
||||||
|
const auto secure_applet_end_phys_addr =
|
||||||
|
slab_end_phys_addr + KSystemControl::SecureAppletMemorySize;
|
||||||
|
if constexpr (KSystemControl::SecureAppletMemorySize > 0) {
|
||||||
|
ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert(
|
||||||
|
GetInteger(slab_end_phys_addr), KSystemControl::SecureAppletMemorySize,
|
||||||
|
KMemoryRegionType_DramKernelSecureAppletMemory));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert a physical region for the unknown debug2 region.
|
||||||
|
constexpr size_t SecureUnknownRegionSize = 0;
|
||||||
|
const size_t secure_unknown_size = SecureUnknownRegionSize;
|
||||||
|
const auto secure_unknown_end_phys_addr = secure_applet_end_phys_addr + secure_unknown_size;
|
||||||
|
if constexpr (SecureUnknownRegionSize > 0) {
|
||||||
|
ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert(
|
||||||
|
GetInteger(secure_applet_end_phys_addr), secure_unknown_size,
|
||||||
|
KMemoryRegionType_DramKernelSecureUnknown));
|
||||||
|
}
|
||||||
|
|
||||||
// Determine size available for kernel page table heaps, requiring > 8 MB.
|
// Determine size available for kernel page table heaps, requiring > 8 MB.
|
||||||
const KPhysicalAddress resource_end_phys_addr = slab_start_phys_addr + resource_region_size;
|
const KPhysicalAddress resource_end_phys_addr = slab_start_phys_addr + resource_region_size;
|
||||||
const size_t page_table_heap_size = resource_end_phys_addr - slab_end_phys_addr;
|
const size_t page_table_heap_size = resource_end_phys_addr - secure_unknown_end_phys_addr;
|
||||||
ASSERT(page_table_heap_size / 4_MiB > 2);
|
ASSERT(page_table_heap_size / 4_MiB > 2);
|
||||||
|
|
||||||
// Insert a physical region for the kernel page table heap region
|
// Insert a physical region for the kernel page table heap region
|
||||||
ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert(
|
ASSERT(memory_layout->GetPhysicalMemoryRegionTree().Insert(
|
||||||
GetInteger(slab_end_phys_addr), page_table_heap_size,
|
GetInteger(secure_unknown_end_phys_addr), page_table_heap_size,
|
||||||
KMemoryRegionType_DramKernelPtHeap));
|
KMemoryRegionType_DramKernelPtHeap));
|
||||||
|
|
||||||
// All DRAM regions that we haven't tagged by this point will be mapped under the linear
|
// All DRAM regions that we haven't tagged by this point will be mapped under the linear
|
||||||
|
|
|
@ -108,10 +108,16 @@ Result SetMemoryAttribute(Core::System& system, u64 address, u64 size, u32 mask,
|
||||||
R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
|
R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
|
||||||
|
|
||||||
// Validate the attribute and mask.
|
// Validate the attribute and mask.
|
||||||
constexpr u32 SupportedMask = static_cast<u32>(MemoryAttribute::Uncached);
|
constexpr u32 SupportedMask =
|
||||||
|
static_cast<u32>(MemoryAttribute::Uncached | MemoryAttribute::PermissionLocked);
|
||||||
R_UNLESS((mask | attr) == mask, ResultInvalidCombination);
|
R_UNLESS((mask | attr) == mask, ResultInvalidCombination);
|
||||||
R_UNLESS((mask | attr | SupportedMask) == SupportedMask, ResultInvalidCombination);
|
R_UNLESS((mask | attr | SupportedMask) == SupportedMask, ResultInvalidCombination);
|
||||||
|
|
||||||
|
// Check that permission locked is either being set or not masked.
|
||||||
|
R_UNLESS((static_cast<Svc::MemoryAttribute>(mask) & Svc::MemoryAttribute::PermissionLocked) ==
|
||||||
|
(static_cast<Svc::MemoryAttribute>(attr) & Svc::MemoryAttribute::PermissionLocked),
|
||||||
|
ResultInvalidCombination);
|
||||||
|
|
||||||
// Validate that the region is in range for the current process.
|
// Validate that the region is in range for the current process.
|
||||||
auto& page_table{GetCurrentProcess(system.Kernel()).GetPageTable()};
|
auto& page_table{GetCurrentProcess(system.Kernel()).GetPageTable()};
|
||||||
R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
|
R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory);
|
||||||
|
|
|
@ -46,6 +46,7 @@ enum class MemoryAttribute : u32 {
|
||||||
IpcLocked = (1 << 1),
|
IpcLocked = (1 << 1),
|
||||||
DeviceShared = (1 << 2),
|
DeviceShared = (1 << 2),
|
||||||
Uncached = (1 << 3),
|
Uncached = (1 << 3),
|
||||||
|
PermissionLocked = (1 << 4),
|
||||||
};
|
};
|
||||||
DECLARE_ENUM_FLAG_OPERATORS(MemoryAttribute);
|
DECLARE_ENUM_FLAG_OPERATORS(MemoryAttribute);
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@ Result AllocateIoForProcessAddressSpace(Common::ProcessAddress* out_map_address,
|
||||||
// Get bounds of where mapping is possible.
|
// Get bounds of where mapping is possible.
|
||||||
const VAddr alias_code_begin = GetInteger(page_table.GetAliasCodeRegionStart());
|
const VAddr alias_code_begin = GetInteger(page_table.GetAliasCodeRegionStart());
|
||||||
const VAddr alias_code_size = page_table.GetAliasCodeRegionSize() / YUZU_PAGESIZE;
|
const VAddr alias_code_size = page_table.GetAliasCodeRegionSize() / YUZU_PAGESIZE;
|
||||||
const auto state = Kernel::KMemoryState::Io;
|
const auto state = Kernel::KMemoryState::IoMemory;
|
||||||
const auto perm = Kernel::KMemoryPermission::UserReadWrite;
|
const auto perm = Kernel::KMemoryPermission::UserReadWrite;
|
||||||
std::mt19937_64 rng{process->GetRandomEntropy(0)};
|
std::mt19937_64 rng{process->GetRandomEntropy(0)};
|
||||||
|
|
||||||
|
|
|
@ -826,12 +826,13 @@ void GameList::PopulateAsync(QVector<UISettings::GameDir>& game_dirs) {
|
||||||
tree_view->setColumnHidden(COLUMN_SIZE, !UISettings::values.show_size);
|
tree_view->setColumnHidden(COLUMN_SIZE, !UISettings::values.show_size);
|
||||||
tree_view->setColumnHidden(COLUMN_PLAY_TIME, !UISettings::values.show_play_time);
|
tree_view->setColumnHidden(COLUMN_PLAY_TIME, !UISettings::values.show_play_time);
|
||||||
|
|
||||||
|
// Before deleting rows, cancel the worker so that it is not using them
|
||||||
|
emit ShouldCancelWorker();
|
||||||
|
|
||||||
// Delete any rows that might already exist if we're repopulating
|
// Delete any rows that might already exist if we're repopulating
|
||||||
item_model->removeRows(0, item_model->rowCount());
|
item_model->removeRows(0, item_model->rowCount());
|
||||||
search_field->clear();
|
search_field->clear();
|
||||||
|
|
||||||
emit ShouldCancelWorker();
|
|
||||||
|
|
||||||
GameListWorker* worker =
|
GameListWorker* worker =
|
||||||
new GameListWorker(vfs, provider, game_dirs, compatibility_list, play_time_manager, system);
|
new GameListWorker(vfs, provider, game_dirs, compatibility_list, play_time_manager, system);
|
||||||
|
|
||||||
|
|
|
@ -293,7 +293,7 @@ void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir) {
|
||||||
void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_path, bool deep_scan,
|
void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_path, bool deep_scan,
|
||||||
GameListDir* parent_dir) {
|
GameListDir* parent_dir) {
|
||||||
const auto callback = [this, target, parent_dir](const std::filesystem::path& path) -> bool {
|
const auto callback = [this, target, parent_dir](const std::filesystem::path& path) -> bool {
|
||||||
if (stop_processing) {
|
if (stop_requested) {
|
||||||
// Breaks the callback loop.
|
// Breaks the callback loop.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -399,7 +399,6 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameListWorker::run() {
|
void GameListWorker::run() {
|
||||||
stop_processing = false;
|
|
||||||
provider->ClearAllEntries();
|
provider->ClearAllEntries();
|
||||||
|
|
||||||
for (UISettings::GameDir& game_dir : game_dirs) {
|
for (UISettings::GameDir& game_dir : game_dirs) {
|
||||||
|
@ -427,9 +426,11 @@ void GameListWorker::run() {
|
||||||
}
|
}
|
||||||
|
|
||||||
emit Finished(watch_list);
|
emit Finished(watch_list);
|
||||||
|
processing_completed.Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameListWorker::Cancel() {
|
void GameListWorker::Cancel() {
|
||||||
this->disconnect();
|
this->disconnect();
|
||||||
stop_processing = true;
|
stop_requested.store(true);
|
||||||
|
processing_completed.Wait();
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include <QRunnable>
|
#include <QRunnable>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
|
#include "common/thread.h"
|
||||||
#include "yuzu/compatibility_list.h"
|
#include "yuzu/compatibility_list.h"
|
||||||
#include "yuzu/play_time_manager.h"
|
#include "yuzu/play_time_manager.h"
|
||||||
|
|
||||||
|
@ -82,7 +83,9 @@ private:
|
||||||
const PlayTime::PlayTimeManager& play_time_manager;
|
const PlayTime::PlayTimeManager& play_time_manager;
|
||||||
|
|
||||||
QStringList watch_list;
|
QStringList watch_list;
|
||||||
std::atomic_bool stop_processing;
|
|
||||||
|
Common::Event processing_completed;
|
||||||
|
std::atomic_bool stop_requested = false;
|
||||||
|
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue