diff options
41 files changed, 1095 insertions, 311 deletions
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt index 6e39e542b..115f72710 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/NativeLibrary.kt | |||
| @@ -15,13 +15,9 @@ import androidx.annotation.Keep | |||
| 15 | import androidx.fragment.app.DialogFragment | 15 | import androidx.fragment.app.DialogFragment |
| 16 | import com.google.android.material.dialog.MaterialAlertDialogBuilder | 16 | import com.google.android.material.dialog.MaterialAlertDialogBuilder |
| 17 | import java.lang.ref.WeakReference | 17 | import java.lang.ref.WeakReference |
| 18 | import org.yuzu.yuzu_emu.YuzuApplication.Companion.appContext | ||
| 19 | import org.yuzu.yuzu_emu.activities.EmulationActivity | 18 | import org.yuzu.yuzu_emu.activities.EmulationActivity |
| 20 | import org.yuzu.yuzu_emu.utils.DocumentsTree.Companion.isNativePath | 19 | import org.yuzu.yuzu_emu.utils.DocumentsTree.Companion.isNativePath |
| 21 | import org.yuzu.yuzu_emu.utils.FileUtil.exists | 20 | import org.yuzu.yuzu_emu.utils.FileUtil |
| 22 | import org.yuzu.yuzu_emu.utils.FileUtil.getFileSize | ||
| 23 | import org.yuzu.yuzu_emu.utils.FileUtil.isDirectory | ||
| 24 | import org.yuzu.yuzu_emu.utils.FileUtil.openContentUri | ||
| 25 | import org.yuzu.yuzu_emu.utils.Log | 21 | import org.yuzu.yuzu_emu.utils.Log |
| 26 | import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable | 22 | import org.yuzu.yuzu_emu.utils.SerializableHelper.serializable |
| 27 | 23 | ||
| @@ -75,7 +71,7 @@ object NativeLibrary { | |||
| 75 | return if (isNativePath(path!!)) { | 71 | return if (isNativePath(path!!)) { |
| 76 | YuzuApplication.documentsTree!!.openContentUri(path, openmode) | 72 | YuzuApplication.documentsTree!!.openContentUri(path, openmode) |
| 77 | } else { | 73 | } else { |
| 78 | openContentUri(appContext, path, openmode) | 74 | FileUtil.openContentUri(path, openmode) |
| 79 | } | 75 | } |
| 80 | } | 76 | } |
| 81 | 77 | ||
| @@ -85,7 +81,7 @@ object NativeLibrary { | |||
| 85 | return if (isNativePath(path!!)) { | 81 | return if (isNativePath(path!!)) { |
| 86 | YuzuApplication.documentsTree!!.getFileSize(path) | 82 | YuzuApplication.documentsTree!!.getFileSize(path) |
| 87 | } else { | 83 | } else { |
| 88 | getFileSize(appContext, path) | 84 | FileUtil.getFileSize(path) |
| 89 | } | 85 | } |
| 90 | } | 86 | } |
| 91 | 87 | ||
| @@ -95,7 +91,7 @@ object NativeLibrary { | |||
| 95 | return if (isNativePath(path!!)) { | 91 | return if (isNativePath(path!!)) { |
| 96 | YuzuApplication.documentsTree!!.exists(path) | 92 | YuzuApplication.documentsTree!!.exists(path) |
| 97 | } else { | 93 | } else { |
| 98 | exists(appContext, path) | 94 | FileUtil.exists(path) |
| 99 | } | 95 | } |
| 100 | } | 96 | } |
| 101 | 97 | ||
| @@ -105,7 +101,7 @@ object NativeLibrary { | |||
| 105 | return if (isNativePath(path!!)) { | 101 | return if (isNativePath(path!!)) { |
| 106 | YuzuApplication.documentsTree!!.isDirectory(path) | 102 | YuzuApplication.documentsTree!!.isDirectory(path) |
| 107 | } else { | 103 | } else { |
| 108 | isDirectory(appContext, path) | 104 | FileUtil.isDirectory(path) |
| 109 | } | 105 | } |
| 110 | } | 106 | } |
| 111 | 107 | ||
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt index 9561748cb..8c053670c 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/YuzuApplication.kt | |||
| @@ -47,7 +47,7 @@ class YuzuApplication : Application() { | |||
| 47 | application = this | 47 | application = this |
| 48 | documentsTree = DocumentsTree() | 48 | documentsTree = DocumentsTree() |
| 49 | DirectoryInitialization.start() | 49 | DirectoryInitialization.start() |
| 50 | GpuDriverHelper.initializeDriverParameters(applicationContext) | 50 | GpuDriverHelper.initializeDriverParameters() |
| 51 | NativeLibrary.logDeviceInfo() | 51 | NativeLibrary.logDeviceInfo() |
| 52 | 52 | ||
| 53 | createNotificationChannels() | 53 | createNotificationChannels() |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/DriverAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/DriverAdapter.kt new file mode 100644 index 000000000..0e818cab9 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/DriverAdapter.kt | |||
| @@ -0,0 +1,117 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | package org.yuzu.yuzu_emu.adapters | ||
| 5 | |||
| 6 | import android.text.TextUtils | ||
| 7 | import android.view.LayoutInflater | ||
| 8 | import android.view.View | ||
| 9 | import android.view.ViewGroup | ||
| 10 | import androidx.recyclerview.widget.AsyncDifferConfig | ||
| 11 | import androidx.recyclerview.widget.DiffUtil | ||
| 12 | import androidx.recyclerview.widget.ListAdapter | ||
| 13 | import androidx.recyclerview.widget.RecyclerView | ||
| 14 | import org.yuzu.yuzu_emu.R | ||
| 15 | import org.yuzu.yuzu_emu.databinding.CardDriverOptionBinding | ||
| 16 | import org.yuzu.yuzu_emu.model.DriverViewModel | ||
| 17 | import org.yuzu.yuzu_emu.utils.GpuDriverHelper | ||
| 18 | import org.yuzu.yuzu_emu.utils.GpuDriverMetadata | ||
| 19 | |||
| 20 | class DriverAdapter(private val driverViewModel: DriverViewModel) : | ||
| 21 | ListAdapter<Pair<String, GpuDriverMetadata>, DriverAdapter.DriverViewHolder>( | ||
| 22 | AsyncDifferConfig.Builder(DiffCallback()).build() | ||
| 23 | ) { | ||
| 24 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DriverViewHolder { | ||
| 25 | val binding = | ||
| 26 | CardDriverOptionBinding.inflate(LayoutInflater.from(parent.context), parent, false) | ||
| 27 | return DriverViewHolder(binding) | ||
| 28 | } | ||
| 29 | |||
| 30 | override fun getItemCount(): Int = currentList.size | ||
| 31 | |||
| 32 | override fun onBindViewHolder(holder: DriverViewHolder, position: Int) = | ||
| 33 | holder.bind(currentList[position]) | ||
| 34 | |||
| 35 | private fun onSelectDriver(position: Int) { | ||
| 36 | driverViewModel.setSelectedDriverIndex(position) | ||
| 37 | notifyItemChanged(driverViewModel.previouslySelectedDriver) | ||
| 38 | notifyItemChanged(driverViewModel.selectedDriver) | ||
| 39 | } | ||
| 40 | |||
| 41 | private fun onDeleteDriver(driverData: Pair<String, GpuDriverMetadata>, position: Int) { | ||
| 42 | if (driverViewModel.selectedDriver > position) { | ||
| 43 | driverViewModel.setSelectedDriverIndex(driverViewModel.selectedDriver - 1) | ||
| 44 | } | ||
| 45 | if (GpuDriverHelper.customDriverData == driverData.second) { | ||
| 46 | driverViewModel.setSelectedDriverIndex(0) | ||
| 47 | } | ||
| 48 | driverViewModel.driversToDelete.add(driverData.first) | ||
| 49 | driverViewModel.removeDriver(driverData) | ||
| 50 | notifyItemRemoved(position) | ||
| 51 | notifyItemChanged(driverViewModel.selectedDriver) | ||
| 52 | } | ||
| 53 | |||
| 54 | inner class DriverViewHolder(val binding: CardDriverOptionBinding) : | ||
| 55 | RecyclerView.ViewHolder(binding.root) { | ||
| 56 | private lateinit var driverData: Pair<String, GpuDriverMetadata> | ||
| 57 | |||
| 58 | fun bind(driverData: Pair<String, GpuDriverMetadata>) { | ||
| 59 | this.driverData = driverData | ||
| 60 | val driver = driverData.second | ||
| 61 | |||
| 62 | binding.apply { | ||
| 63 | radioButton.isChecked = driverViewModel.selectedDriver == bindingAdapterPosition | ||
| 64 | root.setOnClickListener { | ||
| 65 | onSelectDriver(bindingAdapterPosition) | ||
| 66 | } | ||
| 67 | buttonDelete.setOnClickListener { | ||
| 68 | onDeleteDriver(driverData, bindingAdapterPosition) | ||
| 69 | } | ||
| 70 | |||
| 71 | // Delay marquee by 3s | ||
| 72 | title.postDelayed( | ||
| 73 | { | ||
| 74 | title.isSelected = true | ||
| 75 | title.ellipsize = TextUtils.TruncateAt.MARQUEE | ||
| 76 | version.isSelected = true | ||
| 77 | version.ellipsize = TextUtils.TruncateAt.MARQUEE | ||
| 78 | description.isSelected = true | ||
| 79 | description.ellipsize = TextUtils.TruncateAt.MARQUEE | ||
| 80 | }, | ||
| 81 | 3000 | ||
| 82 | ) | ||
| 83 | if (driver.name == null) { | ||
| 84 | title.setText(R.string.system_gpu_driver) | ||
| 85 | description.text = "" | ||
| 86 | version.text = "" | ||
| 87 | version.visibility = View.GONE | ||
| 88 | description.visibility = View.GONE | ||
| 89 | buttonDelete.visibility = View.GONE | ||
| 90 | } else { | ||
| 91 | title.text = driver.name | ||
| 92 | version.text = driver.version | ||
| 93 | description.text = driver.description | ||
| 94 | version.visibility = View.VISIBLE | ||
| 95 | description.visibility = View.VISIBLE | ||
| 96 | buttonDelete.visibility = View.VISIBLE | ||
| 97 | } | ||
| 98 | } | ||
| 99 | } | ||
| 100 | } | ||
| 101 | |||
| 102 | private class DiffCallback : DiffUtil.ItemCallback<Pair<String, GpuDriverMetadata>>() { | ||
| 103 | override fun areItemsTheSame( | ||
| 104 | oldItem: Pair<String, GpuDriverMetadata>, | ||
| 105 | newItem: Pair<String, GpuDriverMetadata> | ||
| 106 | ): Boolean { | ||
| 107 | return oldItem.first == newItem.first | ||
| 108 | } | ||
| 109 | |||
| 110 | override fun areContentsTheSame( | ||
| 111 | oldItem: Pair<String, GpuDriverMetadata>, | ||
| 112 | newItem: Pair<String, GpuDriverMetadata> | ||
| 113 | ): Boolean { | ||
| 114 | return oldItem.second == newItem.second | ||
| 115 | } | ||
| 116 | } | ||
| 117 | } | ||
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt new file mode 100644 index 000000000..10b1d3547 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt | |||
| @@ -0,0 +1,185 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | package org.yuzu.yuzu_emu.fragments | ||
| 5 | |||
| 6 | import android.os.Bundle | ||
| 7 | import android.view.LayoutInflater | ||
| 8 | import android.view.View | ||
| 9 | import android.view.ViewGroup | ||
| 10 | import androidx.activity.result.contract.ActivityResultContracts | ||
| 11 | import androidx.core.view.ViewCompat | ||
| 12 | import androidx.core.view.WindowInsetsCompat | ||
| 13 | import androidx.core.view.updatePadding | ||
| 14 | import androidx.fragment.app.Fragment | ||
| 15 | import androidx.fragment.app.activityViewModels | ||
| 16 | import androidx.lifecycle.lifecycleScope | ||
| 17 | import androidx.navigation.findNavController | ||
| 18 | import androidx.recyclerview.widget.GridLayoutManager | ||
| 19 | import com.google.android.material.transition.MaterialSharedAxis | ||
| 20 | import kotlinx.coroutines.flow.collectLatest | ||
| 21 | import kotlinx.coroutines.launch | ||
| 22 | import org.yuzu.yuzu_emu.R | ||
| 23 | import org.yuzu.yuzu_emu.adapters.DriverAdapter | ||
| 24 | import org.yuzu.yuzu_emu.databinding.FragmentDriverManagerBinding | ||
| 25 | import org.yuzu.yuzu_emu.model.DriverViewModel | ||
| 26 | import org.yuzu.yuzu_emu.model.HomeViewModel | ||
| 27 | import org.yuzu.yuzu_emu.utils.FileUtil | ||
| 28 | import org.yuzu.yuzu_emu.utils.GpuDriverHelper | ||
| 29 | import java.io.IOException | ||
| 30 | |||
| 31 | class DriverManagerFragment : Fragment() { | ||
| 32 | private var _binding: FragmentDriverManagerBinding? = null | ||
| 33 | private val binding get() = _binding!! | ||
| 34 | |||
| 35 | private val homeViewModel: HomeViewModel by activityViewModels() | ||
| 36 | private val driverViewModel: DriverViewModel by activityViewModels() | ||
| 37 | |||
| 38 | override fun onCreate(savedInstanceState: Bundle?) { | ||
| 39 | super.onCreate(savedInstanceState) | ||
| 40 | enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true) | ||
| 41 | returnTransition = MaterialSharedAxis(MaterialSharedAxis.X, false) | ||
| 42 | reenterTransition = MaterialSharedAxis(MaterialSharedAxis.X, false) | ||
| 43 | } | ||
| 44 | |||
| 45 | override fun onCreateView( | ||
| 46 | inflater: LayoutInflater, | ||
| 47 | container: ViewGroup?, | ||
| 48 | savedInstanceState: Bundle? | ||
| 49 | ): View { | ||
| 50 | _binding = FragmentDriverManagerBinding.inflate(inflater) | ||
| 51 | return binding.root | ||
| 52 | } | ||
| 53 | |||
| 54 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | ||
| 55 | super.onViewCreated(view, savedInstanceState) | ||
| 56 | homeViewModel.setNavigationVisibility(visible = false, animated = true) | ||
| 57 | homeViewModel.setStatusBarShadeVisibility(visible = false) | ||
| 58 | |||
| 59 | if (!driverViewModel.isInteractionAllowed) { | ||
| 60 | DriversLoadingDialogFragment().show( | ||
| 61 | childFragmentManager, | ||
| 62 | DriversLoadingDialogFragment.TAG | ||
| 63 | ) | ||
| 64 | } | ||
| 65 | |||
| 66 | binding.toolbarDrivers.setNavigationOnClickListener { | ||
| 67 | binding.root.findNavController().popBackStack() | ||
| 68 | } | ||
| 69 | |||
| 70 | binding.buttonInstall.setOnClickListener { | ||
| 71 | getDriver.launch(arrayOf("application/zip")) | ||
| 72 | } | ||
| 73 | |||
| 74 | binding.listDrivers.apply { | ||
| 75 | layoutManager = GridLayoutManager( | ||
| 76 | requireContext(), | ||
| 77 | resources.getInteger(R.integer.grid_columns) | ||
| 78 | ) | ||
| 79 | adapter = DriverAdapter(driverViewModel) | ||
| 80 | } | ||
| 81 | |||
| 82 | viewLifecycleOwner.lifecycleScope.apply { | ||
| 83 | launch { | ||
| 84 | driverViewModel.driverList.collectLatest { | ||
| 85 | (binding.listDrivers.adapter as DriverAdapter).submitList(it) | ||
| 86 | } | ||
| 87 | } | ||
| 88 | launch { | ||
| 89 | driverViewModel.newDriverInstalled.collect { | ||
| 90 | if (_binding != null && it) { | ||
| 91 | (binding.listDrivers.adapter as DriverAdapter).apply { | ||
| 92 | notifyItemChanged(driverViewModel.previouslySelectedDriver) | ||
| 93 | notifyItemChanged(driverViewModel.selectedDriver) | ||
| 94 | driverViewModel.setNewDriverInstalled(false) | ||
| 95 | } | ||
| 96 | } | ||
| 97 | } | ||
| 98 | } | ||
| 99 | } | ||
| 100 | |||
| 101 | setInsets() | ||
| 102 | } | ||
| 103 | |||
| 104 | // Start installing requested driver | ||
| 105 | override fun onStop() { | ||
| 106 | super.onStop() | ||
| 107 | driverViewModel.onCloseDriverManager() | ||
| 108 | } | ||
| 109 | |||
| 110 | private fun setInsets() = | ||
| 111 | ViewCompat.setOnApplyWindowInsetsListener( | ||
| 112 | binding.root | ||
| 113 | ) { _: View, windowInsets: WindowInsetsCompat -> | ||
| 114 | val barInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) | ||
| 115 | val cutoutInsets = windowInsets.getInsets(WindowInsetsCompat.Type.displayCutout()) | ||
| 116 | |||
| 117 | val leftInsets = barInsets.left + cutoutInsets.left | ||
| 118 | val rightInsets = barInsets.right + cutoutInsets.right | ||
| 119 | |||
| 120 | val mlpAppBar = binding.toolbarDrivers.layoutParams as ViewGroup.MarginLayoutParams | ||
| 121 | mlpAppBar.leftMargin = leftInsets | ||
| 122 | mlpAppBar.rightMargin = rightInsets | ||
| 123 | binding.toolbarDrivers.layoutParams = mlpAppBar | ||
| 124 | |||
| 125 | val mlplistDrivers = binding.listDrivers.layoutParams as ViewGroup.MarginLayoutParams | ||
| 126 | mlplistDrivers.leftMargin = leftInsets | ||
| 127 | mlplistDrivers.rightMargin = rightInsets | ||
| 128 | binding.listDrivers.layoutParams = mlplistDrivers | ||
| 129 | |||
| 130 | val fabSpacing = resources.getDimensionPixelSize(R.dimen.spacing_fab) | ||
| 131 | val mlpFab = | ||
| 132 | binding.buttonInstall.layoutParams as ViewGroup.MarginLayoutParams | ||
| 133 | mlpFab.leftMargin = leftInsets + fabSpacing | ||
| 134 | mlpFab.rightMargin = rightInsets + fabSpacing | ||
| 135 | mlpFab.bottomMargin = barInsets.bottom + fabSpacing | ||
| 136 | binding.buttonInstall.layoutParams = mlpFab | ||
| 137 | |||
| 138 | binding.listDrivers.updatePadding( | ||
| 139 | bottom = barInsets.bottom + | ||
| 140 | resources.getDimensionPixelSize(R.dimen.spacing_bottom_list_fab) | ||
| 141 | ) | ||
| 142 | |||
| 143 | windowInsets | ||
| 144 | } | ||
| 145 | |||
| 146 | private val getDriver = | ||
| 147 | registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> | ||
| 148 | if (result == null) { | ||
| 149 | return@registerForActivityResult | ||
| 150 | } | ||
| 151 | |||
| 152 | IndeterminateProgressDialogFragment.newInstance( | ||
| 153 | requireActivity(), | ||
| 154 | R.string.installing_driver, | ||
| 155 | false | ||
| 156 | ) { | ||
| 157 | // Ignore file exceptions when a user selects an invalid zip | ||
| 158 | try { | ||
| 159 | GpuDriverHelper.copyDriverToInternalStorage(result) | ||
| 160 | } catch (_: IOException) { | ||
| 161 | return@newInstance getString(R.string.select_gpu_driver_error) | ||
| 162 | } | ||
| 163 | |||
| 164 | val driverData = GpuDriverHelper.customDriverData | ||
| 165 | if (driverData.name == null) { | ||
| 166 | return@newInstance getString(R.string.select_gpu_driver_error) | ||
| 167 | } | ||
| 168 | |||
| 169 | val driverInList = | ||
| 170 | driverViewModel.driverList.value.firstOrNull { it.second == driverData } | ||
| 171 | if (driverInList != null) { | ||
| 172 | return@newInstance getString(R.string.driver_already_installed) | ||
| 173 | } else { | ||
| 174 | driverViewModel.addDriver( | ||
| 175 | Pair( | ||
| 176 | "${GpuDriverHelper.driverStoragePath}/${FileUtil.getFilename(result)}", | ||
| 177 | driverData | ||
| 178 | ) | ||
| 179 | ) | ||
| 180 | driverViewModel.setNewDriverInstalled(true) | ||
| 181 | } | ||
| 182 | return@newInstance Any() | ||
| 183 | }.show(childFragmentManager, IndeterminateProgressDialogFragment.TAG) | ||
| 184 | } | ||
| 185 | } | ||
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriversLoadingDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriversLoadingDialogFragment.kt new file mode 100644 index 000000000..f8c34346a --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriversLoadingDialogFragment.kt | |||
| @@ -0,0 +1,75 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | package org.yuzu.yuzu_emu.fragments | ||
| 5 | |||
| 6 | import android.app.Dialog | ||
| 7 | import android.os.Bundle | ||
| 8 | import android.view.LayoutInflater | ||
| 9 | import android.view.View | ||
| 10 | import android.view.ViewGroup | ||
| 11 | import androidx.fragment.app.DialogFragment | ||
| 12 | import androidx.fragment.app.activityViewModels | ||
| 13 | import androidx.lifecycle.Lifecycle | ||
| 14 | import androidx.lifecycle.lifecycleScope | ||
| 15 | import androidx.lifecycle.repeatOnLifecycle | ||
| 16 | import com.google.android.material.dialog.MaterialAlertDialogBuilder | ||
| 17 | import kotlinx.coroutines.launch | ||
| 18 | import org.yuzu.yuzu_emu.R | ||
| 19 | import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding | ||
| 20 | import org.yuzu.yuzu_emu.model.DriverViewModel | ||
| 21 | |||
| 22 | class DriversLoadingDialogFragment : DialogFragment() { | ||
| 23 | private val driverViewModel: DriverViewModel by activityViewModels() | ||
| 24 | |||
| 25 | private lateinit var binding: DialogProgressBarBinding | ||
| 26 | |||
| 27 | override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { | ||
| 28 | binding = DialogProgressBarBinding.inflate(layoutInflater) | ||
| 29 | binding.progressBar.isIndeterminate = true | ||
| 30 | |||
| 31 | isCancelable = false | ||
| 32 | |||
| 33 | return MaterialAlertDialogBuilder(requireContext()) | ||
| 34 | .setTitle(R.string.loading) | ||
| 35 | .setView(binding.root) | ||
| 36 | .create() | ||
| 37 | } | ||
| 38 | |||
| 39 | override fun onCreateView( | ||
| 40 | inflater: LayoutInflater, | ||
| 41 | container: ViewGroup?, | ||
| 42 | savedInstanceState: Bundle? | ||
| 43 | ): View = binding.root | ||
| 44 | |||
| 45 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | ||
| 46 | super.onViewCreated(view, savedInstanceState) | ||
| 47 | viewLifecycleOwner.lifecycleScope.apply { | ||
| 48 | launch { | ||
| 49 | repeatOnLifecycle(Lifecycle.State.RESUMED) { | ||
| 50 | driverViewModel.areDriversLoading.collect { checkForDismiss() } | ||
| 51 | } | ||
| 52 | } | ||
| 53 | launch { | ||
| 54 | repeatOnLifecycle(Lifecycle.State.RESUMED) { | ||
| 55 | driverViewModel.isDriverReady.collect { checkForDismiss() } | ||
| 56 | } | ||
| 57 | } | ||
| 58 | launch { | ||
| 59 | repeatOnLifecycle(Lifecycle.State.RESUMED) { | ||
| 60 | driverViewModel.isDeletingDrivers.collect { checkForDismiss() } | ||
| 61 | } | ||
| 62 | } | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | private fun checkForDismiss() { | ||
| 67 | if (driverViewModel.isInteractionAllowed) { | ||
| 68 | dismiss() | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 72 | companion object { | ||
| 73 | const val TAG = "DriversLoadingDialogFragment" | ||
| 74 | } | ||
| 75 | } | ||
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt index e6ad2aa77..598a9d42b 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt | |||
| @@ -39,6 +39,7 @@ import androidx.window.layout.WindowLayoutInfo | |||
| 39 | import com.google.android.material.dialog.MaterialAlertDialogBuilder | 39 | import com.google.android.material.dialog.MaterialAlertDialogBuilder |
| 40 | import com.google.android.material.slider.Slider | 40 | import com.google.android.material.slider.Slider |
| 41 | import kotlinx.coroutines.Dispatchers | 41 | import kotlinx.coroutines.Dispatchers |
| 42 | import kotlinx.coroutines.flow.collect | ||
| 42 | import kotlinx.coroutines.flow.collectLatest | 43 | import kotlinx.coroutines.flow.collectLatest |
| 43 | import kotlinx.coroutines.launch | 44 | import kotlinx.coroutines.launch |
| 44 | import org.yuzu.yuzu_emu.HomeNavigationDirections | 45 | import org.yuzu.yuzu_emu.HomeNavigationDirections |
| @@ -50,6 +51,7 @@ import org.yuzu.yuzu_emu.databinding.DialogOverlayAdjustBinding | |||
| 50 | import org.yuzu.yuzu_emu.databinding.FragmentEmulationBinding | 51 | import org.yuzu.yuzu_emu.databinding.FragmentEmulationBinding |
| 51 | import org.yuzu.yuzu_emu.features.settings.model.IntSetting | 52 | import org.yuzu.yuzu_emu.features.settings.model.IntSetting |
| 52 | import org.yuzu.yuzu_emu.features.settings.model.Settings | 53 | import org.yuzu.yuzu_emu.features.settings.model.Settings |
| 54 | import org.yuzu.yuzu_emu.model.DriverViewModel | ||
| 53 | import org.yuzu.yuzu_emu.model.Game | 55 | import org.yuzu.yuzu_emu.model.Game |
| 54 | import org.yuzu.yuzu_emu.model.EmulationViewModel | 56 | import org.yuzu.yuzu_emu.model.EmulationViewModel |
| 55 | import org.yuzu.yuzu_emu.overlay.InputOverlay | 57 | import org.yuzu.yuzu_emu.overlay.InputOverlay |
| @@ -70,6 +72,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 70 | private lateinit var game: Game | 72 | private lateinit var game: Game |
| 71 | 73 | ||
| 72 | private val emulationViewModel: EmulationViewModel by activityViewModels() | 74 | private val emulationViewModel: EmulationViewModel by activityViewModels() |
| 75 | private val driverViewModel: DriverViewModel by activityViewModels() | ||
| 73 | 76 | ||
| 74 | private var isInFoldableLayout = false | 77 | private var isInFoldableLayout = false |
| 75 | 78 | ||
| @@ -299,6 +302,21 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 299 | } | 302 | } |
| 300 | } | 303 | } |
| 301 | } | 304 | } |
| 305 | launch { | ||
| 306 | repeatOnLifecycle(Lifecycle.State.RESUMED) { | ||
| 307 | driverViewModel.isDriverReady.collect { | ||
| 308 | if (it && !emulationState.isRunning) { | ||
| 309 | if (!DirectoryInitialization.areDirectoriesReady) { | ||
| 310 | DirectoryInitialization.start() | ||
| 311 | } | ||
| 312 | |||
| 313 | updateScreenLayout() | ||
| 314 | |||
| 315 | emulationState.run(emulationActivity!!.isActivityRecreated) | ||
| 316 | } | ||
| 317 | } | ||
| 318 | } | ||
| 319 | } | ||
| 302 | } | 320 | } |
| 303 | } | 321 | } |
| 304 | 322 | ||
| @@ -332,17 +350,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { | |||
| 332 | } | 350 | } |
| 333 | } | 351 | } |
| 334 | 352 | ||
| 335 | override fun onResume() { | ||
| 336 | super.onResume() | ||
| 337 | if (!DirectoryInitialization.areDirectoriesReady) { | ||
| 338 | DirectoryInitialization.start() | ||
| 339 | } | ||
| 340 | |||
| 341 | updateScreenLayout() | ||
| 342 | |||
| 343 | emulationState.run(emulationActivity!!.isActivityRecreated) | ||
| 344 | } | ||
| 345 | |||
| 346 | override fun onPause() { | 353 | override fun onPause() { |
| 347 | if (emulationState.isRunning && emulationActivity?.isInPictureInPictureMode != true) { | 354 | if (emulationState.isRunning && emulationActivity?.isInPictureInPictureMode != true) { |
| 348 | emulationState.pause() | 355 | emulationState.pause() |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt index 8923c0ea2..fd9785075 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt | |||
| @@ -5,7 +5,6 @@ package org.yuzu.yuzu_emu.fragments | |||
| 5 | 5 | ||
| 6 | import android.Manifest | 6 | import android.Manifest |
| 7 | import android.content.ActivityNotFoundException | 7 | import android.content.ActivityNotFoundException |
| 8 | import android.content.DialogInterface | ||
| 9 | import android.content.Intent | 8 | import android.content.Intent |
| 10 | import android.content.pm.PackageManager | 9 | import android.content.pm.PackageManager |
| 11 | import android.os.Bundle | 10 | import android.os.Bundle |
| @@ -28,7 +27,6 @@ import androidx.fragment.app.activityViewModels | |||
| 28 | import androidx.navigation.findNavController | 27 | import androidx.navigation.findNavController |
| 29 | import androidx.navigation.fragment.findNavController | 28 | import androidx.navigation.fragment.findNavController |
| 30 | import androidx.recyclerview.widget.LinearLayoutManager | 29 | import androidx.recyclerview.widget.LinearLayoutManager |
| 31 | import com.google.android.material.dialog.MaterialAlertDialogBuilder | ||
| 32 | import com.google.android.material.transition.MaterialSharedAxis | 30 | import com.google.android.material.transition.MaterialSharedAxis |
| 33 | import org.yuzu.yuzu_emu.BuildConfig | 31 | import org.yuzu.yuzu_emu.BuildConfig |
| 34 | import org.yuzu.yuzu_emu.HomeNavigationDirections | 32 | import org.yuzu.yuzu_emu.HomeNavigationDirections |
| @@ -37,6 +35,7 @@ import org.yuzu.yuzu_emu.adapters.HomeSettingAdapter | |||
| 37 | import org.yuzu.yuzu_emu.databinding.FragmentHomeSettingsBinding | 35 | import org.yuzu.yuzu_emu.databinding.FragmentHomeSettingsBinding |
| 38 | import org.yuzu.yuzu_emu.features.DocumentProvider | 36 | import org.yuzu.yuzu_emu.features.DocumentProvider |
| 39 | import org.yuzu.yuzu_emu.features.settings.model.Settings | 37 | import org.yuzu.yuzu_emu.features.settings.model.Settings |
| 38 | import org.yuzu.yuzu_emu.model.DriverViewModel | ||
| 40 | import org.yuzu.yuzu_emu.model.HomeSetting | 39 | import org.yuzu.yuzu_emu.model.HomeSetting |
| 41 | import org.yuzu.yuzu_emu.model.HomeViewModel | 40 | import org.yuzu.yuzu_emu.model.HomeViewModel |
| 42 | import org.yuzu.yuzu_emu.ui.main.MainActivity | 41 | import org.yuzu.yuzu_emu.ui.main.MainActivity |
| @@ -50,6 +49,7 @@ class HomeSettingsFragment : Fragment() { | |||
| 50 | private lateinit var mainActivity: MainActivity | 49 | private lateinit var mainActivity: MainActivity |
| 51 | 50 | ||
| 52 | private val homeViewModel: HomeViewModel by activityViewModels() | 51 | private val homeViewModel: HomeViewModel by activityViewModels() |
| 52 | private val driverViewModel: DriverViewModel by activityViewModels() | ||
| 53 | 53 | ||
| 54 | override fun onCreate(savedInstanceState: Bundle?) { | 54 | override fun onCreate(savedInstanceState: Bundle?) { |
| 55 | super.onCreate(savedInstanceState) | 55 | super.onCreate(savedInstanceState) |
| @@ -107,13 +107,17 @@ class HomeSettingsFragment : Fragment() { | |||
| 107 | ) | 107 | ) |
| 108 | add( | 108 | add( |
| 109 | HomeSetting( | 109 | HomeSetting( |
| 110 | R.string.install_gpu_driver, | 110 | R.string.gpu_driver_manager, |
| 111 | R.string.install_gpu_driver_description, | 111 | R.string.install_gpu_driver_description, |
| 112 | R.drawable.ic_exit, | 112 | R.drawable.ic_build, |
| 113 | { driverInstaller() }, | 113 | { |
| 114 | binding.root.findNavController() | ||
| 115 | .navigate(R.id.action_homeSettingsFragment_to_driverManagerFragment) | ||
| 116 | }, | ||
| 114 | { GpuDriverHelper.supportsCustomDriverLoading() }, | 117 | { GpuDriverHelper.supportsCustomDriverLoading() }, |
| 115 | R.string.custom_driver_not_supported, | 118 | R.string.custom_driver_not_supported, |
| 116 | R.string.custom_driver_not_supported_description | 119 | R.string.custom_driver_not_supported_description, |
| 120 | driverViewModel.selectedDriverMetadata | ||
| 117 | ) | 121 | ) |
| 118 | ) | 122 | ) |
| 119 | add( | 123 | add( |
| @@ -292,31 +296,6 @@ class HomeSettingsFragment : Fragment() { | |||
| 292 | } | 296 | } |
| 293 | } | 297 | } |
| 294 | 298 | ||
| 295 | private fun driverInstaller() { | ||
| 296 | // Get the driver name for the dialog message. | ||
| 297 | var driverName = GpuDriverHelper.customDriverName | ||
| 298 | if (driverName == null) { | ||
| 299 | driverName = getString(R.string.system_gpu_driver) | ||
| 300 | } | ||
| 301 | |||
| 302 | MaterialAlertDialogBuilder(requireContext()) | ||
| 303 | .setTitle(getString(R.string.select_gpu_driver_title)) | ||
| 304 | .setMessage(driverName) | ||
| 305 | .setNegativeButton(android.R.string.cancel, null) | ||
| 306 | .setNeutralButton(R.string.select_gpu_driver_default) { _: DialogInterface?, _: Int -> | ||
| 307 | GpuDriverHelper.installDefaultDriver(requireContext()) | ||
| 308 | Toast.makeText( | ||
| 309 | requireContext(), | ||
| 310 | R.string.select_gpu_driver_use_default, | ||
| 311 | Toast.LENGTH_SHORT | ||
| 312 | ).show() | ||
| 313 | } | ||
| 314 | .setPositiveButton(R.string.select_gpu_driver_install) { _: DialogInterface?, _: Int -> | ||
| 315 | mainActivity.getDriver.launch(arrayOf("application/zip")) | ||
| 316 | } | ||
| 317 | .show() | ||
| 318 | } | ||
| 319 | |||
| 320 | private fun shareLog() { | 299 | private fun shareLog() { |
| 321 | val file = DocumentFile.fromSingleUri( | 300 | val file = DocumentFile.fromSingleUri( |
| 322 | mainActivity, | 301 | mainActivity, |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt index f128deda8..7e467814d 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/IndeterminateProgressDialogFragment.kt | |||
| @@ -10,8 +10,8 @@ import android.view.View | |||
| 10 | import android.view.ViewGroup | 10 | import android.view.ViewGroup |
| 11 | import android.widget.Toast | 11 | import android.widget.Toast |
| 12 | import androidx.appcompat.app.AlertDialog | 12 | import androidx.appcompat.app.AlertDialog |
| 13 | import androidx.appcompat.app.AppCompatActivity | ||
| 14 | import androidx.fragment.app.DialogFragment | 13 | import androidx.fragment.app.DialogFragment |
| 14 | import androidx.fragment.app.FragmentActivity | ||
| 15 | import androidx.fragment.app.activityViewModels | 15 | import androidx.fragment.app.activityViewModels |
| 16 | import androidx.lifecycle.Lifecycle | 16 | import androidx.lifecycle.Lifecycle |
| 17 | import androidx.lifecycle.ViewModelProvider | 17 | import androidx.lifecycle.ViewModelProvider |
| @@ -78,6 +78,10 @@ class IndeterminateProgressDialogFragment : DialogFragment() { | |||
| 78 | requireActivity().supportFragmentManager, | 78 | requireActivity().supportFragmentManager, |
| 79 | MessageDialogFragment.TAG | 79 | MessageDialogFragment.TAG |
| 80 | ) | 80 | ) |
| 81 | |||
| 82 | else -> { | ||
| 83 | // Do nothing | ||
| 84 | } | ||
| 81 | } | 85 | } |
| 82 | taskViewModel.clear() | 86 | taskViewModel.clear() |
| 83 | } | 87 | } |
| @@ -115,7 +119,7 @@ class IndeterminateProgressDialogFragment : DialogFragment() { | |||
| 115 | private const val CANCELLABLE = "Cancellable" | 119 | private const val CANCELLABLE = "Cancellable" |
| 116 | 120 | ||
| 117 | fun newInstance( | 121 | fun newInstance( |
| 118 | activity: AppCompatActivity, | 122 | activity: FragmentActivity, |
| 119 | titleId: Int, | 123 | titleId: Int, |
| 120 | cancellable: Boolean = false, | 124 | cancellable: Boolean = false, |
| 121 | task: () -> Any | 125 | task: () -> Any |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/DriverViewModel.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/DriverViewModel.kt new file mode 100644 index 000000000..62945ad65 --- /dev/null +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/DriverViewModel.kt | |||
| @@ -0,0 +1,158 @@ | |||
| 1 | // SPDX-FileCopyrightText: 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | package org.yuzu.yuzu_emu.model | ||
| 5 | |||
| 6 | import androidx.lifecycle.ViewModel | ||
| 7 | import androidx.lifecycle.viewModelScope | ||
| 8 | import kotlinx.coroutines.Dispatchers | ||
| 9 | import kotlinx.coroutines.flow.MutableStateFlow | ||
| 10 | import kotlinx.coroutines.flow.StateFlow | ||
| 11 | import kotlinx.coroutines.launch | ||
| 12 | import kotlinx.coroutines.withContext | ||
| 13 | import org.yuzu.yuzu_emu.R | ||
| 14 | import org.yuzu.yuzu_emu.YuzuApplication | ||
| 15 | import org.yuzu.yuzu_emu.utils.FileUtil | ||
| 16 | import org.yuzu.yuzu_emu.utils.GpuDriverHelper | ||
| 17 | import org.yuzu.yuzu_emu.utils.GpuDriverMetadata | ||
| 18 | import java.io.BufferedOutputStream | ||
| 19 | import java.io.File | ||
| 20 | |||
| 21 | class DriverViewModel : ViewModel() { | ||
| 22 | private val _areDriversLoading = MutableStateFlow(false) | ||
| 23 | val areDriversLoading: StateFlow<Boolean> get() = _areDriversLoading | ||
| 24 | |||
| 25 | private val _isDriverReady = MutableStateFlow(true) | ||
| 26 | val isDriverReady: StateFlow<Boolean> get() = _isDriverReady | ||
| 27 | |||
| 28 | private val _isDeletingDrivers = MutableStateFlow(false) | ||
| 29 | val isDeletingDrivers: StateFlow<Boolean> get() = _isDeletingDrivers | ||
| 30 | |||
| 31 | private val _driverList = MutableStateFlow(mutableListOf<Pair<String, GpuDriverMetadata>>()) | ||
| 32 | val driverList: StateFlow<MutableList<Pair<String, GpuDriverMetadata>>> get() = _driverList | ||
| 33 | |||
| 34 | var previouslySelectedDriver = 0 | ||
| 35 | var selectedDriver = -1 | ||
| 36 | |||
| 37 | private val _selectedDriverMetadata = | ||
| 38 | MutableStateFlow( | ||
| 39 | GpuDriverHelper.customDriverData.name | ||
| 40 | ?: YuzuApplication.appContext.getString(R.string.system_gpu_driver) | ||
| 41 | ) | ||
| 42 | val selectedDriverMetadata: StateFlow<String> get() = _selectedDriverMetadata | ||
| 43 | |||
| 44 | private val _newDriverInstalled = MutableStateFlow(false) | ||
| 45 | val newDriverInstalled: StateFlow<Boolean> get() = _newDriverInstalled | ||
| 46 | |||
| 47 | val driversToDelete = mutableListOf<String>() | ||
| 48 | |||
| 49 | val isInteractionAllowed | ||
| 50 | get() = !areDriversLoading.value && isDriverReady.value && !isDeletingDrivers.value | ||
| 51 | |||
| 52 | init { | ||
| 53 | _areDriversLoading.value = true | ||
| 54 | viewModelScope.launch { | ||
| 55 | withContext(Dispatchers.IO) { | ||
| 56 | val drivers = GpuDriverHelper.getDrivers() | ||
| 57 | val currentDriverMetadata = GpuDriverHelper.customDriverData | ||
| 58 | for (i in drivers.indices) { | ||
| 59 | if (drivers[i].second == currentDriverMetadata) { | ||
| 60 | setSelectedDriverIndex(i) | ||
| 61 | break | ||
| 62 | } | ||
| 63 | } | ||
| 64 | |||
| 65 | // If a user had installed a driver before the manager was implemented, this zips | ||
| 66 | // the installed driver to UserData/gpu_drivers/CustomDriver.zip so that it can | ||
| 67 | // be indexed and exported as expected. | ||
| 68 | if (selectedDriver == -1) { | ||
| 69 | val driverToSave = | ||
| 70 | File(GpuDriverHelper.driverStoragePath, "CustomDriver.zip") | ||
| 71 | driverToSave.createNewFile() | ||
| 72 | FileUtil.zipFromInternalStorage( | ||
| 73 | File(GpuDriverHelper.driverInstallationPath!!), | ||
| 74 | GpuDriverHelper.driverInstallationPath!!, | ||
| 75 | BufferedOutputStream(driverToSave.outputStream()) | ||
| 76 | ) | ||
| 77 | drivers.add(Pair(driverToSave.path, currentDriverMetadata)) | ||
| 78 | setSelectedDriverIndex(drivers.size - 1) | ||
| 79 | } | ||
| 80 | |||
| 81 | _driverList.value = drivers | ||
| 82 | _areDriversLoading.value = false | ||
| 83 | } | ||
| 84 | } | ||
| 85 | } | ||
| 86 | |||
| 87 | fun setSelectedDriverIndex(value: Int) { | ||
| 88 | if (selectedDriver != -1) { | ||
| 89 | previouslySelectedDriver = selectedDriver | ||
| 90 | } | ||
| 91 | selectedDriver = value | ||
| 92 | } | ||
| 93 | |||
| 94 | fun setNewDriverInstalled(value: Boolean) { | ||
| 95 | _newDriverInstalled.value = value | ||
| 96 | } | ||
| 97 | |||
| 98 | fun addDriver(driverData: Pair<String, GpuDriverMetadata>) { | ||
| 99 | val driverIndex = _driverList.value.indexOfFirst { it == driverData } | ||
| 100 | if (driverIndex == -1) { | ||
| 101 | setSelectedDriverIndex(_driverList.value.size) | ||
| 102 | _driverList.value.add(driverData) | ||
| 103 | _selectedDriverMetadata.value = driverData.second.name | ||
| 104 | ?: YuzuApplication.appContext.getString(R.string.system_gpu_driver) | ||
| 105 | } else { | ||
| 106 | setSelectedDriverIndex(driverIndex) | ||
| 107 | } | ||
| 108 | } | ||
| 109 | |||
| 110 | fun removeDriver(driverData: Pair<String, GpuDriverMetadata>) { | ||
| 111 | _driverList.value.remove(driverData) | ||
| 112 | } | ||
| 113 | |||
| 114 | fun onCloseDriverManager() { | ||
| 115 | _isDeletingDrivers.value = true | ||
| 116 | viewModelScope.launch { | ||
| 117 | withContext(Dispatchers.IO) { | ||
| 118 | driversToDelete.forEach { | ||
| 119 | val driver = File(it) | ||
| 120 | if (driver.exists()) { | ||
| 121 | driver.delete() | ||
| 122 | } | ||
| 123 | } | ||
| 124 | driversToDelete.clear() | ||
| 125 | _isDeletingDrivers.value = false | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | if (GpuDriverHelper.customDriverData == driverList.value[selectedDriver].second) { | ||
| 130 | return | ||
| 131 | } | ||
| 132 | |||
| 133 | _isDriverReady.value = false | ||
| 134 | viewModelScope.launch { | ||
| 135 | withContext(Dispatchers.IO) { | ||
| 136 | if (selectedDriver == 0) { | ||
| 137 | GpuDriverHelper.installDefaultDriver() | ||
| 138 | setDriverReady() | ||
| 139 | return@withContext | ||
| 140 | } | ||
| 141 | |||
| 142 | val driverToInstall = File(driverList.value[selectedDriver].first) | ||
| 143 | if (driverToInstall.exists()) { | ||
| 144 | GpuDriverHelper.installCustomDriver(driverToInstall) | ||
| 145 | } else { | ||
| 146 | GpuDriverHelper.installDefaultDriver() | ||
| 147 | } | ||
| 148 | setDriverReady() | ||
| 149 | } | ||
| 150 | } | ||
| 151 | } | ||
| 152 | |||
| 153 | private fun setDriverReady() { | ||
| 154 | _isDriverReady.value = true | ||
| 155 | _selectedDriverMetadata.value = GpuDriverHelper.customDriverData.name | ||
| 156 | ?: YuzuApplication.appContext.getString(R.string.system_gpu_driver) | ||
| 157 | } | ||
| 158 | } | ||
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt index 0fa5df5e5..233aa4101 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt | |||
| @@ -29,12 +29,10 @@ import androidx.navigation.fragment.NavHostFragment | |||
| 29 | import androidx.navigation.ui.setupWithNavController | 29 | import androidx.navigation.ui.setupWithNavController |
| 30 | import androidx.preference.PreferenceManager | 30 | import androidx.preference.PreferenceManager |
| 31 | import com.google.android.material.color.MaterialColors | 31 | import com.google.android.material.color.MaterialColors |
| 32 | import com.google.android.material.dialog.MaterialAlertDialogBuilder | ||
| 33 | import com.google.android.material.navigation.NavigationBarView | 32 | import com.google.android.material.navigation.NavigationBarView |
| 34 | import kotlinx.coroutines.CoroutineScope | 33 | import kotlinx.coroutines.CoroutineScope |
| 35 | import java.io.File | 34 | import java.io.File |
| 36 | import java.io.FilenameFilter | 35 | import java.io.FilenameFilter |
| 37 | import java.io.IOException | ||
| 38 | import kotlinx.coroutines.Dispatchers | 36 | import kotlinx.coroutines.Dispatchers |
| 39 | import kotlinx.coroutines.launch | 37 | import kotlinx.coroutines.launch |
| 40 | import kotlinx.coroutines.withContext | 38 | import kotlinx.coroutines.withContext |
| @@ -43,7 +41,6 @@ import org.yuzu.yuzu_emu.NativeLibrary | |||
| 43 | import org.yuzu.yuzu_emu.R | 41 | import org.yuzu.yuzu_emu.R |
| 44 | import org.yuzu.yuzu_emu.activities.EmulationActivity | 42 | import org.yuzu.yuzu_emu.activities.EmulationActivity |
| 45 | import org.yuzu.yuzu_emu.databinding.ActivityMainBinding | 43 | import org.yuzu.yuzu_emu.databinding.ActivityMainBinding |
| 46 | import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding | ||
| 47 | import org.yuzu.yuzu_emu.features.DocumentProvider | 44 | import org.yuzu.yuzu_emu.features.DocumentProvider |
| 48 | import org.yuzu.yuzu_emu.features.settings.model.Settings | 45 | import org.yuzu.yuzu_emu.features.settings.model.Settings |
| 49 | import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment | 46 | import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment |
| @@ -343,11 +340,10 @@ class MainActivity : AppCompatActivity(), ThemeProvider { | |||
| 343 | 340 | ||
| 344 | val dstPath = DirectoryInitialization.userDirectory + "/keys/" | 341 | val dstPath = DirectoryInitialization.userDirectory + "/keys/" |
| 345 | if (FileUtil.copyUriToInternalStorage( | 342 | if (FileUtil.copyUriToInternalStorage( |
| 346 | applicationContext, | ||
| 347 | result, | 343 | result, |
| 348 | dstPath, | 344 | dstPath, |
| 349 | "prod.keys" | 345 | "prod.keys" |
| 350 | ) | 346 | ) != null |
| 351 | ) { | 347 | ) { |
| 352 | if (NativeLibrary.reloadKeys()) { | 348 | if (NativeLibrary.reloadKeys()) { |
| 353 | Toast.makeText( | 349 | Toast.makeText( |
| @@ -446,11 +442,10 @@ class MainActivity : AppCompatActivity(), ThemeProvider { | |||
| 446 | 442 | ||
| 447 | val dstPath = DirectoryInitialization.userDirectory + "/keys/" | 443 | val dstPath = DirectoryInitialization.userDirectory + "/keys/" |
| 448 | if (FileUtil.copyUriToInternalStorage( | 444 | if (FileUtil.copyUriToInternalStorage( |
| 449 | applicationContext, | ||
| 450 | result, | 445 | result, |
| 451 | dstPath, | 446 | dstPath, |
| 452 | "key_retail.bin" | 447 | "key_retail.bin" |
| 453 | ) | 448 | ) != null |
| 454 | ) { | 449 | ) { |
| 455 | if (NativeLibrary.reloadKeys()) { | 450 | if (NativeLibrary.reloadKeys()) { |
| 456 | Toast.makeText( | 451 | Toast.makeText( |
| @@ -469,59 +464,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider { | |||
| 469 | } | 464 | } |
| 470 | } | 465 | } |
| 471 | 466 | ||
| 472 | val getDriver = | ||
| 473 | registerForActivityResult(ActivityResultContracts.OpenDocument()) { result -> | ||
| 474 | if (result == null) { | ||
| 475 | return@registerForActivityResult | ||
| 476 | } | ||
| 477 | |||
| 478 | val takeFlags = | ||
| 479 | Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION | ||
| 480 | contentResolver.takePersistableUriPermission( | ||
| 481 | result, | ||
| 482 | takeFlags | ||
| 483 | ) | ||
| 484 | |||
| 485 | val progressBinding = DialogProgressBarBinding.inflate(layoutInflater) | ||
| 486 | progressBinding.progressBar.isIndeterminate = true | ||
| 487 | val installationDialog = MaterialAlertDialogBuilder(this) | ||
| 488 | .setTitle(R.string.installing_driver) | ||
| 489 | .setView(progressBinding.root) | ||
| 490 | .show() | ||
| 491 | |||
| 492 | lifecycleScope.launch { | ||
| 493 | withContext(Dispatchers.IO) { | ||
| 494 | // Ignore file exceptions when a user selects an invalid zip | ||
| 495 | try { | ||
| 496 | GpuDriverHelper.installCustomDriver(applicationContext, result) | ||
| 497 | } catch (_: IOException) { | ||
| 498 | } | ||
| 499 | |||
| 500 | withContext(Dispatchers.Main) { | ||
| 501 | installationDialog.dismiss() | ||
| 502 | |||
| 503 | val driverName = GpuDriverHelper.customDriverName | ||
| 504 | if (driverName != null) { | ||
| 505 | Toast.makeText( | ||
| 506 | applicationContext, | ||
| 507 | getString( | ||
| 508 | R.string.select_gpu_driver_install_success, | ||
| 509 | driverName | ||
| 510 | ), | ||
| 511 | Toast.LENGTH_SHORT | ||
| 512 | ).show() | ||
| 513 | } else { | ||
| 514 | Toast.makeText( | ||
| 515 | applicationContext, | ||
| 516 | R.string.select_gpu_driver_error, | ||
| 517 | Toast.LENGTH_LONG | ||
| 518 | ).show() | ||
| 519 | } | ||
| 520 | } | ||
| 521 | } | ||
| 522 | } | ||
| 523 | } | ||
| 524 | |||
| 525 | val installGameUpdate = registerForActivityResult( | 467 | val installGameUpdate = registerForActivityResult( |
| 526 | ActivityResultContracts.OpenMultipleDocuments() | 468 | ActivityResultContracts.OpenMultipleDocuments() |
| 527 | ) { documents: List<Uri> -> | 469 | ) { documents: List<Uri> -> |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DocumentsTree.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DocumentsTree.kt index cf226ad94..eafcf9e42 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DocumentsTree.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/DocumentsTree.kt | |||
| @@ -7,7 +7,6 @@ import android.net.Uri | |||
| 7 | import androidx.documentfile.provider.DocumentFile | 7 | import androidx.documentfile.provider.DocumentFile |
| 8 | import java.io.File | 8 | import java.io.File |
| 9 | import java.util.* | 9 | import java.util.* |
| 10 | import org.yuzu.yuzu_emu.YuzuApplication | ||
| 11 | import org.yuzu.yuzu_emu.model.MinimalDocumentFile | 10 | import org.yuzu.yuzu_emu.model.MinimalDocumentFile |
| 12 | 11 | ||
| 13 | class DocumentsTree { | 12 | class DocumentsTree { |
| @@ -22,7 +21,7 @@ class DocumentsTree { | |||
| 22 | 21 | ||
| 23 | fun openContentUri(filepath: String, openMode: String?): Int { | 22 | fun openContentUri(filepath: String, openMode: String?): Int { |
| 24 | val node = resolvePath(filepath) ?: return -1 | 23 | val node = resolvePath(filepath) ?: return -1 |
| 25 | return FileUtil.openContentUri(YuzuApplication.appContext, node.uri.toString(), openMode) | 24 | return FileUtil.openContentUri(node.uri.toString(), openMode) |
| 26 | } | 25 | } |
| 27 | 26 | ||
| 28 | fun getFileSize(filepath: String): Long { | 27 | fun getFileSize(filepath: String): Long { |
| @@ -30,7 +29,7 @@ class DocumentsTree { | |||
| 30 | return if (node == null || node.isDirectory) { | 29 | return if (node == null || node.isDirectory) { |
| 31 | 0 | 30 | 0 |
| 32 | } else { | 31 | } else { |
| 33 | FileUtil.getFileSize(YuzuApplication.appContext, node.uri.toString()) | 32 | FileUtil.getFileSize(node.uri.toString()) |
| 34 | } | 33 | } |
| 35 | } | 34 | } |
| 36 | 35 | ||
| @@ -67,7 +66,7 @@ class DocumentsTree { | |||
| 67 | * @param parent parent node of this level | 66 | * @param parent parent node of this level |
| 68 | */ | 67 | */ |
| 69 | private fun structTree(parent: DocumentsNode) { | 68 | private fun structTree(parent: DocumentsNode) { |
| 70 | val documents = FileUtil.listFiles(YuzuApplication.appContext, parent.uri!!) | 69 | val documents = FileUtil.listFiles(parent.uri!!) |
| 71 | for (document in documents) { | 70 | for (document in documents) { |
| 72 | val node = DocumentsNode(document) | 71 | val node = DocumentsNode(document) |
| 73 | node.parent = parent | 72 | node.parent = parent |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt index c3f53f1c5..5ee74a52c 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/FileUtil.kt | |||
| @@ -3,7 +3,6 @@ | |||
| 3 | 3 | ||
| 4 | package org.yuzu.yuzu_emu.utils | 4 | package org.yuzu.yuzu_emu.utils |
| 5 | 5 | ||
| 6 | import android.content.Context | ||
| 7 | import android.database.Cursor | 6 | import android.database.Cursor |
| 8 | import android.net.Uri | 7 | import android.net.Uri |
| 9 | import android.provider.DocumentsContract | 8 | import android.provider.DocumentsContract |
| @@ -11,7 +10,6 @@ import androidx.documentfile.provider.DocumentFile | |||
| 11 | import kotlinx.coroutines.flow.StateFlow | 10 | import kotlinx.coroutines.flow.StateFlow |
| 12 | import java.io.BufferedInputStream | 11 | import java.io.BufferedInputStream |
| 13 | import java.io.File | 12 | import java.io.File |
| 14 | import java.io.FileOutputStream | ||
| 15 | import java.io.IOException | 13 | import java.io.IOException |
| 16 | import java.io.InputStream | 14 | import java.io.InputStream |
| 17 | import java.net.URLDecoder | 15 | import java.net.URLDecoder |
| @@ -21,6 +19,8 @@ import org.yuzu.yuzu_emu.YuzuApplication | |||
| 21 | import org.yuzu.yuzu_emu.model.MinimalDocumentFile | 19 | import org.yuzu.yuzu_emu.model.MinimalDocumentFile |
| 22 | import org.yuzu.yuzu_emu.model.TaskState | 20 | import org.yuzu.yuzu_emu.model.TaskState |
| 23 | import java.io.BufferedOutputStream | 21 | import java.io.BufferedOutputStream |
| 22 | import java.lang.NullPointerException | ||
| 23 | import java.nio.charset.StandardCharsets | ||
| 24 | import java.util.zip.ZipOutputStream | 24 | import java.util.zip.ZipOutputStream |
| 25 | 25 | ||
| 26 | object FileUtil { | 26 | object FileUtil { |
| @@ -29,6 +29,8 @@ object FileUtil { | |||
| 29 | const val APPLICATION_OCTET_STREAM = "application/octet-stream" | 29 | const val APPLICATION_OCTET_STREAM = "application/octet-stream" |
| 30 | const val TEXT_PLAIN = "text/plain" | 30 | const val TEXT_PLAIN = "text/plain" |
| 31 | 31 | ||
| 32 | private val context get() = YuzuApplication.appContext | ||
| 33 | |||
| 32 | /** | 34 | /** |
| 33 | * Create a file from directory with filename. | 35 | * Create a file from directory with filename. |
| 34 | * @param context Application context | 36 | * @param context Application context |
| @@ -36,11 +38,11 @@ object FileUtil { | |||
| 36 | * @param filename file display name. | 38 | * @param filename file display name. |
| 37 | * @return boolean | 39 | * @return boolean |
| 38 | */ | 40 | */ |
| 39 | fun createFile(context: Context?, directory: String?, filename: String): DocumentFile? { | 41 | fun createFile(directory: String?, filename: String): DocumentFile? { |
| 40 | var decodedFilename = filename | 42 | var decodedFilename = filename |
| 41 | try { | 43 | try { |
| 42 | val directoryUri = Uri.parse(directory) | 44 | val directoryUri = Uri.parse(directory) |
| 43 | val parent = DocumentFile.fromTreeUri(context!!, directoryUri) ?: return null | 45 | val parent = DocumentFile.fromTreeUri(context, directoryUri) ?: return null |
| 44 | decodedFilename = URLDecoder.decode(decodedFilename, DECODE_METHOD) | 46 | decodedFilename = URLDecoder.decode(decodedFilename, DECODE_METHOD) |
| 45 | var mimeType = APPLICATION_OCTET_STREAM | 47 | var mimeType = APPLICATION_OCTET_STREAM |
| 46 | if (decodedFilename.endsWith(".txt")) { | 48 | if (decodedFilename.endsWith(".txt")) { |
| @@ -56,16 +58,15 @@ object FileUtil { | |||
| 56 | 58 | ||
| 57 | /** | 59 | /** |
| 58 | * Create a directory from directory with filename. | 60 | * Create a directory from directory with filename. |
| 59 | * @param context Application context | ||
| 60 | * @param directory parent path for directory. | 61 | * @param directory parent path for directory. |
| 61 | * @param directoryName directory display name. | 62 | * @param directoryName directory display name. |
| 62 | * @return boolean | 63 | * @return boolean |
| 63 | */ | 64 | */ |
| 64 | fun createDir(context: Context?, directory: String?, directoryName: String?): DocumentFile? { | 65 | fun createDir(directory: String?, directoryName: String?): DocumentFile? { |
| 65 | var decodedDirectoryName = directoryName | 66 | var decodedDirectoryName = directoryName |
| 66 | try { | 67 | try { |
| 67 | val directoryUri = Uri.parse(directory) | 68 | val directoryUri = Uri.parse(directory) |
| 68 | val parent = DocumentFile.fromTreeUri(context!!, directoryUri) ?: return null | 69 | val parent = DocumentFile.fromTreeUri(context, directoryUri) ?: return null |
| 69 | decodedDirectoryName = URLDecoder.decode(decodedDirectoryName, DECODE_METHOD) | 70 | decodedDirectoryName = URLDecoder.decode(decodedDirectoryName, DECODE_METHOD) |
| 70 | val isExist = parent.findFile(decodedDirectoryName) | 71 | val isExist = parent.findFile(decodedDirectoryName) |
| 71 | return isExist ?: parent.createDirectory(decodedDirectoryName) | 72 | return isExist ?: parent.createDirectory(decodedDirectoryName) |
| @@ -77,13 +78,12 @@ object FileUtil { | |||
| 77 | 78 | ||
| 78 | /** | 79 | /** |
| 79 | * Open content uri and return file descriptor to JNI. | 80 | * Open content uri and return file descriptor to JNI. |
| 80 | * @param context Application context | ||
| 81 | * @param path Native content uri path | 81 | * @param path Native content uri path |
| 82 | * @param openMode will be one of "r", "r", "rw", "wa", "rwa" | 82 | * @param openMode will be one of "r", "r", "rw", "wa", "rwa" |
| 83 | * @return file descriptor | 83 | * @return file descriptor |
| 84 | */ | 84 | */ |
| 85 | @JvmStatic | 85 | @JvmStatic |
| 86 | fun openContentUri(context: Context, path: String, openMode: String?): Int { | 86 | fun openContentUri(path: String, openMode: String?): Int { |
| 87 | try { | 87 | try { |
| 88 | val uri = Uri.parse(path) | 88 | val uri = Uri.parse(path) |
| 89 | val parcelFileDescriptor = context.contentResolver.openFileDescriptor(uri, openMode!!) | 89 | val parcelFileDescriptor = context.contentResolver.openFileDescriptor(uri, openMode!!) |
| @@ -103,11 +103,10 @@ object FileUtil { | |||
| 103 | /** | 103 | /** |
| 104 | * Reference: https://stackoverflow.com/questions/42186820/documentfile-is-very-slow | 104 | * Reference: https://stackoverflow.com/questions/42186820/documentfile-is-very-slow |
| 105 | * This function will be faster than DoucmentFile.listFiles | 105 | * This function will be faster than DoucmentFile.listFiles |
| 106 | * @param context Application context | ||
| 107 | * @param uri Directory uri. | 106 | * @param uri Directory uri. |
| 108 | * @return CheapDocument lists. | 107 | * @return CheapDocument lists. |
| 109 | */ | 108 | */ |
| 110 | fun listFiles(context: Context, uri: Uri): Array<MinimalDocumentFile> { | 109 | fun listFiles(uri: Uri): Array<MinimalDocumentFile> { |
| 111 | val resolver = context.contentResolver | 110 | val resolver = context.contentResolver |
| 112 | val columns = arrayOf( | 111 | val columns = arrayOf( |
| 113 | DocumentsContract.Document.COLUMN_DOCUMENT_ID, | 112 | DocumentsContract.Document.COLUMN_DOCUMENT_ID, |
| @@ -145,7 +144,7 @@ object FileUtil { | |||
| 145 | * @param path Native content uri path | 144 | * @param path Native content uri path |
| 146 | * @return bool | 145 | * @return bool |
| 147 | */ | 146 | */ |
| 148 | fun exists(context: Context, path: String?): Boolean { | 147 | fun exists(path: String?): Boolean { |
| 149 | var c: Cursor? = null | 148 | var c: Cursor? = null |
| 150 | try { | 149 | try { |
| 151 | val mUri = Uri.parse(path) | 150 | val mUri = Uri.parse(path) |
| @@ -165,7 +164,7 @@ object FileUtil { | |||
| 165 | * @param path content uri path | 164 | * @param path content uri path |
| 166 | * @return bool | 165 | * @return bool |
| 167 | */ | 166 | */ |
| 168 | fun isDirectory(context: Context, path: String): Boolean { | 167 | fun isDirectory(path: String): Boolean { |
| 169 | val resolver = context.contentResolver | 168 | val resolver = context.contentResolver |
| 170 | val columns = arrayOf( | 169 | val columns = arrayOf( |
| 171 | DocumentsContract.Document.COLUMN_MIME_TYPE | 170 | DocumentsContract.Document.COLUMN_MIME_TYPE |
| @@ -210,10 +209,10 @@ object FileUtil { | |||
| 210 | return filename | 209 | return filename |
| 211 | } | 210 | } |
| 212 | 211 | ||
| 213 | fun getFilesName(context: Context, path: String): Array<String> { | 212 | fun getFilesName(path: String): Array<String> { |
| 214 | val uri = Uri.parse(path) | 213 | val uri = Uri.parse(path) |
| 215 | val files: MutableList<String> = ArrayList() | 214 | val files: MutableList<String> = ArrayList() |
| 216 | for (file in listFiles(context, uri)) { | 215 | for (file in listFiles(uri)) { |
| 217 | files.add(file.filename) | 216 | files.add(file.filename) |
| 218 | } | 217 | } |
| 219 | return files.toTypedArray() | 218 | return files.toTypedArray() |
| @@ -225,7 +224,7 @@ object FileUtil { | |||
| 225 | * @return long file size | 224 | * @return long file size |
| 226 | */ | 225 | */ |
| 227 | @JvmStatic | 226 | @JvmStatic |
| 228 | fun getFileSize(context: Context, path: String): Long { | 227 | fun getFileSize(path: String): Long { |
| 229 | val resolver = context.contentResolver | 228 | val resolver = context.contentResolver |
| 230 | val columns = arrayOf( | 229 | val columns = arrayOf( |
| 231 | DocumentsContract.Document.COLUMN_SIZE | 230 | DocumentsContract.Document.COLUMN_SIZE |
| @@ -245,44 +244,38 @@ object FileUtil { | |||
| 245 | return size | 244 | return size |
| 246 | } | 245 | } |
| 247 | 246 | ||
| 247 | /** | ||
| 248 | * Creates an input stream with a given [Uri] and copies its data to the given path. This will | ||
| 249 | * overwrite any pre-existing files. | ||
| 250 | * | ||
| 251 | * @param sourceUri The [Uri] to copy data from | ||
| 252 | * @param destinationParentPath Destination directory | ||
| 253 | * @param destinationFilename Optionally renames the file once copied | ||
| 254 | */ | ||
| 248 | fun copyUriToInternalStorage( | 255 | fun copyUriToInternalStorage( |
| 249 | context: Context, | 256 | sourceUri: Uri, |
| 250 | sourceUri: Uri?, | ||
| 251 | destinationParentPath: String, | 257 | destinationParentPath: String, |
| 252 | destinationFilename: String | 258 | destinationFilename: String = "" |
| 253 | ): Boolean { | 259 | ): File? = |
| 254 | var input: InputStream? = null | ||
| 255 | var output: FileOutputStream? = null | ||
| 256 | try { | 260 | try { |
| 257 | input = context.contentResolver.openInputStream(sourceUri!!) | 261 | val fileName = |
| 258 | output = FileOutputStream("$destinationParentPath/$destinationFilename") | 262 | if (destinationFilename == "") getFilename(sourceUri) else "/$destinationFilename" |
| 259 | val buffer = ByteArray(1024) | 263 | val inputStream = context.contentResolver.openInputStream(sourceUri)!! |
| 260 | var len: Int | 264 | |
| 261 | while (input!!.read(buffer).also { len = it } != -1) { | 265 | val destinationFile = File("$destinationParentPath$fileName") |
| 262 | output.write(buffer, 0, len) | 266 | if (destinationFile.exists()) { |
| 263 | } | 267 | destinationFile.delete() |
| 264 | output.flush() | ||
| 265 | return true | ||
| 266 | } catch (e: Exception) { | ||
| 267 | Log.error("[FileUtil]: Cannot copy file, error: " + e.message) | ||
| 268 | } finally { | ||
| 269 | if (input != null) { | ||
| 270 | try { | ||
| 271 | input.close() | ||
| 272 | } catch (e: IOException) { | ||
| 273 | Log.error("[FileUtil]: Cannot close input file, error: " + e.message) | ||
| 274 | } | ||
| 275 | } | 268 | } |
| 276 | if (output != null) { | 269 | |
| 277 | try { | 270 | destinationFile.outputStream().use { fos -> |
| 278 | output.close() | 271 | inputStream.use { it.copyTo(fos) } |
| 279 | } catch (e: IOException) { | ||
| 280 | Log.error("[FileUtil]: Cannot close output file, error: " + e.message) | ||
| 281 | } | ||
| 282 | } | 272 | } |
| 273 | destinationFile | ||
| 274 | } catch (e: IOException) { | ||
| 275 | null | ||
| 276 | } catch (e: NullPointerException) { | ||
| 277 | null | ||
| 283 | } | 278 | } |
| 284 | return false | ||
| 285 | } | ||
| 286 | 279 | ||
| 287 | /** | 280 | /** |
| 288 | * Extracts the given zip file into the given directory. | 281 | * Extracts the given zip file into the given directory. |
| @@ -368,4 +361,12 @@ object FileUtil { | |||
| 368 | return fileName.substring(fileName.lastIndexOf(".") + 1) | 361 | return fileName.substring(fileName.lastIndexOf(".") + 1) |
| 369 | .lowercase() | 362 | .lowercase() |
| 370 | } | 363 | } |
| 364 | |||
| 365 | @Throws(IOException::class) | ||
| 366 | fun getStringFromFile(file: File): String = | ||
| 367 | String(file.readBytes(), StandardCharsets.UTF_8) | ||
| 368 | |||
| 369 | @Throws(IOException::class) | ||
| 370 | fun getStringFromInputStream(stream: InputStream): String = | ||
| 371 | String(stream.readBytes(), StandardCharsets.UTF_8) | ||
| 371 | } | 372 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt index e0ee29c9b..9001ca9ab 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameHelper.kt | |||
| @@ -30,7 +30,7 @@ object GameHelper { | |||
| 30 | // Ensure keys are loaded so that ROM metadata can be decrypted. | 30 | // Ensure keys are loaded so that ROM metadata can be decrypted. |
| 31 | NativeLibrary.reloadKeys() | 31 | NativeLibrary.reloadKeys() |
| 32 | 32 | ||
| 33 | addGamesRecursive(games, FileUtil.listFiles(context, gamesUri), 3) | 33 | addGamesRecursive(games, FileUtil.listFiles(gamesUri), 3) |
| 34 | 34 | ||
| 35 | // Cache list of games found on disk | 35 | // Cache list of games found on disk |
| 36 | val serializedGames = mutableSetOf<String>() | 36 | val serializedGames = mutableSetOf<String>() |
| @@ -58,7 +58,7 @@ object GameHelper { | |||
| 58 | if (it.isDirectory) { | 58 | if (it.isDirectory) { |
| 59 | addGamesRecursive( | 59 | addGamesRecursive( |
| 60 | games, | 60 | games, |
| 61 | FileUtil.listFiles(YuzuApplication.appContext, it.uri), | 61 | FileUtil.listFiles(it.uri), |
| 62 | depth - 1 | 62 | depth - 1 |
| 63 | ) | 63 | ) |
| 64 | } else { | 64 | } else { |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt index 1d4695a2a..f6882ce6c 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverHelper.kt | |||
| @@ -3,64 +3,33 @@ | |||
| 3 | 3 | ||
| 4 | package org.yuzu.yuzu_emu.utils | 4 | package org.yuzu.yuzu_emu.utils |
| 5 | 5 | ||
| 6 | import android.content.Context | ||
| 7 | import android.net.Uri | 6 | import android.net.Uri |
| 7 | import android.os.Build | ||
| 8 | import java.io.BufferedInputStream | 8 | import java.io.BufferedInputStream |
| 9 | import java.io.File | 9 | import java.io.File |
| 10 | import java.io.FileInputStream | ||
| 11 | import java.io.FileOutputStream | ||
| 12 | import java.io.IOException | 10 | import java.io.IOException |
| 13 | import java.util.zip.ZipInputStream | ||
| 14 | import org.yuzu.yuzu_emu.NativeLibrary | 11 | import org.yuzu.yuzu_emu.NativeLibrary |
| 15 | import org.yuzu.yuzu_emu.utils.FileUtil.copyUriToInternalStorage | 12 | import org.yuzu.yuzu_emu.YuzuApplication |
| 13 | import java.util.zip.ZipException | ||
| 14 | import java.util.zip.ZipFile | ||
| 16 | 15 | ||
| 17 | object GpuDriverHelper { | 16 | object GpuDriverHelper { |
| 18 | private const val META_JSON_FILENAME = "meta.json" | 17 | private const val META_JSON_FILENAME = "meta.json" |
| 19 | private const val DRIVER_INTERNAL_FILENAME = "gpu_driver.zip" | ||
| 20 | private var fileRedirectionPath: String? = null | 18 | private var fileRedirectionPath: String? = null |
| 21 | private var driverInstallationPath: String? = null | 19 | var driverInstallationPath: String? = null |
| 22 | private var hookLibPath: String? = null | 20 | private var hookLibPath: String? = null |
| 23 | 21 | ||
| 24 | @Throws(IOException::class) | 22 | val driverStoragePath get() = DirectoryInitialization.userDirectory!! + "/gpu_drivers/" |
| 25 | private fun unzip(zipFilePath: String, destDir: String) { | ||
| 26 | val dir = File(destDir) | ||
| 27 | |||
| 28 | // Create output directory if it doesn't exist | ||
| 29 | if (!dir.exists()) dir.mkdirs() | ||
| 30 | |||
| 31 | // Unpack the files. | ||
| 32 | val inputStream = FileInputStream(zipFilePath) | ||
| 33 | val zis = ZipInputStream(BufferedInputStream(inputStream)) | ||
| 34 | val buffer = ByteArray(1024) | ||
| 35 | var ze = zis.nextEntry | ||
| 36 | while (ze != null) { | ||
| 37 | val newFile = File(destDir, ze.name) | ||
| 38 | val canonicalPath = newFile.canonicalPath | ||
| 39 | if (!canonicalPath.startsWith(destDir + ze.name)) { | ||
| 40 | throw SecurityException("Zip file attempted path traversal! " + ze.name) | ||
| 41 | } | ||
| 42 | |||
| 43 | newFile.parentFile!!.mkdirs() | ||
| 44 | val fos = FileOutputStream(newFile) | ||
| 45 | var len: Int | ||
| 46 | while (zis.read(buffer).also { len = it } > 0) { | ||
| 47 | fos.write(buffer, 0, len) | ||
| 48 | } | ||
| 49 | fos.close() | ||
| 50 | zis.closeEntry() | ||
| 51 | ze = zis.nextEntry | ||
| 52 | } | ||
| 53 | zis.closeEntry() | ||
| 54 | } | ||
| 55 | 23 | ||
| 56 | fun initializeDriverParameters(context: Context) { | 24 | fun initializeDriverParameters() { |
| 57 | try { | 25 | try { |
| 58 | // Initialize the file redirection directory. | 26 | // Initialize the file redirection directory. |
| 59 | fileRedirectionPath = | 27 | fileRedirectionPath = YuzuApplication.appContext |
| 60 | context.getExternalFilesDir(null)!!.canonicalPath + "/gpu/vk_file_redirect/" | 28 | .getExternalFilesDir(null)!!.canonicalPath + "/gpu/vk_file_redirect/" |
| 61 | 29 | ||
| 62 | // Initialize the driver installation directory. | 30 | // Initialize the driver installation directory. |
| 63 | driverInstallationPath = context.filesDir.canonicalPath + "/gpu_driver/" | 31 | driverInstallationPath = YuzuApplication.appContext |
| 32 | .filesDir.canonicalPath + "/gpu_driver/" | ||
| 64 | } catch (e: IOException) { | 33 | } catch (e: IOException) { |
| 65 | throw RuntimeException(e) | 34 | throw RuntimeException(e) |
| 66 | } | 35 | } |
| @@ -69,68 +38,169 @@ object GpuDriverHelper { | |||
| 69 | initializeDirectories() | 38 | initializeDirectories() |
| 70 | 39 | ||
| 71 | // Initialize hook libraries directory. | 40 | // Initialize hook libraries directory. |
| 72 | hookLibPath = context.applicationInfo.nativeLibraryDir + "/" | 41 | hookLibPath = YuzuApplication.appContext.applicationInfo.nativeLibraryDir + "/" |
| 73 | 42 | ||
| 74 | // Initialize GPU driver. | 43 | // Initialize GPU driver. |
| 75 | NativeLibrary.initializeGpuDriver( | 44 | NativeLibrary.initializeGpuDriver( |
| 76 | hookLibPath, | 45 | hookLibPath, |
| 77 | driverInstallationPath, | 46 | driverInstallationPath, |
| 78 | customDriverLibraryName, | 47 | customDriverData.libraryName, |
| 79 | fileRedirectionPath | 48 | fileRedirectionPath |
| 80 | ) | 49 | ) |
| 81 | } | 50 | } |
| 82 | 51 | ||
| 83 | fun installDefaultDriver(context: Context) { | 52 | fun getDrivers(): MutableList<Pair<String, GpuDriverMetadata>> { |
| 53 | val driverZips = File(driverStoragePath).listFiles() | ||
| 54 | val drivers: MutableList<Pair<String, GpuDriverMetadata>> = | ||
| 55 | driverZips | ||
| 56 | ?.mapNotNull { | ||
| 57 | val metadata = getMetadataFromZip(it) | ||
| 58 | metadata.name?.let { _ -> Pair(it.path, metadata) } | ||
| 59 | } | ||
| 60 | ?.sortedByDescending { it: Pair<String, GpuDriverMetadata> -> it.second.name } | ||
| 61 | ?.distinct() | ||
| 62 | ?.toMutableList() ?: mutableListOf() | ||
| 63 | |||
| 64 | // TODO: Get system driver information | ||
| 65 | drivers.add(0, Pair("", GpuDriverMetadata())) | ||
| 66 | return drivers | ||
| 67 | } | ||
| 68 | |||
| 69 | fun installDefaultDriver() { | ||
| 84 | // Removing the installed driver will result in the backend using the default system driver. | 70 | // Removing the installed driver will result in the backend using the default system driver. |
| 85 | val driverInstallationDir = File(driverInstallationPath!!) | 71 | File(driverInstallationPath!!).deleteRecursively() |
| 86 | deleteRecursive(driverInstallationDir) | 72 | initializeDriverParameters() |
| 87 | initializeDriverParameters(context) | 73 | } |
| 74 | |||
| 75 | fun copyDriverToInternalStorage(driverUri: Uri): Boolean { | ||
| 76 | // Ensure we have directories. | ||
| 77 | initializeDirectories() | ||
| 78 | |||
| 79 | // Copy the zip file URI to user data | ||
| 80 | val copiedFile = | ||
| 81 | FileUtil.copyUriToInternalStorage(driverUri, driverStoragePath) ?: return false | ||
| 82 | |||
| 83 | // Validate driver | ||
| 84 | val metadata = getMetadataFromZip(copiedFile) | ||
| 85 | if (metadata.name == null) { | ||
| 86 | copiedFile.delete() | ||
| 87 | return false | ||
| 88 | } | ||
| 89 | |||
| 90 | if (metadata.minApi > Build.VERSION.SDK_INT) { | ||
| 91 | copiedFile.delete() | ||
| 92 | return false | ||
| 93 | } | ||
| 94 | return true | ||
| 88 | } | 95 | } |
| 89 | 96 | ||
| 90 | fun installCustomDriver(context: Context, driverPathUri: Uri?) { | 97 | /** |
| 98 | * Copies driver zip into user data directory so that it can be exported along with | ||
| 99 | * other user data and also unzipped into the installation directory | ||
| 100 | */ | ||
| 101 | fun installCustomDriver(driverUri: Uri): Boolean { | ||
| 91 | // Revert to system default in the event the specified driver is bad. | 102 | // Revert to system default in the event the specified driver is bad. |
| 92 | installDefaultDriver(context) | 103 | installDefaultDriver() |
| 93 | 104 | ||
| 94 | // Ensure we have directories. | 105 | // Ensure we have directories. |
| 95 | initializeDirectories() | 106 | initializeDirectories() |
| 96 | 107 | ||
| 97 | // Copy the zip file URI into our private storage. | 108 | // Copy the zip file URI to user data |
| 98 | copyUriToInternalStorage( | 109 | val copiedFile = |
| 99 | context, | 110 | FileUtil.copyUriToInternalStorage(driverUri, driverStoragePath) ?: return false |
| 100 | driverPathUri, | 111 | |
| 101 | driverInstallationPath!!, | 112 | // Validate driver |
| 102 | DRIVER_INTERNAL_FILENAME | 113 | val metadata = getMetadataFromZip(copiedFile) |
| 103 | ) | 114 | if (metadata.name == null) { |
| 115 | copiedFile.delete() | ||
| 116 | return false | ||
| 117 | } | ||
| 118 | |||
| 119 | if (metadata.minApi > Build.VERSION.SDK_INT) { | ||
| 120 | copiedFile.delete() | ||
| 121 | return false | ||
| 122 | } | ||
| 104 | 123 | ||
| 105 | // Unzip the driver. | 124 | // Unzip the driver. |
| 106 | try { | 125 | try { |
| 107 | unzip(driverInstallationPath + DRIVER_INTERNAL_FILENAME, driverInstallationPath!!) | 126 | FileUtil.unzipToInternalStorage( |
| 127 | BufferedInputStream(copiedFile.inputStream()), | ||
| 128 | File(driverInstallationPath!!) | ||
| 129 | ) | ||
| 108 | } catch (e: SecurityException) { | 130 | } catch (e: SecurityException) { |
| 109 | return | 131 | return false |
| 110 | } | 132 | } |
| 111 | 133 | ||
| 112 | // Initialize the driver parameters. | 134 | // Initialize the driver parameters. |
| 113 | initializeDriverParameters(context) | 135 | initializeDriverParameters() |
| 136 | |||
| 137 | return true | ||
| 114 | } | 138 | } |
| 115 | 139 | ||
| 116 | external fun supportsCustomDriverLoading(): Boolean | 140 | /** |
| 141 | * Unzips driver into installation directory | ||
| 142 | */ | ||
| 143 | fun installCustomDriver(driver: File): Boolean { | ||
| 144 | // Revert to system default in the event the specified driver is bad. | ||
| 145 | installDefaultDriver() | ||
| 117 | 146 | ||
| 118 | // Parse the custom driver metadata to retrieve the name. | 147 | // Ensure we have directories. |
| 119 | val customDriverName: String? | 148 | initializeDirectories() |
| 120 | get() { | 149 | |
| 121 | val metadata = GpuDriverMetadata(driverInstallationPath + META_JSON_FILENAME) | 150 | // Validate driver |
| 122 | return metadata.name | 151 | val metadata = getMetadataFromZip(driver) |
| 152 | if (metadata.name == null) { | ||
| 153 | driver.delete() | ||
| 154 | return false | ||
| 123 | } | 155 | } |
| 124 | 156 | ||
| 125 | // Parse the custom driver metadata to retrieve the library name. | 157 | // Unzip the driver to the private installation directory |
| 126 | private val customDriverLibraryName: String? | 158 | try { |
| 127 | get() { | 159 | FileUtil.unzipToInternalStorage( |
| 128 | // Parse the custom driver metadata to retrieve the library name. | 160 | BufferedInputStream(driver.inputStream()), |
| 129 | val metadata = GpuDriverMetadata(driverInstallationPath + META_JSON_FILENAME) | 161 | File(driverInstallationPath!!) |
| 130 | return metadata.libraryName | 162 | ) |
| 163 | } catch (e: SecurityException) { | ||
| 164 | return false | ||
| 131 | } | 165 | } |
| 132 | 166 | ||
| 133 | private fun initializeDirectories() { | 167 | // Initialize the driver parameters. |
| 168 | initializeDriverParameters() | ||
| 169 | |||
| 170 | return true | ||
| 171 | } | ||
| 172 | |||
| 173 | /** | ||
| 174 | * Takes in a zip file and reads the meta.json file for presentation to the UI | ||
| 175 | * | ||
| 176 | * @param driver Zip containing driver and meta.json file | ||
| 177 | * @return A non-null [GpuDriverMetadata] instance that may have null members | ||
| 178 | */ | ||
| 179 | fun getMetadataFromZip(driver: File): GpuDriverMetadata { | ||
| 180 | try { | ||
| 181 | ZipFile(driver).use { zf -> | ||
| 182 | val entries = zf.entries() | ||
| 183 | while (entries.hasMoreElements()) { | ||
| 184 | val entry = entries.nextElement() | ||
| 185 | if (!entry.isDirectory && entry.name.lowercase().contains(".json")) { | ||
| 186 | zf.getInputStream(entry).use { | ||
| 187 | return GpuDriverMetadata(it, entry.size) | ||
| 188 | } | ||
| 189 | } | ||
| 190 | } | ||
| 191 | } | ||
| 192 | } catch (_: ZipException) { | ||
| 193 | } | ||
| 194 | return GpuDriverMetadata() | ||
| 195 | } | ||
| 196 | |||
| 197 | external fun supportsCustomDriverLoading(): Boolean | ||
| 198 | |||
| 199 | // Parse the custom driver metadata to retrieve the name. | ||
| 200 | val customDriverData: GpuDriverMetadata | ||
| 201 | get() = GpuDriverMetadata(File(driverInstallationPath + META_JSON_FILENAME)) | ||
| 202 | |||
| 203 | fun initializeDirectories() { | ||
| 134 | // Ensure the file redirection directory exists. | 204 | // Ensure the file redirection directory exists. |
| 135 | val fileRedirectionDir = File(fileRedirectionPath!!) | 205 | val fileRedirectionDir = File(fileRedirectionPath!!) |
| 136 | if (!fileRedirectionDir.exists()) { | 206 | if (!fileRedirectionDir.exists()) { |
| @@ -141,14 +211,10 @@ object GpuDriverHelper { | |||
| 141 | if (!driverInstallationDir.exists()) { | 211 | if (!driverInstallationDir.exists()) { |
| 142 | driverInstallationDir.mkdirs() | 212 | driverInstallationDir.mkdirs() |
| 143 | } | 213 | } |
| 144 | } | 214 | // Ensure the driver storage directory exists |
| 145 | 215 | val driverStorageDirectory = File(driverStoragePath) | |
| 146 | private fun deleteRecursive(fileOrDirectory: File) { | 216 | if (!driverStorageDirectory.exists()) { |
| 147 | if (fileOrDirectory.isDirectory) { | 217 | driverStorageDirectory.mkdirs() |
| 148 | for (child in fileOrDirectory.listFiles()!!) { | ||
| 149 | deleteRecursive(child) | ||
| 150 | } | ||
| 151 | } | 218 | } |
| 152 | fileOrDirectory.delete() | ||
| 153 | } | 219 | } |
| 154 | } | 220 | } |
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverMetadata.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverMetadata.kt index a4e64070a..511a4171a 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverMetadata.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GpuDriverMetadata.kt | |||
| @@ -4,29 +4,29 @@ | |||
| 4 | package org.yuzu.yuzu_emu.utils | 4 | package org.yuzu.yuzu_emu.utils |
| 5 | 5 | ||
| 6 | import java.io.IOException | 6 | import java.io.IOException |
| 7 | import java.nio.charset.StandardCharsets | ||
| 8 | import java.nio.file.Files | ||
| 9 | import java.nio.file.Paths | ||
| 10 | import org.json.JSONException | 7 | import org.json.JSONException |
| 11 | import org.json.JSONObject | 8 | import org.json.JSONObject |
| 9 | import java.io.File | ||
| 10 | import java.io.InputStream | ||
| 12 | 11 | ||
| 13 | class GpuDriverMetadata(metadataFilePath: String) { | 12 | class GpuDriverMetadata { |
| 14 | var name: String? = null | 13 | /** |
| 15 | var description: String? = null | 14 | * Tries to get driver metadata information from a meta.json [File] |
| 16 | var author: String? = null | 15 | * |
| 17 | var vendor: String? = null | 16 | * @param metadataFile meta.json file provided with a GPU driver |
| 18 | var driverVersion: String? = null | 17 | */ |
| 19 | var minApi = 0 | 18 | constructor(metadataFile: File) { |
| 20 | var libraryName: String? = null | 19 | if (metadataFile.length() > MAX_META_SIZE_BYTES) { |
| 20 | return | ||
| 21 | } | ||
| 21 | 22 | ||
| 22 | init { | ||
| 23 | try { | 23 | try { |
| 24 | val json = JSONObject(getStringFromFile(metadataFilePath)) | 24 | val json = JSONObject(FileUtil.getStringFromFile(metadataFile)) |
| 25 | name = json.getString("name") | 25 | name = json.getString("name") |
| 26 | description = json.getString("description") | 26 | description = json.getString("description") |
| 27 | author = json.getString("author") | 27 | author = json.getString("author") |
| 28 | vendor = json.getString("vendor") | 28 | vendor = json.getString("vendor") |
| 29 | driverVersion = json.getString("driverVersion") | 29 | version = json.getString("driverVersion") |
| 30 | minApi = json.getInt("minApi") | 30 | minApi = json.getInt("minApi") |
| 31 | libraryName = json.getString("libraryName") | 31 | libraryName = json.getString("libraryName") |
| 32 | } catch (e: JSONException) { | 32 | } catch (e: JSONException) { |
| @@ -36,12 +36,84 @@ class GpuDriverMetadata(metadataFilePath: String) { | |||
| 36 | } | 36 | } |
| 37 | } | 37 | } |
| 38 | 38 | ||
| 39 | companion object { | 39 | /** |
| 40 | @Throws(IOException::class) | 40 | * Tries to get driver metadata information from an input stream that's intended to be |
| 41 | private fun getStringFromFile(filePath: String): String { | 41 | * from a zip file |
| 42 | val path = Paths.get(filePath) | 42 | * |
| 43 | val bytes = Files.readAllBytes(path) | 43 | * @param metadataStream ZipEntry input stream |
| 44 | return String(bytes, StandardCharsets.UTF_8) | 44 | * @param size Size of the file in bytes |
| 45 | */ | ||
| 46 | constructor(metadataStream: InputStream, size: Long) { | ||
| 47 | if (size > MAX_META_SIZE_BYTES) { | ||
| 48 | return | ||
| 45 | } | 49 | } |
| 50 | |||
| 51 | try { | ||
| 52 | val json = JSONObject(FileUtil.getStringFromInputStream(metadataStream)) | ||
| 53 | name = json.getString("name") | ||
| 54 | description = json.getString("description") | ||
| 55 | author = json.getString("author") | ||
| 56 | vendor = json.getString("vendor") | ||
| 57 | version = json.getString("driverVersion") | ||
| 58 | minApi = json.getInt("minApi") | ||
| 59 | libraryName = json.getString("libraryName") | ||
| 60 | } catch (e: JSONException) { | ||
| 61 | // JSON is malformed, ignore and treat as unsupported metadata. | ||
| 62 | } catch (e: IOException) { | ||
| 63 | // File is inaccessible, ignore and treat as unsupported metadata. | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | /** | ||
| 68 | * Creates an empty metadata instance | ||
| 69 | */ | ||
| 70 | constructor() | ||
| 71 | |||
| 72 | override fun equals(other: Any?): Boolean { | ||
| 73 | if (other !is GpuDriverMetadata) { | ||
| 74 | return false | ||
| 75 | } | ||
| 76 | |||
| 77 | return other.name == name && | ||
| 78 | other.description == description && | ||
| 79 | other.author == author && | ||
| 80 | other.vendor == vendor && | ||
| 81 | other.version == version && | ||
| 82 | other.minApi == minApi && | ||
| 83 | other.libraryName == libraryName | ||
| 84 | } | ||
| 85 | |||
| 86 | override fun hashCode(): Int { | ||
| 87 | var result = name?.hashCode() ?: 0 | ||
| 88 | result = 31 * result + (description?.hashCode() ?: 0) | ||
| 89 | result = 31 * result + (author?.hashCode() ?: 0) | ||
| 90 | result = 31 * result + (vendor?.hashCode() ?: 0) | ||
| 91 | result = 31 * result + (version?.hashCode() ?: 0) | ||
| 92 | result = 31 * result + minApi | ||
| 93 | result = 31 * result + (libraryName?.hashCode() ?: 0) | ||
| 94 | return result | ||
| 95 | } | ||
| 96 | |||
| 97 | override fun toString(): String = | ||
| 98 | """ | ||
| 99 | Name - $name | ||
| 100 | Description - $description | ||
| 101 | Author - $author | ||
| 102 | Vendor - $vendor | ||
| 103 | Version - $version | ||
| 104 | Min API - $minApi | ||
| 105 | Library Name - $libraryName | ||
| 106 | """.trimMargin().trimIndent() | ||
| 107 | |||
| 108 | var name: String? = null | ||
| 109 | var description: String? = null | ||
| 110 | var author: String? = null | ||
| 111 | var vendor: String? = null | ||
| 112 | var version: String? = null | ||
| 113 | var minApi = 0 | ||
| 114 | var libraryName: String? = null | ||
| 115 | |||
| 116 | companion object { | ||
| 117 | private const val MAX_META_SIZE_BYTES = 500000 | ||
| 46 | } | 118 | } |
| 47 | } | 119 | } |
diff --git a/src/android/app/src/main/res/drawable/ic_build.xml b/src/android/app/src/main/res/drawable/ic_build.xml new file mode 100644 index 000000000..91d52f1b8 --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_build.xml | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android" | ||
| 2 | android:width="24dp" | ||
| 3 | android:height="24dp" | ||
| 4 | android:viewportWidth="24" | ||
| 5 | android:viewportHeight="24"> | ||
| 6 | <path | ||
| 7 | android:fillColor="?attr/colorControlNormal" | ||
| 8 | 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" /> | ||
| 9 | </vector> | ||
diff --git a/src/android/app/src/main/res/drawable/ic_delete.xml b/src/android/app/src/main/res/drawable/ic_delete.xml new file mode 100644 index 000000000..d26a79711 --- /dev/null +++ b/src/android/app/src/main/res/drawable/ic_delete.xml | |||
| @@ -0,0 +1,9 @@ | |||
| 1 | <vector xmlns:android="http://schemas.android.com/apk/res/android" | ||
| 2 | android:width="24dp" | ||
| 3 | android:height="24dp" | ||
| 4 | android:viewportWidth="24" | ||
| 5 | android:viewportHeight="24"> | ||
| 6 | <path | ||
| 7 | android:fillColor="?attr/colorControlNormal" | ||
| 8 | 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" /> | ||
| 9 | </vector> | ||
diff --git a/src/android/app/src/main/res/layout/card_driver_option.xml b/src/android/app/src/main/res/layout/card_driver_option.xml new file mode 100644 index 000000000..1dd9a6d7d --- /dev/null +++ b/src/android/app/src/main/res/layout/card_driver_option.xml | |||
| @@ -0,0 +1,89 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | ||
| 2 | <com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android" | ||
| 3 | xmlns:app="http://schemas.android.com/apk/res-auto" | ||
| 4 | xmlns:tools="http://schemas.android.com/tools" | ||
| 5 | style="?attr/materialCardViewOutlinedStyle" | ||
| 6 | android:layout_width="match_parent" | ||
| 7 | android:layout_height="wrap_content" | ||
| 8 | android:layout_marginHorizontal="16dp" | ||
| 9 | android:layout_marginVertical="12dp" | ||
| 10 | android:background="?attr/selectableItemBackground" | ||
| 11 | android:clickable="true" | ||
| 12 | android:focusable="true"> | ||
| 13 | |||
| 14 | <LinearLayout | ||
| 15 | android:layout_width="match_parent" | ||
| 16 | android:layout_height="wrap_content" | ||
| 17 | android:orientation="horizontal" | ||
| 18 | android:layout_gravity="center" | ||
| 19 | android:padding="16dp"> | ||
| 20 | |||
| 21 | <RadioButton | ||
| 22 | android:id="@+id/radio_button" | ||
| 23 | android:layout_width="wrap_content" | ||
| 24 | android:layout_height="wrap_content" | ||
| 25 | android:layout_gravity="center_vertical" | ||
| 26 | android:clickable="false" | ||
| 27 | android:checked="false" /> | ||
| 28 | |||
| 29 | <LinearLayout | ||
| 30 | android:layout_width="0dp" | ||
| 31 | android:layout_height="wrap_content" | ||
| 32 | android:layout_weight="1" | ||
| 33 | android:orientation="vertical" | ||
| 34 | android:layout_gravity="center_vertical"> | ||
| 35 | |||
| 36 | <com.google.android.material.textview.MaterialTextView | ||
| 37 | android:id="@+id/title" | ||
| 38 | style="@style/TextAppearance.Material3.TitleMedium" | ||
| 39 | android:layout_width="match_parent" | ||
| 40 | android:layout_height="wrap_content" | ||
| 41 | android:ellipsize="none" | ||
| 42 | android:marqueeRepeatLimit="marquee_forever" | ||
| 43 | android:requiresFadingEdge="horizontal" | ||
| 44 | android:singleLine="true" | ||
| 45 | android:textAlignment="viewStart" | ||
| 46 | tools:text="@string/select_gpu_driver_default" /> | ||
| 47 | |||
| 48 | <com.google.android.material.textview.MaterialTextView | ||
| 49 | android:id="@+id/version" | ||
| 50 | style="@style/TextAppearance.Material3.BodyMedium" | ||
| 51 | android:layout_width="match_parent" | ||
| 52 | android:layout_height="wrap_content" | ||
| 53 | android:layout_marginTop="6dp" | ||
| 54 | android:ellipsize="none" | ||
| 55 | android:marqueeRepeatLimit="marquee_forever" | ||
| 56 | android:requiresFadingEdge="horizontal" | ||
| 57 | android:singleLine="true" | ||
| 58 | android:textAlignment="viewStart" | ||
| 59 | tools:text="@string/install_gpu_driver_description" /> | ||
| 60 | |||
| 61 | <com.google.android.material.textview.MaterialTextView | ||
| 62 | android:id="@+id/description" | ||
| 63 | style="@style/TextAppearance.Material3.BodyMedium" | ||
| 64 | android:layout_width="match_parent" | ||
| 65 | android:layout_height="wrap_content" | ||
| 66 | android:layout_marginTop="6dp" | ||
| 67 | android:ellipsize="none" | ||
| 68 | android:marqueeRepeatLimit="marquee_forever" | ||
| 69 | android:requiresFadingEdge="horizontal" | ||
| 70 | android:singleLine="true" | ||
| 71 | android:textAlignment="viewStart" | ||
| 72 | tools:text="@string/install_gpu_driver_description" /> | ||
| 73 | |||
| 74 | </LinearLayout> | ||
| 75 | |||
| 76 | <Button | ||
| 77 | android:id="@+id/button_delete" | ||
| 78 | style="@style/Widget.Material3.Button.IconButton" | ||
| 79 | android:layout_width="wrap_content" | ||
| 80 | android:layout_height="wrap_content" | ||
| 81 | android:layout_gravity="center_vertical" | ||
| 82 | android:contentDescription="@string/delete" | ||
| 83 | android:tooltipText="@string/delete" | ||
| 84 | app:icon="@drawable/ic_delete" | ||
| 85 | app:iconTint="?attr/colorControlNormal" /> | ||
| 86 | |||
| 87 | </LinearLayout> | ||
| 88 | |||
| 89 | </com.google.android.material.card.MaterialCardView> | ||
diff --git a/src/android/app/src/main/res/layout/fragment_driver_manager.xml b/src/android/app/src/main/res/layout/fragment_driver_manager.xml new file mode 100644 index 000000000..6cea2d164 --- /dev/null +++ b/src/android/app/src/main/res/layout/fragment_driver_manager.xml | |||
| @@ -0,0 +1,48 @@ | |||
| 1 | <?xml version="1.0" encoding="utf-8"?> | ||
| 2 | <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||
| 3 | xmlns:app="http://schemas.android.com/apk/res-auto" | ||
| 4 | android:id="@+id/coordinator_licenses" | ||
| 5 | android:layout_width="match_parent" | ||
| 6 | android:layout_height="match_parent" | ||
| 7 | android:background="?attr/colorSurface"> | ||
| 8 | |||
| 9 | <androidx.coordinatorlayout.widget.CoordinatorLayout | ||
| 10 | android:layout_width="match_parent" | ||
| 11 | android:layout_height="match_parent"> | ||
| 12 | |||
| 13 | <com.google.android.material.appbar.AppBarLayout | ||
| 14 | android:id="@+id/appbar_drivers" | ||
| 15 | android:layout_width="match_parent" | ||
| 16 | android:layout_height="wrap_content" | ||
| 17 | android:fitsSystemWindows="true" | ||
| 18 | app:liftOnScrollTargetViewId="@id/list_drivers"> | ||
| 19 | |||
| 20 | <com.google.android.material.appbar.MaterialToolbar | ||
| 21 | android:id="@+id/toolbar_drivers" | ||
| 22 | android:layout_width="match_parent" | ||
| 23 | android:layout_height="?attr/actionBarSize" | ||
| 24 | app:navigationIcon="@drawable/ic_back" | ||
| 25 | app:title="@string/gpu_driver_manager" /> | ||
| 26 | |||
| 27 | </com.google.android.material.appbar.AppBarLayout> | ||
| 28 | |||
| 29 | <androidx.recyclerview.widget.RecyclerView | ||
| 30 | android:id="@+id/list_drivers" | ||
| 31 | android:layout_width="match_parent" | ||
| 32 | android:layout_height="match_parent" | ||
| 33 | android:clipToPadding="false" | ||
| 34 | app:layout_behavior="@string/appbar_scrolling_view_behavior" /> | ||
| 35 | |||
| 36 | </androidx.coordinatorlayout.widget.CoordinatorLayout> | ||
| 37 | |||
| 38 | <com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton | ||
| 39 | android:id="@+id/button_install" | ||
| 40 | android:layout_width="wrap_content" | ||
| 41 | android:layout_height="wrap_content" | ||
| 42 | android:layout_gravity="bottom|end" | ||
| 43 | android:text="@string/install" | ||
| 44 | app:icon="@drawable/ic_add" | ||
| 45 | app:layout_constraintBottom_toBottomOf="parent" | ||
| 46 | app:layout_constraintEnd_toEndOf="parent" /> | ||
| 47 | |||
| 48 | </androidx.constraintlayout.widget.ConstraintLayout> | ||
diff --git a/src/android/app/src/main/res/navigation/home_navigation.xml b/src/android/app/src/main/res/navigation/home_navigation.xml index 2356b802b..82749359d 100644 --- a/src/android/app/src/main/res/navigation/home_navigation.xml +++ b/src/android/app/src/main/res/navigation/home_navigation.xml | |||
| @@ -22,6 +22,9 @@ | |||
| 22 | <action | 22 | <action |
| 23 | android:id="@+id/action_homeSettingsFragment_to_installableFragment" | 23 | android:id="@+id/action_homeSettingsFragment_to_installableFragment" |
| 24 | app:destination="@id/installableFragment" /> | 24 | app:destination="@id/installableFragment" /> |
| 25 | <action | ||
| 26 | android:id="@+id/action_homeSettingsFragment_to_driverManagerFragment" | ||
| 27 | app:destination="@id/driverManagerFragment" /> | ||
| 25 | </fragment> | 28 | </fragment> |
| 26 | 29 | ||
| 27 | <fragment | 30 | <fragment |
| @@ -95,5 +98,9 @@ | |||
| 95 | android:id="@+id/installableFragment" | 98 | android:id="@+id/installableFragment" |
| 96 | android:name="org.yuzu.yuzu_emu.fragments.InstallableFragment" | 99 | android:name="org.yuzu.yuzu_emu.fragments.InstallableFragment" |
| 97 | android:label="InstallableFragment" /> | 100 | android:label="InstallableFragment" /> |
| 101 | <fragment | ||
| 102 | android:id="@+id/driverManagerFragment" | ||
| 103 | android:name="org.yuzu.yuzu_emu.fragments.DriverManagerFragment" | ||
| 104 | android:label="DriverManagerFragment" /> | ||
| 98 | 105 | ||
| 99 | </navigation> | 106 | </navigation> |
diff --git a/src/android/app/src/main/res/values-de/strings.xml b/src/android/app/src/main/res/values-de/strings.xml index dd0f36392..72a47fbdb 100644 --- a/src/android/app/src/main/res/values-de/strings.xml +++ b/src/android/app/src/main/res/values-de/strings.xml | |||
| @@ -168,9 +168,7 @@ | |||
| 168 | <string name="select_gpu_driver_title">Möchtest du deinen aktuellen GPU-Treiber ersetzen?</string> | 168 | <string name="select_gpu_driver_title">Möchtest du deinen aktuellen GPU-Treiber ersetzen?</string> |
| 169 | <string name="select_gpu_driver_install">Installieren</string> | 169 | <string name="select_gpu_driver_install">Installieren</string> |
| 170 | <string name="select_gpu_driver_default">Standard</string> | 170 | <string name="select_gpu_driver_default">Standard</string> |
| 171 | <string name="select_gpu_driver_install_success">%s wurde installiert</string> | ||
| 172 | <string name="select_gpu_driver_use_default">Standard GPU-Treiber wird verwendet</string> | 171 | <string name="select_gpu_driver_use_default">Standard GPU-Treiber wird verwendet</string> |
| 173 | <string name="select_gpu_driver_error">Ungültiger Treiber ausgewählt, Standard-Treiber wird verwendet!</string> | ||
| 174 | <string name="system_gpu_driver">System GPU-Treiber</string> | 172 | <string name="system_gpu_driver">System GPU-Treiber</string> |
| 175 | <string name="installing_driver">Treiber wird installiert...</string> | 173 | <string name="installing_driver">Treiber wird installiert...</string> |
| 176 | 174 | ||
diff --git a/src/android/app/src/main/res/values-es/strings.xml b/src/android/app/src/main/res/values-es/strings.xml index d398f862f..e5bdd5889 100644 --- a/src/android/app/src/main/res/values-es/strings.xml +++ b/src/android/app/src/main/res/values-es/strings.xml | |||
| @@ -171,9 +171,7 @@ | |||
| 171 | <string name="select_gpu_driver_title">¿Quiere reemplazar el driver de GPU actual?</string> | 171 | <string name="select_gpu_driver_title">¿Quiere reemplazar el driver de GPU actual?</string> |
| 172 | <string name="select_gpu_driver_install">Instalar</string> | 172 | <string name="select_gpu_driver_install">Instalar</string> |
| 173 | <string name="select_gpu_driver_default">Predeterminado</string> | 173 | <string name="select_gpu_driver_default">Predeterminado</string> |
| 174 | <string name="select_gpu_driver_install_success">Instalado %s</string> | ||
| 175 | <string name="select_gpu_driver_use_default">Usando el driver de GPU por defecto </string> | 174 | <string name="select_gpu_driver_use_default">Usando el driver de GPU por defecto </string> |
| 176 | <string name="select_gpu_driver_error">¡Driver no válido, utilizando el predeterminado del sistema!</string> | ||
| 177 | <string name="system_gpu_driver">Driver GPU del sistema</string> | 175 | <string name="system_gpu_driver">Driver GPU del sistema</string> |
| 178 | <string name="installing_driver">Instalando driver...</string> | 176 | <string name="installing_driver">Instalando driver...</string> |
| 179 | 177 | ||
diff --git a/src/android/app/src/main/res/values-fr/strings.xml b/src/android/app/src/main/res/values-fr/strings.xml index a7abd9077..1e02828aa 100644 --- a/src/android/app/src/main/res/values-fr/strings.xml +++ b/src/android/app/src/main/res/values-fr/strings.xml | |||
| @@ -171,9 +171,7 @@ | |||
| 171 | <string name="select_gpu_driver_title">Souhaitez vous remplacer votre pilote actuel ?</string> | 171 | <string name="select_gpu_driver_title">Souhaitez vous remplacer votre pilote actuel ?</string> |
| 172 | <string name="select_gpu_driver_install">Installer</string> | 172 | <string name="select_gpu_driver_install">Installer</string> |
| 173 | <string name="select_gpu_driver_default">Défaut</string> | 173 | <string name="select_gpu_driver_default">Défaut</string> |
| 174 | <string name="select_gpu_driver_install_success">%s Installé</string> | ||
| 175 | <string name="select_gpu_driver_use_default">Utilisation du pilote de GPU par défaut</string> | 174 | <string name="select_gpu_driver_use_default">Utilisation du pilote de GPU par défaut</string> |
| 176 | <string name="select_gpu_driver_error">Pilote non valide sélectionné, utilisation du paramètre par défaut du système !</string> | ||
| 177 | <string name="system_gpu_driver">Pilote du GPU du système</string> | 175 | <string name="system_gpu_driver">Pilote du GPU du système</string> |
| 178 | <string name="installing_driver">Installation du pilote...</string> | 176 | <string name="installing_driver">Installation du pilote...</string> |
| 179 | 177 | ||
diff --git a/src/android/app/src/main/res/values-it/strings.xml b/src/android/app/src/main/res/values-it/strings.xml index b18161801..09c9345b0 100644 --- a/src/android/app/src/main/res/values-it/strings.xml +++ b/src/android/app/src/main/res/values-it/strings.xml | |||
| @@ -171,9 +171,7 @@ | |||
| 171 | <string name="select_gpu_driver_title">Vuoi sostituire il driver della tua GPU attuale?</string> | 171 | <string name="select_gpu_driver_title">Vuoi sostituire il driver della tua GPU attuale?</string> |
| 172 | <string name="select_gpu_driver_install">Installa</string> | 172 | <string name="select_gpu_driver_install">Installa</string> |
| 173 | <string name="select_gpu_driver_default">Predefinito</string> | 173 | <string name="select_gpu_driver_default">Predefinito</string> |
| 174 | <string name="select_gpu_driver_install_success">Installato%s</string> | ||
| 175 | <string name="select_gpu_driver_use_default">Utilizza il driver predefinito della GPU.</string> | 174 | <string name="select_gpu_driver_use_default">Utilizza il driver predefinito della GPU.</string> |
| 176 | <string name="select_gpu_driver_error">Il driver selezionato è invalido, è in utilizzo quello predefinito di sistema!</string> | ||
| 177 | <string name="system_gpu_driver">Driver GPU del sistema</string> | 175 | <string name="system_gpu_driver">Driver GPU del sistema</string> |
| 178 | <string name="installing_driver">Installando i driver...</string> | 176 | <string name="installing_driver">Installando i driver...</string> |
| 179 | 177 | ||
diff --git a/src/android/app/src/main/res/values-ja/strings.xml b/src/android/app/src/main/res/values-ja/strings.xml index 88fa5a0bb..a0ea78bef 100644 --- a/src/android/app/src/main/res/values-ja/strings.xml +++ b/src/android/app/src/main/res/values-ja/strings.xml | |||
| @@ -170,9 +170,7 @@ | |||
| 170 | <string name="select_gpu_driver_title">現在のGPUドライバーを置き換えますか?</string> | 170 | <string name="select_gpu_driver_title">現在のGPUドライバーを置き換えますか?</string> |
| 171 | <string name="select_gpu_driver_install">インストール</string> | 171 | <string name="select_gpu_driver_install">インストール</string> |
| 172 | <string name="select_gpu_driver_default">デフォルト</string> | 172 | <string name="select_gpu_driver_default">デフォルト</string> |
| 173 | <string name="select_gpu_driver_install_success">%s をインストールしました</string> | ||
| 174 | <string name="select_gpu_driver_use_default">デフォルトのGPUドライバーを使用します</string> | 173 | <string name="select_gpu_driver_use_default">デフォルトのGPUドライバーを使用します</string> |
| 175 | <string name="select_gpu_driver_error">選択されたドライバが無効なため、システムのデフォルトを使用します!</string> | ||
| 176 | <string name="system_gpu_driver">システムのGPUドライバ</string> | 174 | <string name="system_gpu_driver">システムのGPUドライバ</string> |
| 177 | <string name="installing_driver">インストール中…</string> | 175 | <string name="installing_driver">インストール中…</string> |
| 178 | 176 | ||
diff --git a/src/android/app/src/main/res/values-ko/strings.xml b/src/android/app/src/main/res/values-ko/strings.xml index 4b658255c..214f95706 100644 --- a/src/android/app/src/main/res/values-ko/strings.xml +++ b/src/android/app/src/main/res/values-ko/strings.xml | |||
| @@ -171,9 +171,7 @@ | |||
| 171 | <string name="select_gpu_driver_title">현재 사용 중인 GPU 드라이버를 교체하겠습니까?</string> | 171 | <string name="select_gpu_driver_title">현재 사용 중인 GPU 드라이버를 교체하겠습니까?</string> |
| 172 | <string name="select_gpu_driver_install">설치</string> | 172 | <string name="select_gpu_driver_install">설치</string> |
| 173 | <string name="select_gpu_driver_default">기본값</string> | 173 | <string name="select_gpu_driver_default">기본값</string> |
| 174 | <string name="select_gpu_driver_install_success">설치된 %s</string> | ||
| 175 | <string name="select_gpu_driver_use_default">기본 GPU 드라이버 사용</string> | 174 | <string name="select_gpu_driver_use_default">기본 GPU 드라이버 사용</string> |
| 176 | <string name="select_gpu_driver_error">시스템 기본값을 사용하여 잘못된 드라이버를 선택했습니다!</string> | ||
| 177 | <string name="system_gpu_driver">시스템 GPU 드라이버</string> | 175 | <string name="system_gpu_driver">시스템 GPU 드라이버</string> |
| 178 | <string name="installing_driver">드라이버 설치 중...</string> | 176 | <string name="installing_driver">드라이버 설치 중...</string> |
| 179 | 177 | ||
diff --git a/src/android/app/src/main/res/values-nb/strings.xml b/src/android/app/src/main/res/values-nb/strings.xml index dd602a389..5443cef42 100644 --- a/src/android/app/src/main/res/values-nb/strings.xml +++ b/src/android/app/src/main/res/values-nb/strings.xml | |||
| @@ -171,9 +171,7 @@ | |||
| 171 | <string name="select_gpu_driver_title">Ønsker du å bytte ut din nåværende GPU-driver?</string> | 171 | <string name="select_gpu_driver_title">Ønsker du å bytte ut din nåværende GPU-driver?</string> |
| 172 | <string name="select_gpu_driver_install">Installer</string> | 172 | <string name="select_gpu_driver_install">Installer</string> |
| 173 | <string name="select_gpu_driver_default">Standard</string> | 173 | <string name="select_gpu_driver_default">Standard</string> |
| 174 | <string name="select_gpu_driver_install_success">Installert %s</string> | ||
| 175 | <string name="select_gpu_driver_use_default">Bruk av standard GPU-driver</string> | 174 | <string name="select_gpu_driver_use_default">Bruk av standard GPU-driver</string> |
| 176 | <string name="select_gpu_driver_error">Ugyldig driver valgt, bruker systemstandard!</string> | ||
| 177 | <string name="system_gpu_driver">Systemets GPU-driver</string> | 175 | <string name="system_gpu_driver">Systemets GPU-driver</string> |
| 178 | <string name="installing_driver">Installerer driver...</string> | 176 | <string name="installing_driver">Installerer driver...</string> |
| 179 | 177 | ||
diff --git a/src/android/app/src/main/res/values-pl/strings.xml b/src/android/app/src/main/res/values-pl/strings.xml index 2fdd1f952..899e233d0 100644 --- a/src/android/app/src/main/res/values-pl/strings.xml +++ b/src/android/app/src/main/res/values-pl/strings.xml | |||
| @@ -171,9 +171,7 @@ | |||
| 171 | <string name="select_gpu_driver_title">Chcesz zastąpić obecny sterownik układu graficznego?</string> | 171 | <string name="select_gpu_driver_title">Chcesz zastąpić obecny sterownik układu graficznego?</string> |
| 172 | <string name="select_gpu_driver_install">Zainstaluj</string> | 172 | <string name="select_gpu_driver_install">Zainstaluj</string> |
| 173 | <string name="select_gpu_driver_default">Domyślne</string> | 173 | <string name="select_gpu_driver_default">Domyślne</string> |
| 174 | <string name="select_gpu_driver_install_success">Zainstalowano %s</string> | ||
| 175 | <string name="select_gpu_driver_use_default">Aktywny domyślny sterownik GPU</string> | 174 | <string name="select_gpu_driver_use_default">Aktywny domyślny sterownik GPU</string> |
| 176 | <string name="select_gpu_driver_error">Wybrano błędny sterownik, powrót do domyślnego. </string> | ||
| 177 | <string name="system_gpu_driver">Systemowy sterownik GPU</string> | 175 | <string name="system_gpu_driver">Systemowy sterownik GPU</string> |
| 178 | <string name="installing_driver">Instalowanie sterownika...</string> | 176 | <string name="installing_driver">Instalowanie sterownika...</string> |
| 179 | 177 | ||
diff --git a/src/android/app/src/main/res/values-pt-rBR/strings.xml b/src/android/app/src/main/res/values-pt-rBR/strings.xml index 2f26367fe..caa095364 100644 --- a/src/android/app/src/main/res/values-pt-rBR/strings.xml +++ b/src/android/app/src/main/res/values-pt-rBR/strings.xml | |||
| @@ -171,9 +171,7 @@ | |||
| 171 | <string name="select_gpu_driver_title">Queres substituir o driver do GPU atual? </string> | 171 | <string name="select_gpu_driver_title">Queres substituir o driver do GPU atual? </string> |
| 172 | <string name="select_gpu_driver_install">Instalar</string> | 172 | <string name="select_gpu_driver_install">Instalar</string> |
| 173 | <string name="select_gpu_driver_default">Padrão</string> | 173 | <string name="select_gpu_driver_default">Padrão</string> |
| 174 | <string name="select_gpu_driver_install_success">Instalado%s</string> | ||
| 175 | <string name="select_gpu_driver_use_default">Usar o driver padrão do GPU</string> | 174 | <string name="select_gpu_driver_use_default">Usar o driver padrão do GPU</string> |
| 176 | <string name="select_gpu_driver_error">Driver selecionado inválido, a usar o padrão do sistema!</string> | ||
| 177 | <string name="system_gpu_driver">Driver do GPU padrão</string> | 175 | <string name="system_gpu_driver">Driver do GPU padrão</string> |
| 178 | <string name="installing_driver">A instalar o Driver...</string> | 176 | <string name="installing_driver">A instalar o Driver...</string> |
| 179 | 177 | ||
diff --git a/src/android/app/src/main/res/values-pt-rPT/strings.xml b/src/android/app/src/main/res/values-pt-rPT/strings.xml index 4e1eb4cd7..0a1a47fbb 100644 --- a/src/android/app/src/main/res/values-pt-rPT/strings.xml +++ b/src/android/app/src/main/res/values-pt-rPT/strings.xml | |||
| @@ -171,9 +171,7 @@ | |||
| 171 | <string name="select_gpu_driver_title">Queres substituir o driver do GPU atual? </string> | 171 | <string name="select_gpu_driver_title">Queres substituir o driver do GPU atual? </string> |
| 172 | <string name="select_gpu_driver_install">Instalar</string> | 172 | <string name="select_gpu_driver_install">Instalar</string> |
| 173 | <string name="select_gpu_driver_default">Padrão</string> | 173 | <string name="select_gpu_driver_default">Padrão</string> |
| 174 | <string name="select_gpu_driver_install_success">Instalado%s</string> | ||
| 175 | <string name="select_gpu_driver_use_default">Usar o driver padrão do GPU</string> | 174 | <string name="select_gpu_driver_use_default">Usar o driver padrão do GPU</string> |
| 176 | <string name="select_gpu_driver_error">Driver selecionado inválido, a usar o padrão do sistema!</string> | ||
| 177 | <string name="system_gpu_driver">Driver do GPU padrão</string> | 175 | <string name="system_gpu_driver">Driver do GPU padrão</string> |
| 178 | <string name="installing_driver">A instalar o Driver...</string> | 176 | <string name="installing_driver">A instalar o Driver...</string> |
| 179 | 177 | ||
diff --git a/src/android/app/src/main/res/values-ru/strings.xml b/src/android/app/src/main/res/values-ru/strings.xml index f5695dc93..0bef035d6 100644 --- a/src/android/app/src/main/res/values-ru/strings.xml +++ b/src/android/app/src/main/res/values-ru/strings.xml | |||
| @@ -171,9 +171,7 @@ | |||
| 171 | <string name="select_gpu_driver_title">Хотите заменить текущий драйвер ГП?</string> | 171 | <string name="select_gpu_driver_title">Хотите заменить текущий драйвер ГП?</string> |
| 172 | <string name="select_gpu_driver_install">Установить</string> | 172 | <string name="select_gpu_driver_install">Установить</string> |
| 173 | <string name="select_gpu_driver_default">По умолчанию</string> | 173 | <string name="select_gpu_driver_default">По умолчанию</string> |
| 174 | <string name="select_gpu_driver_install_success">Установлено %s</string> | ||
| 175 | <string name="select_gpu_driver_use_default">Используется стандартный драйвер ГП </string> | 174 | <string name="select_gpu_driver_use_default">Используется стандартный драйвер ГП </string> |
| 176 | <string name="select_gpu_driver_error">Выбран неверный драйвер, используется стандартный системный!</string> | ||
| 177 | <string name="system_gpu_driver">Системный драйвер ГП</string> | 175 | <string name="system_gpu_driver">Системный драйвер ГП</string> |
| 178 | <string name="installing_driver">Установка драйвера...</string> | 176 | <string name="installing_driver">Установка драйвера...</string> |
| 179 | 177 | ||
diff --git a/src/android/app/src/main/res/values-uk/strings.xml b/src/android/app/src/main/res/values-uk/strings.xml index 061bc6f04..5b789ee98 100644 --- a/src/android/app/src/main/res/values-uk/strings.xml +++ b/src/android/app/src/main/res/values-uk/strings.xml | |||
| @@ -171,9 +171,7 @@ | |||
| 171 | <string name="select_gpu_driver_title">Хочете замінити поточний драйвер ГП?</string> | 171 | <string name="select_gpu_driver_title">Хочете замінити поточний драйвер ГП?</string> |
| 172 | <string name="select_gpu_driver_install">Встановити</string> | 172 | <string name="select_gpu_driver_install">Встановити</string> |
| 173 | <string name="select_gpu_driver_default">За замовчуванням</string> | 173 | <string name="select_gpu_driver_default">За замовчуванням</string> |
| 174 | <string name="select_gpu_driver_install_success">Встановлено %s</string> | ||
| 175 | <string name="select_gpu_driver_use_default">Використовується стандартний драйвер ГП</string> | 174 | <string name="select_gpu_driver_use_default">Використовується стандартний драйвер ГП</string> |
| 176 | <string name="select_gpu_driver_error">Обрано неправильний драйвер, використовується стандартний системний!</string> | ||
| 177 | <string name="system_gpu_driver">Системний драйвер ГП</string> | 175 | <string name="system_gpu_driver">Системний драйвер ГП</string> |
| 178 | <string name="installing_driver">Встановлення драйвера...</string> | 176 | <string name="installing_driver">Встановлення драйвера...</string> |
| 179 | 177 | ||
diff --git a/src/android/app/src/main/res/values-zh-rCN/strings.xml b/src/android/app/src/main/res/values-zh-rCN/strings.xml index fe6dd5eaa..c0e885751 100644 --- a/src/android/app/src/main/res/values-zh-rCN/strings.xml +++ b/src/android/app/src/main/res/values-zh-rCN/strings.xml | |||
| @@ -171,9 +171,7 @@ | |||
| 171 | <string name="select_gpu_driver_title">要取代您当前的 GPU 驱动程序吗?</string> | 171 | <string name="select_gpu_driver_title">要取代您当前的 GPU 驱动程序吗?</string> |
| 172 | <string name="select_gpu_driver_install">安装</string> | 172 | <string name="select_gpu_driver_install">安装</string> |
| 173 | <string name="select_gpu_driver_default">系统默认</string> | 173 | <string name="select_gpu_driver_default">系统默认</string> |
| 174 | <string name="select_gpu_driver_install_success">已安装 %s</string> | ||
| 175 | <string name="select_gpu_driver_use_default">使用默认 GPU 驱动程序</string> | 174 | <string name="select_gpu_driver_use_default">使用默认 GPU 驱动程序</string> |
| 176 | <string name="select_gpu_driver_error">选择的驱动程序无效,将使用系统默认的驱动程序!</string> | ||
| 177 | <string name="system_gpu_driver">系统 GPU 驱动程序</string> | 175 | <string name="system_gpu_driver">系统 GPU 驱动程序</string> |
| 178 | <string name="installing_driver">正在安装驱动程序…</string> | 176 | <string name="installing_driver">正在安装驱动程序…</string> |
| 179 | 177 | ||
diff --git a/src/android/app/src/main/res/values-zh-rTW/strings.xml b/src/android/app/src/main/res/values-zh-rTW/strings.xml index 9b3e54224..4a21bf893 100644 --- a/src/android/app/src/main/res/values-zh-rTW/strings.xml +++ b/src/android/app/src/main/res/values-zh-rTW/strings.xml | |||
| @@ -171,9 +171,7 @@ | |||
| 171 | <string name="select_gpu_driver_title">要取代您目前的 GPU 驅動程式嗎?</string> | 171 | <string name="select_gpu_driver_title">要取代您目前的 GPU 驅動程式嗎?</string> |
| 172 | <string name="select_gpu_driver_install">安裝</string> | 172 | <string name="select_gpu_driver_install">安裝</string> |
| 173 | <string name="select_gpu_driver_default">預設</string> | 173 | <string name="select_gpu_driver_default">預設</string> |
| 174 | <string name="select_gpu_driver_install_success">已安裝 %s</string> | ||
| 175 | <string name="select_gpu_driver_use_default">使用預設 GPU 驅動程式</string> | 174 | <string name="select_gpu_driver_use_default">使用預設 GPU 驅動程式</string> |
| 176 | <string name="select_gpu_driver_error">選取的驅動程式無效,將使用系統預設驅動程式!</string> | ||
| 177 | <string name="system_gpu_driver">系統 GPU 驅動程式</string> | 175 | <string name="system_gpu_driver">系統 GPU 驅動程式</string> |
| 178 | <string name="installing_driver">正在安裝驅動程式…</string> | 176 | <string name="installing_driver">正在安裝驅動程式…</string> |
| 179 | 177 | ||
diff --git a/src/android/app/src/main/res/values/dimens.xml b/src/android/app/src/main/res/values/dimens.xml index 7b2296d95..ef855ea6f 100644 --- a/src/android/app/src/main/res/values/dimens.xml +++ b/src/android/app/src/main/res/values/dimens.xml | |||
| @@ -13,6 +13,8 @@ | |||
| 13 | <dimen name="menu_width">256dp</dimen> | 13 | <dimen name="menu_width">256dp</dimen> |
| 14 | <dimen name="card_width">165dp</dimen> | 14 | <dimen name="card_width">165dp</dimen> |
| 15 | <dimen name="icon_inset">24dp</dimen> | 15 | <dimen name="icon_inset">24dp</dimen> |
| 16 | <dimen name="spacing_bottom_list_fab">72dp</dimen> | ||
| 17 | <dimen name="spacing_fab">24dp</dimen> | ||
| 16 | 18 | ||
| 17 | <dimen name="dialog_margin">20dp</dimen> | 19 | <dimen name="dialog_margin">20dp</dimen> |
| 18 | <dimen name="elevated_app_bar">3dp</dimen> | 20 | <dimen name="elevated_app_bar">3dp</dimen> |
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index e51edf872..9e4854221 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml | |||
| @@ -72,6 +72,7 @@ | |||
| 72 | <string name="invalid_keys_error">Invalid encryption keys</string> | 72 | <string name="invalid_keys_error">Invalid encryption keys</string> |
| 73 | <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> | 73 | <string name="dumping_keys_quickstart_link">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string> |
| 74 | <string name="install_keys_failure_description">The selected file is incorrect or corrupt. Please redump your keys.</string> | 74 | <string name="install_keys_failure_description">The selected file is incorrect or corrupt. Please redump your keys.</string> |
| 75 | <string name="gpu_driver_manager">GPU Driver Manager</string> | ||
| 75 | <string name="install_gpu_driver">Install GPU driver</string> | 76 | <string name="install_gpu_driver">Install GPU driver</string> |
| 76 | <string name="install_gpu_driver_description">Install alternative drivers for potentially better performance or accuracy</string> | 77 | <string name="install_gpu_driver_description">Install alternative drivers for potentially better performance or accuracy</string> |
| 77 | <string name="advanced_settings">Advanced settings</string> | 78 | <string name="advanced_settings">Advanced settings</string> |
| @@ -234,15 +235,17 @@ | |||
| 234 | <string name="export_failed">Export failed</string> | 235 | <string name="export_failed">Export failed</string> |
| 235 | <string name="import_failed">Import failed</string> | 236 | <string name="import_failed">Import failed</string> |
| 236 | <string name="cancelling">Cancelling</string> | 237 | <string name="cancelling">Cancelling</string> |
| 238 | <string name="install">Install</string> | ||
| 239 | <string name="delete">Delete</string> | ||
| 237 | 240 | ||
| 238 | <!-- GPU driver installation --> | 241 | <!-- GPU driver installation --> |
| 239 | <string name="select_gpu_driver">Select GPU driver</string> | 242 | <string name="select_gpu_driver">Select GPU driver</string> |
| 240 | <string name="select_gpu_driver_title">Would you like to replace your current GPU driver?</string> | 243 | <string name="select_gpu_driver_title">Would you like to replace your current GPU driver?</string> |
| 241 | <string name="select_gpu_driver_install">Install</string> | 244 | <string name="select_gpu_driver_install">Install</string> |
| 242 | <string name="select_gpu_driver_default">Default</string> | 245 | <string name="select_gpu_driver_default">Default</string> |
| 243 | <string name="select_gpu_driver_install_success">Installed %s</string> | ||
| 244 | <string name="select_gpu_driver_use_default">Using default GPU driver</string> | 246 | <string name="select_gpu_driver_use_default">Using default GPU driver</string> |
| 245 | <string name="select_gpu_driver_error">Invalid driver selected, using system default!</string> | 247 | <string name="select_gpu_driver_error">Invalid driver selected</string> |
| 248 | <string name="driver_already_installed">Driver already installed</string> | ||
| 246 | <string name="system_gpu_driver">System GPU driver</string> | 249 | <string name="system_gpu_driver">System GPU driver</string> |
| 247 | <string name="installing_driver">Installing driver…</string> | 250 | <string name="installing_driver">Installing driver…</string> |
| 248 | 251 | ||
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h index 0dad9338a..47d028d48 100644 --- a/src/common/common_funcs.h +++ b/src/common/common_funcs.h | |||
| @@ -39,8 +39,12 @@ | |||
| 39 | #define Crash() exit(1) | 39 | #define Crash() exit(1) |
| 40 | #endif | 40 | #endif |
| 41 | 41 | ||
| 42 | #define LTO_NOINLINE __attribute__((noinline)) | ||
| 43 | |||
| 42 | #else // _MSC_VER | 44 | #else // _MSC_VER |
| 43 | 45 | ||
| 46 | #define LTO_NOINLINE | ||
| 47 | |||
| 44 | // Locale Cross-Compatibility | 48 | // Locale Cross-Compatibility |
| 45 | #define locale_t _locale_t | 49 | #define locale_t _locale_t |
| 46 | 50 | ||
diff --git a/src/common/elf.h b/src/common/elf.h index 14a5e9597..0b728dc54 100644 --- a/src/common/elf.h +++ b/src/common/elf.h | |||
| @@ -211,6 +211,11 @@ struct Elf64_Rela { | |||
| 211 | Elf64_Sxword r_addend; /* Addend */ | 211 | Elf64_Sxword r_addend; /* Addend */ |
| 212 | }; | 212 | }; |
| 213 | 213 | ||
| 214 | /* RELR relocation table entry */ | ||
| 215 | |||
| 216 | using Elf32_Relr = Elf32_Word; | ||
| 217 | using Elf64_Relr = Elf64_Xword; | ||
| 218 | |||
| 214 | /* How to extract and insert information held in the r_info field. */ | 219 | /* How to extract and insert information held in the r_info field. */ |
| 215 | 220 | ||
| 216 | static inline u32 Elf32RelSymIndex(Elf32_Word r_info) { | 221 | static inline u32 Elf32RelSymIndex(Elf32_Word r_info) { |
| @@ -328,6 +333,9 @@ constexpr u32 ElfDtFiniArray = 26; /* Array with addresses of fini fct */ | |||
| 328 | constexpr u32 ElfDtInitArraySz = 27; /* Size in bytes of DT_INIT_ARRAY */ | 333 | constexpr u32 ElfDtInitArraySz = 27; /* Size in bytes of DT_INIT_ARRAY */ |
| 329 | constexpr u32 ElfDtFiniArraySz = 28; /* Size in bytes of DT_FINI_ARRAY */ | 334 | constexpr u32 ElfDtFiniArraySz = 28; /* Size in bytes of DT_FINI_ARRAY */ |
| 330 | constexpr u32 ElfDtSymtabShndx = 34; /* Address of SYMTAB_SHNDX section */ | 335 | constexpr u32 ElfDtSymtabShndx = 34; /* Address of SYMTAB_SHNDX section */ |
| 336 | constexpr u32 ElfDtRelrsz = 35; /* Size of RELR relative relocations */ | ||
| 337 | constexpr u32 ElfDtRelr = 36; /* Address of RELR relative relocations */ | ||
| 338 | constexpr u32 ElfDtRelrent = 37; /* Size of one RELR relative relocation */ | ||
| 331 | 339 | ||
| 332 | } // namespace ELF | 340 | } // namespace ELF |
| 333 | } // namespace Common | 341 | } // namespace Common |
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index a1134b7e2..cb025c3d6 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -373,7 +373,7 @@ struct KernelCore::Impl { | |||
| 373 | static inline thread_local u8 host_thread_id = UINT8_MAX; | 373 | static inline thread_local u8 host_thread_id = UINT8_MAX; |
| 374 | 374 | ||
| 375 | /// Sets the host thread ID for the caller. | 375 | /// Sets the host thread ID for the caller. |
| 376 | u32 SetHostThreadId(std::size_t core_id) { | 376 | LTO_NOINLINE u32 SetHostThreadId(std::size_t core_id) { |
| 377 | // This should only be called during core init. | 377 | // This should only be called during core init. |
| 378 | ASSERT(host_thread_id == UINT8_MAX); | 378 | ASSERT(host_thread_id == UINT8_MAX); |
| 379 | 379 | ||
| @@ -384,13 +384,13 @@ struct KernelCore::Impl { | |||
| 384 | } | 384 | } |
| 385 | 385 | ||
| 386 | /// Gets the host thread ID for the caller | 386 | /// Gets the host thread ID for the caller |
| 387 | u32 GetHostThreadId() const { | 387 | LTO_NOINLINE u32 GetHostThreadId() const { |
| 388 | return host_thread_id; | 388 | return host_thread_id; |
| 389 | } | 389 | } |
| 390 | 390 | ||
| 391 | // Gets the dummy KThread for the caller, allocating a new one if this is the first time | 391 | // Gets the dummy KThread for the caller, allocating a new one if this is the first time |
| 392 | KThread* GetHostDummyThread(KThread* existing_thread) { | 392 | LTO_NOINLINE KThread* GetHostDummyThread(KThread* existing_thread) { |
| 393 | const auto initialize{[](KThread* thread) { | 393 | const auto initialize{[](KThread* thread) LTO_NOINLINE { |
| 394 | ASSERT(KThread::InitializeDummyThread(thread, nullptr).IsSuccess()); | 394 | ASSERT(KThread::InitializeDummyThread(thread, nullptr).IsSuccess()); |
| 395 | return thread; | 395 | return thread; |
| 396 | }}; | 396 | }}; |
| @@ -424,11 +424,11 @@ struct KernelCore::Impl { | |||
| 424 | 424 | ||
| 425 | static inline thread_local bool is_phantom_mode_for_singlecore{false}; | 425 | static inline thread_local bool is_phantom_mode_for_singlecore{false}; |
| 426 | 426 | ||
| 427 | bool IsPhantomModeForSingleCore() const { | 427 | LTO_NOINLINE bool IsPhantomModeForSingleCore() const { |
| 428 | return is_phantom_mode_for_singlecore; | 428 | return is_phantom_mode_for_singlecore; |
| 429 | } | 429 | } |
| 430 | 430 | ||
| 431 | void SetIsPhantomModeForSingleCore(bool value) { | 431 | LTO_NOINLINE void SetIsPhantomModeForSingleCore(bool value) { |
| 432 | ASSERT(!is_multicore); | 432 | ASSERT(!is_multicore); |
| 433 | is_phantom_mode_for_singlecore = value; | 433 | is_phantom_mode_for_singlecore = value; |
| 434 | } | 434 | } |
| @@ -439,14 +439,14 @@ struct KernelCore::Impl { | |||
| 439 | 439 | ||
| 440 | static inline thread_local KThread* current_thread{nullptr}; | 440 | static inline thread_local KThread* current_thread{nullptr}; |
| 441 | 441 | ||
| 442 | KThread* GetCurrentEmuThread() { | 442 | LTO_NOINLINE KThread* GetCurrentEmuThread() { |
| 443 | if (!current_thread) { | 443 | if (!current_thread) { |
| 444 | current_thread = GetHostDummyThread(nullptr); | 444 | current_thread = GetHostDummyThread(nullptr); |
| 445 | } | 445 | } |
| 446 | return current_thread; | 446 | return current_thread; |
| 447 | } | 447 | } |
| 448 | 448 | ||
| 449 | void SetCurrentEmuThread(KThread* thread) { | 449 | LTO_NOINLINE void SetCurrentEmuThread(KThread* thread) { |
| 450 | current_thread = thread; | 450 | current_thread = thread; |
| 451 | } | 451 | } |
| 452 | 452 | ||
diff --git a/src/core/hle/service/caps/caps_a.cpp b/src/core/hle/service/caps/caps_a.cpp index e22f72bf6..9925720a3 100644 --- a/src/core/hle/service/caps/caps_a.cpp +++ b/src/core/hle/service/caps/caps_a.cpp | |||
| @@ -128,9 +128,9 @@ void IAlbumAccessorService::GetAlbumFileListEx0(HLERequestContext& ctx) { | |||
| 128 | ctx.WriteBuffer(entries); | 128 | ctx.WriteBuffer(entries); |
| 129 | } | 129 | } |
| 130 | 130 | ||
| 131 | IPC::ResponseBuilder rb{ctx, 3}; | 131 | IPC::ResponseBuilder rb{ctx, 4}; |
| 132 | rb.Push(result); | 132 | rb.Push(result); |
| 133 | rb.Push(entries.size()); | 133 | rb.Push<u64>(entries.size()); |
| 134 | } | 134 | } |
| 135 | 135 | ||
| 136 | void IAlbumAccessorService::GetAutoSavingStorage(HLERequestContext& ctx) { | 136 | void IAlbumAccessorService::GetAutoSavingStorage(HLERequestContext& ctx) { |
diff --git a/src/core/hle/service/jit/jit_context.cpp b/src/core/hle/service/jit/jit_context.cpp index 4ed3f02e2..0090e8568 100644 --- a/src/core/hle/service/jit/jit_context.cpp +++ b/src/core/hle/service/jit/jit_context.cpp | |||
| @@ -156,6 +156,8 @@ public: | |||
| 156 | 156 | ||
| 157 | bool LoadNRO(std::span<const u8> data) { | 157 | bool LoadNRO(std::span<const u8> data) { |
| 158 | local_memory.clear(); | 158 | local_memory.clear(); |
| 159 | |||
| 160 | relocbase = local_memory.size(); | ||
| 159 | local_memory.insert(local_memory.end(), data.begin(), data.end()); | 161 | local_memory.insert(local_memory.end(), data.begin(), data.end()); |
| 160 | 162 | ||
| 161 | if (FixupRelocations()) { | 163 | if (FixupRelocations()) { |
| @@ -181,8 +183,8 @@ public: | |||
| 181 | // https://refspecs.linuxbase.org/elf/gabi4+/ch5.dynamic.html | 183 | // https://refspecs.linuxbase.org/elf/gabi4+/ch5.dynamic.html |
| 182 | // https://refspecs.linuxbase.org/elf/gabi4+/ch4.reloc.html | 184 | // https://refspecs.linuxbase.org/elf/gabi4+/ch4.reloc.html |
| 183 | VAddr dynamic_offset{mod_offset + callbacks->MemoryRead32(mod_offset + 4)}; | 185 | VAddr dynamic_offset{mod_offset + callbacks->MemoryRead32(mod_offset + 4)}; |
| 184 | VAddr rela_dyn = 0; | 186 | VAddr rela_dyn = 0, relr_dyn = 0; |
| 185 | size_t num_rela = 0; | 187 | size_t num_rela = 0, num_relr = 0; |
| 186 | while (true) { | 188 | while (true) { |
| 187 | const auto dyn{callbacks->ReadMemory<Elf64_Dyn>(dynamic_offset)}; | 189 | const auto dyn{callbacks->ReadMemory<Elf64_Dyn>(dynamic_offset)}; |
| 188 | dynamic_offset += sizeof(Elf64_Dyn); | 190 | dynamic_offset += sizeof(Elf64_Dyn); |
| @@ -196,6 +198,12 @@ public: | |||
| 196 | if (dyn.d_tag == ElfDtRelasz) { | 198 | if (dyn.d_tag == ElfDtRelasz) { |
| 197 | num_rela = dyn.d_un.d_val / sizeof(Elf64_Rela); | 199 | num_rela = dyn.d_un.d_val / sizeof(Elf64_Rela); |
| 198 | } | 200 | } |
| 201 | if (dyn.d_tag == ElfDtRelr) { | ||
| 202 | relr_dyn = dyn.d_un.d_ptr; | ||
| 203 | } | ||
| 204 | if (dyn.d_tag == ElfDtRelrsz) { | ||
| 205 | num_relr = dyn.d_un.d_val / sizeof(Elf64_Relr); | ||
| 206 | } | ||
| 199 | } | 207 | } |
| 200 | 208 | ||
| 201 | for (size_t i = 0; i < num_rela; i++) { | 209 | for (size_t i = 0; i < num_rela; i++) { |
| @@ -207,6 +215,29 @@ public: | |||
| 207 | callbacks->MemoryWrite64(rela.r_offset, contents + rela.r_addend); | 215 | callbacks->MemoryWrite64(rela.r_offset, contents + rela.r_addend); |
| 208 | } | 216 | } |
| 209 | 217 | ||
| 218 | VAddr relr_where = 0; | ||
| 219 | for (size_t i = 0; i < num_relr; i++) { | ||
| 220 | const auto relr{callbacks->ReadMemory<Elf64_Relr>(relr_dyn + i * sizeof(Elf64_Relr))}; | ||
| 221 | const auto incr{[&](VAddr where) { | ||
| 222 | callbacks->MemoryWrite64(where, callbacks->MemoryRead64(where) + relocbase); | ||
| 223 | }}; | ||
| 224 | |||
| 225 | if ((relr & 1) == 0) { | ||
| 226 | // where pointer | ||
| 227 | relr_where = relocbase + relr; | ||
| 228 | incr(relr_where); | ||
| 229 | relr_where += sizeof(Elf64_Addr); | ||
| 230 | } else { | ||
| 231 | // bitmap | ||
| 232 | for (int bit = 1; bit < 64; bit++) { | ||
| 233 | if ((relr & (1ULL << bit)) != 0) { | ||
| 234 | incr(relr_where + i * sizeof(Elf64_Addr)); | ||
| 235 | } | ||
| 236 | } | ||
| 237 | relr_where += 63 * sizeof(Elf64_Addr); | ||
| 238 | } | ||
| 239 | } | ||
| 240 | |||
| 210 | return true; | 241 | return true; |
| 211 | } | 242 | } |
| 212 | 243 | ||
| @@ -313,6 +344,7 @@ public: | |||
| 313 | Core::Memory::Memory& memory; | 344 | Core::Memory::Memory& memory; |
| 314 | VAddr top_of_stack; | 345 | VAddr top_of_stack; |
| 315 | VAddr heap_pointer; | 346 | VAddr heap_pointer; |
| 347 | VAddr relocbase; | ||
| 316 | }; | 348 | }; |
| 317 | 349 | ||
| 318 | void DynarmicCallbacks64::CallSVC(u32 swi) { | 350 | void DynarmicCallbacks64::CallSVC(u32 swi) { |