Merge branch 'yuzu-emu:master' into master

This commit is contained in:
Aphcity 2023-09-21 12:09:05 +08:00 committed by GitHub
commit 514e207bb8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 324 additions and 88 deletions

View file

@ -49,6 +49,7 @@ class HomeSettingAdapter(
holder.option.onClick.invoke() holder.option.onClick.invoke()
} else { } else {
MessageDialogFragment.newInstance( MessageDialogFragment.newInstance(
activity,
titleId = holder.option.disabledTitleId, titleId = holder.option.disabledTitleId,
descriptionId = holder.option.disabledMessageId descriptionId = holder.option.disabledMessageId
).show(activity.supportFragmentManager, MessageDialogFragment.TAG) ).show(activity.supportFragmentManager, MessageDialogFragment.TAG)

View file

@ -26,6 +26,7 @@ import org.yuzu.yuzu_emu.BuildConfig
import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.FragmentAboutBinding import org.yuzu.yuzu_emu.databinding.FragmentAboutBinding
import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.ui.main.MainActivity
class AboutFragment : Fragment() { class AboutFragment : Fragment() {
private var _binding: FragmentAboutBinding? = null private var _binding: FragmentAboutBinding? = null
@ -92,6 +93,12 @@ class AboutFragment : Fragment() {
} }
} }
val mainActivity = requireActivity() as MainActivity
binding.buttonExport.setOnClickListener { mainActivity.exportUserData.launch("export.zip") }
binding.buttonImport.setOnClickListener {
mainActivity.importUserData.launch(arrayOf("application/zip"))
}
binding.buttonDiscord.setOnClickListener { openLink(getString(R.string.support_link)) } binding.buttonDiscord.setOnClickListener { openLink(getString(R.string.support_link)) }
binding.buttonWebsite.setOnClickListener { openLink(getString(R.string.website_link)) } binding.buttonWebsite.setOnClickListener { openLink(getString(R.string.website_link)) }
binding.buttonGithub.setOnClickListener { openLink(getString(R.string.github_link)) } binding.buttonGithub.setOnClickListener { openLink(getString(R.string.github_link)) }

View file

@ -65,25 +65,20 @@ class ImportExportSavesFragment : DialogFragment() {
} }
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return if (savesFolderRoot == "") { val builder = MaterialAlertDialogBuilder(requireContext())
MaterialAlertDialogBuilder(requireContext()) .setTitle(R.string.manage_save_data)
.setTitle(R.string.manage_save_data) .setPositiveButton(R.string.import_saves) { _, _ ->
.setMessage(R.string.import_export_saves_no_profile) documentPicker.launch(arrayOf("application/zip"))
.setPositiveButton(android.R.string.ok, null) }
.show() .setNeutralButton(android.R.string.cancel, null)
} else {
MaterialAlertDialogBuilder(requireContext()) if (savesFolderRoot != "") {
.setTitle(R.string.manage_save_data) builder.setNegativeButton(R.string.export_saves) { _, _ ->
.setMessage(R.string.manage_save_data_description) exportSave()
.setNegativeButton(R.string.export_saves) { _, _ -> }
exportSave()
}
.setPositiveButton(R.string.import_saves) { _, _ ->
documentPicker.launch(arrayOf("application/zip"))
}
.setNeutralButton(android.R.string.cancel, null)
.show()
} }
return builder.show()
} }
/** /**
@ -187,6 +182,7 @@ class ImportExportSavesFragment : DialogFragment() {
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
if (!validZip) { if (!validZip) {
MessageDialogFragment.newInstance( MessageDialogFragment.newInstance(
requireActivity(),
titleId = R.string.save_file_invalid_zip_structure, titleId = R.string.save_file_invalid_zip_structure,
descriptionId = R.string.save_file_invalid_zip_structure_description descriptionId = R.string.save_file_invalid_zip_structure_description
).show(activity.supportFragmentManager, MessageDialogFragment.TAG) ).show(activity.supportFragmentManager, MessageDialogFragment.TAG)

View file

@ -4,6 +4,7 @@
package org.yuzu.yuzu_emu.fragments package org.yuzu.yuzu_emu.fragments
import android.app.Dialog import android.app.Dialog
import android.content.DialogInterface
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
@ -18,6 +19,7 @@ import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle import androidx.lifecycle.repeatOnLifecycle
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
import org.yuzu.yuzu_emu.model.TaskViewModel import org.yuzu.yuzu_emu.model.TaskViewModel
@ -28,19 +30,27 @@ class IndeterminateProgressDialogFragment : DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val titleId = requireArguments().getInt(TITLE) val titleId = requireArguments().getInt(TITLE)
val cancellable = requireArguments().getBoolean(CANCELLABLE)
binding = DialogProgressBarBinding.inflate(layoutInflater) binding = DialogProgressBarBinding.inflate(layoutInflater)
binding.progressBar.isIndeterminate = true binding.progressBar.isIndeterminate = true
val dialog = MaterialAlertDialogBuilder(requireContext()) val dialog = MaterialAlertDialogBuilder(requireContext())
.setTitle(titleId) .setTitle(titleId)
.setView(binding.root) .setView(binding.root)
.create()
dialog.setCanceledOnTouchOutside(false) if (cancellable) {
dialog.setNegativeButton(android.R.string.cancel) { _: DialogInterface, _: Int ->
taskViewModel.setCancelled(true)
}
}
val alertDialog = dialog.create()
alertDialog.setCanceledOnTouchOutside(false)
if (!taskViewModel.isRunning.value) { if (!taskViewModel.isRunning.value) {
taskViewModel.runTask() taskViewModel.runTask()
} }
return dialog return alertDialog
} }
override fun onCreateView( override fun onCreateView(
@ -53,21 +63,35 @@ class IndeterminateProgressDialogFragment : DialogFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.lifecycleScope.apply {
repeatOnLifecycle(Lifecycle.State.CREATED) { launch {
taskViewModel.isComplete.collect { repeatOnLifecycle(Lifecycle.State.CREATED) {
if (it) { taskViewModel.isComplete.collect {
dismiss() if (it) {
when (val result = taskViewModel.result.value) { dismiss()
is String -> Toast.makeText(requireContext(), result, Toast.LENGTH_LONG) when (val result = taskViewModel.result.value) {
.show() is String -> Toast.makeText(
requireContext(),
result,
Toast.LENGTH_LONG
).show()
is MessageDialogFragment -> result.show( is MessageDialogFragment -> result.show(
requireActivity().supportFragmentManager, requireActivity().supportFragmentManager,
MessageDialogFragment.TAG MessageDialogFragment.TAG
) )
}
taskViewModel.clear()
}
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
taskViewModel.cancelled.collect {
if (it) {
dialog?.setTitle(R.string.cancelling)
} }
taskViewModel.clear()
} }
} }
} }
@ -78,16 +102,19 @@ class IndeterminateProgressDialogFragment : DialogFragment() {
const val TAG = "IndeterminateProgressDialogFragment" const val TAG = "IndeterminateProgressDialogFragment"
private const val TITLE = "Title" private const val TITLE = "Title"
private const val CANCELLABLE = "Cancellable"
fun newInstance( fun newInstance(
activity: AppCompatActivity, activity: AppCompatActivity,
titleId: Int, titleId: Int,
cancellable: Boolean = false,
task: () -> Any task: () -> Any
): IndeterminateProgressDialogFragment { ): IndeterminateProgressDialogFragment {
val dialog = IndeterminateProgressDialogFragment() val dialog = IndeterminateProgressDialogFragment()
val args = Bundle() val args = Bundle()
ViewModelProvider(activity)[TaskViewModel::class.java].task = task ViewModelProvider(activity)[TaskViewModel::class.java].task = task
args.putInt(TITLE, titleId) args.putInt(TITLE, titleId)
args.putBoolean(CANCELLABLE, cancellable)
dialog.arguments = args dialog.arguments = args
return dialog return dialog
} }

View file

@ -4,14 +4,21 @@
package org.yuzu.yuzu_emu.fragments package org.yuzu.yuzu_emu.fragments
import android.app.Dialog import android.app.Dialog
import android.content.DialogInterface
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.ViewModelProvider
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.model.MessageDialogViewModel
class MessageDialogFragment : DialogFragment() { class MessageDialogFragment : DialogFragment() {
private val messageDialogViewModel: MessageDialogViewModel by activityViewModels()
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val titleId = requireArguments().getInt(TITLE_ID) val titleId = requireArguments().getInt(TITLE_ID)
val titleString = requireArguments().getString(TITLE_STRING)!! val titleString = requireArguments().getString(TITLE_STRING)!!
@ -37,6 +44,12 @@ class MessageDialogFragment : DialogFragment() {
return dialog.show() return dialog.show()
} }
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
messageDialogViewModel.dismissAction.invoke()
messageDialogViewModel.clear()
}
private fun openLink(link: String) { private fun openLink(link: String) {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(link)) val intent = Intent(Intent.ACTION_VIEW, Uri.parse(link))
startActivity(intent) startActivity(intent)
@ -52,11 +65,13 @@ class MessageDialogFragment : DialogFragment() {
private const val HELP_LINK = "Link" private const val HELP_LINK = "Link"
fun newInstance( fun newInstance(
activity: FragmentActivity,
titleId: Int = 0, titleId: Int = 0,
titleString: String = "", titleString: String = "",
descriptionId: Int = 0, descriptionId: Int = 0,
descriptionString: String = "", descriptionString: String = "",
helpLinkId: Int = 0 helpLinkId: Int = 0,
dismissAction: () -> Unit = {}
): MessageDialogFragment { ): MessageDialogFragment {
val dialog = MessageDialogFragment() val dialog = MessageDialogFragment()
val bundle = Bundle() val bundle = Bundle()
@ -67,6 +82,8 @@ class MessageDialogFragment : DialogFragment() {
putString(DESCRIPTION_STRING, descriptionString) putString(DESCRIPTION_STRING, descriptionString)
putInt(HELP_LINK, helpLinkId) putInt(HELP_LINK, helpLinkId)
} }
ViewModelProvider(activity)[MessageDialogViewModel::class.java].dismissAction =
dismissAction
dialog.arguments = bundle dialog.arguments = bundle
return dialog return dialog
} }

View file

@ -0,0 +1,14 @@
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.model
import androidx.lifecycle.ViewModel
class MessageDialogViewModel : ViewModel() {
var dismissAction: () -> Unit = {}
fun clear() {
dismissAction = {}
}
}

View file

@ -20,12 +20,20 @@ class TaskViewModel : ViewModel() {
val isRunning: StateFlow<Boolean> get() = _isRunning val isRunning: StateFlow<Boolean> get() = _isRunning
private val _isRunning = MutableStateFlow(false) private val _isRunning = MutableStateFlow(false)
val cancelled: StateFlow<Boolean> get() = _cancelled
private val _cancelled = MutableStateFlow(false)
lateinit var task: () -> Any lateinit var task: () -> Any
fun clear() { fun clear() {
_result.value = Any() _result.value = Any()
_isComplete.value = false _isComplete.value = false
_isRunning.value = false _isRunning.value = false
_cancelled.value = false
}
fun setCancelled(value: Boolean) {
_cancelled.value = value
} }
fun runTask() { fun runTask() {

View file

@ -46,13 +46,21 @@ import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
import org.yuzu.yuzu_emu.model.GamesViewModel import org.yuzu.yuzu_emu.model.GamesViewModel
import org.yuzu.yuzu_emu.model.HomeViewModel import org.yuzu.yuzu_emu.model.HomeViewModel
import org.yuzu.yuzu_emu.model.TaskViewModel
import org.yuzu.yuzu_emu.utils.* import org.yuzu.yuzu_emu.utils.*
import java.io.BufferedInputStream
import java.io.BufferedOutputStream
import java.io.FileOutputStream
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
import java.util.zip.ZipOutputStream
class MainActivity : AppCompatActivity(), ThemeProvider { class MainActivity : AppCompatActivity(), ThemeProvider {
private lateinit var binding: ActivityMainBinding private lateinit var binding: ActivityMainBinding
private val homeViewModel: HomeViewModel by viewModels() private val homeViewModel: HomeViewModel by viewModels()
private val gamesViewModel: GamesViewModel by viewModels() private val gamesViewModel: GamesViewModel by viewModels()
private val taskViewModel: TaskViewModel by viewModels()
override var themeId: Int = 0 override var themeId: Int = 0
@ -307,6 +315,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
fun processKey(result: Uri): Boolean { fun processKey(result: Uri): Boolean {
if (FileUtil.getExtension(result) != "keys") { if (FileUtil.getExtension(result) != "keys") {
MessageDialogFragment.newInstance( MessageDialogFragment.newInstance(
this,
titleId = R.string.reading_keys_failure, titleId = R.string.reading_keys_failure,
descriptionId = R.string.install_prod_keys_failure_extension_description descriptionId = R.string.install_prod_keys_failure_extension_description
).show(supportFragmentManager, MessageDialogFragment.TAG) ).show(supportFragmentManager, MessageDialogFragment.TAG)
@ -336,6 +345,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
return true return true
} else { } else {
MessageDialogFragment.newInstance( MessageDialogFragment.newInstance(
this,
titleId = R.string.invalid_keys_error, titleId = R.string.invalid_keys_error,
descriptionId = R.string.install_keys_failure_description, descriptionId = R.string.install_keys_failure_description,
helpLinkId = R.string.dumping_keys_quickstart_link helpLinkId = R.string.dumping_keys_quickstart_link
@ -376,6 +386,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
val filteredNumOfFiles = cacheFirmwareDir.list(filterNCA)?.size ?: -2 val filteredNumOfFiles = cacheFirmwareDir.list(filterNCA)?.size ?: -2
messageToShow = if (unfilteredNumOfFiles != filteredNumOfFiles) { messageToShow = if (unfilteredNumOfFiles != filteredNumOfFiles) {
MessageDialogFragment.newInstance( MessageDialogFragment.newInstance(
this,
titleId = R.string.firmware_installed_failure, titleId = R.string.firmware_installed_failure,
descriptionId = R.string.firmware_installed_failure_description descriptionId = R.string.firmware_installed_failure_description
) )
@ -395,7 +406,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
IndeterminateProgressDialogFragment.newInstance( IndeterminateProgressDialogFragment.newInstance(
this, this,
R.string.firmware_installing, R.string.firmware_installing,
task task = task
).show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG) ).show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
} }
@ -407,6 +418,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
if (FileUtil.getExtension(result) != "bin") { if (FileUtil.getExtension(result) != "bin") {
MessageDialogFragment.newInstance( MessageDialogFragment.newInstance(
this,
titleId = R.string.reading_keys_failure, titleId = R.string.reading_keys_failure,
descriptionId = R.string.install_amiibo_keys_failure_extension_description descriptionId = R.string.install_amiibo_keys_failure_extension_description
).show(supportFragmentManager, MessageDialogFragment.TAG) ).show(supportFragmentManager, MessageDialogFragment.TAG)
@ -434,6 +446,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
).show() ).show()
} else { } else {
MessageDialogFragment.newInstance( MessageDialogFragment.newInstance(
this,
titleId = R.string.invalid_keys_error, titleId = R.string.invalid_keys_error,
descriptionId = R.string.install_keys_failure_description, descriptionId = R.string.install_keys_failure_description,
helpLinkId = R.string.dumping_keys_quickstart_link helpLinkId = R.string.dumping_keys_quickstart_link
@ -583,12 +596,14 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
installResult.append(separator) installResult.append(separator)
} }
return@newInstance MessageDialogFragment.newInstance( return@newInstance MessageDialogFragment.newInstance(
this,
titleId = R.string.install_game_content_failure, titleId = R.string.install_game_content_failure,
descriptionString = installResult.toString().trim(), descriptionString = installResult.toString().trim(),
helpLinkId = R.string.install_game_content_help_link helpLinkId = R.string.install_game_content_help_link
) )
} else { } else {
return@newInstance MessageDialogFragment.newInstance( return@newInstance MessageDialogFragment.newInstance(
this,
titleId = R.string.install_game_content_success, titleId = R.string.install_game_content_success,
descriptionString = installResult.toString().trim() descriptionString = installResult.toString().trim()
) )
@ -596,4 +611,110 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
}.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG) }.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
} }
} }
val exportUserData = registerForActivityResult(
ActivityResultContracts.CreateDocument("application/zip")
) { result ->
if (result == null) {
return@registerForActivityResult
}
IndeterminateProgressDialogFragment.newInstance(
this,
R.string.exporting_user_data,
true
) {
val zos = ZipOutputStream(
BufferedOutputStream(contentResolver.openOutputStream(result))
)
zos.use { stream ->
File(DirectoryInitialization.userDirectory!!).walkTopDown().forEach { file ->
if (taskViewModel.cancelled.value) {
return@newInstance R.string.user_data_export_cancelled
}
if (!file.isDirectory) {
val newPath = file.path.substring(
DirectoryInitialization.userDirectory!!.length,
file.path.length
)
stream.putNextEntry(ZipEntry(newPath))
stream.write(file.readBytes())
stream.closeEntry()
}
}
}
return@newInstance getString(R.string.user_data_export_success)
}.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
}
val importUserData =
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
if (result == null) {
return@registerForActivityResult
}
IndeterminateProgressDialogFragment.newInstance(
this,
R.string.importing_user_data
) {
val checkStream =
ZipInputStream(BufferedInputStream(contentResolver.openInputStream(result)))
var isYuzuBackup = false
checkStream.use { stream ->
var ze: ZipEntry? = null
while (stream.nextEntry?.also { ze = it } != null) {
if (ze!!.name.trim() == "/config/config.ini") {
isYuzuBackup = true
return@use
}
}
}
if (!isYuzuBackup) {
return@newInstance getString(R.string.invalid_yuzu_backup)
}
File(DirectoryInitialization.userDirectory!!).deleteRecursively()
val zis =
ZipInputStream(BufferedInputStream(contentResolver.openInputStream(result)))
val userDirectory = File(DirectoryInitialization.userDirectory!!)
val canonicalPath = userDirectory.canonicalPath + '/'
zis.use { stream ->
var ze: ZipEntry? = stream.nextEntry
while (ze != null) {
val newFile = File(userDirectory, ze!!.name)
val destinationDirectory =
if (ze!!.isDirectory) newFile else newFile.parentFile
if (!newFile.canonicalPath.startsWith(canonicalPath)) {
throw SecurityException(
"Zip file attempted path traversal! ${ze!!.name}"
)
}
if (!destinationDirectory.isDirectory && !destinationDirectory.mkdirs()) {
throw IOException("Failed to create directory $destinationDirectory")
}
if (!ze!!.isDirectory) {
val buffer = ByteArray(8096)
var read: Int
BufferedOutputStream(FileOutputStream(newFile)).use { bos ->
while (zis.read(buffer).also { read = it } != -1) {
bos.write(buffer, 0, read)
}
}
}
ze = stream.nextEntry
}
}
// Reinitialize relevant data
NativeLibrary.initializeEmulation()
gamesViewModel.reloadGames(false)
return@newInstance getString(R.string.user_data_import_success)
}.show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
}
} }

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?attr/colorControlNormal"
android:pathData="M9,16h6v-6h4l-7,-7 -7,7h4zM5,18h14v2L5,20z" />
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?attr/colorControlNormal"
android:pathData="M19,9h-4V3H9v6H5l7,7 7,-7zM5,18v2h14v-2H5z" />
</vector>

View file

@ -1,24 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <com.google.android.material.progressindicator.LinearProgressIndicator xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"> android:id="@+id/progress_bar"
android:layout_width="match_parent"
<com.google.android.material.progressindicator.LinearProgressIndicator android:layout_height="wrap_content"
android:id="@+id/progress_bar" android:padding="24dp"
android:layout_width="match_parent" app:trackCornerRadius="4dp" />
android:layout_height="wrap_content"
android:layout_margin="24dp"
app:trackCornerRadius="4dp" />
<TextView
android:id="@+id/progress_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="24dp"
android:layout_marginRight="24dp"
android:layout_marginBottom="24dp"
android:gravity="end" />
</LinearLayout>

View file

@ -176,6 +176,67 @@
</LinearLayout> </LinearLayout>
<com.google.android.material.divider.MaterialDivider
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingVertical="16dp"
android:paddingHorizontal="16dp"
android:orientation="vertical"
android:layout_weight="1">
<com.google.android.material.textview.MaterialTextView
style="@style/TextAppearance.Material3.TitleMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:textAlignment="viewStart"
android:text="@string/user_data" />
<com.google.android.material.textview.MaterialTextView
style="@style/TextAppearance.Material3.BodyMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:layout_marginTop="6dp"
android:textAlignment="viewStart"
android:text="@string/user_data_description" />
</LinearLayout>
<Button
android:id="@+id/button_import"
style="@style/Widget.Material3.Button.IconButton.Filled.Tonal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:contentDescription="@string/string_import"
android:tooltipText="@string/string_import"
app:icon="@drawable/ic_import" />
<Button
android:id="@+id/button_export"
style="@style/Widget.Material3.Button.IconButton.Filled.Tonal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginEnd="24dp"
android:layout_gravity="center_vertical"
android:contentDescription="@string/export"
android:tooltipText="@string/export"
app:icon="@drawable/ic_export" />
</LinearLayout>
<com.google.android.material.divider.MaterialDivider <com.google.android.material.divider.MaterialDivider
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"

View file

@ -77,9 +77,7 @@
<string name="notification_no_directory_link">yuzu-Verzeichnis konnte nicht geöffnet werden</string> <string name="notification_no_directory_link">yuzu-Verzeichnis konnte nicht geöffnet werden</string>
<string name="notification_no_directory_link_description">Bitte suche den Benutzerordner manuell über die Seitenleiste des Dateimanagers.</string> <string name="notification_no_directory_link_description">Bitte suche den Benutzerordner manuell über die Seitenleiste des Dateimanagers.</string>
<string name="manage_save_data">Speicherdaten verwalten</string> <string name="manage_save_data">Speicherdaten verwalten</string>
<string name="manage_save_data_description">Speicherdaten gefunden. Bitte wähle unten eine Option aus.</string>
<string name="import_export_saves_description">Speicherdaten importieren oder exportieren</string> <string name="import_export_saves_description">Speicherdaten importieren oder exportieren</string>
<string name="import_export_saves_no_profile">Keine Speicherdaten gefunden. Bitte starte ein Spiel und versuche es erneut.</string>
<string name="save_file_imported_success">Erfolgreich importiert</string> <string name="save_file_imported_success">Erfolgreich importiert</string>
<string name="save_file_invalid_zip_structure">Ungültige Speicherverzeichnisstruktur</string> <string name="save_file_invalid_zip_structure">Ungültige Speicherverzeichnisstruktur</string>
<string name="save_file_invalid_zip_structure_description">Der erste Unterordnername muss die Titel-ID des Spiels sein.</string> <string name="save_file_invalid_zip_structure_description">Der erste Unterordnername muss die Titel-ID des Spiels sein.</string>

View file

@ -79,9 +79,7 @@
<string name="notification_no_directory_link">No se pudo abrir la carpeta yuzu</string> <string name="notification_no_directory_link">No se pudo abrir la carpeta yuzu</string>
<string name="notification_no_directory_link_description">Por favor, busque la carpeta user con el panel lateral del explorador de archivos de forma manual.</string> <string name="notification_no_directory_link_description">Por favor, busque la carpeta user con el panel lateral del explorador de archivos de forma manual.</string>
<string name="manage_save_data">Administrar datos de guardado</string> <string name="manage_save_data">Administrar datos de guardado</string>
<string name="manage_save_data_description">Guardar los datos encontrados. Por favor, seleccione una opción de abajo.</string>
<string name="import_export_saves_description">Importar o exportar archivos de guardado</string> <string name="import_export_saves_description">Importar o exportar archivos de guardado</string>
<string name="import_export_saves_no_profile">No se han encontrado datos de guardado. Por favor, ejecute un juego y vuelva a intentarlo.</string>
<string name="save_file_imported_success">Importado correctamente</string> <string name="save_file_imported_success">Importado correctamente</string>
<string name="save_file_invalid_zip_structure">Estructura del directorio de guardado no válido</string> <string name="save_file_invalid_zip_structure">Estructura del directorio de guardado no válido</string>
<string name="save_file_invalid_zip_structure_description">El nombre de la primera subcarpeta debe ser el Title ID del juego.</string> <string name="save_file_invalid_zip_structure_description">El nombre de la primera subcarpeta debe ser el Title ID del juego.</string>

View file

@ -79,9 +79,7 @@
<string name="notification_no_directory_link">Impossible d\'ouvrir le répertoire de yuzu</string> <string name="notification_no_directory_link">Impossible d\'ouvrir le répertoire de yuzu</string>
<string name="notification_no_directory_link_description">Veuillez localiser manuellement le dossier utilisateur avec le panneau latéral du gestionnaire de fichiers.</string> <string name="notification_no_directory_link_description">Veuillez localiser manuellement le dossier utilisateur avec le panneau latéral du gestionnaire de fichiers.</string>
<string name="manage_save_data">Gérer les données de sauvegarde</string> <string name="manage_save_data">Gérer les données de sauvegarde</string>
<string name="manage_save_data_description">Données de sauvegarde trouvées. Veuillez sélectionner une option ci-dessous.</string>
<string name="import_export_saves_description">Importer ou exporter des fichiers de sauvegarde</string> <string name="import_export_saves_description">Importer ou exporter des fichiers de sauvegarde</string>
<string name="import_export_saves_no_profile">Aucune données de sauvegarde trouvées. Veuillez lancer un jeu et réessayer.</string>
<string name="save_file_imported_success">Importé avec succès</string> <string name="save_file_imported_success">Importé avec succès</string>
<string name="save_file_invalid_zip_structure">Structure de répertoire de sauvegarde non valide</string> <string name="save_file_invalid_zip_structure">Structure de répertoire de sauvegarde non valide</string>
<string name="save_file_invalid_zip_structure_description">Le nom du premier sous-dossier doit être l\'identifiant du titre du jeu.</string> <string name="save_file_invalid_zip_structure_description">Le nom du premier sous-dossier doit être l\'identifiant du titre du jeu.</string>

View file

@ -79,9 +79,7 @@
<string name="notification_no_directory_link">Impossibile aprire la cartella di yuzu</string> <string name="notification_no_directory_link">Impossibile aprire la cartella di yuzu</string>
<string name="notification_no_directory_link_description">Per favore individua la cartella dell\'utente manualmente con il pannello laterale del file manager.</string> <string name="notification_no_directory_link_description">Per favore individua la cartella dell\'utente manualmente con il pannello laterale del file manager.</string>
<string name="manage_save_data">Gestisci i salvataggi</string> <string name="manage_save_data">Gestisci i salvataggi</string>
<string name="manage_save_data_description">Salvataggio non trovato. Seleziona un\'opzione di seguito.</string>
<string name="import_export_saves_description">Importa o esporta i salvataggi</string> <string name="import_export_saves_description">Importa o esporta i salvataggi</string>
<string name="import_export_saves_no_profile">Nessun salvataggio trovato. Avvia un gioco e riprova.</string>
<string name="save_file_imported_success">Importato con successo</string> <string name="save_file_imported_success">Importato con successo</string>
<string name="save_file_invalid_zip_structure">La struttura della cartella dei salvataggi è invalida</string> <string name="save_file_invalid_zip_structure">La struttura della cartella dei salvataggi è invalida</string>
<string name="save_file_invalid_zip_structure_description">La prima sotto cartella <b>deve</b> chiamarsi come l\'ID del titolo del gioco.</string> <string name="save_file_invalid_zip_structure_description">La prima sotto cartella <b>deve</b> chiamarsi come l\'ID del titolo del gioco.</string>

View file

@ -78,9 +78,7 @@
<string name="notification_no_directory_link">yuzuのディレクトリを開けません</string> <string name="notification_no_directory_link">yuzuのディレクトリを開けません</string>
<string name="notification_no_directory_link_description">ファイルマネージャのサイドパネルでユーザーフォルダを手動で探してください。</string> <string name="notification_no_directory_link_description">ファイルマネージャのサイドパネルでユーザーフォルダを手動で探してください。</string>
<string name="manage_save_data">セーブデータを管理</string> <string name="manage_save_data">セーブデータを管理</string>
<string name="manage_save_data_description">セーブデータが見つかりました。以下のオプションから選択してください。</string>
<string name="import_export_saves_description">セーブファイルをインポート/エクスポート</string> <string name="import_export_saves_description">セーブファイルをインポート/エクスポート</string>
<string name="import_export_saves_no_profile">セーブデータがありません。ゲームを起動してから再度お試しください。</string>
<string name="save_file_imported_success">インポートが完了しました</string> <string name="save_file_imported_success">インポートが完了しました</string>
<string name="save_file_invalid_zip_structure">セーブデータのディレクトリ構造が無効です</string> <string name="save_file_invalid_zip_structure">セーブデータのディレクトリ構造が無効です</string>
<string name="save_file_invalid_zip_structure_description">最初のサブフォルダ名は、ゲームのタイトルIDである必要があります。</string> <string name="save_file_invalid_zip_structure_description">最初のサブフォルダ名は、ゲームのタイトルIDである必要があります。</string>

View file

@ -79,9 +79,7 @@
<string name="notification_no_directory_link">yuzu 디렉토리를 열 수 없음</string> <string name="notification_no_directory_link">yuzu 디렉토리를 열 수 없음</string>
<string name="notification_no_directory_link_description">파일 관리자의 사이드 패널에서 사용자 폴더를 수동으로 찾아주세요.</string> <string name="notification_no_directory_link_description">파일 관리자의 사이드 패널에서 사용자 폴더를 수동으로 찾아주세요.</string>
<string name="manage_save_data">저장 데이터 관리</string> <string name="manage_save_data">저장 데이터 관리</string>
<string name="manage_save_data_description">데이터를 저장했습니다. 아래에서 옵션을 선택하세요.</string>
<string name="import_export_saves_description">저장 파일 가져오기 또는 내보내기</string> <string name="import_export_saves_description">저장 파일 가져오기 또는 내보내기</string>
<string name="import_export_saves_no_profile">저장 데이터를 찾을 수 없습니다. 게임을 실행한 후 다시 시도하세요.</string>
<string name="save_file_imported_success">가져오기 성공</string> <string name="save_file_imported_success">가져오기 성공</string>
<string name="save_file_invalid_zip_structure">저장 디렉터리 구조가 잘못됨</string> <string name="save_file_invalid_zip_structure">저장 디렉터리 구조가 잘못됨</string>
<string name="save_file_invalid_zip_structure_description">첫 번째 하위 폴더 이름은 게임의 타이틀 ID여야 합니다.</string> <string name="save_file_invalid_zip_structure_description">첫 번째 하위 폴더 이름은 게임의 타이틀 ID여야 합니다.</string>

View file

@ -79,9 +79,7 @@
<string name="notification_no_directory_link">Kunne ikke åpne yuzu-katalogen</string> <string name="notification_no_directory_link">Kunne ikke åpne yuzu-katalogen</string>
<string name="notification_no_directory_link_description">Finn brukermappen manuelt med filbehandlingens sidepanel.</string> <string name="notification_no_directory_link_description">Finn brukermappen manuelt med filbehandlingens sidepanel.</string>
<string name="manage_save_data">Administrere lagringsdata</string> <string name="manage_save_data">Administrere lagringsdata</string>
<string name="manage_save_data_description">Lagringsdata funnet. Velg et alternativ nedenfor.</string>
<string name="import_export_saves_description">Importer eller eksporter lagringsfiler</string> <string name="import_export_saves_description">Importer eller eksporter lagringsfiler</string>
<string name="import_export_saves_no_profile">Ingen lagringsdata funnet. Start et nytt spill og prøv på nytt.</string>
<string name="save_file_imported_success">Vellykket import</string> <string name="save_file_imported_success">Vellykket import</string>
<string name="save_file_invalid_zip_structure">Ugyldig struktur for lagringskatalog</string> <string name="save_file_invalid_zip_structure">Ugyldig struktur for lagringskatalog</string>
<string name="save_file_invalid_zip_structure_description">Det første undermappenavnet må være spillets tittel-ID.</string> <string name="save_file_invalid_zip_structure_description">Det første undermappenavnet må være spillets tittel-ID.</string>

View file

@ -79,9 +79,7 @@
<string name="notification_no_directory_link">Nie można otworzyć folderu emulatora</string> <string name="notification_no_directory_link">Nie można otworzyć folderu emulatora</string>
<string name="notification_no_directory_link_description">Proszę wybrać ręcznie folder z pomocą panelu bocznego menedżera plików.</string> <string name="notification_no_directory_link_description">Proszę wybrać ręcznie folder z pomocą panelu bocznego menedżera plików.</string>
<string name="manage_save_data">Zarządzaj plikami zapisów gier</string> <string name="manage_save_data">Zarządzaj plikami zapisów gier</string>
<string name="manage_save_data_description">Znaleziono pliki zapisów gier. Wybierz opcję poniżej.</string>
<string name="import_export_saves_description">Importuj lub wyeksportuj pliki zapisów</string> <string name="import_export_saves_description">Importuj lub wyeksportuj pliki zapisów</string>
<string name="import_export_saves_no_profile">Nie znaleziono plików zapisów. Uruchom grę i spróbuj ponownie.</string>
<string name="save_file_imported_success">Zaimportowano pomyślnie</string> <string name="save_file_imported_success">Zaimportowano pomyślnie</string>
<string name="save_file_invalid_zip_structure">Niepoprawna struktura folderów</string> <string name="save_file_invalid_zip_structure">Niepoprawna struktura folderów</string>
<string name="save_file_invalid_zip_structure_description">Pierwszy podkatalog musi zawierać w nazwie numer ID tytułu gry.</string> <string name="save_file_invalid_zip_structure_description">Pierwszy podkatalog musi zawierać w nazwie numer ID tytułu gry.</string>

View file

@ -79,9 +79,7 @@
<string name="notification_no_directory_link">Impossível abrir pasta Yuzu</string> <string name="notification_no_directory_link">Impossível abrir pasta Yuzu</string>
<string name="notification_no_directory_link_description">Localiza a pasta de utilizador manualmente com o painel lateral do gestor de ficheiros.</string> <string name="notification_no_directory_link_description">Localiza a pasta de utilizador manualmente com o painel lateral do gestor de ficheiros.</string>
<string name="manage_save_data">Gerir dados guardados</string> <string name="manage_save_data">Gerir dados guardados</string>
<string name="manage_save_data_description">Dados não encontrados. Por favor seleciona uma opção abaixo.</string>
<string name="import_export_saves_description">Importa ou exporta dados guardados</string> <string name="import_export_saves_description">Importa ou exporta dados guardados</string>
<string name="import_export_saves_no_profile">Dados não encontrados. Por favor lança o jogo e tenta novamente.</string>
<string name="save_file_imported_success">Importado com sucesso</string> <string name="save_file_imported_success">Importado com sucesso</string>
<string name="save_file_invalid_zip_structure">Estrutura de diretório de dados invalida</string> <string name="save_file_invalid_zip_structure">Estrutura de diretório de dados invalida</string>
<string name="save_file_invalid_zip_structure_description">O nome da primeira sub pasta tem de ser a ID do jogo.</string> <string name="save_file_invalid_zip_structure_description">O nome da primeira sub pasta tem de ser a ID do jogo.</string>

View file

@ -79,9 +79,7 @@
<string name="notification_no_directory_link">Impossível abrir pasta Yuzu</string> <string name="notification_no_directory_link">Impossível abrir pasta Yuzu</string>
<string name="notification_no_directory_link_description">Localiza a pasta de utilizador manualmente com o painel lateral do gestor de ficheiros.</string> <string name="notification_no_directory_link_description">Localiza a pasta de utilizador manualmente com o painel lateral do gestor de ficheiros.</string>
<string name="manage_save_data">Gerir dados guardados</string> <string name="manage_save_data">Gerir dados guardados</string>
<string name="manage_save_data_description">Dados não encontrados. Por favor seleciona uma opção abaixo.</string>
<string name="import_export_saves_description">Importa ou exporta dados guardados</string> <string name="import_export_saves_description">Importa ou exporta dados guardados</string>
<string name="import_export_saves_no_profile">Dados não encontrados. Por favor lança o jogo e tenta novamente.</string>
<string name="save_file_imported_success">Importado com sucesso</string> <string name="save_file_imported_success">Importado com sucesso</string>
<string name="save_file_invalid_zip_structure">Estrutura de diretório de dados invalida</string> <string name="save_file_invalid_zip_structure">Estrutura de diretório de dados invalida</string>
<string name="save_file_invalid_zip_structure_description">O nome da primeira sub pasta tem de ser a ID do jogo.</string> <string name="save_file_invalid_zip_structure_description">O nome da primeira sub pasta tem de ser a ID do jogo.</string>

View file

@ -79,9 +79,7 @@
<string name="notification_no_directory_link">Не удалось открыть папку yuzu</string> <string name="notification_no_directory_link">Не удалось открыть папку yuzu</string>
<string name="notification_no_directory_link_description">Пожалуйста, найдите папку пользователя с помощью боковой панели файлового менеджера вручную.</string> <string name="notification_no_directory_link_description">Пожалуйста, найдите папку пользователя с помощью боковой панели файлового менеджера вручную.</string>
<string name="manage_save_data">Управление данными сохранений</string> <string name="manage_save_data">Управление данными сохранений</string>
<string name="manage_save_data_description">Найдено данные сохранений. Пожалуйста, выберите вариант ниже.</string>
<string name="import_export_saves_description">Импорт или экспорт файлов сохранения</string> <string name="import_export_saves_description">Импорт или экспорт файлов сохранения</string>
<string name="import_export_saves_no_profile">Данные сохранений не найдены. Пожалуйста, запустите игру и повторите попытку.</string>
<string name="save_file_imported_success">Успешно импортировано</string> <string name="save_file_imported_success">Успешно импортировано</string>
<string name="save_file_invalid_zip_structure">Недопустимая структура папки сохранения</string> <string name="save_file_invalid_zip_structure">Недопустимая структура папки сохранения</string>
<string name="save_file_invalid_zip_structure_description">Название первой вложенной папки должно быть идентификатором игры.</string> <string name="save_file_invalid_zip_structure_description">Название первой вложенной папки должно быть идентификатором игры.</string>

View file

@ -79,9 +79,7 @@
<string name="notification_no_directory_link">Не вдалося відкрити папку yuzu</string> <string name="notification_no_directory_link">Не вдалося відкрити папку yuzu</string>
<string name="notification_no_directory_link_description">Будь ласка, знайдіть папку користувача за допомогою бічної панелі файлового менеджера вручну.</string> <string name="notification_no_directory_link_description">Будь ласка, знайдіть папку користувача за допомогою бічної панелі файлового менеджера вручну.</string>
<string name="manage_save_data">Керування даними збережень</string> <string name="manage_save_data">Керування даними збережень</string>
<string name="manage_save_data_description">Знайдено дані збережень. Будь ласка, виберіть варіант нижче.</string>
<string name="import_export_saves_description">Імпорт або експорт файлів збереження</string> <string name="import_export_saves_description">Імпорт або експорт файлів збереження</string>
<string name="import_export_saves_no_profile">Дані збережень не знайдено. Будь ласка, запустіть гру та повторіть спробу.</string>
<string name="save_file_imported_success">Успішно імпортовано</string> <string name="save_file_imported_success">Успішно імпортовано</string>
<string name="save_file_invalid_zip_structure">Неприпустима структура папки збереження</string> <string name="save_file_invalid_zip_structure">Неприпустима структура папки збереження</string>
<string name="save_file_invalid_zip_structure_description">Назва першої вкладеної папки має бути ідентифікатором гри.</string> <string name="save_file_invalid_zip_structure_description">Назва першої вкладеної папки має бути ідентифікатором гри.</string>

View file

@ -79,9 +79,7 @@
<string name="notification_no_directory_link">无法打开 yuzu 文件夹</string> <string name="notification_no_directory_link">无法打开 yuzu 文件夹</string>
<string name="notification_no_directory_link_description">请使用文件管理器的侧部面板手动定位用户文件夹。</string> <string name="notification_no_directory_link_description">请使用文件管理器的侧部面板手动定位用户文件夹。</string>
<string name="manage_save_data">管理存档数据</string> <string name="manage_save_data">管理存档数据</string>
<string name="manage_save_data_description">已找到存档数据,请选择下方的选项。</string>
<string name="import_export_saves_description">导入或导出存档</string> <string name="import_export_saves_description">导入或导出存档</string>
<string name="import_export_saves_no_profile">找不到存档数据,请启动游戏并重试。</string>
<string name="save_file_imported_success">已成功导入存档</string> <string name="save_file_imported_success">已成功导入存档</string>
<string name="save_file_invalid_zip_structure">无效的存档目录</string> <string name="save_file_invalid_zip_structure">无效的存档目录</string>
<string name="save_file_invalid_zip_structure_description">第一个子文件夹名称必须为当前游戏的 ID。</string> <string name="save_file_invalid_zip_structure_description">第一个子文件夹名称必须为当前游戏的 ID。</string>

View file

@ -79,9 +79,7 @@
<string name="notification_no_directory_link">無法開啟 yuzu 目錄</string> <string name="notification_no_directory_link">無法開啟 yuzu 目錄</string>
<string name="notification_no_directory_link_description">請使用檔案管理員的側邊面板手動定位到使用者資料夾。</string> <string name="notification_no_directory_link_description">請使用檔案管理員的側邊面板手動定位到使用者資料夾。</string>
<string name="manage_save_data">管理儲存資料</string> <string name="manage_save_data">管理儲存資料</string>
<string name="manage_save_data_description">已找到儲存資料,請選取下方的選項。</string>
<string name="import_export_saves_description">匯入或匯出儲存檔案</string> <string name="import_export_saves_description">匯入或匯出儲存檔案</string>
<string name="import_export_saves_no_profile">找不到儲存資料,請啟動遊戲並重試。</string>
<string name="save_file_imported_success">已成功匯入</string> <string name="save_file_imported_success">已成功匯入</string>
<string name="save_file_invalid_zip_structure">無效的儲存目錄結構</string> <string name="save_file_invalid_zip_structure">無效的儲存目錄結構</string>
<string name="save_file_invalid_zip_structure_description">首個子資料夾名稱必須為遊戲標題 ID。</string> <string name="save_file_invalid_zip_structure_description">首個子資料夾名稱必須為遊戲標題 ID。</string>

View file

@ -88,9 +88,7 @@
<string name="notification_no_directory_link">Could not open yuzu directory</string> <string name="notification_no_directory_link">Could not open yuzu directory</string>
<string name="notification_no_directory_link_description">Please locate the user folder with the file manager\'s side panel manually.</string> <string name="notification_no_directory_link_description">Please locate the user folder with the file manager\'s side panel manually.</string>
<string name="manage_save_data">Manage save data</string> <string name="manage_save_data">Manage save data</string>
<string name="manage_save_data_description">Save data found. Please select an option below.</string>
<string name="import_export_saves_description">Import or export save files</string> <string name="import_export_saves_description">Import or export save files</string>
<string name="import_export_saves_no_profile">No save data found. Please launch a game and retry.</string>
<string name="save_file_imported_success">Imported successfully</string> <string name="save_file_imported_success">Imported successfully</string>
<string name="save_file_invalid_zip_structure">Invalid save directory structure</string> <string name="save_file_invalid_zip_structure">Invalid save directory structure</string>
<string name="save_file_invalid_zip_structure_description">The first subfolder name must be the title ID of the game.</string> <string name="save_file_invalid_zip_structure_description">The first subfolder name must be the title ID of the game.</string>
@ -128,6 +126,15 @@
<string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string> <string name="contributors_link">https://github.com/yuzu-emu/yuzu/graphs/contributors</string>
<string name="licenses_description">Projects that make yuzu for Android possible</string> <string name="licenses_description">Projects that make yuzu for Android possible</string>
<string name="build">Build</string> <string name="build">Build</string>
<string name="user_data">User data</string>
<string name="user_data_description">Import/export all app data.\n\nWhen importing user data, all existing user data will be deleted!</string>
<string name="exporting_user_data">Exporting user data…</string>
<string name="importing_user_data">Importing user data…</string>
<string name="import_user_data">Import user data</string>
<string name="invalid_yuzu_backup">Invalid yuzu backup</string>
<string name="user_data_export_success">User data exported successfully</string>
<string name="user_data_import_success">User data imported successfully</string>
<string name="user_data_export_cancelled">Export cancelled</string>
<string name="support_link">https://discord.gg/u77vRWY</string> <string name="support_link">https://discord.gg/u77vRWY</string>
<string name="website_link">https://yuzu-emu.org/</string> <string name="website_link">https://yuzu-emu.org/</string>
<string name="github_link">https://github.com/yuzu-emu</string> <string name="github_link">https://github.com/yuzu-emu</string>
@ -215,6 +222,9 @@
<string name="auto">Auto</string> <string name="auto">Auto</string>
<string name="submit">Submit</string> <string name="submit">Submit</string>
<string name="string_null">Null</string> <string name="string_null">Null</string>
<string name="string_import">Import</string>
<string name="export">Export</string>
<string name="cancelling">Cancelling</string>
<!-- GPU driver installation --> <!-- GPU driver installation -->
<string name="select_gpu_driver">Select GPU driver</string> <string name="select_gpu_driver">Select GPU driver</string>