Compare commits

..

11 Commits

Author SHA1 Message Date
99c777a17b Android #159 2023-12-13 00:57:15 +00:00
cc0b79270e Merge PR 12335 2023-12-13 00:57:15 +00:00
a5bfa8e00f Merge PR 12237 2023-12-13 00:57:15 +00:00
d590cfb9d0 Merge pull request #12342 from FearlessTobi/fix-msvc
vcpkg: Update fmt to 10.1.1
2023-12-12 15:32:09 -05:00
ded419ef2b Merge pull request #12343 from FearlessTobi/fix-typo
configure_debug: Fix small typo
2023-12-12 15:32:00 -05:00
4c3f898789 configure_debug: Fix small typo 2023-12-12 20:38:54 +01:00
46c259bb20 vcpkg: Update fmt to 10.1.1
Should fix compiling on the latest version of MSVC.
2023-12-12 19:27:20 +01:00
15bebf1695 Merge pull request #12328 from german77/profile_manager
core: Use single instance of profile manager
2023-12-12 11:06:37 -05:00
5c840334b8 Merge pull request #12333 from german77/aruid_free
service: hid: Improve CreateAppletResource implementation and free resources
2023-12-12 11:06:24 -05:00
abfebe5cc4 service: hid: Improve CreateAppletResource implementation and free resources 2023-12-10 16:17:51 -06:00
a22a025c5b core: Use single instance of profile manager 2023-12-10 11:29:43 -06:00
49 changed files with 4404 additions and 206 deletions

View File

@ -1,6 +1,7 @@
| 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.

View File

@ -174,7 +174,8 @@ android {
"-DANDROID_ARM_NEON=true", // cryptopp requires Neon to work
"-DYUZU_USE_BUNDLED_VCPKG=ON",
"-DYUZU_USE_BUNDLED_FFMPEG=ON",
"-DYUZU_ENABLE_LTO=ON"
"-DYUZU_ENABLE_LTO=ON",
"-DCMAKE_EXPORT_COMPILE_COMMANDS=ON"
)
abiFilters("arm64-v8a", "x86_64")

View File

@ -14,7 +14,7 @@ import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.recyclerview.widget.RecyclerView
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.model.GameProperty
import org.yuzu.yuzu_emu.model.InstallableProperty
@ -42,7 +42,7 @@ class GamePropertiesAdapter(
}
else -> InstallablePropertyViewHolder(
CardInstallableBinding.inflate(
CardInstallableIconBinding.inflate(
inflater,
parent,
false
@ -107,13 +107,20 @@ class GamePropertiesAdapter(
}
}
inner class InstallablePropertyViewHolder(val binding: CardInstallableBinding) :
inner class InstallablePropertyViewHolder(val binding: CardInstallableIconBinding) :
GamePropertyViewHolder(binding.root) {
override fun bind(property: GameProperty) {
val installableProperty = property as InstallableProperty
binding.title.setText(installableProperty.titleId)
binding.description.setText(installableProperty.descriptionId)
binding.icon.setImageDrawable(
ResourcesCompat.getDrawable(
binding.icon.context.resources,
installableProperty.iconId,
binding.icon.context.theme
)
)
if (installableProperty.install != null) {
binding.buttonInstall.visibility = View.VISIBLE

View File

@ -22,6 +22,9 @@ interface AbstractSetting {
get() = NativeConfig.usingGlobal(key)
set(value) = NativeConfig.setGlobal(key, value)
val isSaveable: Boolean
get() = NativeConfig.getIsSaveable(key)
fun getValueAsString(needsGlobal: Boolean = false): String
fun reset()

View File

@ -32,8 +32,7 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
override val defaultValue: Boolean by lazy { NativeConfig.getDefaultToString(key).toBoolean() }
override fun getValueAsString(needsGlobal: Boolean): String =
if (getBoolean(needsGlobal)) "1" else "0"
override fun getValueAsString(needsGlobal: Boolean): String = getBoolean(needsGlobal).toString()
override fun reset() = NativeConfig.setBoolean(key, defaultValue)
}

View File

@ -30,6 +30,11 @@ abstract class SettingsItem(
val isEditable: Boolean
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
// 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) {
return false
}
return setting.isRuntimeModifiable
}
@ -59,6 +65,7 @@ abstract class SettingsItem(
val emptySetting = object : AbstractSetting {
override val key: String = ""
override val defaultValue: Any = false
override val isSaveable = true
override fun getValueAsString(needsGlobal: Boolean): String = ""
override fun reset() {}
}
@ -303,6 +310,8 @@ abstract class SettingsItem(
BooleanSetting.FASTMEM_EXCLUSIVES.global = value
}
override val isSaveable = true
override fun getValueAsString(needsGlobal: Boolean): String =
getBoolean().toString()

View File

@ -128,8 +128,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
return
}
if (args.custom) {
SettingsFile.loadCustomConfig(args.game!!)
// Always load custom settings when launching a game from an intent
if (args.custom || intentGame != null) {
SettingsFile.loadCustomConfig(game)
NativeConfig.unloadPerGameConfig()
} else {
NativeConfig.reloadGlobalConfig()

View File

@ -3,6 +3,7 @@
package org.yuzu.yuzu_emu.fragments
import android.annotation.SuppressLint
import android.os.Bundle
import android.text.TextUtils
import android.view.LayoutInflater
@ -73,6 +74,8 @@ class GamePropertiesFragment : Fragment() {
return binding.root
}
// This is using the correct scope, lint is just acting up
@SuppressLint("UnsafeRepeatOnLifecycleDetector")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
homeViewModel.setNavigationVisibility(visible = false, animated = true)
@ -99,12 +102,24 @@ class GamePropertiesFragment : Fragment() {
reloadList()
viewLifecycleOwner.lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
homeViewModel.openImportSaves.collect {
if (it) {
importSaves.launch(arrayOf("application/zip"))
homeViewModel.setOpenImportSaves(false)
viewLifecycleOwner.lifecycleScope.apply {
launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
homeViewModel.openImportSaves.collect {
if (it) {
importSaves.launch(arrayOf("application/zip"))
homeViewModel.setOpenImportSaves(false)
}
}
}
}
launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
homeViewModel.reloadPropertiesList.collect {
if (it) {
reloadList()
homeViewModel.reloadPropertiesList(false)
}
}
}
}
@ -179,6 +194,7 @@ class GamePropertiesFragment : Fragment() {
InstallableProperty(
R.string.save_data,
R.string.save_data_description,
R.drawable.ic_save,
{
MessageDialogFragment.newInstance(
requireActivity(),
@ -214,7 +230,7 @@ class GamePropertiesFragment : Fragment() {
R.string.save_data_deleted_successfully,
Toast.LENGTH_SHORT
).show()
reloadList()
homeViewModel.reloadPropertiesList(true)
}
).show(parentFragmentManager, MessageDialogFragment.TAG)
}
@ -242,13 +258,20 @@ class GamePropertiesFragment : Fragment() {
}
}
) {
shaderCacheDir.deleteRecursively()
Toast.makeText(
YuzuApplication.appContext,
R.string.cleared_shaders_successfully,
Toast.LENGTH_SHORT
).show()
reloadList()
MessageDialogFragment.newInstance(
requireActivity(),
titleId = R.string.clear_shader_cache,
descriptionId = R.string.clear_shader_cache_warning_description,
positiveAction = {
shaderCacheDir.deleteRecursively()
Toast.makeText(
YuzuApplication.appContext,
R.string.cleared_shaders_successfully,
Toast.LENGTH_SHORT
).show()
homeViewModel.reloadPropertiesList(true)
}
).show(parentFragmentManager, MessageDialogFragment.TAG)
}
)
}
@ -388,7 +411,7 @@ class GamePropertiesFragment : Fragment() {
getString(R.string.save_file_imported_success),
Toast.LENGTH_LONG
).show()
reloadList()
homeViewModel.reloadPropertiesList(true)
}
cacheSaveDir.deleteRecursively()

View File

@ -15,7 +15,7 @@ import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.utils.SerializableHelper.parcelable
class LaunchGameDialogFragment : DialogFragment() {
private var selectedItem = 0
private var selectedItem = 1
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val game = requireArguments().parcelable<Game>(GAME)
@ -32,7 +32,7 @@ class LaunchGameDialogFragment : DialogFragment() {
.actionGlobalEmulationActivity(game, selectedItem != 0)
requireParentFragment().findNavController().navigate(action)
}
.setSingleChoiceItems(launchOptions, 0) { _: DialogInterface, i: Int ->
.setSingleChoiceItems(launchOptions, 1) { _: DialogInterface, i: Int ->
selectedItem = i
}
.setNegativeButton(android.R.string.cancel, null)

View File

@ -10,17 +10,18 @@ import kotlinx.coroutines.flow.StateFlow
interface GameProperty {
@get:StringRes
val titleId: Int
get() = -1
@get:StringRes
val descriptionId: Int
get() = -1
@get:DrawableRes
val iconId: Int
}
data class SubmenuProperty(
override val titleId: Int,
override val descriptionId: Int,
@DrawableRes val iconId: Int,
override val iconId: Int,
val details: (() -> String)? = null,
val detailsFlow: StateFlow<String>? = null,
val action: () -> Unit
@ -29,6 +30,7 @@ data class SubmenuProperty(
data class InstallableProperty(
override val titleId: Int,
override val descriptionId: Int,
override val iconId: Int,
val install: (() -> Unit)? = null,
val export: (() -> Unit)? = null
) : GameProperty

View File

@ -28,6 +28,9 @@ class HomeViewModel : ViewModel() {
private val _contentToInstall = MutableStateFlow<List<Uri>?>(null)
val contentToInstall get() = _contentToInstall.asStateFlow()
private val _reloadPropertiesList = MutableStateFlow(false)
val reloadPropertiesList get() = _reloadPropertiesList.asStateFlow()
var navigatedToSetup = false
fun setNavigationVisibility(visible: Boolean, animated: Boolean) {
@ -59,4 +62,8 @@ class HomeViewModel : ViewModel() {
fun setContentToInstall(documents: List<Uri>?) {
_contentToInstall.value = documents
}
fun reloadPropertiesList(reload: Boolean) {
_reloadPropertiesList.value = reload
}
}

View File

@ -110,6 +110,8 @@ object NativeConfig {
@Synchronized
external fun setGlobal(key: String, global: Boolean)
external fun getIsSaveable(key: String): Boolean
external fun getDefaultToString(key: String): String
/**

View File

@ -89,13 +89,13 @@ jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsValid(JNIEnv* env, jobj
return false;
}
const auto fileType = loader->GetFileType();
if (fileType == Loader::FileType::Unknown || fileType == Loader::FileType::Error) {
const auto file_type = loader->GetFileType();
if (file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) {
return false;
}
u64 programId = 0;
Loader::ResultStatus res = loader->ReadProgramId(programId);
u64 program_id = 0;
Loader::ResultStatus res = loader->ReadProgramId(program_id);
if (res != Loader::ResultStatus::Success) {
return false;
}

View File

@ -80,7 +80,7 @@ Core::System& EmulationSession::System() {
return m_system;
}
FileSys::ManualContentProvider* EmulationSession::ContentProvider() {
FileSys::ManualContentProvider* EmulationSession::GetContentProvider() {
return m_manual_provider.get();
}
@ -296,9 +296,6 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string
// Initialize filesystem.
ConfigureFilesystemProvider(filepath);
// Initialize account manager
m_profile_manager = std::make_unique<Service::Account::ProfileManager>();
// Load the ROM.
m_load_result = m_system.Load(EmulationSession::GetInstance().Window(), filepath);
if (m_load_result != Core::SystemResultStatus::Success) {
@ -463,15 +460,13 @@ void EmulationSession::OnEmulationStopped(Core::SystemResultStatus result) {
static_cast<jint>(result));
}
u64 EmulationSession::getProgramId(JNIEnv* env, jstring jprogramId) {
u64 program_id;
u64 EmulationSession::GetProgramId(JNIEnv* env, jstring jprogramId) {
auto program_id_string = GetJString(env, jprogramId);
if (program_id_string.empty()) {
program_id = 0;
} else {
program_id = std::stoull(program_id_string);
try {
return std::stoull(program_id_string);
} catch (...) {
return 0;
}
return program_id;
}
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,
jstring jprogramId,
jstring jupdatePath) {
u64 programId = EmulationSession::getProgramId(env, jprogramId);
u64 program_id = EmulationSession::GetProgramId(env, jprogramId);
std::string updatePath = GetJString(env, jupdatePath);
std::shared_ptr<FileSys::NSP> nsp = std::make_shared<FileSys::NSP>(
EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(updatePath,
FileSys::Mode::Read));
for (const auto& item : nsp->GetNCAs()) {
for (const auto& ncaDetails : item.second) {
if (ncaDetails.second->GetName().ends_with(".cnmt.nca")) {
auto updateId = ncaDetails.second->GetTitleId() & ~0xFFFULL;
if (updateId == programId) {
for (const auto& nca_details : item.second) {
if (nca_details.second->GetName().ends_with(".cnmt.nca")) {
auto update_id = nca_details.second->GetTitleId() & ~0xFFFULL;
if (update_id == program_id) {
return true;
}
}
@ -770,8 +765,8 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmptyUserDirectory(JNIEnv*
auto vfs_nand_dir = EmulationSession::GetInstance().System().GetFilesystem()->OpenDirectory(
Common::FS::PathToUTF8String(nand_dir), FileSys::Mode::Read);
Service::Account::ProfileManager manager;
const auto user_id = manager.GetUser(static_cast<std::size_t>(0));
const auto user_id = EmulationSession::GetInstance().System().GetProfileManager().GetUser(
static_cast<std::size_t>(0));
ASSERT(user_id);
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 programId = EmulationSession::getProgramId(env, jprogramId);
const FileSys::PatchManager pm{programId, system.GetFileSystemController(),
auto program_id = EmulationSession::GetProgramId(env, jprogramId);
const FileSys::PatchManager pm{program_id, system.GetFileSystemController(),
system.GetContentProvider()};
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);
auto addons = pm.GetPatchVersionNames(update_raw);
auto emptyString = ToJString(env, "");
auto emptyStringPair = env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(),
emptyString, emptyString);
auto jemptyString = ToJString(env, "");
auto jemptyStringPair = env->NewObject(IDCache::GetPairClass(), IDCache::GetPairConstructor(),
jemptyString, jemptyString);
jobjectArray jaddonsArray =
env->NewObjectArray(addons.size(), IDCache::GetPairClass(), emptyStringPair);
env->NewObjectArray(addons.size(), IDCache::GetPairClass(), jemptyStringPair);
int i = 0;
for (const auto& addon : addons) {
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 jprogramId) {
auto programId = EmulationSession::getProgramId(env, jprogramId);
auto program_id = EmulationSession::GetProgramId(env, jprogramId);
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(
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);
}
@ -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) {
EmulationSession::GetInstance().ContentProvider()->ClearAllEntries();
EmulationSession::GetInstance().GetContentProvider()->ClearAllEntries();
}
} // extern "C"

View File

@ -21,7 +21,7 @@ public:
static EmulationSession& GetInstance();
const Core::System& System() const;
Core::System& System();
FileSys::ManualContentProvider* ContentProvider();
FileSys::ManualContentProvider* GetContentProvider();
const EmuWindow_Android& Window() const;
EmuWindow_Android& Window();
@ -55,7 +55,7 @@ public:
static void OnEmulationStarted();
static u64 getProgramId(JNIEnv* env, jstring jprogramId);
static u64 GetProgramId(JNIEnv* env, jstring jprogramId);
private:
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_paused = false;
SoftwareKeyboard::AndroidKeyboard* m_software_keyboard{};
std::unique_ptr<Service::Account::ProfileManager> m_profile_manager;
std::unique_ptr<FileSys::ManualContentProvider> m_manual_provider;
// GPU driver parameters

View File

@ -21,13 +21,13 @@ std::unique_ptr<AndroidConfig> per_game_config;
template <typename T>
Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) {
auto key = GetJString(env, jkey);
auto basicSetting = Settings::values.linkage.by_key[key];
if (basicSetting != 0) {
return static_cast<Settings::Setting<T>*>(basicSetting);
auto basic_setting = Settings::values.linkage.by_key[key];
if (basic_setting != 0) {
return static_cast<Settings::Setting<T>*>(basic_setting);
}
auto basicAndroidSetting = AndroidSettings::values.linkage.by_key[key];
if (basicAndroidSetting != 0) {
return static_cast<Settings::Setting<T>*>(basicAndroidSetting);
auto basic_android_setting = AndroidSettings::values.linkage.by_key[key];
if (basic_android_setting != 0) {
return static_cast<Settings::Setting<T>*>(basic_android_setting);
}
LOG_ERROR(Frontend, "[Android Native] Could not find setting - {}", key);
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,
jstring jprogramId,
jstring jfileName) {
auto program_id = EmulationSession::getProgramId(env, jprogramId);
auto program_id = EmulationSession::GetProgramId(env, jprogramId);
auto file_name = GetJString(env, jfileName);
const auto config_file_name = program_id == 0 ? file_name : fmt::format("{:016X}", program_id);
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 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,
jstring jprogramId) {
auto program_id = EmulationSession::getProgramId(env, jprogramId);
auto program_id = EmulationSession::GetProgramId(env, jprogramId);
auto& disabledAddons = Settings::values.disabled_addons[program_id];
jobjectArray jdisabledAddonsArray =
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,
jstring jprogramId,
jobjectArray jdisabledAddons) {
auto program_id = EmulationSession::getProgramId(env, jprogramId);
auto program_id = EmulationSession::GetProgramId(env, jprogramId);
Settings::values.disabled_addons[program_id].clear();
std::vector<std::string> disabled_addons;
const int size = env->GetArrayLength(jdisabledAddons);

View File

@ -1,10 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
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:fillColor="?attr/colorControlNormal"
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>

View File

@ -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>

View File

@ -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>

View File

@ -314,6 +314,7 @@
<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_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="addons_game">Addons: %1$s</string>
<string name="save_data">Save data</string>
@ -334,7 +335,7 @@
<string name="addon_installed_successfully">Addon installed successfully</string>
<string name="verifying_content">Verifying content…</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 -->
<string name="loader_error_encrypted">Your ROM is encrypted</string>

View File

@ -256,7 +256,7 @@ struct Values {
AstcDecodeMode::CpuAsynchronous,
"accelerate_astc",
Category::Renderer};
Setting<VSyncMode, true> vsync_mode{
SwitchableSetting<VSyncMode, true> vsync_mode{
linkage, VSyncMode::Fifo, VSyncMode::Immediate, VSyncMode::FifoRelaxed,
"use_vsync", Category::Renderer, Specialization::RuntimeList, true,
true};

View File

@ -947,15 +947,19 @@ if (HAS_NCE)
set(CMAKE_ASM_FLAGS "${CFLAGS} -x assembler-with-cpp")
target_sources(core PRIVATE
arm/nce/arm_nce_asm_definitions.h
arm/nce/arm_nce.cpp
arm/nce/arm_nce.h
arm/nce/arm_nce.s
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.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()
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)

View File

@ -6,7 +6,7 @@
#include "common/signal_chain.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/core.h"
#include "core/memory.h"
@ -21,7 +21,8 @@ namespace Core {
namespace {
struct sigaction g_orig_action;
struct sigaction g_orig_bus_action;
struct sigaction g_orig_segv_action;
// Verify assembly offsets.
using NativeExecutionParameters = Kernel::KThread::NativeExecutionParameters;
@ -37,6 +38,9 @@ fpsimd_context* GetFloatingPointState(mcontext_t& host_ctx) {
return reinterpret_cast<fpsimd_context*>(header);
}
using namespace Common::Literals;
constexpr u32 StackSize = 32_KiB;
} // namespace
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);
}
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* 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.
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;
}
void ArmNce::HandleHostFault(int sig, void* raw_info, void* raw_context) {
return g_orig_action.sa_sigaction(sig, static_cast<siginfo_t*>(raw_info), raw_context);
bool ArmNce::HandleGuestAlignmentFault(GuestContext* guest_ctx, void* raw_info, void* 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) {
@ -225,18 +256,31 @@ ArmNce::ArmNce(System& system, bool uses_wall_clock, std::size_t core_index)
ArmNce::~ArmNce() = default;
void ArmNce::Initialize() {
m_thread_id = gettid();
if (m_thread_id == -1) {
m_thread_id = gettid();
}
// Setup our signals
static std::once_flag signals;
std::call_once(signals, [] {
// Configure signal stack.
if (!m_stack) {
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);
sigset_t signal_mask;
sigemptyset(&signal_mask);
sigaddset(&signal_mask, ReturnToRunCodeByExceptionLevelChangeSignal);
sigaddset(&signal_mask, BreakFromRunCodeSignal);
sigaddset(&signal_mask, GuestFaultSignal);
sigaddset(&signal_mask, GuestAlignmentFaultSignal);
sigaddset(&signal_mask, GuestAccessFaultSignal);
struct sigaction return_to_run_code_action {};
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;
Common::SigAction(BreakFromRunCodeSignal, &break_from_run_code_action, nullptr);
struct sigaction fault_action {};
fault_action.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_RESTART;
fault_action.sa_sigaction = reinterpret_cast<HandlerType>(&ArmNce::GuestFaultSignalHandler);
fault_action.sa_mask = signal_mask;
Common::SigAction(GuestFaultSignal, &fault_action, &g_orig_action);
struct sigaction alignment_fault_action {};
alignment_fault_action.sa_flags = SA_SIGINFO | SA_ONSTACK;
alignment_fault_action.sa_sigaction =
reinterpret_cast<HandlerType>(&ArmNce::GuestAlignmentFaultSignalHandler);
alignment_fault_action.sa_mask = signal_mask;
Common::SigAction(GuestAlignmentFaultSignal, &alignment_fault_action, nullptr);
// Simplify call for g_orig_action.
// These fields occupy the same space in memory, so this should be a no-op in practice.
if (!(g_orig_action.sa_flags & SA_SIGINFO)) {
g_orig_action.sa_sigaction =
reinterpret_cast<decltype(g_orig_action.sa_sigaction)>(g_orig_action.sa_handler);
}
struct sigaction access_fault_action {};
access_fault_action.sa_flags = SA_SIGINFO | SA_ONSTACK | SA_RESTART;
access_fault_action.sa_sigaction =
reinterpret_cast<HandlerType>(&ArmNce::GuestAccessFaultSignalHandler);
access_fault_action.sa_mask = signal_mask;
Common::SigAction(GuestAccessFaultSignal, &access_fault_action, &g_orig_segv_action);
});
}

View File

@ -61,7 +61,8 @@ private:
static void ReturnToRunCodeByExceptionLevelChangeSignalHandler(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 UnlockThreadParameters(void* tpidr);
@ -70,8 +71,11 @@ private:
// C++ implementation functions for assembly definitions.
static void* RestoreGuestContext(void* raw_context);
static void SaveGuestContext(GuestContext* ctx, void* raw_context);
static bool HandleGuestFault(GuestContext* ctx, void* info, void* raw_context);
static void HandleHostFault(int sig, void* info, void* raw_context);
static bool HandleFailedGuestFault(GuestContext* ctx, 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:
Core::System& m_system;
@ -83,6 +87,9 @@ public:
// Core context.
GuestContext m_guest_ctx{};
Kernel::KThread* m_running_thread{};
// Stack for signal processing.
std::unique_ptr<u8[]> m_stack{};
};
} // namespace Core

View File

@ -130,11 +130,11 @@ _ZN4Core6ArmNce29BreakFromRunCodeSignalHandlerEiPvS1_:
ret
/* static void Core::ArmNce::GuestFaultSignalHandler(int sig, void* info, void* raw_context) */
.section .text._ZN4Core6ArmNce23GuestFaultSignalHandlerEiPvS1_, "ax", %progbits
.global _ZN4Core6ArmNce23GuestFaultSignalHandlerEiPvS1_
.type _ZN4Core6ArmNce23GuestFaultSignalHandlerEiPvS1_, %function
_ZN4Core6ArmNce23GuestFaultSignalHandlerEiPvS1_:
/* static void Core::ArmNce::GuestAlignmentFaultSignalHandler(int sig, void* info, void* raw_context) */
.section .text._ZN4Core6ArmNce32GuestAlignmentFaultSignalHandlerEiPvS1_, "ax", %progbits
.global _ZN4Core6ArmNce32GuestAlignmentFaultSignalHandlerEiPvS1_
.type _ZN4Core6ArmNce32GuestAlignmentFaultSignalHandlerEiPvS1_, %function
_ZN4Core6ArmNce32GuestAlignmentFaultSignalHandlerEiPvS1_:
/* Check to see if we have the correct TLS magic. */
mrs x8, tpidr_el0
ldr w9, [x8, #(TpidrEl0TlsMagic)]
@ -146,7 +146,7 @@ _ZN4Core6ArmNce23GuestFaultSignalHandlerEiPvS1_:
/* Incorrect TLS magic, so this is a host fault. */
/* Tail call the handler. */
b _ZN4Core6ArmNce15HandleHostFaultEiPvS1_
b _ZN4Core6ArmNce24HandleHostAlignmentFaultEiPvS1_
1:
/* Correct TLS magic, so this is a guest fault. */
@ -163,7 +163,53 @@ _ZN4Core6ArmNce23GuestFaultSignalHandlerEiPvS1_:
msr tpidr_el0, x3
/* 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. */
cbz x0, 2f

View File

@ -10,7 +10,8 @@
#define ReturnToRunCodeByExceptionLevelChangeSignal SIGUSR2
#define BreakFromRunCodeSignal SIGURG
#define GuestFaultSignal SIGSEGV
#define GuestAccessFaultSignal SIGSEGV
#define GuestAlignmentFaultSignal SIGBUS
#define GuestContextSp 0xF8
#define GuestContextHostContext 0x320

View 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

View 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

File diff suppressed because it is too large Load Diff

View File

@ -36,6 +36,7 @@
#include "core/hle/kernel/k_scheduler.h"
#include "core/hle/kernel/kernel.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/apm/apm_controller.h"
#include "core/hle/service/filesystem/filesystem.h"
@ -130,8 +131,8 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
struct System::Impl {
explicit Impl(System& system)
: kernel{system}, fs_controller{system}, memory{system}, hid_core{}, room_network{},
cpu_manager{system}, reporter{system}, applet_manager{system}, time_manager{system},
gpu_dirty_memory_write_manager{} {
cpu_manager{system}, reporter{system}, applet_manager{system}, profile_manager{},
time_manager{system}, gpu_dirty_memory_write_manager{} {
memory.SetGPUDirtyManagers(gpu_dirty_memory_write_manager);
}
@ -532,6 +533,7 @@ struct System::Impl {
/// Service State
Service::Glue::ARPManager arp_manager;
Service::Account::ProfileManager profile_manager;
Service::Time::TimeManager time_manager;
/// Service manager
@ -921,6 +923,14 @@ const Service::APM::Controller& System::GetAPMController() const {
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() {
return impl->time_manager;
}

View File

@ -45,6 +45,10 @@ class Memory;
namespace Service {
namespace Account {
class ProfileManager;
} // namespace Account
namespace AM::Applets {
struct AppletFrontendSet;
class AppletManager;
@ -383,6 +387,9 @@ public:
[[nodiscard]] Service::APM::Controller& GetAPMController();
[[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]] const Service::Time::TimeManager& GetTimeManager() const;

View File

@ -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() {
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);
}
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

View File

@ -28,6 +28,8 @@ public:
Result RegisterAppletResourceUserId(u64 aruid, bool enable_input);
void UnregisterAppletResourceUserId(u64 aruid);
void FreeAppletResourceId(u64 aruid);
u64 GetActiveAruid();
Result GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle, u64 aruid);
@ -42,6 +44,9 @@ public:
void SetIsPalmaConnectable(u64 aruid, bool is_connectable);
void EnablePalmaBoostMode(u64 aruid, bool is_enabled);
Result RegisterCoreAppletResource();
Result UnregisterCoreAppletResource();
private:
static constexpr std::size_t AruidIndexMax = 0x20;
@ -81,6 +86,7 @@ private:
u64 active_aruid{};
AruidRegisterList registration_list{};
std::array<AruidData, AruidIndexMax> data{};
s32 ref_counter{};
Core::System& system;
};

View File

@ -20,6 +20,9 @@ constexpr Result InvalidNpadId{ErrorModule::HID, 709};
constexpr Result NpadNotConnected{ErrorModule::HID, 710};
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 ResultAruidAlreadyRegistered{ErrorModule::HID, 1046};
constexpr Result ResultAruidNotRegistered{ErrorModule::HID, 1047};

View File

@ -222,16 +222,14 @@ void IHidServer::CreateAppletResource(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
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);
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};
rb.Push(result);
rb.PushIpcInterface<IAppletResource>(system, resource_manager);
rb.PushIpcInterface<IAppletResource>(system, resource_manager, applet_resource_user_id);
}
void IHidServer::ActivateDebugPad(HLERequestContext& ctx) {

View File

@ -146,10 +146,36 @@ std::shared_ptr<UniquePad> ResourceManager::GetUniquePad() const {
}
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};
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) {
std::scoped_lock lock{shared_mutex};
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);
}
void ResourceManager::FreeAppletResourceId(u64 aruid) {
std::scoped_lock lock{shared_mutex};
applet_resource->FreeAppletResourceId(aruid);
}
void ResourceManager::EnableInput(u64 aruid, bool is_enabled) {
std::scoped_lock lock{shared_mutex};
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);
}
IAppletResource::IAppletResource(Core::System& system_, std::shared_ptr<ResourceManager> resource)
: ServiceFramework{system_, "IAppletResource"}, resource_manager{resource} {
IAppletResource::IAppletResource(Core::System& system_, std::shared_ptr<ResourceManager> resource,
u64 applet_resource_user_id)
: ServiceFramework{system_, "IAppletResource"}, aruid{applet_resource_user_id},
resource_manager{resource} {
static const FunctionInfo functions[] = {
{0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"},
};
@ -274,14 +307,14 @@ IAppletResource::~IAppletResource() {
system.CoreTiming().UnscheduleEvent(default_update_event, 0);
system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event, 0);
system.CoreTiming().UnscheduleEvent(motion_update_event, 0);
resource_manager->FreeAppletResourceId(aruid);
}
void IAppletResource::GetSharedMemoryHandle(HLERequestContext& ctx) {
LOG_DEBUG(Service_HID, "called");
Kernel::KSharedMemory* handle;
const u64 applet_resource_user_id = resource_manager->GetAppletResource()->GetActiveAruid();
const auto result = resource_manager->GetSharedMemoryHandle(&handle, applet_resource_user_id);
const auto result = resource_manager->GetSharedMemoryHandle(&handle, aruid);
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, result=0x{:X}", aruid, result.raw);
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(result);

View File

@ -66,10 +66,13 @@ public:
Result CreateAppletResource(u64 aruid);
Result RegisterCoreAppletResource();
Result UnregisterCoreAppletResource();
Result RegisterAppletResourceUserId(u64 aruid, bool bool_value);
void UnregisterAppletResourceUserId(u64 aruid);
Result GetSharedMemoryHandle(Kernel::KSharedMemory** out_handle, u64 aruid);
void FreeAppletResourceId(u64 aruid);
void EnableInput(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);
private:
Result CreateAppletResourceImpl(u64 aruid);
bool is_initialized{false};
mutable std::mutex shared_mutex;
@ -121,7 +126,8 @@ private:
class IAppletResource final : public ServiceFramework<IAppletResource> {
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;
private:
@ -132,6 +138,7 @@ private:
std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event;
std::shared_ptr<Core::Timing::EventType> motion_update_event;
u64 aruid;
std::shared_ptr<ResourceManager> resource_manager;
};

View File

@ -14,6 +14,8 @@
#include "common/fs/path_util.h"
#include "common/string_util.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/main.h"
#include "yuzu/util/controller_navigation.h"
@ -47,9 +49,9 @@ QPixmap GetIcon(Common::UUID uuid) {
} // Anonymous namespace
QtProfileSelectionDialog::QtProfileSelectionDialog(
Core::HID::HIDCore& hid_core, QWidget* parent,
Core::System& system, QWidget* parent,
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;
instruction_label = new QLabel();
@ -68,7 +70,7 @@ QtProfileSelectionDialog::QtProfileSelectionDialog(
tree_view = new QTreeView;
item_model = new QStandardItemModel(tree_view);
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->setSelectionMode(QHeaderView::SingleSelection);
@ -106,10 +108,10 @@ QtProfileSelectionDialog::QtProfileSelectionDialog(
SelectUser(tree_view->currentIndex());
});
const auto& profiles = profile_manager->GetAllUsers();
const auto& profiles = profile_manager.GetAllUsers();
for (const auto& user : profiles) {
Service::Account::ProfileBase profile{};
if (!profile_manager->GetProfileBase(user, profile))
if (!profile_manager.GetProfileBase(user, profile))
continue;
const auto username = Common::StringFromFixedZeroTerminatedBuffer(
@ -134,7 +136,7 @@ QtProfileSelectionDialog::~QtProfileSelectionDialog() {
int QtProfileSelectionDialog::exec() {
// Skip profile selection when there's only one.
if (profile_manager->GetUserCount() == 1) {
if (profile_manager.GetUserCount() == 1) {
user_index = 0;
return QDialog::Accepted;
}

View File

@ -7,7 +7,6 @@
#include <QDialog>
#include <QList>
#include "core/frontend/applets/profile_select.h"
#include "core/hle/service/acc/profile_manager.h"
class ControllerNavigation;
class GMainWindow;
@ -20,15 +19,19 @@ class QStandardItemModel;
class QTreeView;
class QVBoxLayout;
namespace Core::HID {
class HIDCore;
} // namespace Core::HID
namespace Core {
class System;
}
namespace Service::Account {
class ProfileManager;
}
class QtProfileSelectionDialog final : public QDialog {
Q_OBJECT
public:
explicit QtProfileSelectionDialog(Core::HID::HIDCore& hid_core, QWidget* parent,
explicit QtProfileSelectionDialog(Core::System& system, QWidget* parent,
const Core::Frontend::ProfileSelectParameters& parameters);
~QtProfileSelectionDialog() override;
@ -58,7 +61,7 @@ private:
QScrollArea* scroll_area;
QDialogButtonBox* buttons;
std::unique_ptr<Service::Account::ProfileManager> profile_manager;
Service::Account::ProfileManager& profile_manager;
ControllerNavigation* controller_navigation = nullptr;
};

View File

@ -381,7 +381,7 @@
<item row="5" column="0">
<widget class="QCheckBox" name="disable_buffer_reorder">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;When checked, disables reording of mapped memory uploads which allows to associate uploads with specific draws. May reduce performance in some cases.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;When checked, disables reordering of mapped memory uploads which allows to associate uploads with specific draws. May reduce performance in some cases.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Disable Buffer Reorder</string>

View File

@ -76,9 +76,9 @@ QString GetProfileUsernameFromUser(QWidget* parent, const QString& description_t
}
} // 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>()},
profile_manager(std::make_unique<Service::Account::ProfileManager>()), system{system_} {
profile_manager{system_.GetProfileManager()}, system{system_} {
ui->setupUi(this);
tree_view = new QTreeView;
@ -149,10 +149,10 @@ void ConfigureProfileManager::SetConfiguration() {
}
void ConfigureProfileManager::PopulateUserList() {
const auto& profiles = profile_manager->GetAllUsers();
const auto& profiles = profile_manager.GetAllUsers();
for (const auto& user : profiles) {
Service::Account::ProfileBase profile{};
if (!profile_manager->GetProfileBase(user, profile))
if (!profile_manager.GetProfileBase(user, profile))
continue;
const auto username = Common::StringFromFixedZeroTerminatedBuffer(
@ -167,11 +167,11 @@ void ConfigureProfileManager::PopulateUserList() {
}
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);
const auto username = GetAccountUsername(*profile_manager, *current_user);
const auto username = GetAccountUsername(profile_manager, *current_user);
scene->clear();
scene->addPixmap(
@ -187,11 +187,11 @@ void ConfigureProfileManager::ApplyConfiguration() {
void ConfigureProfileManager::SelectUser(const QModelIndex& index) {
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();
ui->pm_remove->setEnabled(profile_manager->GetUserCount() >= 2);
ui->pm_remove->setEnabled(profile_manager.GetUserCount() >= 2);
ui->pm_rename->setEnabled(true);
ui->pm_set_image->setEnabled(true);
}
@ -204,18 +204,18 @@ void ConfigureProfileManager::AddUser() {
}
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)});
}
void ConfigureProfileManager::RenameUser() {
const auto user = tree_view->currentIndex().row();
const auto uuid = profile_manager->GetUser(user);
const auto uuid = profile_manager.GetUser(user);
ASSERT(uuid);
Service::Account::ProfileBase profile{};
if (!profile_manager->GetProfileBase(*uuid, profile))
if (!profile_manager.GetProfileBase(*uuid, profile))
return;
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::copy(username_std.begin(), username_std.end(), profile.username.begin());
profile_manager->SetProfileBase(*uuid, profile);
profile_manager.SetProfileBase(*uuid, profile);
item_model->setItem(
user, 0,
@ -238,9 +238,9 @@ void ConfigureProfileManager::RenameUser() {
void ConfigureProfileManager::ConfirmDeleteUser() {
const auto index = tree_view->currentIndex().row();
const auto uuid = profile_manager->GetUser(index);
const auto uuid = profile_manager.GetUser(index);
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->show();
@ -252,7 +252,7 @@ void ConfigureProfileManager::DeleteUser(const Common::UUID& uuid) {
}
UpdateCurrentUser();
if (!profile_manager->RemoveUser(uuid)) {
if (!profile_manager.RemoveUser(uuid)) {
return;
}
@ -265,7 +265,7 @@ void ConfigureProfileManager::DeleteUser(const Common::UUID& uuid) {
void ConfigureProfileManager::SetUserImage() {
const auto index = tree_view->currentIndex().row();
const auto uuid = profile_manager->GetUser(index);
const auto uuid = profile_manager.GetUser(index);
ASSERT(uuid);
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,
new QStandardItem{GetIcon(*uuid), FormatUserEntryText(username, *uuid)});
UpdateCurrentUser();

View File

@ -52,7 +52,7 @@ class ConfigureProfileManager : public QWidget {
Q_OBJECT
public:
explicit ConfigureProfileManager(const Core::System& system_, QWidget* parent = nullptr);
explicit ConfigureProfileManager(Core::System& system_, QWidget* parent = nullptr);
~ConfigureProfileManager() override;
void ApplyConfiguration();
@ -85,7 +85,6 @@ private:
std::unique_ptr<Ui::ConfigureProfileManager> ui;
bool enabled = false;
std::unique_ptr<Service::Account::ProfileManager> profile_manager;
Service::Account::ProfileManager& profile_manager;
const Core::System& system;
};

View File

@ -346,7 +346,7 @@ GMainWindow::GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulk
SetDiscordEnabled(UISettings::values.enable_discord_presence.GetValue());
discord_rpc->Update();
play_time_manager = std::make_unique<PlayTime::PlayTimeManager>();
play_time_manager = std::make_unique<PlayTime::PlayTimeManager>(system->GetProfileManager());
system->GetRoomNetwork().Init();
@ -526,8 +526,7 @@ GMainWindow::GMainWindow(std::unique_ptr<QtConfig> config_, bool has_broken_vulk
continue;
}
const Service::Account::ProfileManager manager;
if (!manager.UserExistsIndex(selected_user)) {
if (!system->GetProfileManager().UserExistsIndex(selected_user)) {
LOG_ERROR(Frontend, "Selected user doesn't exist");
continue;
}
@ -691,7 +690,7 @@ void GMainWindow::ControllerSelectorRequestExit() {
void GMainWindow::ProfileSelectorSelectProfile(
const Core::Frontend::ProfileSelectParameters& parameters) {
profile_select_applet = new QtProfileSelectionDialog(system->HIDCore(), this, parameters);
profile_select_applet = new QtProfileSelectionDialog(*system, this, parameters);
SCOPE_EXIT({
profile_select_applet->deleteLater();
profile_select_applet = nullptr;
@ -706,8 +705,8 @@ void GMainWindow::ProfileSelectorSelectProfile(
return;
}
const Service::Account::ProfileManager manager;
const auto uuid = manager.GetUser(static_cast<std::size_t>(profile_select_applet->GetIndex()));
const auto uuid = system->GetProfileManager().GetUser(
static_cast<std::size_t>(profile_select_applet->GetIndex()));
if (!uuid.has_value()) {
emit ProfileSelectorFinishedSelection(std::nullopt);
return;
@ -1856,7 +1855,7 @@ bool GMainWindow::LoadROM(const QString& filename, u64 program_id, std::size_t p
bool GMainWindow::SelectAndSetCurrentUser(
const Core::Frontend::ProfileSelectParameters& parameters) {
QtProfileSelectionDialog dialog(system->HIDCore(), this, parameters);
QtProfileSelectionDialog dialog(*system, this, parameters);
dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
dialog.setWindowModality(Qt::WindowModal);
@ -2271,7 +2270,7 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
.display_options = {},
.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 |
Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
dialog.setWindowModality(Qt::WindowModal);
@ -2288,8 +2287,8 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
return;
}
Service::Account::ProfileManager manager;
const auto user_id = manager.GetUser(static_cast<std::size_t>(index));
const auto user_id =
system->GetProfileManager().GetUser(static_cast<std::size_t>(index));
ASSERT(user_id);
const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath(

View File

@ -27,9 +27,9 @@
Lobby::Lobby(QWidget* parent, QStandardItemModel* list,
std::shared_ptr<Core::AnnounceMultiplayerSession> session, Core::System& system_)
: QDialog(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint),
ui(std::make_unique<Ui::Lobby>()), announce_multiplayer_session(session),
profile_manager(std::make_unique<Service::Account::ProfileManager>()), system{system_},
room_network{system.GetRoomNetwork()} {
ui(std::make_unique<Ui::Lobby>()),
announce_multiplayer_session(session), system{system_}, room_network{
system.GetRoomNetwork()} {
ui->setupUi(this);
// setup the watcher for background connections
@ -299,14 +299,15 @@ void Lobby::OnRefreshLobby() {
}
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{};
if (!current_user.has_value()) {
return "";
}
if (!profile_manager->GetProfileBase(*current_user, profile)) {
if (!system.GetProfileManager().GetProfileBase(*current_user, profile)) {
return "";
}

View File

@ -24,10 +24,6 @@ namespace Core {
class System;
}
namespace Service::Account {
class ProfileManager;
}
/**
* 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.
@ -103,7 +99,6 @@ private:
QFutureWatcher<AnnounceMultiplayerRoom::RoomList> room_list_watcher;
std::weak_ptr<Core::AnnounceMultiplayerSession> announce_multiplayer_session;
std::unique_ptr<Service::Account::ProfileManager> profile_manager;
QFutureWatcher<void>* watcher;
Validation validation;
Core::System& system;

View File

@ -20,8 +20,8 @@ struct PlayTimeElement {
PlayTime play_time;
};
std::optional<std::filesystem::path> GetCurrentUserPlayTimePath() {
const Service::Account::ProfileManager manager;
std::optional<std::filesystem::path> GetCurrentUserPlayTimePath(
const Service::Account::ProfileManager& manager) {
const auto uuid = manager.GetUser(static_cast<s32>(Settings::values.current_user));
if (!uuid.has_value()) {
return std::nullopt;
@ -30,8 +30,9 @@ std::optional<std::filesystem::path> GetCurrentUserPlayTimePath() {
uuid->RawString().append(".bin");
}
[[nodiscard]] bool ReadPlayTimeFile(PlayTimeDatabase& out_play_time_db) {
const auto filename = GetCurrentUserPlayTimePath();
[[nodiscard]] bool ReadPlayTimeFile(PlayTimeDatabase& out_play_time_db,
const Service::Account::ProfileManager& manager) {
const auto filename = GetCurrentUserPlayTimePath(manager);
if (!filename.has_value()) {
LOG_ERROR(Frontend, "Failed to get current user path");
@ -66,8 +67,9 @@ std::optional<std::filesystem::path> GetCurrentUserPlayTimePath() {
return true;
}
[[nodiscard]] bool WritePlayTimeFile(const PlayTimeDatabase& play_time_db) {
const auto filename = GetCurrentUserPlayTimePath();
[[nodiscard]] bool WritePlayTimeFile(const PlayTimeDatabase& play_time_db,
const Service::Account::ProfileManager& manager) {
const auto filename = GetCurrentUserPlayTimePath(manager);
if (!filename.has_value()) {
LOG_ERROR(Frontend, "Failed to get current user path");
@ -96,8 +98,9 @@ std::optional<std::filesystem::path> GetCurrentUserPlayTimePath() {
} // namespace
PlayTimeManager::PlayTimeManager() {
if (!ReadPlayTimeFile(database)) {
PlayTimeManager::PlayTimeManager(Service::Account::ProfileManager& profile_manager)
: manager{profile_manager} {
if (!ReadPlayTimeFile(database, manager)) {
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() {
if (!WritePlayTimeFile(database)) {
if (!WritePlayTimeFile(database, manager)) {
LOG_ERROR(Frontend, "Failed to update play time database!");
}
}

View File

@ -11,6 +11,10 @@
#include "common/common_types.h"
#include "common/polyfill_thread.h"
namespace Service::Account {
class ProfileManager;
}
namespace PlayTime {
using ProgramId = u64;
@ -19,7 +23,7 @@ using PlayTimeDatabase = std::map<ProgramId, PlayTime>;
class PlayTimeManager {
public:
explicit PlayTimeManager();
explicit PlayTimeManager(Service::Account::ProfileManager& profile_manager);
~PlayTimeManager();
YUZU_NON_COPYABLE(PlayTimeManager);
@ -32,11 +36,13 @@ public:
void Stop();
private:
void AutoTimestamp(std::stop_token stop_token);
void Save();
PlayTimeDatabase database;
u64 running_program_id;
std::jthread play_time_thread;
void AutoTimestamp(std::stop_token stop_token);
void Save();
Service::Account::ProfileManager& manager;
};
QString ReadablePlayTime(qulonglong time_seconds);

View File

@ -1,7 +1,7 @@
{
"$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json",
"name": "yuzu",
"builtin-baseline": "cbf56573a987527b39272e88cbdd11389b78c6e4",
"builtin-baseline": "a42af01b72c28a8e1d7b48107b33e4f286a55ef6",
"version": "1.0",
"dependencies": [
"boost-algorithm",
@ -50,7 +50,7 @@
},
{
"name": "fmt",
"version": "10.0.0"
"version": "10.1.1"
}
]
}