Compare commits
1 Commits
android-80
...
android-76
Author | SHA1 | Date | |
---|---|---|---|
fa2090b4b1 |
@ -214,7 +214,7 @@ dependencies {
|
||||
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1")
|
||||
implementation("io.coil-kt:coil:2.2.2")
|
||||
implementation("androidx.core:core-splashscreen:1.0.1")
|
||||
implementation("androidx.window:window:1.2.0-beta03")
|
||||
implementation("androidx.window:window:1.1.0")
|
||||
implementation("org.ini4j:ini4j:0.5.4")
|
||||
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
||||
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
||||
|
@ -49,7 +49,6 @@ class HomeSettingAdapter(
|
||||
holder.option.onClick.invoke()
|
||||
} else {
|
||||
MessageDialogFragment.newInstance(
|
||||
activity,
|
||||
titleId = holder.option.disabledTitleId,
|
||||
descriptionId = holder.option.disabledMessageId
|
||||
).show(activity.supportFragmentManager, MessageDialogFragment.TAG)
|
||||
|
@ -26,7 +26,6 @@ import org.yuzu.yuzu_emu.BuildConfig
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.databinding.FragmentAboutBinding
|
||||
import org.yuzu.yuzu_emu.model.HomeViewModel
|
||||
import org.yuzu.yuzu_emu.ui.main.MainActivity
|
||||
|
||||
class AboutFragment : Fragment() {
|
||||
private var _binding: FragmentAboutBinding? = null
|
||||
@ -93,12 +92,6 @@ 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.buttonWebsite.setOnClickListener { openLink(getString(R.string.website_link)) }
|
||||
binding.buttonGithub.setOnClickListener { openLink(getString(R.string.github_link)) }
|
||||
|
@ -187,7 +187,6 @@ class ImportExportSavesFragment : DialogFragment() {
|
||||
withContext(Dispatchers.Main) {
|
||||
if (!validZip) {
|
||||
MessageDialogFragment.newInstance(
|
||||
requireActivity(),
|
||||
titleId = R.string.save_file_invalid_zip_structure,
|
||||
descriptionId = R.string.save_file_invalid_zip_structure_description
|
||||
).show(activity.supportFragmentManager, MessageDialogFragment.TAG)
|
||||
|
@ -4,7 +4,6 @@
|
||||
package org.yuzu.yuzu_emu.fragments
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.DialogInterface
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
@ -19,7 +18,6 @@ import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import kotlinx.coroutines.launch
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
|
||||
import org.yuzu.yuzu_emu.model.TaskViewModel
|
||||
|
||||
@ -30,27 +28,19 @@ class IndeterminateProgressDialogFragment : DialogFragment() {
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val titleId = requireArguments().getInt(TITLE)
|
||||
val cancellable = requireArguments().getBoolean(CANCELLABLE)
|
||||
|
||||
binding = DialogProgressBarBinding.inflate(layoutInflater)
|
||||
binding.progressBar.isIndeterminate = true
|
||||
val dialog = MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(titleId)
|
||||
.setView(binding.root)
|
||||
|
||||
if (cancellable) {
|
||||
dialog.setNegativeButton(android.R.string.cancel) { _: DialogInterface, _: Int ->
|
||||
taskViewModel.setCancelled(true)
|
||||
}
|
||||
}
|
||||
|
||||
val alertDialog = dialog.create()
|
||||
alertDialog.setCanceledOnTouchOutside(false)
|
||||
.create()
|
||||
dialog.setCanceledOnTouchOutside(false)
|
||||
|
||||
if (!taskViewModel.isRunning.value) {
|
||||
taskViewModel.runTask()
|
||||
}
|
||||
return alertDialog
|
||||
return dialog
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
@ -63,35 +53,21 @@ class IndeterminateProgressDialogFragment : DialogFragment() {
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
viewLifecycleOwner.lifecycleScope.apply {
|
||||
launch {
|
||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
||||
taskViewModel.isComplete.collect {
|
||||
if (it) {
|
||||
dismiss()
|
||||
when (val result = taskViewModel.result.value) {
|
||||
is String -> Toast.makeText(
|
||||
requireContext(),
|
||||
result,
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
||||
taskViewModel.isComplete.collect {
|
||||
if (it) {
|
||||
dismiss()
|
||||
when (val result = taskViewModel.result.value) {
|
||||
is String -> Toast.makeText(requireContext(), result, Toast.LENGTH_LONG)
|
||||
.show()
|
||||
|
||||
is MessageDialogFragment -> result.show(
|
||||
requireActivity().supportFragmentManager,
|
||||
MessageDialogFragment.TAG
|
||||
)
|
||||
}
|
||||
taskViewModel.clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
launch {
|
||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
||||
taskViewModel.cancelled.collect {
|
||||
if (it) {
|
||||
dialog?.setTitle(R.string.cancelling)
|
||||
is MessageDialogFragment -> result.show(
|
||||
requireActivity().supportFragmentManager,
|
||||
MessageDialogFragment.TAG
|
||||
)
|
||||
}
|
||||
taskViewModel.clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -102,19 +78,16 @@ class IndeterminateProgressDialogFragment : DialogFragment() {
|
||||
const val TAG = "IndeterminateProgressDialogFragment"
|
||||
|
||||
private const val TITLE = "Title"
|
||||
private const val CANCELLABLE = "Cancellable"
|
||||
|
||||
fun newInstance(
|
||||
activity: AppCompatActivity,
|
||||
titleId: Int,
|
||||
cancellable: Boolean = false,
|
||||
task: () -> Any
|
||||
): IndeterminateProgressDialogFragment {
|
||||
val dialog = IndeterminateProgressDialogFragment()
|
||||
val args = Bundle()
|
||||
ViewModelProvider(activity)[TaskViewModel::class.java].task = task
|
||||
args.putInt(TITLE, titleId)
|
||||
args.putBoolean(CANCELLABLE, cancellable)
|
||||
dialog.arguments = args
|
||||
return dialog
|
||||
}
|
||||
|
@ -4,21 +4,14 @@
|
||||
package org.yuzu.yuzu_emu.fragments
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
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 org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.model.MessageDialogViewModel
|
||||
|
||||
class MessageDialogFragment : DialogFragment() {
|
||||
private val messageDialogViewModel: MessageDialogViewModel by activityViewModels()
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val titleId = requireArguments().getInt(TITLE_ID)
|
||||
val titleString = requireArguments().getString(TITLE_STRING)!!
|
||||
@ -44,12 +37,6 @@ class MessageDialogFragment : DialogFragment() {
|
||||
return dialog.show()
|
||||
}
|
||||
|
||||
override fun onDismiss(dialog: DialogInterface) {
|
||||
super.onDismiss(dialog)
|
||||
messageDialogViewModel.dismissAction.invoke()
|
||||
messageDialogViewModel.clear()
|
||||
}
|
||||
|
||||
private fun openLink(link: String) {
|
||||
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(link))
|
||||
startActivity(intent)
|
||||
@ -65,13 +52,11 @@ class MessageDialogFragment : DialogFragment() {
|
||||
private const val HELP_LINK = "Link"
|
||||
|
||||
fun newInstance(
|
||||
activity: FragmentActivity,
|
||||
titleId: Int = 0,
|
||||
titleString: String = "",
|
||||
descriptionId: Int = 0,
|
||||
descriptionString: String = "",
|
||||
helpLinkId: Int = 0,
|
||||
dismissAction: () -> Unit = {}
|
||||
helpLinkId: Int = 0
|
||||
): MessageDialogFragment {
|
||||
val dialog = MessageDialogFragment()
|
||||
val bundle = Bundle()
|
||||
@ -82,8 +67,6 @@ class MessageDialogFragment : DialogFragment() {
|
||||
putString(DESCRIPTION_STRING, descriptionString)
|
||||
putInt(HELP_LINK, helpLinkId)
|
||||
}
|
||||
ViewModelProvider(activity)[MessageDialogViewModel::class.java].dismissAction =
|
||||
dismissAction
|
||||
dialog.arguments = bundle
|
||||
return dialog
|
||||
}
|
||||
|
@ -1,14 +0,0 @@
|
||||
// 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 = {}
|
||||
}
|
||||
}
|
@ -20,20 +20,12 @@ class TaskViewModel : ViewModel() {
|
||||
val isRunning: StateFlow<Boolean> get() = _isRunning
|
||||
private val _isRunning = MutableStateFlow(false)
|
||||
|
||||
val cancelled: StateFlow<Boolean> get() = _cancelled
|
||||
private val _cancelled = MutableStateFlow(false)
|
||||
|
||||
lateinit var task: () -> Any
|
||||
|
||||
fun clear() {
|
||||
_result.value = Any()
|
||||
_isComplete.value = false
|
||||
_isRunning.value = false
|
||||
_cancelled.value = false
|
||||
}
|
||||
|
||||
fun setCancelled(value: Boolean) {
|
||||
_cancelled.value = value
|
||||
}
|
||||
|
||||
fun runTask() {
|
||||
|
@ -46,22 +46,13 @@ import org.yuzu.yuzu_emu.fragments.IndeterminateProgressDialogFragment
|
||||
import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
|
||||
import org.yuzu.yuzu_emu.model.GamesViewModel
|
||||
import org.yuzu.yuzu_emu.model.HomeViewModel
|
||||
import org.yuzu.yuzu_emu.model.TaskViewModel
|
||||
import org.yuzu.yuzu_emu.utils.*
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.BufferedOutputStream
|
||||
import java.io.FileInputStream
|
||||
import java.io.FileOutputStream
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipInputStream
|
||||
import java.util.zip.ZipOutputStream
|
||||
|
||||
class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
private lateinit var binding: ActivityMainBinding
|
||||
|
||||
private val homeViewModel: HomeViewModel by viewModels()
|
||||
private val gamesViewModel: GamesViewModel by viewModels()
|
||||
private val taskViewModel: TaskViewModel by viewModels()
|
||||
|
||||
override var themeId: Int = 0
|
||||
|
||||
@ -316,7 +307,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
fun processKey(result: Uri): Boolean {
|
||||
if (FileUtil.getExtension(result) != "keys") {
|
||||
MessageDialogFragment.newInstance(
|
||||
this,
|
||||
titleId = R.string.reading_keys_failure,
|
||||
descriptionId = R.string.install_prod_keys_failure_extension_description
|
||||
).show(supportFragmentManager, MessageDialogFragment.TAG)
|
||||
@ -346,7 +336,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
return true
|
||||
} else {
|
||||
MessageDialogFragment.newInstance(
|
||||
this,
|
||||
titleId = R.string.invalid_keys_error,
|
||||
descriptionId = R.string.install_keys_failure_description,
|
||||
helpLinkId = R.string.dumping_keys_quickstart_link
|
||||
@ -387,7 +376,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
val filteredNumOfFiles = cacheFirmwareDir.list(filterNCA)?.size ?: -2
|
||||
messageToShow = if (unfilteredNumOfFiles != filteredNumOfFiles) {
|
||||
MessageDialogFragment.newInstance(
|
||||
this,
|
||||
titleId = R.string.firmware_installed_failure,
|
||||
descriptionId = R.string.firmware_installed_failure_description
|
||||
)
|
||||
@ -407,7 +395,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
IndeterminateProgressDialogFragment.newInstance(
|
||||
this,
|
||||
R.string.firmware_installing,
|
||||
task = task
|
||||
task
|
||||
).show(supportFragmentManager, IndeterminateProgressDialogFragment.TAG)
|
||||
}
|
||||
|
||||
@ -419,7 +407,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
|
||||
if (FileUtil.getExtension(result) != "bin") {
|
||||
MessageDialogFragment.newInstance(
|
||||
this,
|
||||
titleId = R.string.reading_keys_failure,
|
||||
descriptionId = R.string.install_amiibo_keys_failure_extension_description
|
||||
).show(supportFragmentManager, MessageDialogFragment.TAG)
|
||||
@ -447,7 +434,6 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
).show()
|
||||
} else {
|
||||
MessageDialogFragment.newInstance(
|
||||
this,
|
||||
titleId = R.string.invalid_keys_error,
|
||||
descriptionId = R.string.install_keys_failure_description,
|
||||
helpLinkId = R.string.dumping_keys_quickstart_link
|
||||
@ -597,14 +583,12 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
installResult.append(separator)
|
||||
}
|
||||
return@newInstance MessageDialogFragment.newInstance(
|
||||
this,
|
||||
titleId = R.string.install_game_content_failure,
|
||||
descriptionString = installResult.toString().trim(),
|
||||
helpLinkId = R.string.install_game_content_help_link
|
||||
)
|
||||
} else {
|
||||
return@newInstance MessageDialogFragment.newInstance(
|
||||
this,
|
||||
titleId = R.string.install_game_content_success,
|
||||
descriptionString = installResult.toString().trim()
|
||||
)
|
||||
@ -612,119 +596,4 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||
}.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))
|
||||
|
||||
val buffer = ByteArray(8096)
|
||||
var read: Int
|
||||
FileInputStream(file).use { fis ->
|
||||
while (fis.read(buffer).also { read = it } != -1) {
|
||||
stream.write(buffer, 0, read)
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
val itemName = ze!!.name.trim()
|
||||
if (itemName == "/config/config.ini" || itemName == "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)
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +0,0 @@
|
||||
<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>
|
@ -1,9 +0,0 @@
|
||||
<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>
|
@ -1,8 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.google.android.material.progressindicator.LinearProgressIndicator xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/progress_bar"
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="24dp"
|
||||
app:trackCornerRadius="4dp" />
|
||||
android:layout_height="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.progressindicator.LinearProgressIndicator
|
||||
android:id="@+id/progress_bar"
|
||||
android:layout_width="match_parent"
|
||||
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>
|
||||
|
@ -176,67 +176,6 @@
|
||||
|
||||
</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
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
@ -128,15 +128,6 @@
|
||||
<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="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="website_link">https://yuzu-emu.org/</string>
|
||||
<string name="github_link">https://github.com/yuzu-emu</string>
|
||||
@ -224,9 +215,6 @@
|
||||
<string name="auto">Auto</string>
|
||||
<string name="submit">Submit</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 -->
|
||||
<string name="select_gpu_driver">Select GPU driver</string>
|
||||
|
@ -47,7 +47,6 @@ PartitionFilesystem::PartitionFilesystem(VirtualFile file) {
|
||||
// Actually read in now...
|
||||
std::vector<u8> file_data = file->ReadBytes(metadata_size);
|
||||
const std::size_t total_size = file_data.size();
|
||||
file_data.push_back(0);
|
||||
|
||||
if (total_size != metadata_size) {
|
||||
status = Loader::ResultStatus::ErrorIncorrectPFSFileSize;
|
||||
|
@ -7,9 +7,7 @@
|
||||
#include "core/frontend/applets/mii_edit.h"
|
||||
#include "core/hle/service/am/am.h"
|
||||
#include "core/hle/service/am/applets/applet_mii_edit.h"
|
||||
#include "core/hle/service/mii/mii.h"
|
||||
#include "core/hle/service/mii/mii_manager.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
|
||||
namespace Service::AM::Applets {
|
||||
|
||||
@ -58,12 +56,6 @@ void MiiEdit::Initialize() {
|
||||
sizeof(MiiEditAppletInputV4));
|
||||
break;
|
||||
}
|
||||
|
||||
manager = system.ServiceManager().GetService<Mii::MiiDBModule>("mii:e")->GetMiiManager();
|
||||
if (manager == nullptr) {
|
||||
manager = std::make_shared<Mii::MiiManager>();
|
||||
}
|
||||
manager->Initialize(metadata);
|
||||
}
|
||||
|
||||
bool MiiEdit::TransactionComplete() const {
|
||||
@ -86,46 +78,22 @@ void MiiEdit::Execute() {
|
||||
// This is a default stub for each of the MiiEdit applet modes.
|
||||
switch (applet_input_common.applet_mode) {
|
||||
case MiiEditAppletMode::ShowMiiEdit:
|
||||
case MiiEditAppletMode::AppendMii:
|
||||
case MiiEditAppletMode::AppendMiiImage:
|
||||
case MiiEditAppletMode::UpdateMiiImage:
|
||||
MiiEditOutput(MiiEditResult::Success, 0);
|
||||
break;
|
||||
case MiiEditAppletMode::AppendMii: {
|
||||
Mii::StoreData store_data{};
|
||||
store_data.BuildRandom(Mii::Age::All, Mii::Gender::All, Mii::Race::All);
|
||||
store_data.SetNickname({u'y', u'u', u'z', u'u'});
|
||||
store_data.SetChecksum();
|
||||
const auto result = manager->AddOrReplace(metadata, store_data);
|
||||
|
||||
if (result.IsError()) {
|
||||
MiiEditOutput(MiiEditResult::Cancel, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
s32 index = manager->FindIndex(store_data.GetCreateId(), false);
|
||||
|
||||
if (index == -1) {
|
||||
MiiEditOutput(MiiEditResult::Cancel, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
MiiEditOutput(MiiEditResult::Success, index);
|
||||
break;
|
||||
}
|
||||
case MiiEditAppletMode::CreateMii: {
|
||||
Mii::CharInfo char_info{};
|
||||
manager->BuildRandom(char_info, Mii::Age::All, Mii::Gender::All, Mii::Race::All);
|
||||
|
||||
const MiiEditCharInfo edit_char_info{
|
||||
.mii_info{char_info},
|
||||
};
|
||||
|
||||
MiiEditOutputForCharInfoEditing(MiiEditResult::Success, edit_char_info);
|
||||
break;
|
||||
}
|
||||
case MiiEditAppletMode::CreateMii:
|
||||
case MiiEditAppletMode::EditMii: {
|
||||
Mii::CharInfo char_info{};
|
||||
Mii::StoreData store_data{};
|
||||
store_data.BuildBase(Mii::Gender::Male);
|
||||
char_info.SetFromStoreData(store_data);
|
||||
|
||||
const MiiEditCharInfo edit_char_info{
|
||||
.mii_info{applet_input_v4.char_info.mii_info},
|
||||
.mii_info{applet_input_common.applet_mode == MiiEditAppletMode::EditMii
|
||||
? applet_input_v4.char_info.mii_info
|
||||
: char_info},
|
||||
};
|
||||
|
||||
MiiEditOutputForCharInfoEditing(MiiEditResult::Success, edit_char_info);
|
||||
|
@ -11,11 +11,6 @@ namespace Core {
|
||||
class System;
|
||||
} // namespace Core
|
||||
|
||||
namespace Service::Mii {
|
||||
struct DatabaseSessionMetadata;
|
||||
class MiiManager;
|
||||
} // namespace Service::Mii
|
||||
|
||||
namespace Service::AM::Applets {
|
||||
|
||||
class MiiEdit final : public Applet {
|
||||
@ -45,8 +40,6 @@ private:
|
||||
MiiEditAppletInputV4 applet_input_v4{};
|
||||
|
||||
bool is_complete{false};
|
||||
std::shared_ptr<Mii::MiiManager> manager = nullptr;
|
||||
Mii::DatabaseSessionMetadata metadata{};
|
||||
};
|
||||
|
||||
} // namespace Service::AM::Applets
|
||||
|
@ -18,10 +18,8 @@ namespace Service::Mii {
|
||||
|
||||
class IDatabaseService final : public ServiceFramework<IDatabaseService> {
|
||||
public:
|
||||
explicit IDatabaseService(Core::System& system_, std::shared_ptr<MiiManager> mii_manager,
|
||||
bool is_system_)
|
||||
: ServiceFramework{system_, "IDatabaseService"}, manager{mii_manager}, is_system{
|
||||
is_system_} {
|
||||
explicit IDatabaseService(Core::System& system_, bool is_system_)
|
||||
: ServiceFramework{system_, "IDatabaseService"}, is_system{is_system_} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &IDatabaseService::IsUpdated, "IsUpdated"},
|
||||
@ -56,7 +54,7 @@ public:
|
||||
|
||||
RegisterHandlers(functions);
|
||||
|
||||
manager->Initialize(metadata);
|
||||
manager.Initialize(metadata);
|
||||
}
|
||||
|
||||
private:
|
||||
@ -66,7 +64,7 @@ private:
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with source_flag={}", source_flag);
|
||||
|
||||
const bool is_updated = manager->IsUpdated(metadata, source_flag);
|
||||
const bool is_updated = manager.IsUpdated(metadata, source_flag);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
@ -76,7 +74,7 @@ private:
|
||||
void IsFullDatabase(HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Mii, "called");
|
||||
|
||||
const bool is_full_database = manager->IsFullDatabase();
|
||||
const bool is_full_database = manager.IsFullDatabase();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
@ -87,7 +85,7 @@ private:
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto source_flag{rp.PopRaw<SourceFlag>()};
|
||||
|
||||
const u32 mii_count = manager->GetCount(metadata, source_flag);
|
||||
const u32 mii_count = manager.GetCount(metadata, source_flag);
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called with source_flag={}, mii_count={}", source_flag, mii_count);
|
||||
|
||||
@ -103,7 +101,7 @@ private:
|
||||
|
||||
u32 mii_count{};
|
||||
std::vector<CharInfoElement> char_info_elements(output_size);
|
||||
const auto result = manager->Get(metadata, char_info_elements, mii_count, source_flag);
|
||||
const auto result = manager.Get(metadata, char_info_elements, mii_count, source_flag);
|
||||
|
||||
if (mii_count != 0) {
|
||||
ctx.WriteBuffer(char_info_elements);
|
||||
@ -124,7 +122,7 @@ private:
|
||||
|
||||
u32 mii_count{};
|
||||
std::vector<CharInfo> char_info(output_size);
|
||||
const auto result = manager->Get(metadata, char_info, mii_count, source_flag);
|
||||
const auto result = manager.Get(metadata, char_info, mii_count, source_flag);
|
||||
|
||||
if (mii_count != 0) {
|
||||
ctx.WriteBuffer(char_info);
|
||||
@ -146,7 +144,7 @@ private:
|
||||
LOG_INFO(Service_Mii, "called with source_flag={}", source_flag);
|
||||
|
||||
CharInfo new_char_info{};
|
||||
const auto result = manager->UpdateLatest(metadata, new_char_info, char_info, source_flag);
|
||||
const auto result = manager.UpdateLatest(metadata, new_char_info, char_info, source_flag);
|
||||
if (result.IsFailure()) {
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
@ -185,7 +183,7 @@ private:
|
||||
}
|
||||
|
||||
CharInfo char_info{};
|
||||
manager->BuildRandom(char_info, age, gender, race);
|
||||
manager.BuildRandom(char_info, age, gender, race);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
|
||||
rb.Push(ResultSuccess);
|
||||
@ -205,7 +203,7 @@ private:
|
||||
}
|
||||
|
||||
CharInfo char_info{};
|
||||
manager->BuildDefault(char_info, index);
|
||||
manager.BuildDefault(char_info, index);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
|
||||
rb.Push(ResultSuccess);
|
||||
@ -219,7 +217,7 @@ private:
|
||||
|
||||
u32 mii_count{};
|
||||
std::vector<StoreDataElement> store_data_elements(output_size);
|
||||
const auto result = manager->Get(metadata, store_data_elements, mii_count, source_flag);
|
||||
const auto result = manager.Get(metadata, store_data_elements, mii_count, source_flag);
|
||||
|
||||
if (mii_count != 0) {
|
||||
ctx.WriteBuffer(store_data_elements);
|
||||
@ -240,7 +238,7 @@ private:
|
||||
|
||||
u32 mii_count{};
|
||||
std::vector<StoreData> store_data(output_size);
|
||||
const auto result = manager->Get(metadata, store_data, mii_count, source_flag);
|
||||
const auto result = manager.Get(metadata, store_data, mii_count, source_flag);
|
||||
|
||||
if (mii_count != 0) {
|
||||
ctx.WriteBuffer(store_data);
|
||||
@ -268,7 +266,7 @@ private:
|
||||
|
||||
StoreData new_store_data{};
|
||||
if (result.IsSuccess()) {
|
||||
result = manager->UpdateLatest(metadata, new_store_data, store_data, source_flag);
|
||||
result = manager.UpdateLatest(metadata, new_store_data, store_data, source_flag);
|
||||
}
|
||||
|
||||
if (result.IsFailure()) {
|
||||
@ -290,7 +288,7 @@ private:
|
||||
LOG_INFO(Service_Mii, "called with create_id={}, is_special={}",
|
||||
create_id.FormattedString(), is_special);
|
||||
|
||||
const s32 index = manager->FindIndex(create_id, is_special);
|
||||
const s32 index = manager.FindIndex(create_id, is_special);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
@ -311,14 +309,14 @@ private:
|
||||
}
|
||||
|
||||
if (result.IsSuccess()) {
|
||||
const u32 count = manager->GetCount(metadata, SourceFlag::Database);
|
||||
const u32 count = manager.GetCount(metadata, SourceFlag::Database);
|
||||
if (new_index < 0 || new_index >= static_cast<s32>(count)) {
|
||||
result = ResultInvalidArgument;
|
||||
}
|
||||
}
|
||||
|
||||
if (result.IsSuccess()) {
|
||||
result = manager->Move(metadata, new_index, create_id);
|
||||
result = manager.Move(metadata, new_index, create_id);
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@ -338,7 +336,7 @@ private:
|
||||
}
|
||||
|
||||
if (result.IsSuccess()) {
|
||||
result = manager->AddOrReplace(metadata, store_data);
|
||||
result = manager.AddOrReplace(metadata, store_data);
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@ -358,7 +356,7 @@ private:
|
||||
}
|
||||
|
||||
if (result.IsSuccess()) {
|
||||
result = manager->Delete(metadata, create_id);
|
||||
result = manager.Delete(metadata, create_id);
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@ -378,7 +376,7 @@ private:
|
||||
}
|
||||
|
||||
if (result.IsSuccess()) {
|
||||
result = manager->DestroyFile(metadata);
|
||||
result = manager.DestroyFile(metadata);
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@ -398,7 +396,7 @@ private:
|
||||
}
|
||||
|
||||
if (result.IsSuccess()) {
|
||||
result = manager->DeleteFile();
|
||||
result = manager.DeleteFile();
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@ -418,7 +416,7 @@ private:
|
||||
}
|
||||
|
||||
if (result.IsSuccess()) {
|
||||
result = manager->Format(metadata);
|
||||
result = manager.Format(metadata);
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
@ -436,7 +434,7 @@ private:
|
||||
}
|
||||
|
||||
if (result.IsSuccess()) {
|
||||
is_broken_with_clear_flag = manager->IsBrokenWithClearFlag(metadata);
|
||||
is_broken_with_clear_flag = manager.IsBrokenWithClearFlag(metadata);
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
@ -451,7 +449,7 @@ private:
|
||||
LOG_DEBUG(Service_Mii, "called");
|
||||
|
||||
s32 index{};
|
||||
const auto result = manager->GetIndex(metadata, info, index);
|
||||
const auto result = manager.GetIndex(metadata, info, index);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(result);
|
||||
@ -464,7 +462,7 @@ private:
|
||||
|
||||
LOG_INFO(Service_Mii, "called, interface_version={:08X}", interface_version);
|
||||
|
||||
manager->SetInterfaceVersion(metadata, interface_version);
|
||||
manager.SetInterfaceVersion(metadata, interface_version);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
@ -477,7 +475,7 @@ private:
|
||||
LOG_INFO(Service_Mii, "called");
|
||||
|
||||
CharInfo char_info{};
|
||||
const auto result = manager->ConvertV3ToCharInfo(char_info, mii_v3);
|
||||
const auto result = manager.ConvertV3ToCharInfo(char_info, mii_v3);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
|
||||
rb.Push(result);
|
||||
@ -491,7 +489,7 @@ private:
|
||||
LOG_INFO(Service_Mii, "called");
|
||||
|
||||
CharInfo char_info{};
|
||||
const auto result = manager->ConvertCoreDataToCharInfo(char_info, core_data);
|
||||
const auto result = manager.ConvertCoreDataToCharInfo(char_info, core_data);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2 + sizeof(CharInfo) / sizeof(u32)};
|
||||
rb.Push(result);
|
||||
@ -505,7 +503,7 @@ private:
|
||||
LOG_INFO(Service_Mii, "called");
|
||||
|
||||
CoreData core_data{};
|
||||
const auto result = manager->ConvertCharInfoToCoreData(core_data, char_info);
|
||||
const auto result = manager.ConvertCharInfoToCoreData(core_data, char_info);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2 + sizeof(CoreData) / sizeof(u32)};
|
||||
rb.Push(result);
|
||||
@ -518,46 +516,41 @@ private:
|
||||
|
||||
LOG_INFO(Service_Mii, "called");
|
||||
|
||||
const auto result = manager->Append(metadata, char_info);
|
||||
const auto result = manager.Append(metadata, char_info);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(result);
|
||||
}
|
||||
|
||||
std::shared_ptr<MiiManager> manager = nullptr;
|
||||
MiiManager manager{};
|
||||
DatabaseSessionMetadata metadata{};
|
||||
bool is_system{};
|
||||
};
|
||||
|
||||
MiiDBModule::MiiDBModule(Core::System& system_, const char* name_,
|
||||
std::shared_ptr<MiiManager> mii_manager, bool is_system_)
|
||||
: ServiceFramework{system_, name_}, manager{mii_manager}, is_system{is_system_} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &MiiDBModule::GetDatabaseService, "GetDatabaseService"},
|
||||
};
|
||||
// clang-format on
|
||||
class MiiDBModule final : public ServiceFramework<MiiDBModule> {
|
||||
public:
|
||||
explicit MiiDBModule(Core::System& system_, const char* name_, bool is_system_)
|
||||
: ServiceFramework{system_, name_}, is_system{is_system_} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &MiiDBModule::GetDatabaseService, "GetDatabaseService"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
|
||||
if (manager == nullptr) {
|
||||
manager = std::make_shared<MiiManager>();
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
}
|
||||
|
||||
MiiDBModule::~MiiDBModule() = default;
|
||||
private:
|
||||
void GetDatabaseService(HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IDatabaseService>(system, is_system);
|
||||
|
||||
void MiiDBModule::GetDatabaseService(HLERequestContext& ctx) {
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IDatabaseService>(system, manager, is_system);
|
||||
LOG_DEBUG(Service_Mii, "called");
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_Mii, "called");
|
||||
}
|
||||
|
||||
std::shared_ptr<MiiManager> MiiDBModule::GetMiiManager() {
|
||||
return manager;
|
||||
}
|
||||
bool is_system{};
|
||||
};
|
||||
|
||||
class MiiImg final : public ServiceFramework<MiiImg> {
|
||||
public:
|
||||
@ -603,12 +596,11 @@ private:
|
||||
|
||||
void LoopProcess(Core::System& system) {
|
||||
auto server_manager = std::make_unique<ServerManager>(system);
|
||||
std::shared_ptr<MiiManager> manager = nullptr;
|
||||
|
||||
server_manager->RegisterNamedService(
|
||||
"mii:e", std::make_shared<MiiDBModule>(system, "mii:e", manager, true));
|
||||
server_manager->RegisterNamedService(
|
||||
"mii:u", std::make_shared<MiiDBModule>(system, "mii:u", manager, false));
|
||||
server_manager->RegisterNamedService("mii:e",
|
||||
std::make_shared<MiiDBModule>(system, "mii:e", true));
|
||||
server_manager->RegisterNamedService("mii:u",
|
||||
std::make_shared<MiiDBModule>(system, "mii:u", false));
|
||||
server_manager->RegisterNamedService("miiimg", std::make_shared<MiiImg>(system));
|
||||
ServerManager::RunServer(std::move(server_manager));
|
||||
}
|
||||
|
@ -3,29 +3,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Service::Mii {
|
||||
class MiiManager;
|
||||
|
||||
class MiiDBModule final : public ServiceFramework<MiiDBModule> {
|
||||
public:
|
||||
explicit MiiDBModule(Core::System& system_, const char* name_,
|
||||
std::shared_ptr<MiiManager> mii_manager, bool is_system_);
|
||||
~MiiDBModule() override;
|
||||
|
||||
std::shared_ptr<MiiManager> GetMiiManager();
|
||||
|
||||
private:
|
||||
void GetDatabaseService(HLERequestContext& ctx);
|
||||
|
||||
std::shared_ptr<MiiManager> manager = nullptr;
|
||||
bool is_system{};
|
||||
};
|
||||
|
||||
void LoopProcess(Core::System& system);
|
||||
|
||||
|
@ -130,11 +130,11 @@ Result MiiManager::GetIndex(const DatabaseSessionMetadata& metadata, const CharI
|
||||
}
|
||||
|
||||
s32 index{};
|
||||
const bool is_special = metadata.magic == MiiMagic;
|
||||
const auto result = database_manager.FindIndex(index, char_info.GetCreateId(), is_special);
|
||||
Result result = {};
|
||||
// FindIndex(index);
|
||||
|
||||
if (result.IsError()) {
|
||||
index = -1;
|
||||
return ResultNotFound;
|
||||
}
|
||||
|
||||
if (index == -1) {
|
||||
|
@ -37,7 +37,7 @@ void CharInfo::SetFromStoreData(const StoreData& store_data) {
|
||||
eyebrow_aspect = store_data.GetEyebrowAspect();
|
||||
eyebrow_rotate = store_data.GetEyebrowRotate();
|
||||
eyebrow_x = store_data.GetEyebrowX();
|
||||
eyebrow_y = store_data.GetEyebrowY();
|
||||
eyebrow_y = store_data.GetEyebrowY() + 3;
|
||||
nose_type = store_data.GetNoseType();
|
||||
nose_scale = store_data.GetNoseScale();
|
||||
nose_y = store_data.GetNoseY();
|
||||
|
@ -171,7 +171,7 @@ void CoreData::BuildRandom(Age age, Gender gender, Race race) {
|
||||
u8 glasses_type{};
|
||||
while (glasses_type_start < glasses_type_info.values[glasses_type]) {
|
||||
if (++glasses_type >= glasses_type_info.values_count) {
|
||||
glasses_type = 0;
|
||||
ASSERT(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -179,7 +179,6 @@ void CoreData::BuildRandom(Age age, Gender gender, Race race) {
|
||||
SetGlassType(static_cast<GlassType>(glasses_type));
|
||||
SetGlassColor(RawData::GetGlassColorFromVer3(0));
|
||||
SetGlassScale(4);
|
||||
SetGlassY(static_cast<u8>(axis_y + 10));
|
||||
|
||||
SetMoleType(MoleType::None);
|
||||
SetMoleScale(4);
|
||||
|
@ -1716,18 +1716,18 @@ const std::array<RandomMiiData4, 18> RandomMiiMouthType{
|
||||
const std::array<RandomMiiData2, 3> RandomMiiGlassType{
|
||||
RandomMiiData2{
|
||||
.arg_1 = 0,
|
||||
.values_count = 4,
|
||||
.values = {90, 94, 96, 100},
|
||||
.values_count = 9,
|
||||
.values = {90, 94, 96, 100, 0, 0, 0, 0, 0},
|
||||
},
|
||||
RandomMiiData2{
|
||||
.arg_1 = 1,
|
||||
.values_count = 8,
|
||||
.values = {83, 86, 90, 93, 94, 96, 98, 100},
|
||||
.values_count = 9,
|
||||
.values = {83, 86, 90, 93, 94, 96, 98, 100, 0},
|
||||
},
|
||||
RandomMiiData2{
|
||||
.arg_1 = 2,
|
||||
.values_count = 8,
|
||||
.values = {78, 83, 0, 93, 0, 0, 98, 100},
|
||||
.values_count = 9,
|
||||
.values = {78, 83, 0, 93, 0, 0, 98, 100, 0},
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -7,12 +7,15 @@
|
||||
|
||||
namespace Shader::Backend::SPIRV {
|
||||
namespace {
|
||||
Id Image(EmitContext& ctx, IR::TextureInstInfo info) {
|
||||
Id Image(EmitContext& ctx, const IR::Value& index, IR::TextureInstInfo info) {
|
||||
if (!index.IsImmediate()) {
|
||||
throw NotImplementedException("Indirect image indexing");
|
||||
}
|
||||
if (info.type == TextureType::Buffer) {
|
||||
const ImageBufferDefinition def{ctx.image_buffers.at(info.descriptor_index)};
|
||||
const ImageBufferDefinition def{ctx.image_buffers.at(index.U32())};
|
||||
return def.id;
|
||||
} else {
|
||||
const ImageDefinition def{ctx.images.at(info.descriptor_index)};
|
||||
const ImageDefinition def{ctx.images.at(index.U32())};
|
||||
return def.id;
|
||||
}
|
||||
}
|
||||
@ -25,12 +28,8 @@ std::pair<Id, Id> AtomicArgs(EmitContext& ctx) {
|
||||
|
||||
Id ImageAtomicU32(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id value,
|
||||
Id (Sirit::Module::*atomic_func)(Id, Id, Id, Id, Id)) {
|
||||
if (!index.IsImmediate() || index.U32() != 0) {
|
||||
// TODO: handle layers
|
||||
throw NotImplementedException("Image indexing");
|
||||
}
|
||||
const auto info{inst->Flags<IR::TextureInstInfo>()};
|
||||
const Id image{Image(ctx, info)};
|
||||
const Id image{Image(ctx, index, info)};
|
||||
const Id pointer{ctx.OpImageTexelPointer(ctx.image_u32, image, coords, ctx.Const(0U))};
|
||||
const auto [scope, semantics]{AtomicArgs(ctx)};
|
||||
return (ctx.*atomic_func)(ctx.U32[1], pointer, scope, semantics, value);
|
||||
|
@ -74,6 +74,11 @@ spv::ImageFormat GetImageFormat(ImageFormat format) {
|
||||
throw InvalidArgument("Invalid image format {}", format);
|
||||
}
|
||||
|
||||
spv::ImageFormat GetImageFormatForBuffer(ImageFormat format) {
|
||||
const auto spv_format = GetImageFormat(format);
|
||||
return spv_format == spv::ImageFormat::Unknown ? spv::ImageFormat::R32ui : spv_format;
|
||||
}
|
||||
|
||||
Id ImageType(EmitContext& ctx, const ImageDescriptor& desc) {
|
||||
const spv::ImageFormat format{GetImageFormat(desc.format)};
|
||||
const Id type{ctx.U32[1]};
|
||||
@ -1270,7 +1275,7 @@ void EmitContext::DefineImageBuffers(const Info& info, u32& binding) {
|
||||
if (desc.count != 1) {
|
||||
throw NotImplementedException("Array of image buffers");
|
||||
}
|
||||
const spv::ImageFormat format{GetImageFormat(desc.format)};
|
||||
const spv::ImageFormat format{GetImageFormatForBuffer(desc.format)};
|
||||
const Id image_type{TypeImage(U32[1], spv::Dim::Buffer, false, false, false, 2, format)};
|
||||
const Id pointer_type{TypePointer(spv::StorageClass::UniformConstant, image_type)};
|
||||
const Id id{AddGlobalVariable(pointer_type, spv::StorageClass::UniformConstant)};
|
||||
|
@ -109,11 +109,10 @@ void MaxwellDMA::Launch() {
|
||||
const bool is_const_a_dst = regs.remap_const.dst_x == RemapConst::Swizzle::CONST_A;
|
||||
if (regs.launch_dma.remap_enable != 0 && is_const_a_dst) {
|
||||
ASSERT(regs.remap_const.component_size_minus_one == 3);
|
||||
accelerate.BufferClear(regs.offset_out, regs.line_length_in,
|
||||
regs.remap_const.remap_consta_value);
|
||||
accelerate.BufferClear(regs.offset_out, regs.line_length_in, regs.remap_consta_value);
|
||||
read_buffer.resize_destructive(regs.line_length_in * sizeof(u32));
|
||||
std::span<u32> span(reinterpret_cast<u32*>(read_buffer.data()), regs.line_length_in);
|
||||
std::ranges::fill(span, regs.remap_const.remap_consta_value);
|
||||
std::ranges::fill(span, regs.remap_consta_value);
|
||||
memory_manager.WriteBlockUnsafe(regs.offset_out,
|
||||
reinterpret_cast<u8*>(read_buffer.data()),
|
||||
regs.line_length_in * sizeof(u32));
|
||||
|
@ -214,15 +214,14 @@ public:
|
||||
NO_WRITE = 6,
|
||||
};
|
||||
|
||||
u32 remap_consta_value;
|
||||
u32 remap_constb_value;
|
||||
PackedGPUVAddr address;
|
||||
|
||||
union {
|
||||
BitField<0, 12, u32> dst_components_raw;
|
||||
BitField<0, 3, Swizzle> dst_x;
|
||||
BitField<4, 3, Swizzle> dst_y;
|
||||
BitField<8, 3, Swizzle> dst_z;
|
||||
BitField<12, 3, Swizzle> dst_w;
|
||||
BitField<0, 12, u32> dst_components_raw;
|
||||
BitField<16, 2, u32> component_size_minus_one;
|
||||
BitField<20, 2, u32> num_src_components_minus_one;
|
||||
BitField<24, 2, u32> num_dst_components_minus_one;
|
||||
@ -275,57 +274,55 @@ private:
|
||||
struct Regs {
|
||||
union {
|
||||
struct {
|
||||
INSERT_PADDING_BYTES_NOINIT(0x100);
|
||||
u32 reserved[0x40];
|
||||
u32 nop;
|
||||
INSERT_PADDING_BYTES_NOINIT(0x3C);
|
||||
u32 reserved01[0xf];
|
||||
u32 pm_trigger;
|
||||
INSERT_PADDING_BYTES_NOINIT(0xFC);
|
||||
u32 reserved02[0x3f];
|
||||
Semaphore semaphore;
|
||||
INSERT_PADDING_BYTES_NOINIT(0x8);
|
||||
u32 reserved03[0x2];
|
||||
RenderEnable render_enable;
|
||||
PhysMode src_phys_mode;
|
||||
PhysMode dst_phys_mode;
|
||||
INSERT_PADDING_BYTES_NOINIT(0x98);
|
||||
u32 reserved04[0x26];
|
||||
LaunchDMA launch_dma;
|
||||
INSERT_PADDING_BYTES_NOINIT(0xFC);
|
||||
u32 reserved05[0x3f];
|
||||
PackedGPUVAddr offset_in;
|
||||
PackedGPUVAddr offset_out;
|
||||
s32 pitch_in;
|
||||
s32 pitch_out;
|
||||
u32 line_length_in;
|
||||
u32 line_count;
|
||||
INSERT_PADDING_BYTES_NOINIT(0x2E0);
|
||||
u32 reserved06[0xb6];
|
||||
u32 remap_consta_value;
|
||||
u32 remap_constb_value;
|
||||
RemapConst remap_const;
|
||||
DMA::Parameters dst_params;
|
||||
INSERT_PADDING_BYTES_NOINIT(0x4);
|
||||
u32 reserved07[0x1];
|
||||
DMA::Parameters src_params;
|
||||
INSERT_PADDING_BYTES_NOINIT(0x9D4);
|
||||
u32 reserved08[0x275];
|
||||
u32 pm_trigger_end;
|
||||
INSERT_PADDING_BYTES_NOINIT(0xEE8);
|
||||
u32 reserved09[0x3ba];
|
||||
};
|
||||
std::array<u32, NUM_REGS> reg_array;
|
||||
};
|
||||
} regs{};
|
||||
static_assert(sizeof(Regs) == NUM_REGS * 4);
|
||||
|
||||
#define ASSERT_REG_POSITION(field_name, position) \
|
||||
static_assert(offsetof(MaxwellDMA::Regs, field_name) == position, \
|
||||
static_assert(offsetof(MaxwellDMA::Regs, field_name) == position * 4, \
|
||||
"Field " #field_name " has invalid position")
|
||||
|
||||
ASSERT_REG_POSITION(semaphore, 0x240);
|
||||
ASSERT_REG_POSITION(render_enable, 0x254);
|
||||
ASSERT_REG_POSITION(src_phys_mode, 0x260);
|
||||
ASSERT_REG_POSITION(launch_dma, 0x300);
|
||||
ASSERT_REG_POSITION(offset_in, 0x400);
|
||||
ASSERT_REG_POSITION(offset_out, 0x408);
|
||||
ASSERT_REG_POSITION(pitch_in, 0x410);
|
||||
ASSERT_REG_POSITION(pitch_out, 0x414);
|
||||
ASSERT_REG_POSITION(line_length_in, 0x418);
|
||||
ASSERT_REG_POSITION(line_count, 0x41C);
|
||||
ASSERT_REG_POSITION(remap_const, 0x700);
|
||||
ASSERT_REG_POSITION(dst_params, 0x70C);
|
||||
ASSERT_REG_POSITION(src_params, 0x728);
|
||||
ASSERT_REG_POSITION(pm_trigger_end, 0x1114);
|
||||
ASSERT_REG_POSITION(launch_dma, 0xC0);
|
||||
ASSERT_REG_POSITION(offset_in, 0x100);
|
||||
ASSERT_REG_POSITION(offset_out, 0x102);
|
||||
ASSERT_REG_POSITION(pitch_in, 0x104);
|
||||
ASSERT_REG_POSITION(pitch_out, 0x105);
|
||||
ASSERT_REG_POSITION(line_length_in, 0x106);
|
||||
ASSERT_REG_POSITION(line_count, 0x107);
|
||||
ASSERT_REG_POSITION(remap_const, 0x1C0);
|
||||
ASSERT_REG_POSITION(dst_params, 0x1C3);
|
||||
ASSERT_REG_POSITION(src_params, 0x1CA);
|
||||
|
||||
#undef ASSERT_REG_POSITION
|
||||
};
|
||||
|
||||
|
@ -185,7 +185,7 @@ struct FormatTuple {
|
||||
{VK_FORMAT_BC2_SRGB_BLOCK}, // BC2_SRGB
|
||||
{VK_FORMAT_BC3_SRGB_BLOCK}, // BC3_SRGB
|
||||
{VK_FORMAT_BC7_SRGB_BLOCK}, // BC7_SRGB
|
||||
{VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT}, // A4B4G4R4_UNORM
|
||||
{VK_FORMAT_R4G4B4A4_UNORM_PACK16}, // A4B4G4R4_UNORM
|
||||
{VK_FORMAT_R4G4_UNORM_PACK8}, // G4R4_UNORM
|
||||
{VK_FORMAT_ASTC_4x4_SRGB_BLOCK}, // ASTC_2D_4X4_SRGB
|
||||
{VK_FORMAT_ASTC_8x8_SRGB_BLOCK}, // ASTC_2D_8X8_SRGB
|
||||
|
@ -600,7 +600,7 @@ void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage im
|
||||
}
|
||||
|
||||
void TryTransformSwizzleIfNeeded(PixelFormat format, std::array<SwizzleSource, 4>& swizzle,
|
||||
bool emulate_bgr565, bool emulate_a4b4g4r4) {
|
||||
bool emulate_bgr565) {
|
||||
switch (format) {
|
||||
case PixelFormat::A1B5G5R5_UNORM:
|
||||
std::ranges::transform(swizzle, swizzle.begin(), SwapBlueRed);
|
||||
@ -616,11 +616,6 @@ void TryTransformSwizzleIfNeeded(PixelFormat format, std::array<SwizzleSource, 4
|
||||
case PixelFormat::G4R4_UNORM:
|
||||
std::ranges::transform(swizzle, swizzle.begin(), SwapGreenRed);
|
||||
break;
|
||||
case PixelFormat::A4B4G4R4_UNORM:
|
||||
if (emulate_a4b4g4r4) {
|
||||
std::ranges::reverse(swizzle);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -1654,8 +1649,7 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI
|
||||
};
|
||||
if (!info.IsRenderTarget()) {
|
||||
swizzle = info.Swizzle();
|
||||
TryTransformSwizzleIfNeeded(format, swizzle, device->MustEmulateBGR565(),
|
||||
!device->IsExt4444FormatsSupported());
|
||||
TryTransformSwizzleIfNeeded(format, swizzle, device->MustEmulateBGR565());
|
||||
if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) {
|
||||
std::ranges::transform(swizzle, swizzle.begin(), ConvertGreenRed);
|
||||
}
|
||||
|
@ -76,11 +76,6 @@ constexpr std::array VK_FORMAT_R32G32B32_SFLOAT{
|
||||
VK_FORMAT_UNDEFINED,
|
||||
};
|
||||
|
||||
constexpr std::array VK_FORMAT_A4B4G4R4_UNORM_PACK16{
|
||||
VK_FORMAT_R4G4B4A4_UNORM_PACK16,
|
||||
VK_FORMAT_UNDEFINED,
|
||||
};
|
||||
|
||||
} // namespace Alternatives
|
||||
|
||||
enum class NvidiaArchitecture {
|
||||
@ -115,8 +110,6 @@ constexpr const VkFormat* GetFormatAlternatives(VkFormat format) {
|
||||
return Alternatives::R8G8B8_SSCALED.data();
|
||||
case VK_FORMAT_R32G32B32_SFLOAT:
|
||||
return Alternatives::VK_FORMAT_R32G32B32_SFLOAT.data();
|
||||
case VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT:
|
||||
return Alternatives::VK_FORMAT_A4B4G4R4_UNORM_PACK16.data();
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
@ -245,7 +238,6 @@ std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(vk::Physica
|
||||
VK_FORMAT_R32_SINT,
|
||||
VK_FORMAT_R32_UINT,
|
||||
VK_FORMAT_R4G4B4A4_UNORM_PACK16,
|
||||
VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT,
|
||||
VK_FORMAT_R4G4_UNORM_PACK8,
|
||||
VK_FORMAT_R5G5B5A1_UNORM_PACK16,
|
||||
VK_FORMAT_R5G6B5_UNORM_PACK16,
|
||||
|
@ -45,7 +45,6 @@ VK_DEFINE_HANDLE(VmaAllocator)
|
||||
FEATURE(EXT, ExtendedDynamicState, EXTENDED_DYNAMIC_STATE, extended_dynamic_state) \
|
||||
FEATURE(EXT, ExtendedDynamicState2, EXTENDED_DYNAMIC_STATE_2, extended_dynamic_state2) \
|
||||
FEATURE(EXT, ExtendedDynamicState3, EXTENDED_DYNAMIC_STATE_3, extended_dynamic_state3) \
|
||||
FEATURE(EXT, 4444Formats, 4444_FORMATS, format_a4b4g4r4) \
|
||||
FEATURE(EXT, IndexTypeUint8, INDEX_TYPE_UINT8, index_type_uint8) \
|
||||
FEATURE(EXT, LineRasterization, LINE_RASTERIZATION, line_rasterization) \
|
||||
FEATURE(EXT, PrimitiveTopologyListRestart, PRIMITIVE_TOPOLOGY_LIST_RESTART, \
|
||||
@ -98,7 +97,6 @@ VK_DEFINE_HANDLE(VmaAllocator)
|
||||
EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME) \
|
||||
EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_2_EXTENSION_NAME) \
|
||||
EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME) \
|
||||
EXTENSION_NAME(VK_EXT_4444_FORMATS_EXTENSION_NAME) \
|
||||
EXTENSION_NAME(VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME) \
|
||||
EXTENSION_NAME(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME) \
|
||||
EXTENSION_NAME(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME) \
|
||||
@ -146,7 +144,6 @@ VK_DEFINE_HANDLE(VmaAllocator)
|
||||
#define FOR_EACH_VK_RECOMMENDED_FEATURE(FEATURE_NAME) \
|
||||
FEATURE_NAME(custom_border_color, customBorderColors) \
|
||||
FEATURE_NAME(extended_dynamic_state, extendedDynamicState) \
|
||||
FEATURE_NAME(format_a4b4g4r4, formatA4B4G4R4) \
|
||||
FEATURE_NAME(index_type_uint8, indexTypeUint8) \
|
||||
FEATURE_NAME(primitive_topology_list_restart, primitiveTopologyListRestart) \
|
||||
FEATURE_NAME(provoking_vertex, provokingVertexLast) \
|
||||
@ -491,11 +488,6 @@ public:
|
||||
return extensions.extended_dynamic_state3;
|
||||
}
|
||||
|
||||
/// Returns true if the device supports VK_EXT_4444_formats.
|
||||
bool IsExt4444FormatsSupported() const {
|
||||
return features.format_a4b4g4r4.formatA4B4G4R4;
|
||||
}
|
||||
|
||||
/// Returns true if the device supports VK_EXT_extended_dynamic_state3.
|
||||
bool IsExtExtendedDynamicState3BlendingSupported() const {
|
||||
return dynamic_state3_blending;
|
||||
|
Reference in New Issue
Block a user