mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2024-11-14 21:37:52 -07:00
Android: Implement a UI for Adrenotools
This commit is contained in:
parent
23bebc5270
commit
2da7d16b7c
@ -83,13 +83,6 @@
|
||||
android:theme="@style/Theme.Dolphin.Main"
|
||||
android:label="@string/settings"/>
|
||||
|
||||
<activity
|
||||
android:name=".features.settings.ui.GpuDriverActivity"
|
||||
android:exported="false"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:theme="@style/Theme.Dolphin.Main"
|
||||
android:label="@string/settings"/>
|
||||
|
||||
<activity
|
||||
android:name=".features.cheats.ui.CheatsActivity"
|
||||
android:exported="false"
|
||||
|
@ -49,7 +49,8 @@ enum class MenuTag {
|
||||
WIIMOTE_MOTION_INPUT_1("wiimote_motion_input", 0),
|
||||
WIIMOTE_MOTION_INPUT_2("wiimote_motion_input", 1),
|
||||
WIIMOTE_MOTION_INPUT_3("wiimote_motion_input", 2),
|
||||
WIIMOTE_MOTION_INPUT_4("wiimote_motion_input", 3);
|
||||
WIIMOTE_MOTION_INPUT_4("wiimote_motion_input", 3),
|
||||
GPU_DRIVERS("gpu_drivers");
|
||||
|
||||
var tag: String
|
||||
private set
|
||||
|
@ -1,5 +1,9 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
// GPU driver implementation partially based on:
|
||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
package org.dolphinemu.dolphinemu.features.settings.ui
|
||||
|
||||
import android.content.Context
|
||||
@ -20,6 +24,7 @@ import androidx.lifecycle.ViewModelProvider
|
||||
import com.google.android.material.appbar.CollapsingToolbarLayout
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import org.dolphinemu.dolphinemu.NativeLibrary
|
||||
import org.dolphinemu.dolphinemu.R
|
||||
import org.dolphinemu.dolphinemu.databinding.ActivitySettingsBinding
|
||||
@ -27,6 +32,7 @@ import org.dolphinemu.dolphinemu.features.settings.model.Settings
|
||||
import org.dolphinemu.dolphinemu.features.settings.ui.SettingsFragment.Companion.newInstance
|
||||
import org.dolphinemu.dolphinemu.ui.main.MainPresenter
|
||||
import org.dolphinemu.dolphinemu.utils.FileBrowserHelper
|
||||
import org.dolphinemu.dolphinemu.utils.GpuDriverInstallResult
|
||||
import org.dolphinemu.dolphinemu.utils.InsetsHelper
|
||||
import org.dolphinemu.dolphinemu.utils.SerializableHelper.serializable
|
||||
import org.dolphinemu.dolphinemu.utils.ThemeHelper.enableScrollTint
|
||||
@ -165,8 +171,21 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
|
||||
super.onActivityResult(requestCode, resultCode, result)
|
||||
|
||||
// If the user picked a file, as opposed to just backing out.
|
||||
if (resultCode == RESULT_OK) {
|
||||
if (requestCode != MainPresenter.REQUEST_DIRECTORY) {
|
||||
if (resultCode != RESULT_OK) {
|
||||
return
|
||||
}
|
||||
|
||||
when (requestCode) {
|
||||
MainPresenter.REQUEST_DIRECTORY -> {
|
||||
val path = FileBrowserHelper.getSelectedPath(result)
|
||||
fragment!!.adapter!!.onFilePickerConfirmation(path!!)
|
||||
}
|
||||
|
||||
MainPresenter.REQUEST_GAME_FILE
|
||||
or MainPresenter.REQUEST_SD_FILE
|
||||
or MainPresenter.REQUEST_WAD_FILE
|
||||
or MainPresenter.REQUEST_WII_SAVE_FILE
|
||||
or MainPresenter.REQUEST_NAND_BIN_FILE -> {
|
||||
val uri = canonicalizeIfPossible(result!!.data!!)
|
||||
val validExtensions: Set<String> =
|
||||
if (requestCode == MainPresenter.REQUEST_GAME_FILE) FileBrowserHelper.GAME_EXTENSIONS else FileBrowserHelper.RAW_EXTENSION
|
||||
@ -178,9 +197,6 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView {
|
||||
contentResolver.takePersistableUriPermission(uri, takeFlags)
|
||||
fragment!!.adapter!!.onFilePickerConfirmation(uri.toString())
|
||||
}
|
||||
} else {
|
||||
val path = FileBrowserHelper.getSelectedPath(result)
|
||||
fragment!!.adapter!!.onFilePickerConfirmation(path!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,10 +3,15 @@
|
||||
package org.dolphinemu.dolphinemu.features.settings.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.updatePadding
|
||||
@ -14,10 +19,15 @@ import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import org.dolphinemu.dolphinemu.R
|
||||
import org.dolphinemu.dolphinemu.databinding.FragmentSettingsBinding
|
||||
import org.dolphinemu.dolphinemu.features.settings.model.Settings
|
||||
import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem
|
||||
import org.dolphinemu.dolphinemu.ui.main.MainActivity
|
||||
import org.dolphinemu.dolphinemu.ui.main.MainPresenter
|
||||
import org.dolphinemu.dolphinemu.utils.GpuDriverInstallResult
|
||||
import org.dolphinemu.dolphinemu.utils.SerializableHelper.serializable
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
@ -111,6 +121,11 @@ class SettingsFragment : Fragment(), SettingsFragmentView {
|
||||
}
|
||||
|
||||
override fun loadSubMenu(menuKey: MenuTag) {
|
||||
if (menuKey == MenuTag.GPU_DRIVERS) {
|
||||
showGpuDriverDialog()
|
||||
return
|
||||
}
|
||||
|
||||
activityView!!.showSettingsFragment(
|
||||
menuKey,
|
||||
null,
|
||||
@ -170,6 +185,74 @@ class SettingsFragment : Fragment(), SettingsFragmentView {
|
||||
}
|
||||
}
|
||||
|
||||
override fun showGpuDriverDialog() {
|
||||
if (presenter.gpuDriver == null) {
|
||||
return
|
||||
}
|
||||
val msg = "${presenter!!.gpuDriver!!.name} ${presenter!!.gpuDriver!!.driverVersion}"
|
||||
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(getString(R.string.gpu_driver_dialog_title))
|
||||
.setMessage(msg)
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.setNeutralButton(R.string.gpu_driver_dialog_system) { _: DialogInterface?, _: Int ->
|
||||
presenter.useSystemDriver()
|
||||
}
|
||||
.setPositiveButton(R.string.gpu_driver_dialog_install) { _: DialogInterface?, _: Int ->
|
||||
askForDriverFile()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun askForDriverFile() {
|
||||
val intent = Intent(Intent.ACTION_GET_CONTENT).apply {
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
type = "application/zip"
|
||||
}
|
||||
startActivityForResult(intent, MainPresenter.REQUEST_GPU_DRIVER)
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
|
||||
// If the user picked a file, as opposed to just backing out.
|
||||
if (resultCode != AppCompatActivity.RESULT_OK) {
|
||||
return
|
||||
}
|
||||
|
||||
if (requestCode != MainPresenter.REQUEST_GPU_DRIVER) {
|
||||
return
|
||||
}
|
||||
|
||||
val uri = data?.data ?: return
|
||||
presenter.installDriver(uri)
|
||||
}
|
||||
|
||||
override fun onDriverInstallDone(result: GpuDriverInstallResult) {
|
||||
val view = binding?.root ?: return
|
||||
Snackbar
|
||||
.make(view, resolveInstallResultString(result), Snackbar.LENGTH_LONG)
|
||||
.show()
|
||||
}
|
||||
|
||||
override fun onDriverUninstallDone() {
|
||||
Toast.makeText(
|
||||
requireContext(),
|
||||
R.string.gpu_driver_dialog_uninstall_done,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
|
||||
private fun resolveInstallResultString(result: GpuDriverInstallResult) = when (result) {
|
||||
GpuDriverInstallResult.Success -> getString(R.string.gpu_driver_install_success)
|
||||
GpuDriverInstallResult.InvalidArchive -> getString(R.string.gpu_driver_install_invalid_archive)
|
||||
GpuDriverInstallResult.MissingMetadata -> getString(R.string.gpu_driver_install_missing_metadata)
|
||||
GpuDriverInstallResult.InvalidMetadata -> getString(R.string.gpu_driver_install_invalid_metadata)
|
||||
GpuDriverInstallResult.UnsupportedAndroidVersion -> getString(R.string.gpu_driver_install_unsupported_android_version)
|
||||
GpuDriverInstallResult.AlreadyInstalled -> getString(R.string.gpu_driver_install_already_installed)
|
||||
GpuDriverInstallResult.FileNotFound -> getString(R.string.gpu_driver_install_file_not_found)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val ARGUMENT_MENU_TAG = "menu_tag"
|
||||
private const val ARGUMENT_GAME_ID = "game_id"
|
||||
|
@ -4,11 +4,16 @@ package org.dolphinemu.dolphinemu.features.settings.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.text.TextUtils
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.collection.ArraySet
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.dolphinemu.dolphinemu.NativeLibrary
|
||||
import org.dolphinemu.dolphinemu.R
|
||||
import org.dolphinemu.dolphinemu.activities.UserDataActivity
|
||||
@ -25,6 +30,7 @@ import org.dolphinemu.dolphinemu.features.input.ui.ProfileDialog
|
||||
import org.dolphinemu.dolphinemu.features.input.ui.ProfileDialogPresenter
|
||||
import org.dolphinemu.dolphinemu.features.settings.model.*
|
||||
import org.dolphinemu.dolphinemu.features.settings.model.view.*
|
||||
import org.dolphinemu.dolphinemu.model.GpuDriverMetadata
|
||||
import org.dolphinemu.dolphinemu.ui.main.MainPresenter
|
||||
import org.dolphinemu.dolphinemu.utils.*
|
||||
import java.util.*
|
||||
@ -45,6 +51,9 @@ class SettingsFragmentPresenter(
|
||||
private var controllerNumber = 0
|
||||
private var controllerType = 0
|
||||
|
||||
var gpuDriver: GpuDriverMetadata? = null
|
||||
private val libNameSetting: StringSetting = StringSetting.GFX_DRIVER_LIB_NAME
|
||||
|
||||
fun onCreate(menuTag: MenuTag, gameId: String?, extras: Bundle) {
|
||||
this.gameId = gameId
|
||||
this.menuTag = menuTag
|
||||
@ -56,6 +65,11 @@ class SettingsFragmentPresenter(
|
||||
controllerNumber = menuTag.subType
|
||||
} else if (menuTag.isSerialPort1Menu) {
|
||||
serialPort1Type = extras.getInt(ARG_SERIALPORT1_TYPE)
|
||||
} else if (menuTag == MenuTag.GRAPHICS) {
|
||||
this.gpuDriver =
|
||||
GpuDriverHelper.getInstalledDriverMetadata() ?: GpuDriverHelper.getSystemDriverMetadata(
|
||||
context.applicationContext
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1250,6 +1264,15 @@ class SettingsFragmentPresenter(
|
||||
MenuTag.ADVANCED_GRAPHICS
|
||||
)
|
||||
)
|
||||
|
||||
if (GpuDriverHelper.supportsCustomDriverLoading() && this.gpuDriver != null) {
|
||||
sl.add(
|
||||
SubmenuSetting(
|
||||
context,
|
||||
R.string.gpu_driver_submenu, MenuTag.GPU_DRIVERS
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addEnhanceSettings(sl: ArrayList<SettingsItem>) {
|
||||
@ -2113,7 +2136,7 @@ class SettingsFragmentPresenter(
|
||||
profileString: String,
|
||||
controllerNumber: Int
|
||||
) {
|
||||
val profiles = ProfileDialogPresenter(menuTag).getProfileNames(false)
|
||||
val profiles = ProfileDialogPresenter(menuTag!!).getProfileNames(false)
|
||||
val profileKey = profileString + "Profile" + (controllerNumber + 1)
|
||||
sl.add(
|
||||
StringSingleChoiceSetting(
|
||||
@ -2324,6 +2347,45 @@ class SettingsFragmentPresenter(
|
||||
)
|
||||
}
|
||||
|
||||
fun installDriver(uri: Uri) {
|
||||
val context = this.context.applicationContext
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val stream = context.contentResolver.openInputStream(uri)
|
||||
if (stream == null) {
|
||||
GpuDriverHelper.uninstallDriver()
|
||||
withContext(Dispatchers.Main) {
|
||||
fragmentView.onDriverInstallDone(GpuDriverInstallResult.FileNotFound)
|
||||
}
|
||||
return@launch
|
||||
}
|
||||
|
||||
val result = GpuDriverHelper.installDriver(stream)
|
||||
withContext(Dispatchers.Main) {
|
||||
with(this@SettingsFragmentPresenter) {
|
||||
this.gpuDriver = GpuDriverHelper.getInstalledDriverMetadata()
|
||||
?: GpuDriverHelper.getSystemDriverMetadata(context) ?: return@withContext
|
||||
this.libNameSetting.setString(this.settings!!, this.gpuDriver!!.libraryName)
|
||||
}
|
||||
fragmentView.onDriverInstallDone(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun useSystemDriver() {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
GpuDriverHelper.uninstallDriver()
|
||||
withContext(Dispatchers.Main) {
|
||||
with(this@SettingsFragmentPresenter) {
|
||||
this.gpuDriver =
|
||||
GpuDriverHelper.getInstalledDriverMetadata()
|
||||
?: GpuDriverHelper.getSystemDriverMetadata(context.applicationContext)
|
||||
this.libNameSetting.setString(this.settings!!, "")
|
||||
}
|
||||
fragmentView.onDriverUninstallDone()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val LOG_TYPE_NAMES = NativeLibrary.GetLogTypeNames()
|
||||
const val ARG_CONTROLLER_TYPE = "controller_type"
|
||||
|
@ -6,6 +6,7 @@ import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import org.dolphinemu.dolphinemu.features.settings.model.Settings
|
||||
import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem
|
||||
import org.dolphinemu.dolphinemu.utils.GpuDriverInstallResult
|
||||
|
||||
/**
|
||||
* Abstraction for a screen showing a list of settings. Instances of
|
||||
@ -111,4 +112,21 @@ interface SettingsFragmentView {
|
||||
* @param visible Whether the warning should be visible.
|
||||
*/
|
||||
fun setOldControllerSettingsWarningVisibility(visible: Boolean)
|
||||
|
||||
/**
|
||||
* Called when the driver installation is finished
|
||||
*
|
||||
* @param result The result of the driver installation
|
||||
*/
|
||||
fun onDriverInstallDone(result: GpuDriverInstallResult)
|
||||
|
||||
/**
|
||||
* Called when the driver uninstall process is finished
|
||||
*/
|
||||
fun onDriverUninstallDone()
|
||||
|
||||
/**
|
||||
* Shows a dialog asking the user to install or uninstall a GPU driver
|
||||
*/
|
||||
fun showGpuDriverDialog()
|
||||
}
|
||||
|
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
* Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
*/
|
||||
|
||||
package org.dolphinemu.dolphinemu.model
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.SerializationException
|
||||
import kotlinx.serialization.json.*
|
||||
import java.io.File
|
||||
|
||||
data class GpuDriverMetadata(
|
||||
val name : String,
|
||||
val author : String,
|
||||
val packageVersion : String,
|
||||
val vendor : String,
|
||||
val driverVersion : String,
|
||||
val minApi : Int,
|
||||
val description : String,
|
||||
val libraryName : String,
|
||||
) {
|
||||
private constructor(metadataV1 : GpuDriverMetadataV1) : this(
|
||||
name = metadataV1.name,
|
||||
author = metadataV1.author,
|
||||
packageVersion = metadataV1.packageVersion,
|
||||
vendor = metadataV1.vendor,
|
||||
driverVersion = metadataV1.driverVersion,
|
||||
minApi = metadataV1.minApi,
|
||||
description = metadataV1.description,
|
||||
libraryName = metadataV1.libraryName,
|
||||
)
|
||||
|
||||
val label get() = "${name}-v${packageVersion}"
|
||||
|
||||
companion object {
|
||||
private const val SCHEMA_VERSION_V1 = 1
|
||||
|
||||
fun deserialize(metadataFile : File) : GpuDriverMetadata {
|
||||
val metadataJson = Json.parseToJsonElement(metadataFile.readText())
|
||||
|
||||
return when (metadataJson.jsonObject["schemaVersion"]?.jsonPrimitive?.intOrNull) {
|
||||
SCHEMA_VERSION_V1 -> GpuDriverMetadata(Json.decodeFromJsonElement<GpuDriverMetadataV1>(metadataJson))
|
||||
else -> throw SerializationException("Unsupported metadata version")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
private data class GpuDriverMetadataV1(
|
||||
val schemaVersion : Int,
|
||||
val name : String,
|
||||
val author : String,
|
||||
val packageVersion : String,
|
||||
val vendor : String,
|
||||
val driverVersion : String,
|
||||
val minApi : Int,
|
||||
val description : String,
|
||||
val libraryName : String,
|
||||
)
|
@ -286,6 +286,7 @@ class MainPresenter(private val mainView: MainView, private val activity: Fragme
|
||||
const val REQUEST_WAD_FILE = 4
|
||||
const val REQUEST_WII_SAVE_FILE = 5
|
||||
const val REQUEST_NAND_BIN_FILE = 6
|
||||
const val REQUEST_GPU_DRIVER = 7
|
||||
|
||||
private var shouldRescanLibrary = true
|
||||
|
||||
|
@ -0,0 +1,148 @@
|
||||
// Copyright 2023 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
// Partially based on:
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
// Partially based on:
|
||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
package org.dolphinemu.dolphinemu.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import kotlinx.serialization.SerializationException
|
||||
import org.dolphinemu.dolphinemu.R
|
||||
import org.dolphinemu.dolphinemu.model.GpuDriverMetadata
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
|
||||
private const val GPU_DRIVER_META_FILE = "meta.json"
|
||||
|
||||
interface GpuDriverHelper {
|
||||
companion object {
|
||||
/**
|
||||
* Returns information about the system GPU driver.
|
||||
* @return An array containing the driver vendor and the driver version, in this order, or `null` if an error occurred
|
||||
*/
|
||||
private external fun getSystemDriverInfo(): Array<String>?
|
||||
|
||||
/**
|
||||
* Queries the driver for custom driver loading support.
|
||||
* @return `true` if the device supports loading custom drivers, `false` otherwise
|
||||
*/
|
||||
external fun supportsCustomDriverLoading(): Boolean
|
||||
|
||||
/**
|
||||
* Queries the driver for manual max clock forcing support
|
||||
*/
|
||||
external fun supportsForceMaxGpuClocks(): Boolean
|
||||
|
||||
/**
|
||||
* Calls into the driver to force the GPU to run at the maximum possible clock speed
|
||||
* @param force Whether to enable or disable the forced clocks
|
||||
*/
|
||||
external fun forceMaxGpuClocks(enable: Boolean)
|
||||
|
||||
/**
|
||||
* Uninstalls the currently installed custom driver
|
||||
*/
|
||||
fun uninstallDriver() {
|
||||
File(DirectoryInitialization.getExtractedDriverDirectory())
|
||||
.deleteRecursively()
|
||||
|
||||
File(DirectoryInitialization.getExtractedDriverDirectory()).mkdir()
|
||||
}
|
||||
|
||||
fun getInstalledDriverMetadata(): GpuDriverMetadata? {
|
||||
val metadataFile = File(
|
||||
DirectoryInitialization.getExtractedDriverDirectory(),
|
||||
GPU_DRIVER_META_FILE
|
||||
)
|
||||
if (!metadataFile.exists()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return try {
|
||||
GpuDriverMetadata.deserialize(metadataFile)
|
||||
} catch (e: SerializationException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches metadata about the system driver.
|
||||
* @return A [GpuDriverMetadata] object containing data about the system driver
|
||||
*/
|
||||
fun getSystemDriverMetadata(context: Context): GpuDriverMetadata? {
|
||||
val systemDriverInfo = getSystemDriverInfo()
|
||||
if (systemDriverInfo.isNullOrEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return GpuDriverMetadata(
|
||||
name = context.getString(R.string.system_driver),
|
||||
author = "",
|
||||
packageVersion = "",
|
||||
vendor = systemDriverInfo[0],
|
||||
driverVersion = systemDriverInfo[1],
|
||||
minApi = 0,
|
||||
description = context.getString(R.string.system_driver_desc),
|
||||
libraryName = ""
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs the given driver to the emulator's drivers directory.
|
||||
* @param stream InputStream of a driver package
|
||||
* @return The exit status of the installation process
|
||||
*/
|
||||
fun installDriver(stream: InputStream): GpuDriverInstallResult {
|
||||
uninstallDriver()
|
||||
|
||||
val driverDir = File(DirectoryInitialization.getExtractedDriverDirectory())
|
||||
try {
|
||||
ZipUtils.unzip(stream, driverDir)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
uninstallDriver()
|
||||
return GpuDriverInstallResult.InvalidArchive
|
||||
}
|
||||
|
||||
// Check that the metadata file exists
|
||||
val metadataFile = File(driverDir, GPU_DRIVER_META_FILE)
|
||||
if (!metadataFile.isFile) {
|
||||
uninstallDriver()
|
||||
return GpuDriverInstallResult.MissingMetadata
|
||||
}
|
||||
|
||||
// Check that the driver metadata is valid
|
||||
val driverMetadata = try {
|
||||
GpuDriverMetadata.deserialize(metadataFile)
|
||||
} catch (e: SerializationException) {
|
||||
uninstallDriver()
|
||||
return GpuDriverInstallResult.InvalidMetadata
|
||||
}
|
||||
|
||||
// Check that the device satisfies the driver's minimum Android version requirements
|
||||
if (Build.VERSION.SDK_INT < driverMetadata.minApi) {
|
||||
uninstallDriver()
|
||||
return GpuDriverInstallResult.UnsupportedAndroidVersion
|
||||
}
|
||||
|
||||
return GpuDriverInstallResult.Success
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class GpuDriverInstallResult {
|
||||
Success,
|
||||
InvalidArchive,
|
||||
MissingMetadata,
|
||||
InvalidMetadata,
|
||||
UnsupportedAndroidVersion,
|
||||
AlreadyInstalled,
|
||||
FileNotFound
|
||||
}
|
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
* Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
*/
|
||||
|
||||
package org.dolphinemu.dolphinemu.utils
|
||||
|
||||
import java.io.*
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipFile
|
||||
import java.util.zip.ZipInputStream
|
||||
|
||||
interface ZipUtils {
|
||||
companion object {
|
||||
/**
|
||||
* Extracts a zip file to the given target directory.
|
||||
* @exception IOException if unzipping fails for any reason
|
||||
*/
|
||||
@Throws(IOException::class)
|
||||
fun unzip(file : File, targetDirectory : File) {
|
||||
ZipFile(file).use { zipFile ->
|
||||
for (zipEntry in zipFile.entries()) {
|
||||
val destFile = createNewFile(targetDirectory, zipEntry)
|
||||
// If the zip entry is a file, we need to create its parent directories
|
||||
val destDirectory : File? = if (zipEntry.isDirectory) destFile else destFile.parentFile
|
||||
|
||||
// Create the destination directory
|
||||
if (destDirectory == null || (!destDirectory.isDirectory && !destDirectory.mkdirs()))
|
||||
throw FileNotFoundException("Failed to create destination directory: $destDirectory")
|
||||
|
||||
// If the entry is a directory we don't need to copy anything
|
||||
if (zipEntry.isDirectory)
|
||||
continue
|
||||
|
||||
// Copy bytes to destination
|
||||
try {
|
||||
zipFile.getInputStream(zipEntry).use { inputStream ->
|
||||
destFile.outputStream().use { outputStream ->
|
||||
inputStream.copyTo(outputStream)
|
||||
}
|
||||
}
|
||||
} catch (e : IOException) {
|
||||
if (destFile.exists())
|
||||
destFile.delete()
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts a zip file from the given stream to the given target directory.
|
||||
*
|
||||
* This method is ~5x slower than [unzip], as [ZipInputStream] uses a fixed `512` bytes buffer for inflation,
|
||||
* instead of `8192` bytes or more used by input streams returned by [ZipFile].
|
||||
* This results in ~8x the amount of JNI calls, producing an increased number of array bounds checking, which kills performance.
|
||||
* Usage of this method is discouraged when possible, [unzip] should be used instead.
|
||||
* Nevertheless, it's the only option when extracting zips obtained from content URIs, as a [File] object cannot be obtained from them.
|
||||
* @exception IOException if unzipping fails for any reason
|
||||
*/
|
||||
@Throws(IOException::class)
|
||||
fun unzip(stream : InputStream, targetDirectory : File) {
|
||||
ZipInputStream(BufferedInputStream(stream)).use { zis ->
|
||||
do {
|
||||
// Get the next entry, break if we've reached the end
|
||||
val zipEntry = zis.nextEntry ?: break
|
||||
|
||||
val destFile = createNewFile(targetDirectory, zipEntry)
|
||||
// If the zip entry is a file, we need to create its parent directories
|
||||
val destDirectory : File? = if (zipEntry.isDirectory) destFile else destFile.parentFile
|
||||
|
||||
// Create the destination directory
|
||||
if (destDirectory == null || (!destDirectory.isDirectory && !destDirectory.mkdirs()))
|
||||
throw FileNotFoundException("Failed to create destination directory: $destDirectory")
|
||||
|
||||
// If the entry is a directory we don't need to copy anything
|
||||
if (zipEntry.isDirectory)
|
||||
continue
|
||||
|
||||
// Copy bytes to destination
|
||||
try {
|
||||
BufferedOutputStream(destFile.outputStream()).use { zis.copyTo(it) }
|
||||
} catch (e : IOException) {
|
||||
if (destFile.exists())
|
||||
destFile.delete()
|
||||
throw e
|
||||
}
|
||||
} while (true)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely creates a new destination file where the given zip entry will be extracted to.
|
||||
*
|
||||
* @exception IOException if the file was being created outside of the target directory
|
||||
* **see:** [Zip Slip](https://github.com/snyk/zip-slip-vulnerability)
|
||||
*/
|
||||
@Throws(IOException::class)
|
||||
private fun createNewFile(destinationDir : File, zipEntry : ZipEntry) : File {
|
||||
val destFile = File(destinationDir, zipEntry.name)
|
||||
val destDirPath = destinationDir.canonicalPath
|
||||
val destFilePath = destFile.canonicalPath
|
||||
|
||||
if (!destFilePath.startsWith(destDirPath + File.separator))
|
||||
throw IOException("Entry is outside of the target dir: " + zipEntry.name)
|
||||
|
||||
return destFile
|
||||
}
|
||||
}
|
||||
}
|
@ -845,14 +845,19 @@ It can efficiently compress both junk data and encrypted Wii data.
|
||||
<string name="system_driver_desc">The GPU driver that is part of the OS.</string>
|
||||
|
||||
<!-- Custom GPU drivers -->
|
||||
<string name="gpu_driver_submenu">GPU driver</string>
|
||||
<string name="gpu_driver_dialog_title">Select the GPU driver for Dolphin</string>
|
||||
<string name="gpu_driver_dialog_system">Default</string>
|
||||
<string name="gpu_driver_dialog_install">Install driver</string>
|
||||
<string name="gpu_driver_dialog_uninstall_done">Successfully switched to the system driver</string>
|
||||
<string name="gpu_driver_submenu">GPU Driver</string>
|
||||
<string name="gpu_driver_install_inprogress">Installing the GPU driver…</string>
|
||||
<string name="gpu_driver_install_success">GPU driver installed successfully</string>
|
||||
<string name="gpu_driver_install_invalid_archive">Failed to unzip the provided driver package</string>
|
||||
<string name="gpu_driver_install_missing_metadata">The supplied driver package is invalid due to missing metadata</string>
|
||||
<string name="gpu_driver_install_invalid_metadata">The supplied driver package contains invalid metadata, it may be corrupted</string>
|
||||
<string name="gpu_driver_install_unsupported_android_version">Your device doesn\'t meet the minimum Android version requirements for the supplied driver</string>
|
||||
<string name="gpu_driver_install_already_installed">The supplied driver package is already installled</string>
|
||||
<string name="gpu_driver_install_missing_metadata">The supplied driver package is invalid due to missing metadata.</string>
|
||||
<string name="gpu_driver_install_invalid_metadata">The supplied driver package contains invalid metadata, it may be corrupted.</string>
|
||||
<string name="gpu_driver_install_unsupported_android_version">Your device doesn\'t meet the minimum Android version requirements for the supplied driver.</string>
|
||||
<string name="gpu_driver_install_already_installed">The supplied driver package is already installed.</string>
|
||||
<string name="gpu_driver_install_file_not_found">The selected file could not be found or accessed.</string>
|
||||
|
||||
<!-- Emulated USB Devices -->
|
||||
<string name="emulated_usb_devices">Emulated USB Devices</string>
|
||||
|
Loading…
Reference in New Issue
Block a user