Compare commits
11 Commits
android-15
...
android-15
Author | SHA1 | Date | |
---|---|---|---|
99c777a17b | |||
cc0b79270e | |||
a5bfa8e00f | |||
d590cfb9d0 | |||
ded419ef2b | |||
4c3f898789 | |||
46c259bb20 | |||
15bebf1695 | |||
5c840334b8 | |||
abfebe5cc4 | |||
a22a025c5b |
@ -1,6 +1,7 @@
|
|||||||
| Pull Request | Commit | Title | Author | Merged? |
|
| Pull Request | Commit | Title | Author | Merged? |
|
||||||
|----|----|----|----|----|
|
|----|----|----|----|----|
|
||||||
| [12335](https://github.com/yuzu-emu/yuzu//pull/12335) | [`edcd6d547`](https://github.com/yuzu-emu/yuzu//pull/12335/files) | android: Game Properties | [t895](https://github.com/t895/) | Yes |
|
| [12237](https://github.com/yuzu-emu/yuzu//pull/12237) | [`a05c24242`](https://github.com/yuzu-emu/yuzu//pull/12237/files) | nce: implement instruction emulation for misaligned memory accesses | [liamwhite](https://github.com/liamwhite/) | Yes |
|
||||||
|
| [12335](https://github.com/yuzu-emu/yuzu//pull/12335) | [`86d26914a`](https://github.com/yuzu-emu/yuzu//pull/12335/files) | android: Game Properties | [t895](https://github.com/t895/) | Yes |
|
||||||
|
|
||||||
|
|
||||||
End of merge log. You can find the original README.md below the break.
|
End of merge log. You can find the original README.md below the break.
|
||||||
|
2
externals/vcpkg
vendored
2
externals/vcpkg
vendored
Submodule externals/vcpkg updated: ef2eef1734...a42af01b72
@ -174,7 +174,8 @@ android {
|
|||||||
"-DANDROID_ARM_NEON=true", // cryptopp requires Neon to work
|
"-DANDROID_ARM_NEON=true", // cryptopp requires Neon to work
|
||||||
"-DYUZU_USE_BUNDLED_VCPKG=ON",
|
"-DYUZU_USE_BUNDLED_VCPKG=ON",
|
||||||
"-DYUZU_USE_BUNDLED_FFMPEG=ON",
|
"-DYUZU_USE_BUNDLED_FFMPEG=ON",
|
||||||
"-DYUZU_ENABLE_LTO=ON"
|
"-DYUZU_ENABLE_LTO=ON",
|
||||||
|
"-DCMAKE_EXPORT_COMPILE_COMMANDS=ON"
|
||||||
)
|
)
|
||||||
|
|
||||||
abiFilters("arm64-v8a", "x86_64")
|
abiFilters("arm64-v8a", "x86_64")
|
||||||
|
@ -14,7 +14,7 @@ import androidx.lifecycle.lifecycleScope
|
|||||||
import androidx.lifecycle.repeatOnLifecycle
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.yuzu.yuzu_emu.databinding.CardInstallableBinding
|
import org.yuzu.yuzu_emu.databinding.CardInstallableIconBinding
|
||||||
import org.yuzu.yuzu_emu.databinding.CardSimpleOutlinedBinding
|
import org.yuzu.yuzu_emu.databinding.CardSimpleOutlinedBinding
|
||||||
import org.yuzu.yuzu_emu.model.GameProperty
|
import org.yuzu.yuzu_emu.model.GameProperty
|
||||||
import org.yuzu.yuzu_emu.model.InstallableProperty
|
import org.yuzu.yuzu_emu.model.InstallableProperty
|
||||||
@ -42,7 +42,7 @@ class GamePropertiesAdapter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
else -> InstallablePropertyViewHolder(
|
else -> InstallablePropertyViewHolder(
|
||||||
CardInstallableBinding.inflate(
|
CardInstallableIconBinding.inflate(
|
||||||
inflater,
|
inflater,
|
||||||
parent,
|
parent,
|
||||||
false
|
false
|
||||||
@ -107,13 +107,20 @@ class GamePropertiesAdapter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class InstallablePropertyViewHolder(val binding: CardInstallableBinding) :
|
inner class InstallablePropertyViewHolder(val binding: CardInstallableIconBinding) :
|
||||||
GamePropertyViewHolder(binding.root) {
|
GamePropertyViewHolder(binding.root) {
|
||||||
override fun bind(property: GameProperty) {
|
override fun bind(property: GameProperty) {
|
||||||
val installableProperty = property as InstallableProperty
|
val installableProperty = property as InstallableProperty
|
||||||
|
|
||||||
binding.title.setText(installableProperty.titleId)
|
binding.title.setText(installableProperty.titleId)
|
||||||
binding.description.setText(installableProperty.descriptionId)
|
binding.description.setText(installableProperty.descriptionId)
|
||||||
|
binding.icon.setImageDrawable(
|
||||||
|
ResourcesCompat.getDrawable(
|
||||||
|
binding.icon.context.resources,
|
||||||
|
installableProperty.iconId,
|
||||||
|
binding.icon.context.theme
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
if (installableProperty.install != null) {
|
if (installableProperty.install != null) {
|
||||||
binding.buttonInstall.visibility = View.VISIBLE
|
binding.buttonInstall.visibility = View.VISIBLE
|
||||||
|
@ -22,6 +22,9 @@ interface AbstractSetting {
|
|||||||
get() = NativeConfig.usingGlobal(key)
|
get() = NativeConfig.usingGlobal(key)
|
||||||
set(value) = NativeConfig.setGlobal(key, value)
|
set(value) = NativeConfig.setGlobal(key, value)
|
||||||
|
|
||||||
|
val isSaveable: Boolean
|
||||||
|
get() = NativeConfig.getIsSaveable(key)
|
||||||
|
|
||||||
fun getValueAsString(needsGlobal: Boolean = false): String
|
fun getValueAsString(needsGlobal: Boolean = false): String
|
||||||
|
|
||||||
fun reset()
|
fun reset()
|
||||||
|
@ -32,8 +32,7 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
|
|||||||
|
|
||||||
override val defaultValue: Boolean by lazy { NativeConfig.getDefaultToString(key).toBoolean() }
|
override val defaultValue: Boolean by lazy { NativeConfig.getDefaultToString(key).toBoolean() }
|
||||||
|
|
||||||
override fun getValueAsString(needsGlobal: Boolean): String =
|
override fun getValueAsString(needsGlobal: Boolean): String = getBoolean(needsGlobal).toString()
|
||||||
if (getBoolean(needsGlobal)) "1" else "0"
|
|
||||||
|
|
||||||
override fun reset() = NativeConfig.setBoolean(key, defaultValue)
|
override fun reset() = NativeConfig.setBoolean(key, defaultValue)
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,11 @@ abstract class SettingsItem(
|
|||||||
|
|
||||||
val isEditable: Boolean
|
val isEditable: Boolean
|
||||||
get() {
|
get() {
|
||||||
|
// Can't edit settings that aren't saveable in per-game config even if they are switchable
|
||||||
|
if (NativeConfig.isPerGameConfigLoaded() && !setting.isSaveable) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
if (!NativeLibrary.isRunning()) return true
|
if (!NativeLibrary.isRunning()) return true
|
||||||
|
|
||||||
// Prevent editing settings that were modified in per-game config while editing global
|
// Prevent editing settings that were modified in per-game config while editing global
|
||||||
@ -37,6 +42,7 @@ abstract class SettingsItem(
|
|||||||
if (!NativeConfig.isPerGameConfigLoaded() && !setting.global) {
|
if (!NativeConfig.isPerGameConfigLoaded() && !setting.global) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return setting.isRuntimeModifiable
|
return setting.isRuntimeModifiable
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,6 +65,7 @@ abstract class SettingsItem(
|
|||||||
val emptySetting = object : AbstractSetting {
|
val emptySetting = object : AbstractSetting {
|
||||||
override val key: String = ""
|
override val key: String = ""
|
||||||
override val defaultValue: Any = false
|
override val defaultValue: Any = false
|
||||||
|
override val isSaveable = true
|
||||||
override fun getValueAsString(needsGlobal: Boolean): String = ""
|
override fun getValueAsString(needsGlobal: Boolean): String = ""
|
||||||
override fun reset() {}
|
override fun reset() {}
|
||||||
}
|
}
|
||||||
@ -303,6 +310,8 @@ abstract class SettingsItem(
|
|||||||
BooleanSetting.FASTMEM_EXCLUSIVES.global = value
|
BooleanSetting.FASTMEM_EXCLUSIVES.global = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val isSaveable = true
|
||||||
|
|
||||||
override fun getValueAsString(needsGlobal: Boolean): String =
|
override fun getValueAsString(needsGlobal: Boolean): String =
|
||||||
getBoolean().toString()
|
getBoolean().toString()
|
||||||
|
|
||||||
|
@ -128,8 +128,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.custom) {
|
// Always load custom settings when launching a game from an intent
|
||||||
SettingsFile.loadCustomConfig(args.game!!)
|
if (args.custom || intentGame != null) {
|
||||||
|
SettingsFile.loadCustomConfig(game)
|
||||||
NativeConfig.unloadPerGameConfig()
|
NativeConfig.unloadPerGameConfig()
|
||||||
} else {
|
} else {
|
||||||
NativeConfig.reloadGlobalConfig()
|
NativeConfig.reloadGlobalConfig()
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
package org.yuzu.yuzu_emu.fragments
|
package org.yuzu.yuzu_emu.fragments
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
@ -73,6 +74,8 @@ class GamePropertiesFragment : Fragment() {
|
|||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is using the correct scope, lint is just acting up
|
||||||
|
@SuppressLint("UnsafeRepeatOnLifecycleDetector")
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
homeViewModel.setNavigationVisibility(visible = false, animated = true)
|
homeViewModel.setNavigationVisibility(visible = false, animated = true)
|
||||||
@ -99,7 +102,8 @@ class GamePropertiesFragment : Fragment() {
|
|||||||
|
|
||||||
reloadList()
|
reloadList()
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
viewLifecycleOwner.lifecycleScope.apply {
|
||||||
|
launch {
|
||||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||||
homeViewModel.openImportSaves.collect {
|
homeViewModel.openImportSaves.collect {
|
||||||
if (it) {
|
if (it) {
|
||||||
@ -109,6 +113,17 @@ class GamePropertiesFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
launch {
|
||||||
|
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||||
|
homeViewModel.reloadPropertiesList.collect {
|
||||||
|
if (it) {
|
||||||
|
reloadList()
|
||||||
|
homeViewModel.reloadPropertiesList(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setInsets()
|
setInsets()
|
||||||
}
|
}
|
||||||
@ -179,6 +194,7 @@ class GamePropertiesFragment : Fragment() {
|
|||||||
InstallableProperty(
|
InstallableProperty(
|
||||||
R.string.save_data,
|
R.string.save_data,
|
||||||
R.string.save_data_description,
|
R.string.save_data_description,
|
||||||
|
R.drawable.ic_save,
|
||||||
{
|
{
|
||||||
MessageDialogFragment.newInstance(
|
MessageDialogFragment.newInstance(
|
||||||
requireActivity(),
|
requireActivity(),
|
||||||
@ -214,7 +230,7 @@ class GamePropertiesFragment : Fragment() {
|
|||||||
R.string.save_data_deleted_successfully,
|
R.string.save_data_deleted_successfully,
|
||||||
Toast.LENGTH_SHORT
|
Toast.LENGTH_SHORT
|
||||||
).show()
|
).show()
|
||||||
reloadList()
|
homeViewModel.reloadPropertiesList(true)
|
||||||
}
|
}
|
||||||
).show(parentFragmentManager, MessageDialogFragment.TAG)
|
).show(parentFragmentManager, MessageDialogFragment.TAG)
|
||||||
}
|
}
|
||||||
@ -242,13 +258,20 @@ class GamePropertiesFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
|
MessageDialogFragment.newInstance(
|
||||||
|
requireActivity(),
|
||||||
|
titleId = R.string.clear_shader_cache,
|
||||||
|
descriptionId = R.string.clear_shader_cache_warning_description,
|
||||||
|
positiveAction = {
|
||||||
shaderCacheDir.deleteRecursively()
|
shaderCacheDir.deleteRecursively()
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
YuzuApplication.appContext,
|
YuzuApplication.appContext,
|
||||||
R.string.cleared_shaders_successfully,
|
R.string.cleared_shaders_successfully,
|
||||||
Toast.LENGTH_SHORT
|
Toast.LENGTH_SHORT
|
||||||
).show()
|
).show()
|
||||||
reloadList()
|
homeViewModel.reloadPropertiesList(true)
|
||||||
|
}
|
||||||
|
).show(parentFragmentManager, MessageDialogFragment.TAG)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -388,7 +411,7 @@ class GamePropertiesFragment : Fragment() {
|
|||||||
getString(R.string.save_file_imported_success),
|
getString(R.string.save_file_imported_success),
|
||||||
Toast.LENGTH_LONG
|
Toast.LENGTH_LONG
|
||||||
).show()
|
).show()
|
||||||
reloadList()
|
homeViewModel.reloadPropertiesList(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
cacheSaveDir.deleteRecursively()
|
cacheSaveDir.deleteRecursively()
|
||||||
|
@ -15,7 +15,7 @@ import org.yuzu.yuzu_emu.model.Game
|
|||||||
import org.yuzu.yuzu_emu.utils.SerializableHelper.parcelable
|
import org.yuzu.yuzu_emu.utils.SerializableHelper.parcelable
|
||||||
|
|
||||||
class LaunchGameDialogFragment : DialogFragment() {
|
class LaunchGameDialogFragment : DialogFragment() {
|
||||||
private var selectedItem = 0
|
private var selectedItem = 1
|
||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
val game = requireArguments().parcelable<Game>(GAME)
|
val game = requireArguments().parcelable<Game>(GAME)
|
||||||
@ -32,7 +32,7 @@ class LaunchGameDialogFragment : DialogFragment() {
|
|||||||
.actionGlobalEmulationActivity(game, selectedItem != 0)
|
.actionGlobalEmulationActivity(game, selectedItem != 0)
|
||||||
requireParentFragment().findNavController().navigate(action)
|
requireParentFragment().findNavController().navigate(action)
|
||||||
}
|
}
|
||||||
.setSingleChoiceItems(launchOptions, 0) { _: DialogInterface, i: Int ->
|
.setSingleChoiceItems(launchOptions, 1) { _: DialogInterface, i: Int ->
|
||||||
selectedItem = i
|
selectedItem = i
|
||||||
}
|
}
|
||||||
.setNegativeButton(android.R.string.cancel, null)
|
.setNegativeButton(android.R.string.cancel, null)
|
||||||
|
@ -10,17 +10,18 @@ import kotlinx.coroutines.flow.StateFlow
|
|||||||
interface GameProperty {
|
interface GameProperty {
|
||||||
@get:StringRes
|
@get:StringRes
|
||||||
val titleId: Int
|
val titleId: Int
|
||||||
get() = -1
|
|
||||||
|
|
||||||
@get:StringRes
|
@get:StringRes
|
||||||
val descriptionId: Int
|
val descriptionId: Int
|
||||||
get() = -1
|
|
||||||
|
@get:DrawableRes
|
||||||
|
val iconId: Int
|
||||||
}
|
}
|
||||||
|
|
||||||
data class SubmenuProperty(
|
data class SubmenuProperty(
|
||||||
override val titleId: Int,
|
override val titleId: Int,
|
||||||
override val descriptionId: Int,
|
override val descriptionId: Int,
|
||||||
@DrawableRes val iconId: Int,
|
override val iconId: Int,
|
||||||
val details: (() -> String)? = null,
|
val details: (() -> String)? = null,
|
||||||
val detailsFlow: StateFlow<String>? = null,
|
val detailsFlow: StateFlow<String>? = null,
|
||||||
val action: () -> Unit
|
val action: () -> Unit
|
||||||
@ -29,6 +30,7 @@ data class SubmenuProperty(
|
|||||||
data class InstallableProperty(
|
data class InstallableProperty(
|
||||||
override val titleId: Int,
|
override val titleId: Int,
|
||||||
override val descriptionId: Int,
|
override val descriptionId: Int,
|
||||||
|
override val iconId: Int,
|
||||||
val install: (() -> Unit)? = null,
|
val install: (() -> Unit)? = null,
|
||||||
val export: (() -> Unit)? = null
|
val export: (() -> Unit)? = null
|
||||||
) : GameProperty
|
) : GameProperty
|
||||||
|
@ -28,6 +28,9 @@ class HomeViewModel : ViewModel() {
|
|||||||
private val _contentToInstall = MutableStateFlow<List<Uri>?>(null)
|
private val _contentToInstall = MutableStateFlow<List<Uri>?>(null)
|
||||||
val contentToInstall get() = _contentToInstall.asStateFlow()
|
val contentToInstall get() = _contentToInstall.asStateFlow()
|
||||||
|
|
||||||
|
private val _reloadPropertiesList = MutableStateFlow(false)
|
||||||
|
val reloadPropertiesList get() = _reloadPropertiesList.asStateFlow()
|
||||||
|
|
||||||
var navigatedToSetup = false
|
var navigatedToSetup = false
|
||||||
|
|
||||||
fun setNavigationVisibility(visible: Boolean, animated: Boolean) {
|
fun setNavigationVisibility(visible: Boolean, animated: Boolean) {
|
||||||
@ -59,4 +62,8 @@ class HomeViewModel : ViewModel() {
|
|||||||
fun setContentToInstall(documents: List<Uri>?) {
|
fun setContentToInstall(documents: List<Uri>?) {
|
||||||
_contentToInstall.value = documents
|
_contentToInstall.value = documents
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun reloadPropertiesList(reload: Boolean) {
|
||||||
|
_reloadPropertiesList.value = reload
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,6 +110,8 @@ object NativeConfig {
|
|||||||
@Synchronized
|
@Synchronized
|
||||||
external fun setGlobal(key: String, global: Boolean)
|
external fun setGlobal(key: String, global: Boolean)
|
||||||
|
|
||||||
|
external fun getIsSaveable(key: String): Boolean
|
||||||
|
|
||||||
external fun getDefaultToString(key: String): String
|
external fun getDefaultToString(key: String): String
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -89,13 +89,13 @@ jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsValid(JNIEnv* env, jobj
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto fileType = loader->GetFileType();
|
const auto file_type = loader->GetFileType();
|
||||||
if (fileType == Loader::FileType::Unknown || fileType == Loader::FileType::Error) {
|
if (file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 programId = 0;
|
u64 program_id = 0;
|
||||||
Loader::ResultStatus res = loader->ReadProgramId(programId);
|
Loader::ResultStatus res = loader->ReadProgramId(program_id);
|
||||||
if (res != Loader::ResultStatus::Success) {
|
if (res != Loader::ResultStatus::Success) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -80,7 +80,7 @@ Core::System& EmulationSession::System() {
|
|||||||
return m_system;
|
return m_system;
|
||||||
}
|
}
|
||||||
|
|
||||||
FileSys::ManualContentProvider* EmulationSession::ContentProvider() {
|
FileSys::ManualContentProvider* EmulationSession::GetContentProvider() {
|
||||||
return m_manual_provider.get();
|
return m_manual_provider.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -296,9 +296,6 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string
|
|||||||
// Initialize filesystem.
|
// Initialize filesystem.
|
||||||
ConfigureFilesystemProvider(filepath);
|
ConfigureFilesystemProvider(filepath);
|
||||||
|
|
||||||
// Initialize account manager
|
|
||||||
m_profile_manager = std::make_unique<Service::Account::ProfileManager>();
|
|
||||||
|
|
||||||
// Load the ROM.
|
// Load the ROM.
|
||||||
m_load_result = m_system.Load(EmulationSession::GetInstance().Window(), filepath);
|
m_load_result = m_system.Load(EmulationSession::GetInstance().Window(), filepath);
|
||||||
if (m_load_result != Core::SystemResultStatus::Success) {
|
if (m_load_result != Core::SystemResultStatus::Success) {
|
||||||
@ -463,15 +460,13 @@ void EmulationSession::OnEmulationStopped(Core::SystemResultStatus result) {
|
|||||||
static_cast<jint>(result));
|
static_cast<jint>(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 EmulationSession::getProgramId(JNIEnv* env, jstring jprogramId) {
|
u64 EmulationSession::GetProgramId(JNIEnv* env, jstring jprogramId) {
|
||||||
u64 program_id;
|
|
||||||
auto program_id_string = GetJString(env, jprogramId);
|
auto program_id_string = GetJString(env, jprogramId);
|
||||||
if (program_id_string.empty()) {
|
try {
|
||||||
program_id = 0;
|
return std::stoull(program_id_string);
|
||||||
} else {
|
} catch (...) {
|
||||||
program_id = std::stoull(program_id_string);
|
return 0;
|
||||||
}
|
}
|
||||||
return program_id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Core::SystemResultStatus RunEmulation(const std::string& filepath) {
|
static Core::SystemResultStatus RunEmulation(const std::string& filepath) {
|
||||||
@ -526,16 +521,16 @@ int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject
|
|||||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* env, jobject jobj,
|
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* env, jobject jobj,
|
||||||
jstring jprogramId,
|
jstring jprogramId,
|
||||||
jstring jupdatePath) {
|
jstring jupdatePath) {
|
||||||
u64 programId = EmulationSession::getProgramId(env, jprogramId);
|
u64 program_id = EmulationSession::GetProgramId(env, jprogramId);
|
||||||
std::string updatePath = GetJString(env, jupdatePath);
|
std::string updatePath = GetJString(env, jupdatePath);
|
||||||
std::shared_ptr<FileSys::NSP> nsp = std::make_shared<FileSys::NSP>(
|
std::shared_ptr<FileSys::NSP> nsp = std::make_shared<FileSys::NSP>(
|
||||||
EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(updatePath,
|
EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(updatePath,
|
||||||
FileSys::Mode::Read));
|
FileSys::Mode::Read));
|
||||||
for (const auto& item : nsp->GetNCAs()) {
|
for (const auto& item : nsp->GetNCAs()) {
|
||||||
for (const auto& ncaDetails : item.second) {
|
for (const auto& nca_details : item.second) {
|
||||||
if (ncaDetails.second->GetName().ends_with(".cnmt.nca")) {
|
if (nca_details.second->GetName().ends_with(".cnmt.nca")) {
|
||||||
auto updateId = ncaDetails.second->GetTitleId() & ~0xFFFULL;
|
auto update_id = nca_details.second->GetTitleId() & ~0xFFFULL;
|
||||||
if (updateId == programId) {
|
if (update_id == program_id) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -770,8 +765,8 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmptyUserDirectory(JNIEnv*
|
|||||||
auto vfs_nand_dir = EmulationSession::GetInstance().System().GetFilesystem()->OpenDirectory(
|
auto vfs_nand_dir = EmulationSession::GetInstance().System().GetFilesystem()->OpenDirectory(
|
||||||
Common::FS::PathToUTF8String(nand_dir), FileSys::Mode::Read);
|
Common::FS::PathToUTF8String(nand_dir), FileSys::Mode::Read);
|
||||||
|
|
||||||
Service::Account::ProfileManager manager;
|
const auto user_id = EmulationSession::GetInstance().System().GetProfileManager().GetUser(
|
||||||
const auto user_id = manager.GetUser(static_cast<std::size_t>(0));
|
static_cast<std::size_t>(0));
|
||||||
ASSERT(user_id);
|
ASSERT(user_id);
|
||||||
|
|
||||||
const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath(
|
const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath(
|
||||||
@ -840,8 +835,8 @@ jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getAddonsForFile(JNIEnv* env,
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto& system = EmulationSession::GetInstance().System();
|
auto& system = EmulationSession::GetInstance().System();
|
||||||
auto programId = EmulationSession::getProgramId(env, jprogramId);
|
auto program_id = EmulationSession::GetProgramId(env, jprogramId);
|
||||||
const FileSys::PatchManager pm{programId, system.GetFileSystemController(),
|
const FileSys::PatchManager pm{program_id, system.GetFileSystemController(),
|
||||||
system.GetContentProvider()};
|
system.GetContentProvider()};
|
||||||
const auto loader = Loader::GetLoader(system, vFile);
|
const auto loader = Loader::GetLoader(system, vFile);
|
||||||
|
|
||||||
@ -849,11 +844,11 @@ jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getAddonsForFile(JNIEnv* env,
|
|||||||
loader->ReadUpdateRaw(update_raw);
|
loader->ReadUpdateRaw(update_raw);
|
||||||
|
|
||||||
auto addons = pm.GetPatchVersionNames(update_raw);
|
auto addons = pm.GetPatchVersionNames(update_raw);
|
||||||
auto emptyString = ToJString(env, "");
|
auto jemptyString = ToJString(env, "");
|
||||||
auto emptyStringPair = env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(),
|
auto jemptyStringPair = env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(),
|
||||||
emptyString, emptyString);
|
jemptyString, jemptyString);
|
||||||
jobjectArray jaddonsArray =
|
jobjectArray jaddonsArray =
|
||||||
env->NewObjectArray(addons.size(), IDCache::GetPairClass(), emptyStringPair);
|
env->NewObjectArray(addons.size(), IDCache::GetPairClass(), jemptyStringPair);
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (const auto& addon : addons) {
|
for (const auto& addon : addons) {
|
||||||
jobject jaddon = env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(),
|
jobject jaddon = env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(),
|
||||||
@ -866,7 +861,7 @@ jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_getAddonsForFile(JNIEnv* env,
|
|||||||
|
|
||||||
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject jobj,
|
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject jobj,
|
||||||
jstring jprogramId) {
|
jstring jprogramId) {
|
||||||
auto programId = EmulationSession::getProgramId(env, jprogramId);
|
auto program_id = EmulationSession::GetProgramId(env, jprogramId);
|
||||||
|
|
||||||
auto& system = EmulationSession::GetInstance().System();
|
auto& system = EmulationSession::GetInstance().System();
|
||||||
|
|
||||||
@ -881,7 +876,7 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject j
|
|||||||
|
|
||||||
const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath(
|
const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath(
|
||||||
system, vfsNandDir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData,
|
system, vfsNandDir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData,
|
||||||
programId, user_id->AsU128(), 0);
|
program_id, user_id->AsU128(), 0);
|
||||||
return ToJString(env, user_save_data_path);
|
return ToJString(env, user_save_data_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -891,7 +886,7 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_addFileToFilesystemProvider(JNIEnv* e
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_clearFilesystemProvider(JNIEnv* env, jobject jobj) {
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_clearFilesystemProvider(JNIEnv* env, jobject jobj) {
|
||||||
EmulationSession::GetInstance().ContentProvider()->ClearAllEntries();
|
EmulationSession::GetInstance().GetContentProvider()->ClearAllEntries();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
|
@ -21,7 +21,7 @@ public:
|
|||||||
static EmulationSession& GetInstance();
|
static EmulationSession& GetInstance();
|
||||||
const Core::System& System() const;
|
const Core::System& System() const;
|
||||||
Core::System& System();
|
Core::System& System();
|
||||||
FileSys::ManualContentProvider* ContentProvider();
|
FileSys::ManualContentProvider* GetContentProvider();
|
||||||
|
|
||||||
const EmuWindow_Android& Window() const;
|
const EmuWindow_Android& Window() const;
|
||||||
EmuWindow_Android& Window();
|
EmuWindow_Android& Window();
|
||||||
@ -55,7 +55,7 @@ public:
|
|||||||
|
|
||||||
static void OnEmulationStarted();
|
static void OnEmulationStarted();
|
||||||
|
|
||||||
static u64 getProgramId(JNIEnv* env, jstring jprogramId);
|
static u64 GetProgramId(JNIEnv* env, jstring jprogramId);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max);
|
static void LoadDiskCacheProgress(VideoCore::LoadCallbackStage stage, int progress, int max);
|
||||||
@ -76,7 +76,6 @@ private:
|
|||||||
std::atomic<bool> m_is_running = false;
|
std::atomic<bool> m_is_running = false;
|
||||||
std::atomic<bool> m_is_paused = false;
|
std::atomic<bool> m_is_paused = false;
|
||||||
SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{};
|
SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{};
|
||||||
std::unique_ptr<Service::Account::ProfileManager> m_profile_manager;
|
|
||||||
std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider;
|
std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider;
|
||||||
|
|
||||||
// GPU driver parameters
|
// GPU driver parameters
|
||||||
|
@ -21,13 +21,13 @@ std::unique_ptr<AndroidConfig> per_game_config;
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) {
|
Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) {
|
||||||
auto key = GetJString(env, jkey);
|
auto key = GetJString(env, jkey);
|
||||||
auto basicSetting = Settings::values.linkage.by_key[key];
|
auto basic_setting = Settings::values.linkage.by_key[key];
|
||||||
if (basicSetting != 0) {
|
if (basic_setting != 0) {
|
||||||
return static_cast<Settings::Setting<T>*>(basicSetting);
|
return static_cast<Settings::Setting<T>*>(basic_setting);
|
||||||
}
|
}
|
||||||
auto basicAndroidSetting = AndroidSettings::values.linkage.by_key[key];
|
auto basic_android_setting = AndroidSettings::values.linkage.by_key[key];
|
||||||
if (basicAndroidSetting != 0) {
|
if (basic_android_setting != 0) {
|
||||||
return static_cast<Settings::Setting<T>*>(basicAndroidSetting);
|
return static_cast<Settings::Setting<T>*>(basic_android_setting);
|
||||||
}
|
}
|
||||||
LOG_ERROR(Frontend, "[Android Native] Could not find setting - {}", key);
|
LOG_ERROR(Frontend, "[Android Native] Could not find setting - {}", key);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -54,7 +54,7 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_saveGlobalConfig(JNIEnv* env, jo
|
|||||||
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_initializePerGameConfig(JNIEnv* env, jobject obj,
|
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_initializePerGameConfig(JNIEnv* env, jobject obj,
|
||||||
jstring jprogramId,
|
jstring jprogramId,
|
||||||
jstring jfileName) {
|
jstring jfileName) {
|
||||||
auto program_id = EmulationSession::getProgramId(env, jprogramId);
|
auto program_id = EmulationSession::GetProgramId(env, jprogramId);
|
||||||
auto file_name = GetJString(env, jfileName);
|
auto file_name = GetJString(env, jfileName);
|
||||||
const auto config_file_name = program_id == 0 ? file_name : fmt::format("{:016X}", program_id);
|
const auto config_file_name = program_id == 0 ? file_name : fmt::format("{:016X}", program_id);
|
||||||
per_game_config =
|
per_game_config =
|
||||||
@ -249,6 +249,15 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setGlobal(JNIEnv* env, jobject o
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jboolean Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getIsSaveable(JNIEnv* env, jobject obj,
|
||||||
|
jstring jkey) {
|
||||||
|
auto setting = getSetting<std::string>(env, jkey);
|
||||||
|
if (setting != nullptr) {
|
||||||
|
return setting->Save();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
jstring Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getDefaultToString(JNIEnv* env, jobject obj,
|
jstring Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getDefaultToString(JNIEnv* env, jobject obj,
|
||||||
jstring jkey) {
|
jstring jkey) {
|
||||||
auto setting = getSetting<std::string>(env, jkey);
|
auto setting = getSetting<std::string>(env, jkey);
|
||||||
@ -311,7 +320,7 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_addGameDir(JNIEnv* env, jobject
|
|||||||
|
|
||||||
jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getDisabledAddons(JNIEnv* env, jobject obj,
|
jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getDisabledAddons(JNIEnv* env, jobject obj,
|
||||||
jstring jprogramId) {
|
jstring jprogramId) {
|
||||||
auto program_id = EmulationSession::getProgramId(env, jprogramId);
|
auto program_id = EmulationSession::GetProgramId(env, jprogramId);
|
||||||
auto& disabledAddons = Settings::values.disabled_addons[program_id];
|
auto& disabledAddons = Settings::values.disabled_addons[program_id];
|
||||||
jobjectArray jdisabledAddonsArray =
|
jobjectArray jdisabledAddonsArray =
|
||||||
env->NewObjectArray(disabledAddons.size(), IDCache::GetStringClass(), ToJString(env, ""));
|
env->NewObjectArray(disabledAddons.size(), IDCache::GetStringClass(), ToJString(env, ""));
|
||||||
@ -324,7 +333,7 @@ jobjectArray Java_org_yuzu_yuzu_1emu_utils_NativeConfig_getDisabledAddons(JNIEnv
|
|||||||
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setDisabledAddons(JNIEnv* env, jobject obj,
|
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_setDisabledAddons(JNIEnv* env, jobject obj,
|
||||||
jstring jprogramId,
|
jstring jprogramId,
|
||||||
jobjectArray jdisabledAddons) {
|
jobjectArray jdisabledAddons) {
|
||||||
auto program_id = EmulationSession::getProgramId(env, jprogramId);
|
auto program_id = EmulationSession::GetProgramId(env, jprogramId);
|
||||||
Settings::values.disabled_addons[program_id].clear();
|
Settings::values.disabled_addons[program_id].clear();
|
||||||
std::vector<std::string> disabled_addons;
|
std::vector<std::string> disabled_addons;
|
||||||
const int size = env->GetArrayLength(jdisabledAddons);
|
const int size = env->GetArrayLength(jdisabledAddons);
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:viewportWidth="960"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="960"
|
android:viewportHeight="24">
|
||||||
android:tint="?attr/colorControlNormal">
|
|
||||||
<path
|
<path
|
||||||
android:fillColor="@android:color/white"
|
android:fillColor="?attr/colorControlNormal"
|
||||||
android:pathData="M200,840Q167,840 143.5,816.5Q120,793 120,760L120,200Q120,167 143.5,143.5Q167,120 200,120L647,120Q663,120 677.5,126Q692,132 703,143L817,257Q828,268 834,282.5Q840,297 840,313L840,760Q840,793 816.5,816.5Q793,840 760,840L200,840ZM760,314L646,200L200,200Q200,200 200,200Q200,200 200,200L200,760Q200,760 200,760Q200,760 200,760L760,760Q760,760 760,760Q760,760 760,760L760,314ZM480,720Q530,720 565,685Q600,650 600,600Q600,550 565,515Q530,480 480,480Q430,480 395,515Q360,550 360,600Q360,650 395,685Q430,720 480,720ZM280,400L560,400Q577,400 588.5,388.5Q600,377 600,360L600,280Q600,263 588.5,251.5Q577,240 560,240L280,240Q263,240 251.5,251.5Q240,263 240,280L240,360Q240,377 251.5,388.5Q263,400 280,400ZM200,314L200,760Q200,760 200,760Q200,760 200,760L200,760Q200,760 200,760Q200,760 200,760L200,200Q200,200 200,200Q200,200 200,200L200,200L200,314Z"/>
|
android:pathData="M17,3L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,7l-4,-4zM12,19c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3 3,1.34 3,3 -1.34,3 -3,3zM15,9L5,9L5,5h10v4z" />
|
||||||
</vector>
|
</vector>
|
||||||
|
@ -0,0 +1,82 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
style="?attr/materialCardViewOutlinedStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="16dp"
|
||||||
|
android:layout_marginVertical="12dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:paddingHorizontal="24dp"
|
||||||
|
android:paddingVertical="16dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/icon"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_marginEnd="20dp"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
app:tint="?attr/colorOnSurface"
|
||||||
|
tools:src="@drawable/ic_settings" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
android:id="@+id/title"
|
||||||
|
style="@style/TextAppearance.Material3.TitleMedium"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/user_data"
|
||||||
|
android:textAlignment="viewStart" />
|
||||||
|
|
||||||
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
android:id="@+id/description"
|
||||||
|
style="@style/TextAppearance.Material3.BodyMedium"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="6dp"
|
||||||
|
android:text="@string/user_data_description"
|
||||||
|
android:textAlignment="viewStart" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<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_gravity="center_vertical"
|
||||||
|
android:contentDescription="@string/export"
|
||||||
|
android:tooltipText="@string/export"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:icon="@drawable/ic_export"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/button_install"
|
||||||
|
style="@style/Widget.Material3.Button.IconButton.Filled.Tonal"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:layout_marginStart="12dp"
|
||||||
|
android:contentDescription="@string/string_import"
|
||||||
|
android:tooltipText="@string/string_import"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:icon="@drawable/ic_import"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
@ -0,0 +1,89 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
style="?attr/materialCardViewOutlinedStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="16dp"
|
||||||
|
android:layout_marginVertical="12dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:paddingHorizontal="24dp"
|
||||||
|
android:paddingVertical="16dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/icon"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_marginEnd="20dp"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
app:tint="?attr/colorOnSurface"
|
||||||
|
tools:src="@drawable/ic_settings" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
android:id="@+id/title"
|
||||||
|
style="@style/TextAppearance.Material3.TitleMedium"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/user_data"
|
||||||
|
android:textAlignment="viewStart" />
|
||||||
|
|
||||||
|
<com.google.android.material.textview.MaterialTextView
|
||||||
|
android:id="@+id/description"
|
||||||
|
style="@style/TextAppearance.Material3.BodyMedium"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="6dp"
|
||||||
|
android:text="@string/user_data_description"
|
||||||
|
android:textAlignment="viewStart" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/button_install"
|
||||||
|
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"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:icon="@drawable/ic_import"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
<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_gravity="center_vertical"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:contentDescription="@string/export"
|
||||||
|
android:tooltipText="@string/export"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:icon="@drawable/ic_export"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
@ -314,6 +314,7 @@
|
|||||||
<string name="add_ons_description">Toggle mods, updates and DLC</string>
|
<string name="add_ons_description">Toggle mods, updates and DLC</string>
|
||||||
<string name="clear_shader_cache">Clear shader cache</string>
|
<string name="clear_shader_cache">Clear shader cache</string>
|
||||||
<string name="clear_shader_cache_description">Removes all shaders built while playing this game</string>
|
<string name="clear_shader_cache_description">Removes all shaders built while playing this game</string>
|
||||||
|
<string name="clear_shader_cache_warning_description">You will experience more stuttering as the shader cache regenerates</string>
|
||||||
<string name="cleared_shaders_successfully">Cleared shaders successfully</string>
|
<string name="cleared_shaders_successfully">Cleared shaders successfully</string>
|
||||||
<string name="addons_game">Addons: %1$s</string>
|
<string name="addons_game">Addons: %1$s</string>
|
||||||
<string name="save_data">Save data</string>
|
<string name="save_data">Save data</string>
|
||||||
@ -334,7 +335,7 @@
|
|||||||
<string name="addon_installed_successfully">Addon installed successfully</string>
|
<string name="addon_installed_successfully">Addon installed successfully</string>
|
||||||
<string name="verifying_content">Verifying content…</string>
|
<string name="verifying_content">Verifying content…</string>
|
||||||
<string name="content_install_notice">Content install notice</string>
|
<string name="content_install_notice">Content install notice</string>
|
||||||
<string name="content_install_notice_description">The content that you selected does not match this game.\nInstall anyways?</string>
|
<string name="content_install_notice_description">The content that you selected does not match this game.\nInstall anyway?</string>
|
||||||
|
|
||||||
<!-- ROM loading errors -->
|
<!-- ROM loading errors -->
|
||||||
<string name="loader_error_encrypted">Your ROM is encrypted</string>
|
<string name="loader_error_encrypted">Your ROM is encrypted</string>
|
||||||
|
@ -256,7 +256,7 @@ struct Values {
|
|||||||
AstcDecodeMode::CpuAsynchronous,
|
AstcDecodeMode::CpuAsynchronous,
|
||||||
"accelerate_astc",
|
"accelerate_astc",
|
||||||
Category::Renderer};
|
Category::Renderer};
|
||||||
Setting<VSyncMode, true> vsync_mode{
|
SwitchableSetting<VSyncMode, true> vsync_mode{
|
||||||
linkage, VSyncMode::Fifo, VSyncMode::Immediate, VSyncMode::FifoRelaxed,
|
linkage, VSyncMode::Fifo, VSyncMode::Immediate, VSyncMode::FifoRelaxed,
|
||||||
"use_vsync", Category::Renderer, Specialization::RuntimeList, true,
|
"use_vsync", Category::Renderer, Specialization::RuntimeList, true,
|
||||||
true};
|
true};
|
||||||
|
@ -947,15 +947,19 @@ if (HAS_NCE)
|
|||||||
set(CMAKE_ASM_FLAGS "${CFLAGS} -x assembler-with-cpp")
|
set(CMAKE_ASM_FLAGS "${CFLAGS} -x assembler-with-cpp")
|
||||||
|
|
||||||
target_sources(core PRIVATE
|
target_sources(core PRIVATE
|
||||||
|
arm/nce/arm_nce_asm_definitions.h
|
||||||
arm/nce/arm_nce.cpp
|
arm/nce/arm_nce.cpp
|
||||||
arm/nce/arm_nce.h
|
arm/nce/arm_nce.h
|
||||||
arm/nce/arm_nce.s
|
arm/nce/arm_nce.s
|
||||||
arm/nce/guest_context.h
|
arm/nce/guest_context.h
|
||||||
|
arm/nce/instructions.h
|
||||||
|
arm/nce/interpreter_visitor.cpp
|
||||||
|
arm/nce/interpreter_visitor.h
|
||||||
arm/nce/patcher.cpp
|
arm/nce/patcher.cpp
|
||||||
arm/nce/patcher.h
|
arm/nce/patcher.h
|
||||||
arm/nce/instructions.h
|
arm/nce/visitor_base.h
|
||||||
)
|
)
|
||||||
target_link_libraries(core PRIVATE merry::oaknut)
|
target_link_libraries(core PRIVATE merry::mcl merry::oaknut)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
|
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
#include "common/signal_chain.h"
|
#include "common/signal_chain.h"
|
||||||
#include "core/arm/nce/arm_nce.h"
|
#include "core/arm/nce/arm_nce.h"
|
||||||
#include "core/arm/nce/guest_context.h"
|
#include "core/arm/nce/interpreter_visitor.h"
|
||||||
#include "core/arm/nce/patcher.h"
|
#include "core/arm/nce/patcher.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/memory.h"
|
#include "core/memory.h"
|
||||||
@ -21,7 +21,8 @@ namespace Core {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
struct sigaction g_orig_action;
|
struct sigaction g_orig_bus_action;
|
||||||
|
struct sigaction g_orig_segv_action;
|
||||||
|
|
||||||
// Verify assembly offsets.
|
// Verify assembly offsets.
|
||||||
using NativeExecutionParameters = Kernel::KThread::NativeExecutionParameters;
|
using NativeExecutionParameters = Kernel::KThread::NativeExecutionParameters;
|
||||||
@ -37,6 +38,9 @@ fpsimd_context* GetFloatingPointState(mcontext_t& host_ctx) {
|
|||||||
return reinterpret_cast<fpsimd_context*>(header);
|
return reinterpret_cast<fpsimd_context*>(header);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using namespace Common::Literals;
|
||||||
|
constexpr u32 StackSize = 32_KiB;
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void* ArmNce::RestoreGuestContext(void* raw_context) {
|
void* ArmNce::RestoreGuestContext(void* raw_context) {
|
||||||
@ -104,19 +108,10 @@ void ArmNce::SaveGuestContext(GuestContext* guest_ctx, void* raw_context) {
|
|||||||
host_ctx.regs[0] = guest_ctx->esr_el1.exchange(0);
|
host_ctx.regs[0] = guest_ctx->esr_el1.exchange(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ArmNce::HandleGuestFault(GuestContext* guest_ctx, void* raw_info, void* raw_context) {
|
bool ArmNce::HandleFailedGuestFault(GuestContext* guest_ctx, void* raw_info, void* raw_context) {
|
||||||
auto& host_ctx = static_cast<ucontext_t*>(raw_context)->uc_mcontext;
|
auto& host_ctx = static_cast<ucontext_t*>(raw_context)->uc_mcontext;
|
||||||
auto* info = static_cast<siginfo_t*>(raw_info);
|
auto* info = static_cast<siginfo_t*>(raw_info);
|
||||||
|
|
||||||
// Try to handle an invalid access.
|
|
||||||
// TODO: handle accesses which split a page?
|
|
||||||
const Common::ProcessAddress addr =
|
|
||||||
(reinterpret_cast<u64>(info->si_addr) & ~Memory::YUZU_PAGEMASK);
|
|
||||||
if (guest_ctx->system->ApplicationMemory().InvalidateNCE(addr, Memory::YUZU_PAGESIZE)) {
|
|
||||||
// We handled the access successfully and are returning to guest code.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We can't handle the access, so determine why we crashed.
|
// We can't handle the access, so determine why we crashed.
|
||||||
const bool is_prefetch_abort = host_ctx.pc == reinterpret_cast<u64>(info->si_addr);
|
const bool is_prefetch_abort = host_ctx.pc == reinterpret_cast<u64>(info->si_addr);
|
||||||
|
|
||||||
@ -143,8 +138,44 @@ bool ArmNce::HandleGuestFault(GuestContext* guest_ctx, void* raw_info, void* raw
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ArmNce::HandleHostFault(int sig, void* raw_info, void* raw_context) {
|
bool ArmNce::HandleGuestAlignmentFault(GuestContext* guest_ctx, void* raw_info, void* raw_context) {
|
||||||
return g_orig_action.sa_sigaction(sig, static_cast<siginfo_t*>(raw_info), raw_context);
|
auto& host_ctx = static_cast<ucontext_t*>(raw_context)->uc_mcontext;
|
||||||
|
auto* fpctx = GetFloatingPointState(host_ctx);
|
||||||
|
auto& memory = guest_ctx->system->ApplicationMemory();
|
||||||
|
|
||||||
|
// Match and execute an instruction.
|
||||||
|
auto next_pc = MatchAndExecuteOneInstruction(memory, &host_ctx, fpctx);
|
||||||
|
if (next_pc) {
|
||||||
|
host_ctx.pc = *next_pc;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We couldn't handle the access.
|
||||||
|
return HandleFailedGuestFault(guest_ctx, raw_info, raw_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ArmNce::HandleGuestAccessFault(GuestContext* guest_ctx, void* raw_info, void* raw_context) {
|
||||||
|
auto* info = static_cast<siginfo_t*>(raw_info);
|
||||||
|
|
||||||
|
// Try to handle an invalid access.
|
||||||
|
// TODO: handle accesses which split a page?
|
||||||
|
const Common::ProcessAddress addr =
|
||||||
|
(reinterpret_cast<u64>(info->si_addr) & ~Memory::YUZU_PAGEMASK);
|
||||||
|
if (guest_ctx->system->ApplicationMemory().InvalidateNCE(addr, Memory::YUZU_PAGESIZE)) {
|
||||||
|
// We handled the access successfully and are returning to guest code.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We couldn't handle the access.
|
||||||
|
return HandleFailedGuestFault(guest_ctx, raw_info, raw_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArmNce::HandleHostAlignmentFault(int sig, void* raw_info, void* raw_context) {
|
||||||
|
return g_orig_bus_action.sa_sigaction(sig, static_cast<siginfo_t*>(raw_info), raw_context);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArmNce::HandleHostAccessFault(int sig, void* raw_info, void* raw_context) {
|
||||||
|
return g_orig_segv_action.sa_sigaction(sig, static_cast<siginfo_t*>(raw_info), raw_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ArmNce::LockThread(Kernel::KThread* thread) {
|
void ArmNce::LockThread(Kernel::KThread* thread) {
|
||||||
@ -225,18 +256,31 @@ ArmNce::ArmNce(System& system, bool uses_wall_clock, std::size_t core_index)
|
|||||||
ArmNce::~ArmNce() = default;
|
ArmNce::~ArmNce() = default;
|
||||||
|
|
||||||
void ArmNce::Initialize() {
|
void ArmNce::Initialize() {
|
||||||
|
if (m_thread_id == -1) {
|
||||||
m_thread_id = gettid();
|
m_thread_id = gettid();
|
||||||
|
}
|
||||||
|
|
||||||
// Setup our signals
|
// Configure signal stack.
|
||||||
static std::once_flag signals;
|
if (!m_stack) {
|
||||||
std::call_once(signals, [] {
|
m_stack = std::make_unique<u8[]>(StackSize);
|
||||||
|
|
||||||
|
stack_t ss{};
|
||||||
|
ss.ss_sp = m_stack.get();
|
||||||
|
ss.ss_size = StackSize;
|
||||||
|
sigaltstack(&ss, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up signals.
|
||||||
|
static std::once_flag flag;
|
||||||
|
std::call_once(flag, [] {
|
||||||
using HandlerType = decltype(sigaction::sa_sigaction);
|
using HandlerType = decltype(sigaction::sa_sigaction);
|
||||||
|
|
||||||
sigset_t signal_mask;
|
sigset_t signal_mask;
|
||||||
sigemptyset(&signal_mask);
|
sigemptyset(&signal_mask);
|
||||||
sigaddset(&signal_mask, ReturnToRunCodeByExceptionLevelChangeSignal);
|
sigaddset(&signal_mask, ReturnToRunCodeByExceptionLevelChangeSignal);
|
||||||
sigaddset(&signal_mask, BreakFromRunCodeSignal);
|
sigaddset(&signal_mask, BreakFromRunCodeSignal);
|
||||||
sigaddset(&signal_mask, GuestFaultSignal);
|
sigaddset(&signal_mask, GuestAlignmentFaultSignal);
|
||||||
|
sigaddset(&signal_mask, GuestAccessFaultSignal);
|
||||||
|
|
||||||
struct sigaction return_to_run_code_action {};
|
struct sigaction return_to_run_code_action {};
|
||||||
return_to_run_code_action.sa_flags = SA_SIGINFO | SA_ONSTACK;
|
return_to_run_code_action.sa_flags = SA_SIGINFO | SA_ONSTACK;
|
||||||
@ -253,18 +297,19 @@ void ArmNce::Initialize() {
|
|||||||
break_from_run_code_action.sa_mask = signal_mask;
|
break_from_run_code_action.sa_mask = signal_mask;
|
||||||
Common::SigAction(BreakFromRunCodeSignal, &break_from_run_code_action, nullptr);
|
Common::SigAction(BreakFromRunCodeSignal, &break_from_run_code_action, nullptr);
|
||||||
|
|
||||||
struct sigaction fault_action {};
|
struct sigaction alignment_fault_action {};
|
||||||
fault_action.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_RESTART;
|
alignment_fault_action.sa_flags = SA_SIGINFO | SA_ONSTACK;
|
||||||
fault_action.sa_sigaction = reinterpret_cast<HandlerType>(&ArmNce::GuestFaultSignalHandler);
|
alignment_fault_action.sa_sigaction =
|
||||||
fault_action.sa_mask = signal_mask;
|
reinterpret_cast<HandlerType>(&ArmNce::GuestAlignmentFaultSignalHandler);
|
||||||
Common::SigAction(GuestFaultSignal, &fault_action, &g_orig_action);
|
alignment_fault_action.sa_mask = signal_mask;
|
||||||
|
Common::SigAction(GuestAlignmentFaultSignal, &alignment_fault_action, nullptr);
|
||||||
|
|
||||||
// Simplify call for g_orig_action.
|
struct sigaction access_fault_action {};
|
||||||
// These fields occupy the same space in memory, so this should be a no-op in practice.
|
access_fault_action.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_RESTART;
|
||||||
if (!(g_orig_action.sa_flags & SA_SIGINFO)) {
|
access_fault_action.sa_sigaction =
|
||||||
g_orig_action.sa_sigaction =
|
reinterpret_cast<HandlerType>(&ArmNce::GuestAccessFaultSignalHandler);
|
||||||
reinterpret_cast<decltype(g_orig_action.sa_sigaction)>(g_orig_action.sa_handler);
|
access_fault_action.sa_mask = signal_mask;
|
||||||
}
|
Common::SigAction(GuestAccessFaultSignal, &access_fault_action, &g_orig_segv_action);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,7 +61,8 @@ private:
|
|||||||
static void ReturnToRunCodeByExceptionLevelChangeSignalHandler(int sig, void* info,
|
static void ReturnToRunCodeByExceptionLevelChangeSignalHandler(int sig, void* info,
|
||||||
void* raw_context);
|
void* raw_context);
|
||||||
static void BreakFromRunCodeSignalHandler(int sig, void* info, void* raw_context);
|
static void BreakFromRunCodeSignalHandler(int sig, void* info, void* raw_context);
|
||||||
static void GuestFaultSignalHandler(int sig, void* info, void* raw_context);
|
static void GuestAlignmentFaultSignalHandler(int sig, void* info, void* raw_context);
|
||||||
|
static void GuestAccessFaultSignalHandler(int sig, void* info, void* raw_context);
|
||||||
|
|
||||||
static void LockThreadParameters(void* tpidr);
|
static void LockThreadParameters(void* tpidr);
|
||||||
static void UnlockThreadParameters(void* tpidr);
|
static void UnlockThreadParameters(void* tpidr);
|
||||||
@ -70,8 +71,11 @@ private:
|
|||||||
// C++ implementation functions for assembly definitions.
|
// C++ implementation functions for assembly definitions.
|
||||||
static void* RestoreGuestContext(void* raw_context);
|
static void* RestoreGuestContext(void* raw_context);
|
||||||
static void SaveGuestContext(GuestContext* ctx, void* raw_context);
|
static void SaveGuestContext(GuestContext* ctx, void* raw_context);
|
||||||
static bool HandleGuestFault(GuestContext* ctx, void* info, void* raw_context);
|
static bool HandleFailedGuestFault(GuestContext* ctx, void* info, void* raw_context);
|
||||||
static void HandleHostFault(int sig, void* info, void* raw_context);
|
static bool HandleGuestAlignmentFault(GuestContext* ctx, void* info, void* raw_context);
|
||||||
|
static bool HandleGuestAccessFault(GuestContext* ctx, void* info, void* raw_context);
|
||||||
|
static void HandleHostAlignmentFault(int sig, void* info, void* raw_context);
|
||||||
|
static void HandleHostAccessFault(int sig, void* info, void* raw_context);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Core::System& m_system;
|
Core::System& m_system;
|
||||||
@ -83,6 +87,9 @@ public:
|
|||||||
// Core context.
|
// Core context.
|
||||||
GuestContext m_guest_ctx{};
|
GuestContext m_guest_ctx{};
|
||||||
Kernel::KThread* m_running_thread{};
|
Kernel::KThread* m_running_thread{};
|
||||||
|
|
||||||
|
// Stack for signal processing.
|
||||||
|
std::unique_ptr<u8[]> m_stack{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Core
|
} // namespace Core
|
||||||
|
@ -130,11 +130,11 @@ _ZN4Core6ArmNce29BreakFromRunCodeSignalHandlerEiPvS1_:
|
|||||||
ret
|
ret
|
||||||
|
|
||||||
|
|
||||||
/* static void Core::ArmNce::GuestFaultSignalHandler(int sig, void* info, void* raw_context) */
|
/* static void Core::ArmNce::GuestAlignmentFaultSignalHandler(int sig, void* info, void* raw_context) */
|
||||||
.section .text._ZN4Core6ArmNce23GuestFaultSignalHandlerEiPvS1_, "ax", %progbits
|
.section .text._ZN4Core6ArmNce32GuestAlignmentFaultSignalHandlerEiPvS1_, "ax", %progbits
|
||||||
.global _ZN4Core6ArmNce23GuestFaultSignalHandlerEiPvS1_
|
.global _ZN4Core6ArmNce32GuestAlignmentFaultSignalHandlerEiPvS1_
|
||||||
.type _ZN4Core6ArmNce23GuestFaultSignalHandlerEiPvS1_, %function
|
.type _ZN4Core6ArmNce32GuestAlignmentFaultSignalHandlerEiPvS1_, %function
|
||||||
_ZN4Core6ArmNce23GuestFaultSignalHandlerEiPvS1_:
|
_ZN4Core6ArmNce32GuestAlignmentFaultSignalHandlerEiPvS1_:
|
||||||
/* Check to see if we have the correct TLS magic. */
|
/* Check to see if we have the correct TLS magic. */
|
||||||
mrs x8, tpidr_el0
|
mrs x8, tpidr_el0
|
||||||
ldr w9, [x8, #(TpidrEl0TlsMagic)]
|
ldr w9, [x8, #(TpidrEl0TlsMagic)]
|
||||||
@ -146,7 +146,7 @@ _ZN4Core6ArmNce23GuestFaultSignalHandlerEiPvS1_:
|
|||||||
|
|
||||||
/* Incorrect TLS magic, so this is a host fault. */
|
/* Incorrect TLS magic, so this is a host fault. */
|
||||||
/* Tail call the handler. */
|
/* Tail call the handler. */
|
||||||
b _ZN4Core6ArmNce15HandleHostFaultEiPvS1_
|
b _ZN4Core6ArmNce24HandleHostAlignmentFaultEiPvS1_
|
||||||
|
|
||||||
1:
|
1:
|
||||||
/* Correct TLS magic, so this is a guest fault. */
|
/* Correct TLS magic, so this is a guest fault. */
|
||||||
@ -163,7 +163,53 @@ _ZN4Core6ArmNce23GuestFaultSignalHandlerEiPvS1_:
|
|||||||
msr tpidr_el0, x3
|
msr tpidr_el0, x3
|
||||||
|
|
||||||
/* Call the handler. */
|
/* Call the handler. */
|
||||||
bl _ZN4Core6ArmNce16HandleGuestFaultEPNS_12GuestContextEPvS3_
|
bl _ZN4Core6ArmNce25HandleGuestAlignmentFaultEPNS_12GuestContextEPvS3_
|
||||||
|
|
||||||
|
/* If the handler returned false, we want to preserve the host tpidr_el0. */
|
||||||
|
cbz x0, 2f
|
||||||
|
|
||||||
|
/* Otherwise, restore guest tpidr_el0. */
|
||||||
|
msr tpidr_el0, x19
|
||||||
|
|
||||||
|
2:
|
||||||
|
ldr x19, [sp, #0x10]
|
||||||
|
ldp x29, x30, [sp], #0x20
|
||||||
|
ret
|
||||||
|
|
||||||
|
/* static void Core::ArmNce::GuestAccessFaultSignalHandler(int sig, void* info, void* raw_context) */
|
||||||
|
.section .text._ZN4Core6ArmNce29GuestAccessFaultSignalHandlerEiPvS1_, "ax", %progbits
|
||||||
|
.global _ZN4Core6ArmNce29GuestAccessFaultSignalHandlerEiPvS1_
|
||||||
|
.type _ZN4Core6ArmNce29GuestAccessFaultSignalHandlerEiPvS1_, %function
|
||||||
|
_ZN4Core6ArmNce29GuestAccessFaultSignalHandlerEiPvS1_:
|
||||||
|
/* Check to see if we have the correct TLS magic. */
|
||||||
|
mrs x8, tpidr_el0
|
||||||
|
ldr w9, [x8, #(TpidrEl0TlsMagic)]
|
||||||
|
|
||||||
|
LOAD_IMMEDIATE_32(w10, TlsMagic)
|
||||||
|
|
||||||
|
cmp w9, w10
|
||||||
|
b.eq 1f
|
||||||
|
|
||||||
|
/* Incorrect TLS magic, so this is a host fault. */
|
||||||
|
/* Tail call the handler. */
|
||||||
|
b _ZN4Core6ArmNce21HandleHostAccessFaultEiPvS1_
|
||||||
|
|
||||||
|
1:
|
||||||
|
/* Correct TLS magic, so this is a guest fault. */
|
||||||
|
stp x29, x30, [sp, #-0x20]!
|
||||||
|
str x19, [sp, #0x10]
|
||||||
|
mov x29, sp
|
||||||
|
|
||||||
|
/* Save the old tpidr_el0. */
|
||||||
|
mov x19, x8
|
||||||
|
|
||||||
|
/* Restore host tpidr_el0. */
|
||||||
|
ldr x0, [x8, #(TpidrEl0NativeContext)]
|
||||||
|
ldr x3, [x0, #(GuestContextHostContext + HostContextTpidrEl0)]
|
||||||
|
msr tpidr_el0, x3
|
||||||
|
|
||||||
|
/* Call the handler. */
|
||||||
|
bl _ZN4Core6ArmNce22HandleGuestAccessFaultEPNS_12GuestContextEPvS3_
|
||||||
|
|
||||||
/* If the handler returned false, we want to preserve the host tpidr_el0. */
|
/* If the handler returned false, we want to preserve the host tpidr_el0. */
|
||||||
cbz x0, 2f
|
cbz x0, 2f
|
||||||
|
@ -10,7 +10,8 @@
|
|||||||
|
|
||||||
#define ReturnToRunCodeByExceptionLevelChangeSignal SIGUSR2
|
#define ReturnToRunCodeByExceptionLevelChangeSignal SIGUSR2
|
||||||
#define BreakFromRunCodeSignal SIGURG
|
#define BreakFromRunCodeSignal SIGURG
|
||||||
#define GuestFaultSignal SIGSEGV
|
#define GuestAccessFaultSignal SIGSEGV
|
||||||
|
#define GuestAlignmentFaultSignal SIGBUS
|
||||||
|
|
||||||
#define GuestContextSp 0xF8
|
#define GuestContextSp 0xF8
|
||||||
#define GuestContextHostContext 0x320
|
#define GuestContextHostContext 0x320
|
||||||
|
825
src/core/arm/nce/interpreter_visitor.cpp
Normal file
825
src/core/arm/nce/interpreter_visitor.cpp
Normal file
@ -0,0 +1,825 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2023 merryhime <https://mary.rs>
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "common/bit_cast.h"
|
||||||
|
#include "core/arm/nce/interpreter_visitor.h"
|
||||||
|
|
||||||
|
#include <dynarmic/frontend/A64/decoder/a64.h>
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
|
||||||
|
template <u32 BitSize>
|
||||||
|
u64 SignExtendToLong(u64 value) {
|
||||||
|
u64 mask = 1ULL << (BitSize - 1);
|
||||||
|
value &= (1ULL << BitSize) - 1;
|
||||||
|
return (value ^ mask) - mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u64 SignExtendToLong(u64 value, u64 bitsize) {
|
||||||
|
switch (bitsize) {
|
||||||
|
case 8:
|
||||||
|
return SignExtendToLong<8>(value);
|
||||||
|
case 16:
|
||||||
|
return SignExtendToLong<16>(value);
|
||||||
|
case 32:
|
||||||
|
return SignExtendToLong<32>(value);
|
||||||
|
default:
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <u64 BitSize>
|
||||||
|
u32 SignExtendToWord(u32 value) {
|
||||||
|
u32 mask = 1ULL << (BitSize - 1);
|
||||||
|
value &= (1ULL << BitSize) - 1;
|
||||||
|
return (value ^ mask) - mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u32 SignExtendToWord(u32 value, u64 bitsize) {
|
||||||
|
switch (bitsize) {
|
||||||
|
case 8:
|
||||||
|
return SignExtendToWord<8>(value);
|
||||||
|
case 16:
|
||||||
|
return SignExtendToWord<16>(value);
|
||||||
|
default:
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static u64 SignExtend(u64 value, u64 bitsize, u64 regsize) {
|
||||||
|
if (regsize == 64) {
|
||||||
|
return SignExtendToLong(value, bitsize);
|
||||||
|
} else {
|
||||||
|
return SignExtendToWord(static_cast<u32>(value), bitsize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static u128 VectorGetElement(u128 value, u64 bitsize) {
|
||||||
|
switch (bitsize) {
|
||||||
|
case 8:
|
||||||
|
return {value[0] & ((1ULL << 8) - 1), 0};
|
||||||
|
case 16:
|
||||||
|
return {value[0] & ((1ULL << 16) - 1), 0};
|
||||||
|
case 32:
|
||||||
|
return {value[0] & ((1ULL << 32) - 1), 0};
|
||||||
|
case 64:
|
||||||
|
return {value[0], 0};
|
||||||
|
default:
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 InterpreterVisitor::ExtendReg(size_t bitsize, Reg reg, Imm<3> option, u8 shift) {
|
||||||
|
ASSERT(shift <= 4);
|
||||||
|
ASSERT(bitsize == 32 || bitsize == 64);
|
||||||
|
u64 val = this->GetReg(reg);
|
||||||
|
size_t len;
|
||||||
|
u64 extended;
|
||||||
|
bool signed_extend;
|
||||||
|
|
||||||
|
switch (option.ZeroExtend()) {
|
||||||
|
case 0b000: { // UXTB
|
||||||
|
val &= ((1ULL << 8) - 1);
|
||||||
|
len = 8;
|
||||||
|
signed_extend = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0b001: { // UXTH
|
||||||
|
val &= ((1ULL << 16) - 1);
|
||||||
|
len = 16;
|
||||||
|
signed_extend = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0b010: { // UXTW
|
||||||
|
val &= ((1ULL << 32) - 1);
|
||||||
|
len = 32;
|
||||||
|
signed_extend = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0b011: { // UXTX
|
||||||
|
len = 64;
|
||||||
|
signed_extend = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0b100: { // SXTB
|
||||||
|
val &= ((1ULL << 8) - 1);
|
||||||
|
len = 8;
|
||||||
|
signed_extend = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0b101: { // SXTH
|
||||||
|
val &= ((1ULL << 16) - 1);
|
||||||
|
len = 16;
|
||||||
|
signed_extend = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0b110: { // SXTW
|
||||||
|
val &= ((1ULL << 32) - 1);
|
||||||
|
len = 32;
|
||||||
|
signed_extend = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0b111: { // SXTX
|
||||||
|
len = 64;
|
||||||
|
signed_extend = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len < bitsize && signed_extend) {
|
||||||
|
extended = SignExtend(val, len, bitsize);
|
||||||
|
} else {
|
||||||
|
extended = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
return extended << shift;
|
||||||
|
}
|
||||||
|
|
||||||
|
u128 InterpreterVisitor::GetVec(Vec v) {
|
||||||
|
return m_fpsimd_regs[static_cast<u32>(v)];
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 InterpreterVisitor::GetReg(Reg r) {
|
||||||
|
return m_regs[static_cast<u32>(r)];
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 InterpreterVisitor::GetSp() {
|
||||||
|
return m_sp;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 InterpreterVisitor::GetPc() {
|
||||||
|
return m_pc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InterpreterVisitor::SetVec(Vec v, u128 value) {
|
||||||
|
m_fpsimd_regs[static_cast<u32>(v)] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InterpreterVisitor::SetReg(Reg r, u64 value) {
|
||||||
|
m_regs[static_cast<u32>(r)] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InterpreterVisitor::SetSp(u64 value) {
|
||||||
|
m_sp = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InterpreterVisitor::Ordered(size_t size, bool L, bool o0, Reg Rn, Reg Rt) {
|
||||||
|
const auto memop = L ? MemOp::Load : MemOp::Store;
|
||||||
|
const size_t elsize = 8 << size;
|
||||||
|
const size_t datasize = elsize;
|
||||||
|
|
||||||
|
// Operation
|
||||||
|
const size_t dbytes = datasize / 8;
|
||||||
|
|
||||||
|
u64 address;
|
||||||
|
if (Rn == Reg::SP) {
|
||||||
|
address = this->GetSp();
|
||||||
|
} else {
|
||||||
|
address = this->GetReg(Rn);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (memop) {
|
||||||
|
case MemOp::Store: {
|
||||||
|
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||||
|
u64 value = this->GetReg(Rt);
|
||||||
|
m_memory.WriteBlock(address, &value, dbytes);
|
||||||
|
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MemOp::Load: {
|
||||||
|
u64 value = 0;
|
||||||
|
m_memory.ReadBlock(address, &value, dbytes);
|
||||||
|
this->SetReg(Rt, value);
|
||||||
|
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InterpreterVisitor::STLLR(Imm<2> sz, Reg Rn, Reg Rt) {
|
||||||
|
const size_t size = sz.ZeroExtend<size_t>();
|
||||||
|
const bool L = 0;
|
||||||
|
const bool o0 = 0;
|
||||||
|
return this->Ordered(size, L, o0, Rn, Rt);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InterpreterVisitor::STLR(Imm<2> sz, Reg Rn, Reg Rt) {
|
||||||
|
const size_t size = sz.ZeroExtend<size_t>();
|
||||||
|
const bool L = 0;
|
||||||
|
const bool o0 = 1;
|
||||||
|
return this->Ordered(size, L, o0, Rn, Rt);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InterpreterVisitor::LDLAR(Imm<2> sz, Reg Rn, Reg Rt) {
|
||||||
|
const size_t size = sz.ZeroExtend<size_t>();
|
||||||
|
const bool L = 1;
|
||||||
|
const bool o0 = 0;
|
||||||
|
return this->Ordered(size, L, o0, Rn, Rt);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InterpreterVisitor::LDAR(Imm<2> sz, Reg Rn, Reg Rt) {
|
||||||
|
const size_t size = sz.ZeroExtend<size_t>();
|
||||||
|
const bool L = 1;
|
||||||
|
const bool o0 = 1;
|
||||||
|
return this->Ordered(size, L, o0, Rn, Rt);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InterpreterVisitor::LDR_lit_gen(bool opc_0, Imm<19> imm19, Reg Rt) {
|
||||||
|
const size_t size = opc_0 == 0 ? 4 : 8;
|
||||||
|
const s64 offset = Dynarmic::concatenate(imm19, Imm<2>{0}).SignExtend<s64>();
|
||||||
|
const u64 address = this->GetPc() + offset;
|
||||||
|
|
||||||
|
u64 data = 0;
|
||||||
|
m_memory.ReadBlock(address, &data, size);
|
||||||
|
|
||||||
|
this->SetReg(Rt, data);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InterpreterVisitor::LDR_lit_fpsimd(Imm<2> opc, Imm<19> imm19, Vec Vt) {
|
||||||
|
if (opc == 0b11) {
|
||||||
|
// Unallocated encoding
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const u64 size = 4 << opc.ZeroExtend();
|
||||||
|
const u64 offset = imm19.SignExtend<u64>() << 2;
|
||||||
|
const u64 address = this->GetPc() + offset;
|
||||||
|
|
||||||
|
u128 data{};
|
||||||
|
m_memory.ReadBlock(address, &data, size);
|
||||||
|
this->SetVec(Vt, data);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InterpreterVisitor::STP_LDP_gen(Imm<2> opc, bool not_postindex, bool wback, Imm<1> L,
|
||||||
|
Imm<7> imm7, Reg Rt2, Reg Rn, Reg Rt) {
|
||||||
|
if ((L == 0 && opc.Bit<0>() == 1) || opc == 0b11) {
|
||||||
|
// Unallocated encoding
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto memop = L == 1 ? MemOp::Load : MemOp::Store;
|
||||||
|
if (memop == MemOp::Load && wback && (Rt == Rn || Rt2 == Rn) && Rn != Reg::R31) {
|
||||||
|
// Unpredictable instruction
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (memop == MemOp::Store && wback && (Rt == Rn || Rt2 == Rn) && Rn != Reg::R31) {
|
||||||
|
// Unpredictable instruction
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (memop == MemOp::Load && Rt == Rt2) {
|
||||||
|
// Unpredictable instruction
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 address;
|
||||||
|
if (Rn == Reg::SP) {
|
||||||
|
address = this->GetSp();
|
||||||
|
} else {
|
||||||
|
address = this->GetReg(Rn);
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool postindex = !not_postindex;
|
||||||
|
const bool signed_ = opc.Bit<0>() != 0;
|
||||||
|
const size_t scale = 2 + opc.Bit<1>();
|
||||||
|
const size_t datasize = 8 << scale;
|
||||||
|
const u64 offset = imm7.SignExtend<u64>() << scale;
|
||||||
|
|
||||||
|
if (!postindex) {
|
||||||
|
address += offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t dbytes = datasize / 8;
|
||||||
|
switch (memop) {
|
||||||
|
case MemOp::Store: {
|
||||||
|
u64 data1 = this->GetReg(Rt);
|
||||||
|
u64 data2 = this->GetReg(Rt2);
|
||||||
|
m_memory.WriteBlock(address, &data1, dbytes);
|
||||||
|
m_memory.WriteBlock(address + dbytes, &data2, dbytes);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MemOp::Load: {
|
||||||
|
u64 data1 = 0, data2 = 0;
|
||||||
|
m_memory.ReadBlock(address, &data1, dbytes);
|
||||||
|
m_memory.ReadBlock(address + dbytes, &data2, dbytes);
|
||||||
|
if (signed_) {
|
||||||
|
this->SetReg(Rt, SignExtend(data1, datasize, 64));
|
||||||
|
this->SetReg(Rt2, SignExtend(data2, datasize, 64));
|
||||||
|
} else {
|
||||||
|
this->SetReg(Rt, data1);
|
||||||
|
this->SetReg(Rt2, data2);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wback) {
|
||||||
|
if (postindex) {
|
||||||
|
address += offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Rn == Reg::SP) {
|
||||||
|
this->SetSp(address);
|
||||||
|
} else {
|
||||||
|
this->SetReg(Rn, address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InterpreterVisitor::STP_LDP_fpsimd(Imm<2> opc, bool not_postindex, bool wback, Imm<1> L,
|
||||||
|
Imm<7> imm7, Vec Vt2, Reg Rn, Vec Vt) {
|
||||||
|
if (opc == 0b11) {
|
||||||
|
// Unallocated encoding
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto memop = L == 1 ? MemOp::Load : MemOp::Store;
|
||||||
|
if (memop == MemOp::Load && Vt == Vt2) {
|
||||||
|
// Unpredictable instruction
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 address;
|
||||||
|
if (Rn == Reg::SP) {
|
||||||
|
address = this->GetSp();
|
||||||
|
} else {
|
||||||
|
address = this->GetReg(Rn);
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool postindex = !not_postindex;
|
||||||
|
const size_t scale = 2 + opc.ZeroExtend<size_t>();
|
||||||
|
const size_t datasize = 8 << scale;
|
||||||
|
const u64 offset = imm7.SignExtend<u64>() << scale;
|
||||||
|
const size_t dbytes = datasize / 8;
|
||||||
|
|
||||||
|
if (!postindex) {
|
||||||
|
address += offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (memop) {
|
||||||
|
case MemOp::Store: {
|
||||||
|
u128 data1 = VectorGetElement(this->GetVec(Vt), datasize);
|
||||||
|
u128 data2 = VectorGetElement(this->GetVec(Vt2), datasize);
|
||||||
|
m_memory.WriteBlock(address, &data1, dbytes);
|
||||||
|
m_memory.WriteBlock(address + dbytes, &data2, dbytes);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MemOp::Load: {
|
||||||
|
u128 data1{}, data2{};
|
||||||
|
m_memory.ReadBlock(address, &data1, dbytes);
|
||||||
|
m_memory.ReadBlock(address + dbytes, &data2, dbytes);
|
||||||
|
this->SetVec(Vt, data1);
|
||||||
|
this->SetVec(Vt2, data2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wback) {
|
||||||
|
if (postindex) {
|
||||||
|
address += offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Rn == Reg::SP) {
|
||||||
|
this->SetSp(address);
|
||||||
|
} else {
|
||||||
|
this->SetReg(Rn, address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InterpreterVisitor::RegisterImmediate(bool wback, bool postindex, size_t scale, u64 offset,
|
||||||
|
Imm<2> size, Imm<2> opc, Reg Rn, Reg Rt) {
|
||||||
|
MemOp memop;
|
||||||
|
bool signed_ = false;
|
||||||
|
size_t regsize = 0;
|
||||||
|
|
||||||
|
if (opc.Bit<1>() == 0) {
|
||||||
|
memop = opc.Bit<0>() ? MemOp::Load : MemOp::Store;
|
||||||
|
regsize = size == 0b11 ? 64 : 32;
|
||||||
|
signed_ = false;
|
||||||
|
} else if (size == 0b11) {
|
||||||
|
memop = MemOp::Prefetch;
|
||||||
|
ASSERT(!opc.Bit<0>());
|
||||||
|
} else {
|
||||||
|
memop = MemOp::Load;
|
||||||
|
ASSERT(!(size == 0b10 && opc.Bit<0>() == 1));
|
||||||
|
regsize = opc.Bit<0>() ? 32 : 64;
|
||||||
|
signed_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (memop == MemOp::Load && wback && Rn == Rt && Rn != Reg::R31) {
|
||||||
|
// Unpredictable instruction
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (memop == MemOp::Store && wback && Rn == Rt && Rn != Reg::R31) {
|
||||||
|
// Unpredictable instruction
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 address;
|
||||||
|
if (Rn == Reg::SP) {
|
||||||
|
address = this->GetSp();
|
||||||
|
} else {
|
||||||
|
address = this->GetReg(Rn);
|
||||||
|
}
|
||||||
|
if (!postindex) {
|
||||||
|
address += offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t datasize = 8 << scale;
|
||||||
|
switch (memop) {
|
||||||
|
case MemOp::Store: {
|
||||||
|
u64 data = this->GetReg(Rt);
|
||||||
|
m_memory.WriteBlock(address, &data, datasize / 8);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MemOp::Load: {
|
||||||
|
u64 data = 0;
|
||||||
|
m_memory.ReadBlock(address, &data, datasize / 8);
|
||||||
|
if (signed_) {
|
||||||
|
this->SetReg(Rt, SignExtend(data, datasize, regsize));
|
||||||
|
} else {
|
||||||
|
this->SetReg(Rt, data);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MemOp::Prefetch:
|
||||||
|
// this->Prefetch(address, Rt)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wback) {
|
||||||
|
if (postindex) {
|
||||||
|
address += offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Rn == Reg::SP) {
|
||||||
|
this->SetSp(address);
|
||||||
|
} else {
|
||||||
|
this->SetReg(Rn, address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InterpreterVisitor::STRx_LDRx_imm_1(Imm<2> size, Imm<2> opc, Imm<9> imm9, bool not_postindex,
|
||||||
|
Reg Rn, Reg Rt) {
|
||||||
|
const bool wback = true;
|
||||||
|
const bool postindex = !not_postindex;
|
||||||
|
const size_t scale = size.ZeroExtend<size_t>();
|
||||||
|
const u64 offset = imm9.SignExtend<u64>();
|
||||||
|
|
||||||
|
return this->RegisterImmediate(wback, postindex, scale, offset, size, opc, Rn, Rt);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InterpreterVisitor::STRx_LDRx_imm_2(Imm<2> size, Imm<2> opc, Imm<12> imm12, Reg Rn, Reg Rt) {
|
||||||
|
const bool wback = false;
|
||||||
|
const bool postindex = false;
|
||||||
|
const size_t scale = size.ZeroExtend<size_t>();
|
||||||
|
const u64 offset = imm12.ZeroExtend<u64>() << scale;
|
||||||
|
|
||||||
|
return this->RegisterImmediate(wback, postindex, scale, offset, size, opc, Rn, Rt);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InterpreterVisitor::STURx_LDURx(Imm<2> size, Imm<2> opc, Imm<9> imm9, Reg Rn, Reg Rt) {
|
||||||
|
const bool wback = false;
|
||||||
|
const bool postindex = false;
|
||||||
|
const size_t scale = size.ZeroExtend<size_t>();
|
||||||
|
const u64 offset = imm9.SignExtend<u64>();
|
||||||
|
|
||||||
|
return this->RegisterImmediate(wback, postindex, scale, offset, size, opc, Rn, Rt);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InterpreterVisitor::SIMDImmediate(bool wback, bool postindex, size_t scale, u64 offset,
|
||||||
|
MemOp memop, Reg Rn, Vec Vt) {
|
||||||
|
const size_t datasize = 8 << scale;
|
||||||
|
|
||||||
|
u64 address;
|
||||||
|
if (Rn == Reg::SP) {
|
||||||
|
address = this->GetSp();
|
||||||
|
} else {
|
||||||
|
address = this->GetReg(Rn);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!postindex) {
|
||||||
|
address += offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (memop) {
|
||||||
|
case MemOp::Store: {
|
||||||
|
u128 data = VectorGetElement(this->GetVec(Vt), datasize);
|
||||||
|
m_memory.WriteBlock(address, &data, datasize / 8);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MemOp::Load: {
|
||||||
|
u128 data{};
|
||||||
|
m_memory.ReadBlock(address, &data, datasize);
|
||||||
|
this->SetVec(Vt, data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wback) {
|
||||||
|
if (postindex) {
|
||||||
|
address += offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Rn == Reg::SP) {
|
||||||
|
this->SetSp(address);
|
||||||
|
} else {
|
||||||
|
this->SetReg(Rn, address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InterpreterVisitor::STR_imm_fpsimd_1(Imm<2> size, Imm<1> opc_1, Imm<9> imm9,
|
||||||
|
bool not_postindex, Reg Rn, Vec Vt) {
|
||||||
|
const size_t scale = Dynarmic::concatenate(opc_1, size).ZeroExtend<size_t>();
|
||||||
|
if (scale > 4) {
|
||||||
|
// Unallocated encoding
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool wback = true;
|
||||||
|
const bool postindex = !not_postindex;
|
||||||
|
const u64 offset = imm9.SignExtend<u64>();
|
||||||
|
|
||||||
|
return this->SIMDImmediate(wback, postindex, scale, offset, MemOp::Store, Rn, Vt);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InterpreterVisitor::STR_imm_fpsimd_2(Imm<2> size, Imm<1> opc_1, Imm<12> imm12, Reg Rn,
|
||||||
|
Vec Vt) {
|
||||||
|
const size_t scale = Dynarmic::concatenate(opc_1, size).ZeroExtend<size_t>();
|
||||||
|
if (scale > 4) {
|
||||||
|
// Unallocated encoding
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool wback = false;
|
||||||
|
const bool postindex = false;
|
||||||
|
const u64 offset = imm12.ZeroExtend<u64>() << scale;
|
||||||
|
|
||||||
|
return this->SIMDImmediate(wback, postindex, scale, offset, MemOp::Store, Rn, Vt);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InterpreterVisitor::LDR_imm_fpsimd_1(Imm<2> size, Imm<1> opc_1, Imm<9> imm9,
|
||||||
|
bool not_postindex, Reg Rn, Vec Vt) {
|
||||||
|
const size_t scale = Dynarmic::concatenate(opc_1, size).ZeroExtend<size_t>();
|
||||||
|
if (scale > 4) {
|
||||||
|
// Unallocated encoding
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool wback = true;
|
||||||
|
const bool postindex = !not_postindex;
|
||||||
|
const u64 offset = imm9.SignExtend<u64>();
|
||||||
|
|
||||||
|
return this->SIMDImmediate(wback, postindex, scale, offset, MemOp::Load, Rn, Vt);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InterpreterVisitor::LDR_imm_fpsimd_2(Imm<2> size, Imm<1> opc_1, Imm<12> imm12, Reg Rn,
|
||||||
|
Vec Vt) {
|
||||||
|
const size_t scale = Dynarmic::concatenate(opc_1, size).ZeroExtend<size_t>();
|
||||||
|
if (scale > 4) {
|
||||||
|
// Unallocated encoding
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool wback = false;
|
||||||
|
const bool postindex = false;
|
||||||
|
const u64 offset = imm12.ZeroExtend<u64>() << scale;
|
||||||
|
|
||||||
|
return this->SIMDImmediate(wback, postindex, scale, offset, MemOp::Load, Rn, Vt);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InterpreterVisitor::STUR_fpsimd(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, Reg Rn, Vec Vt) {
|
||||||
|
const size_t scale = Dynarmic::concatenate(opc_1, size).ZeroExtend<size_t>();
|
||||||
|
if (scale > 4) {
|
||||||
|
// Unallocated encoding
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool wback = false;
|
||||||
|
const bool postindex = false;
|
||||||
|
const u64 offset = imm9.SignExtend<u64>();
|
||||||
|
|
||||||
|
return this->SIMDImmediate(wback, postindex, scale, offset, MemOp::Store, Rn, Vt);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InterpreterVisitor::LDUR_fpsimd(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, Reg Rn, Vec Vt) {
|
||||||
|
const size_t scale = Dynarmic::concatenate(opc_1, size).ZeroExtend<size_t>();
|
||||||
|
if (scale > 4) {
|
||||||
|
// Unallocated encoding
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool wback = false;
|
||||||
|
const bool postindex = false;
|
||||||
|
const u64 offset = imm9.SignExtend<u64>();
|
||||||
|
|
||||||
|
return this->SIMDImmediate(wback, postindex, scale, offset, MemOp::Load, Rn, Vt);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InterpreterVisitor::RegisterOffset(size_t scale, u8 shift, Imm<2> size, Imm<1> opc_1,
|
||||||
|
Imm<1> opc_0, Reg Rm, Imm<3> option, Reg Rn, Reg Rt) {
|
||||||
|
MemOp memop;
|
||||||
|
size_t regsize = 64;
|
||||||
|
bool signed_ = false;
|
||||||
|
|
||||||
|
if (opc_1 == 0) {
|
||||||
|
memop = opc_0 == 1 ? MemOp::Load : MemOp::Store;
|
||||||
|
regsize = size == 0b11 ? 64 : 32;
|
||||||
|
signed_ = false;
|
||||||
|
} else if (size == 0b11) {
|
||||||
|
memop = MemOp::Prefetch;
|
||||||
|
if (opc_0 == 1) {
|
||||||
|
// Unallocated encoding
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
memop = MemOp::Load;
|
||||||
|
if (size == 0b10 && opc_0 == 1) {
|
||||||
|
// Unallocated encoding
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
regsize = opc_0 == 1 ? 32 : 64;
|
||||||
|
signed_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t datasize = 8 << scale;
|
||||||
|
|
||||||
|
// Operation
|
||||||
|
const u64 offset = this->ExtendReg(64, Rm, option, shift);
|
||||||
|
|
||||||
|
u64 address;
|
||||||
|
if (Rn == Reg::SP) {
|
||||||
|
address = this->GetSp();
|
||||||
|
} else {
|
||||||
|
address = this->GetReg(Rn);
|
||||||
|
}
|
||||||
|
address += offset;
|
||||||
|
|
||||||
|
switch (memop) {
|
||||||
|
case MemOp::Store: {
|
||||||
|
u64 data = this->GetReg(Rt);
|
||||||
|
m_memory.WriteBlock(address, &data, datasize / 8);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MemOp::Load: {
|
||||||
|
u64 data = 0;
|
||||||
|
m_memory.ReadBlock(address, &data, datasize / 8);
|
||||||
|
if (signed_) {
|
||||||
|
this->SetReg(Rt, SignExtend(data, datasize, regsize));
|
||||||
|
} else {
|
||||||
|
this->SetReg(Rt, data);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MemOp::Prefetch:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InterpreterVisitor::STRx_reg(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn,
|
||||||
|
Reg Rt) {
|
||||||
|
const Imm<1> opc_0{0};
|
||||||
|
const size_t scale = size.ZeroExtend<size_t>();
|
||||||
|
const u8 shift = S ? static_cast<u8>(scale) : 0;
|
||||||
|
if (!option.Bit<1>()) {
|
||||||
|
// Unallocated encoding
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return this->RegisterOffset(scale, shift, size, opc_1, opc_0, Rm, option, Rn, Rt);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InterpreterVisitor::LDRx_reg(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn,
|
||||||
|
Reg Rt) {
|
||||||
|
const Imm<1> opc_0{1};
|
||||||
|
const size_t scale = size.ZeroExtend<size_t>();
|
||||||
|
const u8 shift = S ? static_cast<u8>(scale) : 0;
|
||||||
|
if (!option.Bit<1>()) {
|
||||||
|
// Unallocated encoding
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return this->RegisterOffset(scale, shift, size, opc_1, opc_0, Rm, option, Rn, Rt);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InterpreterVisitor::SIMDOffset(size_t scale, u8 shift, Imm<1> opc_0, Reg Rm, Imm<3> option,
|
||||||
|
Reg Rn, Vec Vt) {
|
||||||
|
const auto memop = opc_0 == 1 ? MemOp::Load : MemOp::Store;
|
||||||
|
const size_t datasize = 8 << scale;
|
||||||
|
|
||||||
|
// Operation
|
||||||
|
const u64 offset = this->ExtendReg(64, Rm, option, shift);
|
||||||
|
|
||||||
|
u64 address;
|
||||||
|
if (Rn == Reg::SP) {
|
||||||
|
address = this->GetSp();
|
||||||
|
} else {
|
||||||
|
address = this->GetReg(Rn);
|
||||||
|
}
|
||||||
|
address += offset;
|
||||||
|
|
||||||
|
switch (memop) {
|
||||||
|
case MemOp::Store: {
|
||||||
|
u128 data = VectorGetElement(this->GetVec(Vt), datasize);
|
||||||
|
m_memory.WriteBlock(address, &data, datasize / 8);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MemOp::Load: {
|
||||||
|
u128 data{};
|
||||||
|
m_memory.ReadBlock(address, &data, datasize / 8);
|
||||||
|
this->SetVec(Vt, data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InterpreterVisitor::STR_reg_fpsimd(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S,
|
||||||
|
Reg Rn, Vec Vt) {
|
||||||
|
const Imm<1> opc_0{0};
|
||||||
|
const size_t scale = Dynarmic::concatenate(opc_1, size).ZeroExtend<size_t>();
|
||||||
|
if (scale > 4) {
|
||||||
|
// Unallocated encoding
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const u8 shift = S ? static_cast<u8>(scale) : 0;
|
||||||
|
if (!option.Bit<1>()) {
|
||||||
|
// Unallocated encoding
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return this->SIMDOffset(scale, shift, opc_0, Rm, option, Rn, Vt);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InterpreterVisitor::LDR_reg_fpsimd(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S,
|
||||||
|
Reg Rn, Vec Vt) {
|
||||||
|
const Imm<1> opc_0{1};
|
||||||
|
const size_t scale = Dynarmic::concatenate(opc_1, size).ZeroExtend<size_t>();
|
||||||
|
if (scale > 4) {
|
||||||
|
// Unallocated encoding
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const u8 shift = S ? static_cast<u8>(scale) : 0;
|
||||||
|
if (!option.Bit<1>()) {
|
||||||
|
// Unallocated encoding
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return this->SIMDOffset(scale, shift, opc_0, Rm, option, Rn, Vt);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<u64> MatchAndExecuteOneInstruction(Core::Memory::Memory& memory, mcontext_t* context,
|
||||||
|
fpsimd_context* fpsimd_context) {
|
||||||
|
// Construct the interpreter.
|
||||||
|
std::span<u64, 31> regs(reinterpret_cast<u64*>(context->regs), 31);
|
||||||
|
std::span<u128, 32> vregs(reinterpret_cast<u128*>(fpsimd_context->vregs), 32);
|
||||||
|
u64& sp = *reinterpret_cast<u64*>(&context->sp);
|
||||||
|
const u64& pc = *reinterpret_cast<u64*>(&context->pc);
|
||||||
|
|
||||||
|
InterpreterVisitor visitor(memory, regs, vregs, sp, pc);
|
||||||
|
|
||||||
|
// Read the instruction at the program counter.
|
||||||
|
u32 instruction = memory.Read32(pc);
|
||||||
|
bool was_executed = false;
|
||||||
|
|
||||||
|
// Interpret the instruction.
|
||||||
|
if (auto decoder = Dynarmic::A64::Decode<VisitorBase>(instruction)) {
|
||||||
|
was_executed = decoder->get().call(visitor, instruction);
|
||||||
|
} else {
|
||||||
|
LOG_ERROR(Core_ARM, "Unallocated encoding: {:#x}", instruction);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (was_executed) {
|
||||||
|
return pc + 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Core
|
103
src/core/arm/nce/interpreter_visitor.h
Normal file
103
src/core/arm/nce/interpreter_visitor.h
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2023 merryhime <https://mary.rs>
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <signal.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "core/arm/nce/visitor_base.h"
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
|
||||||
|
namespace Memory {
|
||||||
|
class Memory;
|
||||||
|
}
|
||||||
|
|
||||||
|
class InterpreterVisitor final : public VisitorBase {
|
||||||
|
public:
|
||||||
|
explicit InterpreterVisitor(Core::Memory::Memory& memory, std::span<u64, 31> regs,
|
||||||
|
std::span<u128, 32> fpsimd_regs, u64& sp, const u64& pc)
|
||||||
|
: m_memory(memory), m_regs(regs), m_fpsimd_regs(fpsimd_regs), m_sp(sp), m_pc(pc) {}
|
||||||
|
~InterpreterVisitor() override = default;
|
||||||
|
|
||||||
|
enum class MemOp {
|
||||||
|
Load,
|
||||||
|
Store,
|
||||||
|
Prefetch,
|
||||||
|
};
|
||||||
|
|
||||||
|
u128 GetVec(Vec v);
|
||||||
|
u64 GetReg(Reg r);
|
||||||
|
u64 GetSp();
|
||||||
|
u64 GetPc();
|
||||||
|
|
||||||
|
void SetVec(Vec v, u128 value);
|
||||||
|
void SetReg(Reg r, u64 value);
|
||||||
|
void SetSp(u64 value);
|
||||||
|
|
||||||
|
u64 ExtendReg(size_t bitsize, Reg reg, Imm<3> option, u8 shift);
|
||||||
|
|
||||||
|
// Loads and stores - Load/Store Exclusive
|
||||||
|
bool Ordered(size_t size, bool L, bool o0, Reg Rn, Reg Rt);
|
||||||
|
bool STLLR(Imm<2> size, Reg Rn, Reg Rt) override;
|
||||||
|
bool STLR(Imm<2> size, Reg Rn, Reg Rt) override;
|
||||||
|
bool LDLAR(Imm<2> size, Reg Rn, Reg Rt) override;
|
||||||
|
bool LDAR(Imm<2> size, Reg Rn, Reg Rt) override;
|
||||||
|
|
||||||
|
// Loads and stores - Load register (literal)
|
||||||
|
bool LDR_lit_gen(bool opc_0, Imm<19> imm19, Reg Rt) override;
|
||||||
|
bool LDR_lit_fpsimd(Imm<2> opc, Imm<19> imm19, Vec Vt) override;
|
||||||
|
|
||||||
|
// Loads and stores - Load/Store register pair
|
||||||
|
bool STP_LDP_gen(Imm<2> opc, bool not_postindex, bool wback, Imm<1> L, Imm<7> imm7, Reg Rt2,
|
||||||
|
Reg Rn, Reg Rt) override;
|
||||||
|
bool STP_LDP_fpsimd(Imm<2> opc, bool not_postindex, bool wback, Imm<1> L, Imm<7> imm7, Vec Vt2,
|
||||||
|
Reg Rn, Vec Vt) override;
|
||||||
|
|
||||||
|
// Loads and stores - Load/Store register (immediate)
|
||||||
|
bool RegisterImmediate(bool wback, bool postindex, size_t scale, u64 offset, Imm<2> size,
|
||||||
|
Imm<2> opc, Reg Rn, Reg Rt);
|
||||||
|
bool STRx_LDRx_imm_1(Imm<2> size, Imm<2> opc, Imm<9> imm9, bool not_postindex, Reg Rn,
|
||||||
|
Reg Rt) override;
|
||||||
|
bool STRx_LDRx_imm_2(Imm<2> size, Imm<2> opc, Imm<12> imm12, Reg Rn, Reg Rt) override;
|
||||||
|
bool STURx_LDURx(Imm<2> size, Imm<2> opc, Imm<9> imm9, Reg Rn, Reg Rt) override;
|
||||||
|
|
||||||
|
bool SIMDImmediate(bool wback, bool postindex, size_t scale, u64 offset, MemOp memop, Reg Rn,
|
||||||
|
Vec Vt);
|
||||||
|
bool STR_imm_fpsimd_1(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, bool not_postindex, Reg Rn,
|
||||||
|
Vec Vt) override;
|
||||||
|
bool STR_imm_fpsimd_2(Imm<2> size, Imm<1> opc_1, Imm<12> imm12, Reg Rn, Vec Vt) override;
|
||||||
|
bool LDR_imm_fpsimd_1(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, bool not_postindex, Reg Rn,
|
||||||
|
Vec Vt) override;
|
||||||
|
bool LDR_imm_fpsimd_2(Imm<2> size, Imm<1> opc_1, Imm<12> imm12, Reg Rn, Vec Vt) override;
|
||||||
|
bool STUR_fpsimd(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, Reg Rn, Vec Vt) override;
|
||||||
|
bool LDUR_fpsimd(Imm<2> size, Imm<1> opc_1, Imm<9> imm9, Reg Rn, Vec Vt) override;
|
||||||
|
|
||||||
|
// Loads and stores - Load/Store register (register offset)
|
||||||
|
bool RegisterOffset(size_t scale, u8 shift, Imm<2> size, Imm<1> opc_1, Imm<1> opc_0, Reg Rm,
|
||||||
|
Imm<3> option, Reg Rn, Reg Rt);
|
||||||
|
bool STRx_reg(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn,
|
||||||
|
Reg Rt) override;
|
||||||
|
bool LDRx_reg(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn,
|
||||||
|
Reg Rt) override;
|
||||||
|
|
||||||
|
bool SIMDOffset(size_t scale, u8 shift, Imm<1> opc_0, Reg Rm, Imm<3> option, Reg Rn, Vec Vt);
|
||||||
|
bool STR_reg_fpsimd(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn,
|
||||||
|
Vec Vt) override;
|
||||||
|
bool LDR_reg_fpsimd(Imm<2> size, Imm<1> opc_1, Reg Rm, Imm<3> option, bool S, Reg Rn,
|
||||||
|
Vec Vt) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Core::Memory::Memory& m_memory;
|
||||||
|
std::span<u64, 31> m_regs;
|
||||||
|
std::span<u128, 32> m_fpsimd_regs;
|
||||||
|
u64& m_sp;
|
||||||
|
const u64& m_pc;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::optional<u64> MatchAndExecuteOneInstruction(Core::Memory::Memory& memory, mcontext_t* context,
|
||||||
|
fpsimd_context* fpsimd_context);
|
||||||
|
|
||||||
|
} // namespace Core
|
2777
src/core/arm/nce/visitor_base.h
Normal file
2777
src/core/arm/nce/visitor_base.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -36,6 +36,7 @@
|
|||||||
#include "core/hle/kernel/k_scheduler.h"
|
#include "core/hle/kernel/k_scheduler.h"
|
||||||
#include "core/hle/kernel/kernel.h"
|
#include "core/hle/kernel/kernel.h"
|
||||||
#include "core/hle/kernel/physical_core.h"
|
#include "core/hle/kernel/physical_core.h"
|
||||||
|
#include "core/hle/service/acc/profile_manager.h"
|
||||||
#include "core/hle/service/am/applets/applets.h"
|
#include "core/hle/service/am/applets/applets.h"
|
||||||
#include "core/hle/service/apm/apm_controller.h"
|
#include "core/hle/service/apm/apm_controller.h"
|
||||||
#include "core/hle/service/filesystem/filesystem.h"
|
#include "core/hle/service/filesystem/filesystem.h"
|
||||||
@ -130,8 +131,8 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
|
|||||||
struct System::Impl {
|
struct System::Impl {
|
||||||
explicit Impl(System& system)
|
explicit Impl(System& system)
|
||||||
: kernel{system}, fs_controller{system}, memory{system}, hid_core{}, room_network{},
|
: kernel{system}, fs_controller{system}, memory{system}, hid_core{}, room_network{},
|
||||||
cpu_manager{system}, reporter{system}, applet_manager{system}, time_manager{system},
|
cpu_manager{system}, reporter{system}, applet_manager{system}, profile_manager{},
|
||||||
gpu_dirty_memory_write_manager{} {
|
time_manager{system}, gpu_dirty_memory_write_manager{} {
|
||||||
memory.SetGPUDirtyManagers(gpu_dirty_memory_write_manager);
|
memory.SetGPUDirtyManagers(gpu_dirty_memory_write_manager);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -532,6 +533,7 @@ struct System::Impl {
|
|||||||
|
|
||||||
/// Service State
|
/// Service State
|
||||||
Service::Glue::ARPManager arp_manager;
|
Service::Glue::ARPManager arp_manager;
|
||||||
|
Service::Account::ProfileManager profile_manager;
|
||||||
Service::Time::TimeManager time_manager;
|
Service::Time::TimeManager time_manager;
|
||||||
|
|
||||||
/// Service manager
|
/// Service manager
|
||||||
@ -921,6 +923,14 @@ const Service::APM::Controller& System::GetAPMController() const {
|
|||||||
return impl->apm_controller;
|
return impl->apm_controller;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Service::Account::ProfileManager& System::GetProfileManager() {
|
||||||
|
return impl->profile_manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Service::Account::ProfileManager& System::GetProfileManager() const {
|
||||||
|
return impl->profile_manager;
|
||||||
|
}
|
||||||
|
|
||||||
Service::Time::TimeManager& System::GetTimeManager() {
|
Service::Time::TimeManager& System::GetTimeManager() {
|
||||||
return impl->time_manager;
|
return impl->time_manager;
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,10 @@ class Memory;
|
|||||||
|
|
||||||
namespace Service {
|
namespace Service {
|
||||||
|
|
||||||
|
namespace Account {
|
||||||
|
class ProfileManager;
|
||||||
|
} // namespace Account
|
||||||
|
|
||||||
namespace AM::Applets {
|
namespace AM::Applets {
|
||||||
struct AppletFrontendSet;
|
struct AppletFrontendSet;
|
||||||
class AppletManager;
|
class AppletManager;
|
||||||
@ -383,6 +387,9 @@ public:
|
|||||||
[[nodiscard]] Service::APM::Controller& GetAPMController();
|
[[nodiscard]] Service::APM::Controller& GetAPMController();
|
||||||
[[nodiscard]] const Service::APM::Controller& GetAPMController() const;
|
[[nodiscard]] const Service::APM::Controller& GetAPMController() const;
|
||||||
|
|
||||||
|
[[nodiscard]] Service::Account::ProfileManager& GetProfileManager();
|
||||||
|
[[nodiscard]] const Service::Account::ProfileManager& GetProfileManager() const;
|
||||||
|
|
||||||
[[nodiscard]] Service::Time::TimeManager& GetTimeManager();
|
[[nodiscard]] Service::Time::TimeManager& GetTimeManager();
|
||||||
[[nodiscard]] const Service::Time::TimeManager& GetTimeManager() const;
|
[[nodiscard]] const Service::Time::TimeManager& GetTimeManager() const;
|
||||||
|
|
||||||
|
@ -112,6 +112,19 @@ void AppletResource::UnregisterAppletResourceUserId(u64 aruid) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AppletResource::FreeAppletResourceId(u64 aruid) {
|
||||||
|
u64 index = GetIndexFromAruid(aruid);
|
||||||
|
if (index >= AruidIndexMax) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& aruid_data = data[index];
|
||||||
|
if (aruid_data.flag.is_assigned) {
|
||||||
|
aruid_data.shared_memory_handle = nullptr;
|
||||||
|
aruid_data.flag.is_assigned.Assign(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
u64 AppletResource::GetActiveAruid() {
|
u64 AppletResource::GetActiveAruid() {
|
||||||
return active_aruid;
|
return active_aruid;
|
||||||
}
|
}
|
||||||
@ -196,4 +209,80 @@ void AppletResource::EnablePalmaBoostMode(u64 aruid, bool is_enabled) {
|
|||||||
data[index].flag.enable_palma_boost_mode.Assign(is_enabled);
|
data[index].flag.enable_palma_boost_mode.Assign(is_enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result AppletResource::RegisterCoreAppletResource() {
|
||||||
|
if (ref_counter == std::numeric_limits<s32>::max() - 1) {
|
||||||
|
return ResultAppletResourceOverflow;
|
||||||
|
}
|
||||||
|
if (ref_counter == 0) {
|
||||||
|
const u64 index = GetIndexFromAruid(0);
|
||||||
|
if (index < AruidIndexMax) {
|
||||||
|
return ResultAruidAlreadyRegistered;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t data_index = AruidIndexMax;
|
||||||
|
for (std::size_t i = 0; i < AruidIndexMax; i++) {
|
||||||
|
if (!data[i].flag.is_initialized) {
|
||||||
|
data_index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data_index == AruidIndexMax) {
|
||||||
|
return ResultAruidNoAvailableEntries;
|
||||||
|
}
|
||||||
|
|
||||||
|
AruidData& aruid_data = data[data_index];
|
||||||
|
|
||||||
|
aruid_data.aruid = 0;
|
||||||
|
aruid_data.flag.is_initialized.Assign(true);
|
||||||
|
aruid_data.flag.enable_pad_input.Assign(true);
|
||||||
|
aruid_data.flag.enable_six_axis_sensor.Assign(true);
|
||||||
|
aruid_data.flag.bit_18.Assign(true);
|
||||||
|
aruid_data.flag.enable_touchscreen.Assign(true);
|
||||||
|
|
||||||
|
data_index = AruidIndexMax;
|
||||||
|
for (std::size_t i = 0; i < AruidIndexMax; i++) {
|
||||||
|
if (registration_list.flag[i] == RegistrationStatus::Initialized) {
|
||||||
|
if (registration_list.aruid[i] != 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
data_index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (registration_list.flag[i] == RegistrationStatus::None) {
|
||||||
|
data_index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Result result = ResultSuccess;
|
||||||
|
|
||||||
|
if (data_index == AruidIndexMax) {
|
||||||
|
result = CreateAppletResource(0);
|
||||||
|
} else {
|
||||||
|
registration_list.flag[data_index] = RegistrationStatus::Initialized;
|
||||||
|
registration_list.aruid[data_index] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.IsError()) {
|
||||||
|
UnregisterAppletResourceUserId(0);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ref_counter++;
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result AppletResource::UnregisterCoreAppletResource() {
|
||||||
|
if (ref_counter == 0) {
|
||||||
|
return ResultAppletResourceNotInitialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (--ref_counter == 0) {
|
||||||
|
UnregisterAppletResourceUserId(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Service::HID
|
} // namespace Service::HID
|
||||||
|
@ -28,6 +28,8 @@ public:
|
|||||||
Result RegisterAppletResourceUserId(u64 aruid, bool enable_input);
|
Result RegisterAppletResourceUserId(u64 aruid, bool enable_input);
|
||||||
void UnregisterAppletResourceUserId(u64 aruid);
|
void UnregisterAppletResourceUserId(u64 aruid);
|
||||||
|
|
||||||
|
void FreeAppletResourceId(u64 aruid);
|
||||||
|
|
||||||
u64 GetActiveAruid();
|
u64 GetActiveAruid();
|
||||||
Result GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle, u64 aruid);
|
Result GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle, u64 aruid);
|
||||||
|
|
||||||
@ -42,6 +44,9 @@ public:
|
|||||||
void SetIsPalmaConnectable(u64 aruid, bool is_connectable);
|
void SetIsPalmaConnectable(u64 aruid, bool is_connectable);
|
||||||
void EnablePalmaBoostMode(u64 aruid, bool is_enabled);
|
void EnablePalmaBoostMode(u64 aruid, bool is_enabled);
|
||||||
|
|
||||||
|
Result RegisterCoreAppletResource();
|
||||||
|
Result UnregisterCoreAppletResource();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr std::size_t AruidIndexMax = 0x20;
|
static constexpr std::size_t AruidIndexMax = 0x20;
|
||||||
|
|
||||||
@ -81,6 +86,7 @@ private:
|
|||||||
u64 active_aruid{};
|
u64 active_aruid{};
|
||||||
AruidRegisterList registration_list{};
|
AruidRegisterList registration_list{};
|
||||||
std::array<AruidData, AruidIndexMax> data{};
|
std::array<AruidData, AruidIndexMax> data{};
|
||||||
|
s32 ref_counter{};
|
||||||
|
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
};
|
};
|
||||||
|
@ -20,6 +20,9 @@ constexpr Result InvalidNpadId{ErrorModule::HID, 709};
|
|||||||
constexpr Result NpadNotConnected{ErrorModule::HID, 710};
|
constexpr Result NpadNotConnected{ErrorModule::HID, 710};
|
||||||
constexpr Result InvalidArraySize{ErrorModule::HID, 715};
|
constexpr Result InvalidArraySize{ErrorModule::HID, 715};
|
||||||
|
|
||||||
|
constexpr Result ResultAppletResourceOverflow{ErrorModule::HID, 1041};
|
||||||
|
constexpr Result ResultAppletResourceNotInitialized{ErrorModule::HID, 1042};
|
||||||
|
constexpr Result ResultSharedMemoryNotInitialized{ErrorModule::HID, 1043};
|
||||||
constexpr Result ResultAruidNoAvailableEntries{ErrorModule::HID, 1044};
|
constexpr Result ResultAruidNoAvailableEntries{ErrorModule::HID, 1044};
|
||||||
constexpr Result ResultAruidAlreadyRegistered{ErrorModule::HID, 1046};
|
constexpr Result ResultAruidAlreadyRegistered{ErrorModule::HID, 1046};
|
||||||
constexpr Result ResultAruidNotRegistered{ErrorModule::HID, 1047};
|
constexpr Result ResultAruidNotRegistered{ErrorModule::HID, 1047};
|
||||||
|
@ -222,16 +222,14 @@ void IHidServer::CreateAppletResource(HLERequestContext& ctx) {
|
|||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
const auto applet_resource_user_id{rp.Pop<u64>()};
|
const auto applet_resource_user_id{rp.Pop<u64>()};
|
||||||
|
|
||||||
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
|
|
||||||
|
|
||||||
Result result = GetResourceManager()->CreateAppletResource(applet_resource_user_id);
|
Result result = GetResourceManager()->CreateAppletResource(applet_resource_user_id);
|
||||||
if (result.IsSuccess()) {
|
|
||||||
result = GetResourceManager()->GetNpad()->Activate(applet_resource_user_id);
|
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, result=0x{:X}",
|
||||||
}
|
applet_resource_user_id, result.raw);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||||
rb.Push(result);
|
rb.Push(result);
|
||||||
rb.PushIpcInterface<IAppletResource>(system, resource_manager);
|
rb.PushIpcInterface<IAppletResource>(system, resource_manager, applet_resource_user_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IHidServer::ActivateDebugPad(HLERequestContext& ctx) {
|
void IHidServer::ActivateDebugPad(HLERequestContext& ctx) {
|
||||||
|
@ -146,10 +146,36 @@ std::shared_ptr<UniquePad> ResourceManager::GetUniquePad() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Result ResourceManager::CreateAppletResource(u64 aruid) {
|
Result ResourceManager::CreateAppletResource(u64 aruid) {
|
||||||
|
if (aruid == 0) {
|
||||||
|
const auto result = RegisterCoreAppletResource();
|
||||||
|
if (result.IsError()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return GetNpad()->Activate();
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto result = CreateAppletResourceImpl(aruid);
|
||||||
|
if (result.IsError()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return GetNpad()->Activate(aruid);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ResourceManager::CreateAppletResourceImpl(u64 aruid) {
|
||||||
std::scoped_lock lock{shared_mutex};
|
std::scoped_lock lock{shared_mutex};
|
||||||
return applet_resource->CreateAppletResource(aruid);
|
return applet_resource->CreateAppletResource(aruid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result ResourceManager::RegisterCoreAppletResource() {
|
||||||
|
std::scoped_lock lock{shared_mutex};
|
||||||
|
return applet_resource->RegisterCoreAppletResource();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ResourceManager::UnregisterCoreAppletResource() {
|
||||||
|
std::scoped_lock lock{shared_mutex};
|
||||||
|
return applet_resource->UnregisterCoreAppletResource();
|
||||||
|
}
|
||||||
|
|
||||||
Result ResourceManager::RegisterAppletResourceUserId(u64 aruid, bool bool_value) {
|
Result ResourceManager::RegisterAppletResourceUserId(u64 aruid, bool bool_value) {
|
||||||
std::scoped_lock lock{shared_mutex};
|
std::scoped_lock lock{shared_mutex};
|
||||||
return applet_resource->RegisterAppletResourceUserId(aruid, bool_value);
|
return applet_resource->RegisterAppletResourceUserId(aruid, bool_value);
|
||||||
@ -165,6 +191,11 @@ Result ResourceManager::GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle
|
|||||||
return applet_resource->GetSharedMemoryHandle(out_handle, aruid);
|
return applet_resource->GetSharedMemoryHandle(out_handle, aruid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ResourceManager::FreeAppletResourceId(u64 aruid) {
|
||||||
|
std::scoped_lock lock{shared_mutex};
|
||||||
|
applet_resource->FreeAppletResourceId(aruid);
|
||||||
|
}
|
||||||
|
|
||||||
void ResourceManager::EnableInput(u64 aruid, bool is_enabled) {
|
void ResourceManager::EnableInput(u64 aruid, bool is_enabled) {
|
||||||
std::scoped_lock lock{shared_mutex};
|
std::scoped_lock lock{shared_mutex};
|
||||||
applet_resource->EnableInput(aruid, is_enabled);
|
applet_resource->EnableInput(aruid, is_enabled);
|
||||||
@ -219,8 +250,10 @@ void ResourceManager::UpdateMotion(std::uintptr_t user_data, std::chrono::nanose
|
|||||||
console_six_axis->OnUpdate(core_timing);
|
console_six_axis->OnUpdate(core_timing);
|
||||||
}
|
}
|
||||||
|
|
||||||
IAppletResource::IAppletResource(Core::System& system_, std::shared_ptr<ResourceManager> resource)
|
IAppletResource::IAppletResource(Core::System& system_, std::shared_ptr<ResourceManager> resource,
|
||||||
: ServiceFramework{system_, "IAppletResource"}, resource_manager{resource} {
|
u64 applet_resource_user_id)
|
||||||
|
: ServiceFramework{system_, "IAppletResource"}, aruid{applet_resource_user_id},
|
||||||
|
resource_manager{resource} {
|
||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
{0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"},
|
{0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"},
|
||||||
};
|
};
|
||||||
@ -274,14 +307,14 @@ IAppletResource::~IAppletResource() {
|
|||||||
system.CoreTiming().UnscheduleEvent(default_update_event, 0);
|
system.CoreTiming().UnscheduleEvent(default_update_event, 0);
|
||||||
system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event, 0);
|
system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event, 0);
|
||||||
system.CoreTiming().UnscheduleEvent(motion_update_event, 0);
|
system.CoreTiming().UnscheduleEvent(motion_update_event, 0);
|
||||||
|
resource_manager->FreeAppletResourceId(aruid);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IAppletResource::GetSharedMemoryHandle(HLERequestContext& ctx) {
|
void IAppletResource::GetSharedMemoryHandle(HLERequestContext& ctx) {
|
||||||
LOG_DEBUG(Service_HID, "called");
|
|
||||||
|
|
||||||
Kernel::KSharedMemory* handle;
|
Kernel::KSharedMemory* handle;
|
||||||
const u64 applet_resource_user_id = resource_manager->GetAppletResource()->GetActiveAruid();
|
const auto result = resource_manager->GetSharedMemoryHandle(&handle, aruid);
|
||||||
const auto result = resource_manager->GetSharedMemoryHandle(&handle, applet_resource_user_id);
|
|
||||||
|
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, result=0x{:X}", aruid, result.raw);
|
||||||
|
|
||||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||||
rb.Push(result);
|
rb.Push(result);
|
||||||
|
@ -66,10 +66,13 @@ public:
|
|||||||
|
|
||||||
Result CreateAppletResource(u64 aruid);
|
Result CreateAppletResource(u64 aruid);
|
||||||
|
|
||||||
|
Result RegisterCoreAppletResource();
|
||||||
|
Result UnregisterCoreAppletResource();
|
||||||
Result RegisterAppletResourceUserId(u64 aruid, bool bool_value);
|
Result RegisterAppletResourceUserId(u64 aruid, bool bool_value);
|
||||||
void UnregisterAppletResourceUserId(u64 aruid);
|
void UnregisterAppletResourceUserId(u64 aruid);
|
||||||
|
|
||||||
Result GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle, u64 aruid);
|
Result GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle, u64 aruid);
|
||||||
|
void FreeAppletResourceId(u64 aruid);
|
||||||
|
|
||||||
void EnableInput(u64 aruid, bool is_enabled);
|
void EnableInput(u64 aruid, bool is_enabled);
|
||||||
void EnableSixAxisSensor(u64 aruid, bool is_enabled);
|
void EnableSixAxisSensor(u64 aruid, bool is_enabled);
|
||||||
@ -82,6 +85,8 @@ public:
|
|||||||
void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
|
void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
Result CreateAppletResourceImpl(u64 aruid);
|
||||||
|
|
||||||
bool is_initialized{false};
|
bool is_initialized{false};
|
||||||
|
|
||||||
mutable std::mutex shared_mutex;
|
mutable std::mutex shared_mutex;
|
||||||
@ -121,7 +126,8 @@ private:
|
|||||||
|
|
||||||
class IAppletResource final : public ServiceFramework<IAppletResource> {
|
class IAppletResource final : public ServiceFramework<IAppletResource> {
|
||||||
public:
|
public:
|
||||||
explicit IAppletResource(Core::System& system_, std::shared_ptr<ResourceManager> resource);
|
explicit IAppletResource(Core::System& system_, std::shared_ptr<ResourceManager> resource,
|
||||||
|
u64 applet_resource_user_id);
|
||||||
~IAppletResource() override;
|
~IAppletResource() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -132,6 +138,7 @@ private:
|
|||||||
std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event;
|
std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event;
|
||||||
std::shared_ptr<Core::Timing::EventType> motion_update_event;
|
std::shared_ptr<Core::Timing::EventType> motion_update_event;
|
||||||
|
|
||||||
|
u64 aruid;
|
||||||
std::shared_ptr<ResourceManager> resource_manager;
|
std::shared_ptr<ResourceManager> resource_manager;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
#include "common/fs/path_util.h"
|
#include "common/fs/path_util.h"
|
||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
#include "core/constants.h"
|
#include "core/constants.h"
|
||||||
|
#include "core/core.h"
|
||||||
|
#include "core/hle/service/acc/profile_manager.h"
|
||||||
#include "yuzu/applets/qt_profile_select.h"
|
#include "yuzu/applets/qt_profile_select.h"
|
||||||
#include "yuzu/main.h"
|
#include "yuzu/main.h"
|
||||||
#include "yuzu/util/controller_navigation.h"
|
#include "yuzu/util/controller_navigation.h"
|
||||||
@ -47,9 +49,9 @@ QPixmap GetIcon(Common::UUID uuid) {
|
|||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
QtProfileSelectionDialog::QtProfileSelectionDialog(
|
QtProfileSelectionDialog::QtProfileSelectionDialog(
|
||||||
Core::HID::HIDCore& hid_core, QWidget* parent,
|
Core::System& system, QWidget* parent,
|
||||||
const Core::Frontend::ProfileSelectParameters& parameters)
|
const Core::Frontend::ProfileSelectParameters& parameters)
|
||||||
: QDialog(parent), profile_manager(std::make_unique<Service::Account::ProfileManager>()) {
|
: QDialog(parent), profile_manager{system.GetProfileManager()} {
|
||||||
outer_layout = new QVBoxLayout;
|
outer_layout = new QVBoxLayout;
|
||||||
|
|
||||||
instruction_label = new QLabel();
|
instruction_label = new QLabel();
|
||||||
@ -68,7 +70,7 @@ QtProfileSelectionDialog::QtProfileSelectionDialog(
|
|||||||
tree_view = new QTreeView;
|
tree_view = new QTreeView;
|
||||||
item_model = new QStandardItemModel(tree_view);
|
item_model = new QStandardItemModel(tree_view);
|
||||||
tree_view->setModel(item_model);
|
tree_view->setModel(item_model);
|
||||||
controller_navigation = new ControllerNavigation(hid_core, this);
|
controller_navigation = new ControllerNavigation(system.HIDCore(), this);
|
||||||
|
|
||||||
tree_view->setAlternatingRowColors(true);
|
tree_view->setAlternatingRowColors(true);
|
||||||
tree_view->setSelectionMode(QHeaderView::SingleSelection);
|
tree_view->setSelectionMode(QHeaderView::SingleSelection);
|
||||||
@ -106,10 +108,10 @@ QtProfileSelectionDialog::QtProfileSelectionDialog(
|
|||||||
SelectUser(tree_view->currentIndex());
|
SelectUser(tree_view->currentIndex());
|
||||||
});
|
});
|
||||||
|
|
||||||
const auto& profiles = profile_manager->GetAllUsers();
|
const auto& profiles = profile_manager.GetAllUsers();
|
||||||
for (const auto& user : profiles) {
|
for (const auto& user : profiles) {
|
||||||
Service::Account::ProfileBase profile{};
|
Service::Account::ProfileBase profile{};
|
||||||
if (!profile_manager->GetProfileBase(user, profile))
|
if (!profile_manager.GetProfileBase(user, profile))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const auto username = Common::StringFromFixedZeroTerminatedBuffer(
|
const auto username = Common::StringFromFixedZeroTerminatedBuffer(
|
||||||
@ -134,7 +136,7 @@ QtProfileSelectionDialog::~QtProfileSelectionDialog() {
|
|||||||
|
|
||||||
int QtProfileSelectionDialog::exec() {
|
int QtProfileSelectionDialog::exec() {
|
||||||
// Skip profile selection when there's only one.
|
// Skip profile selection when there's only one.
|
||||||
if (profile_manager->GetUserCount() == 1) {
|
if (profile_manager.GetUserCount() == 1) {
|
||||||
user_index = 0;
|
user_index = 0;
|
||||||
return QDialog::Accepted;
|
return QDialog::Accepted;
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include "core/frontend/applets/profile_select.h"
|
#include "core/frontend/applets/profile_select.h"
|
||||||
#include "core/hle/service/acc/profile_manager.h"
|
|
||||||
|
|
||||||
class ControllerNavigation;
|
class ControllerNavigation;
|
||||||
class GMainWindow;
|
class GMainWindow;
|
||||||
@ -20,15 +19,19 @@ class QStandardItemModel;
|
|||||||
class QTreeView;
|
class QTreeView;
|
||||||
class QVBoxLayout;
|
class QVBoxLayout;
|
||||||
|
|
||||||
namespace Core::HID {
|
namespace Core {
|
||||||
class HIDCore;
|
class System;
|
||||||
} // namespace Core::HID
|
}
|
||||||
|
|
||||||
|
namespace Service::Account {
|
||||||
|
class ProfileManager;
|
||||||
|
}
|
||||||
|
|
||||||
class QtProfileSelectionDialog final : public QDialog {
|
class QtProfileSelectionDialog final : public QDialog {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit QtProfileSelectionDialog(Core::HID::HIDCore& hid_core, QWidget* parent,
|
explicit QtProfileSelectionDialog(Core::System& system, QWidget* parent,
|
||||||
const Core::Frontend::ProfileSelectParameters& parameters);
|
const Core::Frontend::ProfileSelectParameters& parameters);
|
||||||
~QtProfileSelectionDialog() override;
|
~QtProfileSelectionDialog() override;
|
||||||
|
|
||||||
@ -58,7 +61,7 @@ private:
|
|||||||
QScrollArea* scroll_area;
|
QScrollArea* scroll_area;
|
||||||
QDialogButtonBox* buttons;
|
QDialogButtonBox* buttons;
|
||||||
|
|
||||||
std::unique_ptr<Service::Account::ProfileManager> profile_manager;
|
Service::Account::ProfileManager& profile_manager;
|
||||||
ControllerNavigation* controller_navigation = nullptr;
|
ControllerNavigation* controller_navigation = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -381,7 +381,7 @@
|
|||||||
<item row="5" column="0">
|
<item row="5" column="0">
|
||||||
<widget class="QCheckBox" name="disable_buffer_reorder">
|
<widget class="QCheckBox" name="disable_buffer_reorder">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><html><head/><body><p>When checked, disables reording of mapped memory uploads which allows to associate uploads with specific draws. May reduce performance in some cases.</p></body></html></string>
|
<string><html><head/><body><p>When checked, disables reordering of mapped memory uploads which allows to associate uploads with specific draws. May reduce performance in some cases.</p></body></html></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Disable Buffer Reorder</string>
|
<string>Disable Buffer Reorder</string>
|
||||||
|
@ -76,9 +76,9 @@ QString GetProfileUsernameFromUser(QWidget* parent, const QString& description_t
|
|||||||
}
|
}
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
ConfigureProfileManager::ConfigureProfileManager(const Core::System& system_, QWidget* parent)
|
ConfigureProfileManager::ConfigureProfileManager(Core::System& system_, QWidget* parent)
|
||||||
: QWidget(parent), ui{std::make_unique<Ui::ConfigureProfileManager>()},
|
: QWidget(parent), ui{std::make_unique<Ui::ConfigureProfileManager>()},
|
||||||
profile_manager(std::make_unique<Service::Account::ProfileManager>()), system{system_} {
|
profile_manager{system_.GetProfileManager()}, system{system_} {
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
tree_view = new QTreeView;
|
tree_view = new QTreeView;
|
||||||
@ -149,10 +149,10 @@ void ConfigureProfileManager::SetConfiguration() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ConfigureProfileManager::PopulateUserList() {
|
void ConfigureProfileManager::PopulateUserList() {
|
||||||
const auto& profiles = profile_manager->GetAllUsers();
|
const auto& profiles = profile_manager.GetAllUsers();
|
||||||
for (const auto& user : profiles) {
|
for (const auto& user : profiles) {
|
||||||
Service::Account::ProfileBase profile{};
|
Service::Account::ProfileBase profile{};
|
||||||
if (!profile_manager->GetProfileBase(user, profile))
|
if (!profile_manager.GetProfileBase(user, profile))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const auto username = Common::StringFromFixedZeroTerminatedBuffer(
|
const auto username = Common::StringFromFixedZeroTerminatedBuffer(
|
||||||
@ -167,11 +167,11 @@ void ConfigureProfileManager::PopulateUserList() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ConfigureProfileManager::UpdateCurrentUser() {
|
void ConfigureProfileManager::UpdateCurrentUser() {
|
||||||
ui->pm_add->setEnabled(profile_manager->GetUserCount() < Service::Account::MAX_USERS);
|
ui->pm_add->setEnabled(profile_manager.GetUserCount() < Service::Account::MAX_USERS);
|
||||||
|
|
||||||
const auto& current_user = profile_manager->GetUser(Settings::values.current_user.GetValue());
|
const auto& current_user = profile_manager.GetUser(Settings::values.current_user.GetValue());
|
||||||
ASSERT(current_user);
|
ASSERT(current_user);
|
||||||
const auto username = GetAccountUsername(*profile_manager, *current_user);
|
const auto username = GetAccountUsername(profile_manager, *current_user);
|
||||||
|
|
||||||
scene->clear();
|
scene->clear();
|
||||||
scene->addPixmap(
|
scene->addPixmap(
|
||||||
@ -187,11 +187,11 @@ void ConfigureProfileManager::ApplyConfiguration() {
|
|||||||
|
|
||||||
void ConfigureProfileManager::SelectUser(const QModelIndex& index) {
|
void ConfigureProfileManager::SelectUser(const QModelIndex& index) {
|
||||||
Settings::values.current_user =
|
Settings::values.current_user =
|
||||||
std::clamp<s32>(index.row(), 0, static_cast<s32>(profile_manager->GetUserCount() - 1));
|
std::clamp<s32>(index.row(), 0, static_cast<s32>(profile_manager.GetUserCount() - 1));
|
||||||
|
|
||||||
UpdateCurrentUser();
|
UpdateCurrentUser();
|
||||||
|
|
||||||
ui->pm_remove->setEnabled(profile_manager->GetUserCount() >= 2);
|
ui->pm_remove->setEnabled(profile_manager.GetUserCount() >= 2);
|
||||||
ui->pm_rename->setEnabled(true);
|
ui->pm_rename->setEnabled(true);
|
||||||
ui->pm_set_image->setEnabled(true);
|
ui->pm_set_image->setEnabled(true);
|
||||||
}
|
}
|
||||||
@ -204,18 +204,18 @@ void ConfigureProfileManager::AddUser() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const auto uuid = Common::UUID::MakeRandom();
|
const auto uuid = Common::UUID::MakeRandom();
|
||||||
profile_manager->CreateNewUser(uuid, username.toStdString());
|
profile_manager.CreateNewUser(uuid, username.toStdString());
|
||||||
|
|
||||||
item_model->appendRow(new QStandardItem{GetIcon(uuid), FormatUserEntryText(username, uuid)});
|
item_model->appendRow(new QStandardItem{GetIcon(uuid), FormatUserEntryText(username, uuid)});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigureProfileManager::RenameUser() {
|
void ConfigureProfileManager::RenameUser() {
|
||||||
const auto user = tree_view->currentIndex().row();
|
const auto user = tree_view->currentIndex().row();
|
||||||
const auto uuid = profile_manager->GetUser(user);
|
const auto uuid = profile_manager.GetUser(user);
|
||||||
ASSERT(uuid);
|
ASSERT(uuid);
|
||||||
|
|
||||||
Service::Account::ProfileBase profile{};
|
Service::Account::ProfileBase profile{};
|
||||||
if (!profile_manager->GetProfileBase(*uuid, profile))
|
if (!profile_manager.GetProfileBase(*uuid, profile))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const auto new_username = GetProfileUsernameFromUser(this, tr("Enter a new username:"));
|
const auto new_username = GetProfileUsernameFromUser(this, tr("Enter a new username:"));
|
||||||
@ -227,7 +227,7 @@ void ConfigureProfileManager::RenameUser() {
|
|||||||
std::fill(profile.username.begin(), profile.username.end(), '\0');
|
std::fill(profile.username.begin(), profile.username.end(), '\0');
|
||||||
std::copy(username_std.begin(), username_std.end(), profile.username.begin());
|
std::copy(username_std.begin(), username_std.end(), profile.username.begin());
|
||||||
|
|
||||||
profile_manager->SetProfileBase(*uuid, profile);
|
profile_manager.SetProfileBase(*uuid, profile);
|
||||||
|
|
||||||
item_model->setItem(
|
item_model->setItem(
|
||||||
user, 0,
|
user, 0,
|
||||||
@ -238,9 +238,9 @@ void ConfigureProfileManager::RenameUser() {
|
|||||||
|
|
||||||
void ConfigureProfileManager::ConfirmDeleteUser() {
|
void ConfigureProfileManager::ConfirmDeleteUser() {
|
||||||
const auto index = tree_view->currentIndex().row();
|
const auto index = tree_view->currentIndex().row();
|
||||||
const auto uuid = profile_manager->GetUser(index);
|
const auto uuid = profile_manager.GetUser(index);
|
||||||
ASSERT(uuid);
|
ASSERT(uuid);
|
||||||
const auto username = GetAccountUsername(*profile_manager, *uuid);
|
const auto username = GetAccountUsername(profile_manager, *uuid);
|
||||||
|
|
||||||
confirm_dialog->SetInfo(username, *uuid, [this, uuid]() { DeleteUser(*uuid); });
|
confirm_dialog->SetInfo(username, *uuid, [this, uuid]() { DeleteUser(*uuid); });
|
||||||
confirm_dialog->show();
|
confirm_dialog->show();
|
||||||
@ -252,7 +252,7 @@ void ConfigureProfileManager::DeleteUser(const Common::UUID& uuid) {
|
|||||||
}
|
}
|
||||||
UpdateCurrentUser();
|
UpdateCurrentUser();
|
||||||
|
|
||||||
if (!profile_manager->RemoveUser(uuid)) {
|
if (!profile_manager.RemoveUser(uuid)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,7 +265,7 @@ void ConfigureProfileManager::DeleteUser(const Common::UUID& uuid) {
|
|||||||
|
|
||||||
void ConfigureProfileManager::SetUserImage() {
|
void ConfigureProfileManager::SetUserImage() {
|
||||||
const auto index = tree_view->currentIndex().row();
|
const auto index = tree_view->currentIndex().row();
|
||||||
const auto uuid = profile_manager->GetUser(index);
|
const auto uuid = profile_manager.GetUser(index);
|
||||||
ASSERT(uuid);
|
ASSERT(uuid);
|
||||||
|
|
||||||
const auto file = QFileDialog::getOpenFileName(this, tr("Select User Image"), QString(),
|
const auto file = QFileDialog::getOpenFileName(this, tr("Select User Image"), QString(),
|
||||||
@ -317,7 +317,7 @@ void ConfigureProfileManager::SetUserImage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto username = GetAccountUsername(*profile_manager, *uuid);
|
const auto username = GetAccountUsername(profile_manager, *uuid);
|
||||||
item_model->setItem(index, 0,
|
item_model->setItem(index, 0,
|
||||||
new QStandardItem{GetIcon(*uuid), FormatUserEntryText(username, *uuid)});
|
new QStandardItem{GetIcon(*uuid), FormatUserEntryText(username, *uuid)});
|
||||||
UpdateCurrentUser();
|
UpdateCurrentUser();
|
||||||
|
@ -52,7 +52,7 @@ class ConfigureProfileManager : public QWidget {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ConfigureProfileManager(const Core::System& system_, QWidget* parent = nullptr);
|
explicit ConfigureProfileManager(Core::System& system_, QWidget* parent = nullptr);
|
||||||
~ConfigureProfileManager() override;
|
~ConfigureProfileManager() override;
|
||||||
|
|
||||||
void ApplyConfiguration();
|
void ApplyConfiguration();
|
||||||
@ -85,7 +85,6 @@ private:
|
|||||||
std::unique_ptr<Ui::ConfigureProfileManager> ui;
|
std::unique_ptr<Ui::ConfigureProfileManager> ui;
|
||||||
bool enabled = false;
|
bool enabled = false;
|
||||||
|
|
||||||
std::unique_ptr<Service::Account::ProfileManager> profile_manager;
|
Service::Account::ProfileManager& profile_manager;
|
||||||
|
|
||||||
const Core::System& system;
|
const Core::System& system;
|
||||||
};
|
};
|
||||||
|
@ -346,7 +346,7 @@ GMainWindow::GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulk
|
|||||||
SetDiscordEnabled(UISettings::values.enable_discord_presence.GetValue());
|
SetDiscordEnabled(UISettings::values.enable_discord_presence.GetValue());
|
||||||
discord_rpc->Update();
|
discord_rpc->Update();
|
||||||
|
|
||||||
play_time_manager = std::make_unique<PlayTime::PlayTimeManager>();
|
play_time_manager = std::make_unique<PlayTime::PlayTimeManager>(system->GetProfileManager());
|
||||||
|
|
||||||
system->GetRoomNetwork().Init();
|
system->GetRoomNetwork().Init();
|
||||||
|
|
||||||
@ -526,8 +526,7 @@ GMainWindow::GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulk
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Service::Account::ProfileManager manager;
|
if (!system->GetProfileManager().UserExistsIndex(selected_user)) {
|
||||||
if (!manager.UserExistsIndex(selected_user)) {
|
|
||||||
LOG_ERROR(Frontend, "Selected user doesn't exist");
|
LOG_ERROR(Frontend, "Selected user doesn't exist");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -691,7 +690,7 @@ void GMainWindow::ControllerSelectorRequestExit() {
|
|||||||
|
|
||||||
void GMainWindow::ProfileSelectorSelectProfile(
|
void GMainWindow::ProfileSelectorSelectProfile(
|
||||||
const Core::Frontend::ProfileSelectParameters& parameters) {
|
const Core::Frontend::ProfileSelectParameters& parameters) {
|
||||||
profile_select_applet = new QtProfileSelectionDialog(system->HIDCore(), this, parameters);
|
profile_select_applet = new QtProfileSelectionDialog(*system, this, parameters);
|
||||||
SCOPE_EXIT({
|
SCOPE_EXIT({
|
||||||
profile_select_applet->deleteLater();
|
profile_select_applet->deleteLater();
|
||||||
profile_select_applet = nullptr;
|
profile_select_applet = nullptr;
|
||||||
@ -706,8 +705,8 @@ void GMainWindow::ProfileSelectorSelectProfile(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Service::Account::ProfileManager manager;
|
const auto uuid = system->GetProfileManager().GetUser(
|
||||||
const auto uuid = manager.GetUser(static_cast<std::size_t>(profile_select_applet->GetIndex()));
|
static_cast<std::size_t>(profile_select_applet->GetIndex()));
|
||||||
if (!uuid.has_value()) {
|
if (!uuid.has_value()) {
|
||||||
emit ProfileSelectorFinishedSelection(std::nullopt);
|
emit ProfileSelectorFinishedSelection(std::nullopt);
|
||||||
return;
|
return;
|
||||||
@ -1856,7 +1855,7 @@ bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t p
|
|||||||
|
|
||||||
bool GMainWindow::SelectAndSetCurrentUser(
|
bool GMainWindow::SelectAndSetCurrentUser(
|
||||||
const Core::Frontend::ProfileSelectParameters& parameters) {
|
const Core::Frontend::ProfileSelectParameters& parameters) {
|
||||||
QtProfileSelectionDialog dialog(system->HIDCore(), this, parameters);
|
QtProfileSelectionDialog dialog(*system, this, parameters);
|
||||||
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
|
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
|
||||||
Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
|
Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
|
||||||
dialog.setWindowModality(Qt::WindowModal);
|
dialog.setWindowModality(Qt::WindowModal);
|
||||||
@ -2271,7 +2270,7 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
|
|||||||
.display_options = {},
|
.display_options = {},
|
||||||
.purpose = Service::AM::Applets::UserSelectionPurpose::General,
|
.purpose = Service::AM::Applets::UserSelectionPurpose::General,
|
||||||
};
|
};
|
||||||
QtProfileSelectionDialog dialog(system->HIDCore(), this, parameters);
|
QtProfileSelectionDialog dialog(*system, this, parameters);
|
||||||
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
|
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
|
||||||
Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
|
Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
|
||||||
dialog.setWindowModality(Qt::WindowModal);
|
dialog.setWindowModality(Qt::WindowModal);
|
||||||
@ -2288,8 +2287,8 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Service::Account::ProfileManager manager;
|
const auto user_id =
|
||||||
const auto user_id = manager.GetUser(static_cast<std::size_t>(index));
|
system->GetProfileManager().GetUser(static_cast<std::size_t>(index));
|
||||||
ASSERT(user_id);
|
ASSERT(user_id);
|
||||||
|
|
||||||
const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath(
|
const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath(
|
||||||
|
@ -27,9 +27,9 @@
|
|||||||
Lobby::Lobby(QWidget* parent, QStandardItemModel* list,
|
Lobby::Lobby(QWidget* parent, QStandardItemModel* list,
|
||||||
std::shared_ptr<Core::AnnounceMultiplayerSession> session, Core::System& system_)
|
std::shared_ptr<Core::AnnounceMultiplayerSession> session, Core::System& system_)
|
||||||
: QDialog(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint),
|
: QDialog(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint),
|
||||||
ui(std::make_unique<Ui::Lobby>()), announce_multiplayer_session(session),
|
ui(std::make_unique<Ui::Lobby>()),
|
||||||
profile_manager(std::make_unique<Service::Account::ProfileManager>()), system{system_},
|
announce_multiplayer_session(session), system{system_}, room_network{
|
||||||
room_network{system.GetRoomNetwork()} {
|
system.GetRoomNetwork()} {
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
// setup the watcher for background connections
|
// setup the watcher for background connections
|
||||||
@ -299,14 +299,15 @@ void Lobby::OnRefreshLobby() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string Lobby::GetProfileUsername() {
|
std::string Lobby::GetProfileUsername() {
|
||||||
const auto& current_user = profile_manager->GetUser(Settings::values.current_user.GetValue());
|
const auto& current_user =
|
||||||
|
system.GetProfileManager().GetUser(Settings::values.current_user.GetValue());
|
||||||
Service::Account::ProfileBase profile{};
|
Service::Account::ProfileBase profile{};
|
||||||
|
|
||||||
if (!current_user.has_value()) {
|
if (!current_user.has_value()) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!profile_manager->GetProfileBase(*current_user, profile)) {
|
if (!system.GetProfileManager().GetProfileBase(*current_user, profile)) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,10 +24,6 @@ namespace Core {
|
|||||||
class System;
|
class System;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Service::Account {
|
|
||||||
class ProfileManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listing of all public games pulled from services. The lobby should be simple enough for users to
|
* Listing of all public games pulled from services. The lobby should be simple enough for users to
|
||||||
* find the game they want to play, and join it.
|
* find the game they want to play, and join it.
|
||||||
@ -103,7 +99,6 @@ private:
|
|||||||
|
|
||||||
QFutureWatcher<AnnounceMultiplayerRoom::RoomList> room_list_watcher;
|
QFutureWatcher<AnnounceMultiplayerRoom::RoomList> room_list_watcher;
|
||||||
std::weak_ptr<Core::AnnounceMultiplayerSession> announce_multiplayer_session;
|
std::weak_ptr<Core::AnnounceMultiplayerSession> announce_multiplayer_session;
|
||||||
std::unique_ptr<Service::Account::ProfileManager> profile_manager;
|
|
||||||
QFutureWatcher<void>* watcher;
|
QFutureWatcher<void>* watcher;
|
||||||
Validation validation;
|
Validation validation;
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
|
@ -20,8 +20,8 @@ struct PlayTimeElement {
|
|||||||
PlayTime play_time;
|
PlayTime play_time;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::optional<std::filesystem::path> GetCurrentUserPlayTimePath() {
|
std::optional<std::filesystem::path> GetCurrentUserPlayTimePath(
|
||||||
const Service::Account::ProfileManager manager;
|
const Service::Account::ProfileManager& manager) {
|
||||||
const auto uuid = manager.GetUser(static_cast<s32>(Settings::values.current_user));
|
const auto uuid = manager.GetUser(static_cast<s32>(Settings::values.current_user));
|
||||||
if (!uuid.has_value()) {
|
if (!uuid.has_value()) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
@ -30,8 +30,9 @@ std::optional<std::filesystem::path> GetCurrentUserPlayTimePath() {
|
|||||||
uuid->RawString().append(".bin");
|
uuid->RawString().append(".bin");
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool ReadPlayTimeFile(PlayTimeDatabase& out_play_time_db) {
|
[[nodiscard]] bool ReadPlayTimeFile(PlayTimeDatabase& out_play_time_db,
|
||||||
const auto filename = GetCurrentUserPlayTimePath();
|
const Service::Account::ProfileManager& manager) {
|
||||||
|
const auto filename = GetCurrentUserPlayTimePath(manager);
|
||||||
|
|
||||||
if (!filename.has_value()) {
|
if (!filename.has_value()) {
|
||||||
LOG_ERROR(Frontend, "Failed to get current user path");
|
LOG_ERROR(Frontend, "Failed to get current user path");
|
||||||
@ -66,8 +67,9 @@ std::optional<std::filesystem::path> GetCurrentUserPlayTimePath() {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool WritePlayTimeFile(const PlayTimeDatabase& play_time_db) {
|
[[nodiscard]] bool WritePlayTimeFile(const PlayTimeDatabase& play_time_db,
|
||||||
const auto filename = GetCurrentUserPlayTimePath();
|
const Service::Account::ProfileManager& manager) {
|
||||||
|
const auto filename = GetCurrentUserPlayTimePath(manager);
|
||||||
|
|
||||||
if (!filename.has_value()) {
|
if (!filename.has_value()) {
|
||||||
LOG_ERROR(Frontend, "Failed to get current user path");
|
LOG_ERROR(Frontend, "Failed to get current user path");
|
||||||
@ -96,8 +98,9 @@ std::optional<std::filesystem::path> GetCurrentUserPlayTimePath() {
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
PlayTimeManager::PlayTimeManager() {
|
PlayTimeManager::PlayTimeManager(Service::Account::ProfileManager& profile_manager)
|
||||||
if (!ReadPlayTimeFile(database)) {
|
: manager{profile_manager} {
|
||||||
|
if (!ReadPlayTimeFile(database, manager)) {
|
||||||
LOG_ERROR(Frontend, "Failed to read play time database! Resetting to default.");
|
LOG_ERROR(Frontend, "Failed to read play time database! Resetting to default.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -142,7 +145,7 @@ void PlayTimeManager::AutoTimestamp(std::stop_token stop_token) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void PlayTimeManager::Save() {
|
void PlayTimeManager::Save() {
|
||||||
if (!WritePlayTimeFile(database)) {
|
if (!WritePlayTimeFile(database, manager)) {
|
||||||
LOG_ERROR(Frontend, "Failed to update play time database!");
|
LOG_ERROR(Frontend, "Failed to update play time database!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,10 @@
|
|||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/polyfill_thread.h"
|
#include "common/polyfill_thread.h"
|
||||||
|
|
||||||
|
namespace Service::Account {
|
||||||
|
class ProfileManager;
|
||||||
|
}
|
||||||
|
|
||||||
namespace PlayTime {
|
namespace PlayTime {
|
||||||
|
|
||||||
using ProgramId = u64;
|
using ProgramId = u64;
|
||||||
@ -19,7 +23,7 @@ using PlayTimeDatabase = std::map<ProgramId, PlayTime>;
|
|||||||
|
|
||||||
class PlayTimeManager {
|
class PlayTimeManager {
|
||||||
public:
|
public:
|
||||||
explicit PlayTimeManager();
|
explicit PlayTimeManager(Service::Account::ProfileManager& profile_manager);
|
||||||
~PlayTimeManager();
|
~PlayTimeManager();
|
||||||
|
|
||||||
YUZU_NON_COPYABLE(PlayTimeManager);
|
YUZU_NON_COPYABLE(PlayTimeManager);
|
||||||
@ -32,11 +36,13 @@ public:
|
|||||||
void Stop();
|
void Stop();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void AutoTimestamp(std::stop_token stop_token);
|
||||||
|
void Save();
|
||||||
|
|
||||||
PlayTimeDatabase database;
|
PlayTimeDatabase database;
|
||||||
u64 running_program_id;
|
u64 running_program_id;
|
||||||
std::jthread play_time_thread;
|
std::jthread play_time_thread;
|
||||||
void AutoTimestamp(std::stop_token stop_token);
|
Service::Account::ProfileManager& manager;
|
||||||
void Save();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
QString ReadablePlayTime(qulonglong time_seconds);
|
QString ReadablePlayTime(qulonglong time_seconds);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json",
|
"$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json",
|
||||||
"name": "yuzu",
|
"name": "yuzu",
|
||||||
"builtin-baseline": "cbf56573a987527b39272e88cbdd11389b78c6e4",
|
"builtin-baseline": "a42af01b72c28a8e1d7b48107b33e4f286a55ef6",
|
||||||
"version": "1.0",
|
"version": "1.0",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
"boost-algorithm",
|
"boost-algorithm",
|
||||||
@ -50,7 +50,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "fmt",
|
"name": "fmt",
|
||||||
"version": "10.0.0"
|
"version": "10.1.1"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user