From ffb9e743dd0bf164e9b1afd950d52e919b7e2a79 Mon Sep 17 00:00:00 2001 From: Willi Ye Date: Sun, 19 Jul 2020 22:35:50 +0200 Subject: [PATCH] Add profile service to support custom usernames --- app/CMakeLists.txt | 1 + app/src/main/cpp/skyline/common.h | 1 + .../account/IAccountServiceForApplication.cpp | 12 +++++ .../account/IAccountServiceForApplication.h | 11 ++++- .../cpp/skyline/services/account/IProfile.cpp | 24 +++++++++ .../cpp/skyline/services/account/IProfile.h | 41 ++++++++++++++++ .../main/cpp/skyline/services/base_service.h | 1 + .../preference/CustomEditTextPreference.kt | 49 +++++++++++++++++++ app/src/main/res/values/attrs.xml | 6 +++ app/src/main/res/values/strings.xml | 2 + app/src/main/res/xml/preferences.xml | 5 ++ 11 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 app/src/main/cpp/skyline/services/account/IProfile.cpp create mode 100644 app/src/main/cpp/skyline/services/account/IProfile.h create mode 100644 app/src/main/java/emu/skyline/preference/CustomEditTextPreference.kt create mode 100644 app/src/main/res/values/attrs.xml diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 4582d73e..bc88b3b1 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -118,6 +118,7 @@ add_library(skyline SHARED ${source_DIR}/skyline/services/lm/ILogger.cpp ${source_DIR}/skyline/services/account/IAccountServiceForApplication.cpp ${source_DIR}/skyline/services/account/IManagerForApplication.cpp + ${source_DIR}/skyline/services/account/IProfile.cpp ${source_DIR}/skyline/services/friends/IServiceCreator.cpp ${source_DIR}/skyline/services/friends/IFriendService.cpp ${source_DIR}/skyline/services/nfp/IUserManager.cpp diff --git a/app/src/main/cpp/skyline/common.h b/app/src/main/cpp/skyline/common.h index c2414c41..705e2873 100644 --- a/app/src/main/cpp/skyline/common.h +++ b/app/src/main/cpp/skyline/common.h @@ -54,6 +54,7 @@ namespace skyline { constexpr u32 MaxHandles = 0xEE01; //!< "Too many handles" constexpr u32 NotFound = 0xF201; //!< "Not found" constexpr u32 Unimpl = 0x177202; //!< "Unimplemented behaviour" + constexpr u32 InvArg = 0x2c7c; //!< "Argument is invalid" } }; diff --git a/app/src/main/cpp/skyline/services/account/IAccountServiceForApplication.cpp b/app/src/main/cpp/skyline/services/account/IAccountServiceForApplication.cpp index 545de332..b745ab95 100644 --- a/app/src/main/cpp/skyline/services/account/IAccountServiceForApplication.cpp +++ b/app/src/main/cpp/skyline/services/account/IAccountServiceForApplication.cpp @@ -3,11 +3,13 @@ #include "IManagerForApplication.h" #include "IAccountServiceForApplication.h" +#include "IProfile.h" namespace skyline::service::account { IAccountServiceForApplication::IAccountServiceForApplication(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager, Service::account_IAccountServiceForApplication, "account:IAccountServiceForApplication", { {0x1, SFUNC(IAccountServiceForApplication::GetUserExistence)}, {0x4, SFUNC(IAccountServiceForApplication::GetLastOpenedUser)}, + {0x5, SFUNC(IAccountServiceForApplication::GetProfile)}, {0x64, SFUNC(IAccountServiceForApplication::InitializeApplicationInfoV0)}, {0x65, SFUNC(IAccountServiceForApplication::GetBaasAccountManagerForApplication)} }) {} @@ -24,6 +26,16 @@ namespace skyline::service::account { void IAccountServiceForApplication::InitializeApplicationInfoV0(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {} + void IAccountServiceForApplication::GetProfile(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + auto id = request.Pop(); + if (id != constant::DefaultUserId) { + response.errorCode = constant::status::InvArg; + return; + } + + manager.RegisterService(std::make_shared(state, manager, id), session, response); + } + void IAccountServiceForApplication::GetBaasAccountManagerForApplication(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { manager.RegisterService(SRVREG(IManagerForApplication), session, response); } diff --git a/app/src/main/cpp/skyline/services/account/IAccountServiceForApplication.h b/app/src/main/cpp/skyline/services/account/IAccountServiceForApplication.h index 31688b7c..05104940 100644 --- a/app/src/main/cpp/skyline/services/account/IAccountServiceForApplication.h +++ b/app/src/main/cpp/skyline/services/account/IAccountServiceForApplication.h @@ -19,9 +19,13 @@ namespace skyline { * @param userId The user ID to compare with * @return Whether this user ID matches the one given as a parameter */ - inline constexpr bool operator==(const UserId& userId) { + inline constexpr bool operator==(const UserId &userId) { return upper == userId.upper && lower == userId.lower; } + + inline constexpr bool operator!=(const UserId &userId) { + return !(*this == userId); + } }; /** * @brief IAccountServiceForApplication or acc:u0 provides functions for reading user information (https://switchbrew.org/wiki/Account_services#acc:u0) @@ -45,6 +49,11 @@ namespace skyline { */ void InitializeApplicationInfoV0(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + /** + * @brief This returns a handle to an IProfile which can be used for reading user information + */ + void GetProfile(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + /** * @brief This returns a handle to an IManagerForApplication which can be used for reading Nintendo Online info */ diff --git a/app/src/main/cpp/skyline/services/account/IProfile.cpp b/app/src/main/cpp/skyline/services/account/IProfile.cpp new file mode 100644 index 00000000..8b366de8 --- /dev/null +++ b/app/src/main/cpp/skyline/services/account/IProfile.cpp @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#include +#include "IProfile.h" + +namespace skyline::service::account { + IProfile::IProfile(const DeviceState &state, ServiceManager &manager, const UserId &userId) : BaseService(state, manager, Service::account_IProfile, "account:IProfile", { + {0x0, SFUNC(IProfile::Get)} + }) {} + + void IProfile::Get(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { + AccountUserData userData{}; + AccountProfileBase profileBase{}; + + std::string username = state.settings->GetString("username_value"); + size_t usernameSize = std::min(sizeof(profileBase), username.size()); + std::memcpy(profileBase.nickname, username.c_str(), usernameSize); + profileBase.nickname[usernameSize] = '\0'; + + state.process->WriteMemory(userData, request.outputBuf.at(0).address); + response.Push(profileBase); + } +} diff --git a/app/src/main/cpp/skyline/services/account/IProfile.h b/app/src/main/cpp/skyline/services/account/IProfile.h new file mode 100644 index 00000000..b8f16252 --- /dev/null +++ b/app/src/main/cpp/skyline/services/account/IProfile.h @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#pragma once + +#include "IAccountServiceForApplication.h" +#include + +namespace skyline::service::account { + + /// UserData + typedef struct { + u32 unk_x0; ///< Unknown. + u32 iconID; ///< Icon ID. 0 = Mii, the rest are character icon IDs. + u8 iconBackgroundColorID; ///< Profile icon background color ID + u8 unk_x9[0x7]; ///< Unknown. + u8 miiID[0x10]; ///< Some ID related to the Mii? All zeros when a character icon is used. + u8 unk_x20[0x60]; ///< Usually zeros? + } AccountUserData; + + /// ProfileBase + typedef struct { + UserId uid; ///< \ref AccountUid + u64 lastEditTimestamp; ///< POSIX UTC timestamp, for the last account edit. + char nickname[0x20]; ///< UTF-8 Nickname. + } AccountProfileBase; + + /** + * @brief IProfile provides functions for reading user profile (https://switchbrew.org/wiki/Account_services#IProfile) + */ + class IProfile : public BaseService { + public: + IProfile(const DeviceState &state, ServiceManager &manager, const UserId &userId); + + private: + /** + * @brief This returns AccountUserData (optional) and AccountProfileBase + */ + void Get(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); + }; +} diff --git a/app/src/main/cpp/skyline/services/base_service.h b/app/src/main/cpp/skyline/services/base_service.h index 69fa6a4e..861a7fa4 100644 --- a/app/src/main/cpp/skyline/services/base_service.h +++ b/app/src/main/cpp/skyline/services/base_service.h @@ -73,6 +73,7 @@ namespace skyline::service { lm_ILogger, account_IAccountServiceForApplication, account_IManagerForApplication, + account_IProfile, friends_IServiceCreator, friends_IFriendService, nfp_IUserManager, diff --git a/app/src/main/java/emu/skyline/preference/CustomEditTextPreference.kt b/app/src/main/java/emu/skyline/preference/CustomEditTextPreference.kt new file mode 100644 index 00000000..c20efe41 --- /dev/null +++ b/app/src/main/java/emu/skyline/preference/CustomEditTextPreference.kt @@ -0,0 +1,49 @@ +/* + * SPDX-License-Identifier: MPL-2.0 + * Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) + */ + +package emu.skyline.preference + +import android.content.Context +import android.text.InputFilter +import android.text.InputFilter.LengthFilter +import android.util.AttributeSet +import androidx.preference.EditTextPreference +import emu.skyline.R + +/** + * [EditTextPreference] lacks the feature to set the automatically value as summary. + * This class adds this missing thing. Also added useful attributes. + */ +class CustomEditTextPreference : EditTextPreference { + + constructor(context : Context, attrs : AttributeSet?, defStyleAttr : Int, defStyleRes : Int) : super(context, attrs, defStyleAttr, defStyleRes) { + attrs?.let { + val a = context.obtainStyledAttributes(it, R.styleable.CustomEditTextPreference, defStyleAttr, 0) + val limit = a.getInt(R.styleable.CustomEditTextPreference_limit, -1) + a.recycle() + + if (limit >= 0) { + setOnBindEditTextListener { editText -> editText.filters = arrayOf(LengthFilter(limit)) } + } + } + + setOnPreferenceChangeListener { _, newValue -> + summary = newValue.toString() + true + } + } + + constructor(context : Context, attrs : AttributeSet?, defStyleAttr : Int) : this(context, attrs, defStyleAttr, 0) + + constructor(context : Context, attrs : AttributeSet?) : this(context, attrs, androidx.preference.R.attr.editTextPreferenceStyle) + + constructor(context : Context) : this(context, null) + + override fun onAttached() { + super.onAttached() + + summary = text + } +} \ No newline at end of file diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml new file mode 100644 index 00000000..93e97d41 --- /dev/null +++ b/app/src/main/res/values/attrs.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c8bc2389..66eeb588 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -38,6 +38,8 @@ The system will emulate being in handheld mode The system will emulate being in docked mode Theme + Username + @string/app_name Licenses The license of Skyline (MPL 2.0) diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index da7cc221..22fdf007 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -62,6 +62,11 @@ android:summaryOn="@string/log_compact_desc_on" app:key="log_compact" app:title="@string/log_compact" /> +